During flush store GrOp draw and upload records on GrOpFlushState instead of on the ops themselves.
Bug: skia:
Change-Id: Id99267d9e7762829a3f9bebce0e92e7b97a092f8
Reviewed-on: https://skia-review.googlesource.com/66680
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 9b15f3c..66cb6cc 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -193,7 +193,7 @@
}
// Upload all data to the GPU
- fFlushState.preIssueDraws();
+ fFlushState.preExecuteDraws();
// Execute the onFlush op lists first, if any.
for (sk_sp<GrOpList>& onFlushOpList : onFlushOpLists) {
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index fa70a05..6ac5d13 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -12,13 +12,29 @@
#include "GrResourceProvider.h"
#include "GrTexture.h"
+template <typename T>
+template <typename... Args>
+T& GrOpFlushState::List<T>::append(SkArenaAlloc* arena, Args... args) {
+ SkASSERT(!fHead == !fTail);
+ auto* n = arena->make<Node>(std::forward<Args>(args)...);
+ if (!fTail) {
+ fHead = fTail = n;
+ } else {
+ fTail = fTail->fNext = n;
+ }
+ return fTail->fT;
+}
+
+template <typename T>
+typename GrOpFlushState::List<T>::Iter& GrOpFlushState::List<T>::Iter::operator++() {
+ fCurr = fCurr->fNext;
+ return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
GrOpFlushState::GrOpFlushState(GrGpu* gpu, GrResourceProvider* resourceProvider)
- : fGpu(gpu)
- , fResourceProvider(resourceProvider)
- , fCommandBuffer(nullptr)
- , fVertexPool(gpu)
- , fIndexPool(gpu)
- , fOpArgs(nullptr) {}
+ : fVertexPool(gpu), fIndexPool(gpu), fGpu(gpu), fResourceProvider(resourceProvider) {}
const GrCaps& GrOpFlushState::caps() const {
return *fGpu->caps();
@@ -28,6 +44,51 @@
return fCommandBuffer->asRTCommandBuffer();
}
+void GrOpFlushState::executeDrawsAndUploadsForMeshDrawOp(uint32_t opID, const SkRect& opBounds) {
+ SkASSERT(this->rtCommandBuffer());
+ while (fCurrDraw != fDraws.end() && fCurrDraw->fOpID == opID) {
+ GrDeferredUploadToken drawToken = this->nextTokenToFlush();
+ while (fCurrUpload != fInlineUploads.end() &&
+ fCurrUpload->fUploadBeforeToken == drawToken) {
+ this->rtCommandBuffer()->inlineUpload(this, fCurrUpload->fUpload);
+ ++fCurrUpload;
+ }
+ SkASSERT(fCurrDraw->fPipeline->proxy() == this->drawOpArgs().fProxy);
+ this->rtCommandBuffer()->draw(*fCurrDraw->fPipeline, *fCurrDraw->fGeometryProcessor,
+ fMeshes.begin() + fCurrMesh, nullptr, fCurrDraw->fMeshCnt,
+ opBounds);
+ fCurrMesh += fCurrDraw->fMeshCnt;
+ this->flushToken();
+ ++fCurrDraw;
+ }
+}
+
+void GrOpFlushState::preExecuteDraws() {
+ fVertexPool.unmap();
+ fIndexPool.unmap();
+ for (auto& upload : fAsapUploads) {
+ this->doUpload(upload);
+ }
+ // Setup execution iterators.
+ fCurrDraw = fDraws.begin();
+ fCurrUpload = fInlineUploads.begin();
+ fCurrMesh = 0;
+}
+
+void GrOpFlushState::reset() {
+ SkASSERT(fCurrDraw == fDraws.end());
+ SkASSERT(fCurrUpload == fInlineUploads.end());
+ fVertexPool.reset();
+ fIndexPool.reset();
+ fArena.reset();
+ fAsapUploads.reset();
+ fInlineUploads.reset();
+ fDraws.reset();
+ fMeshes.reset();
+ fCurrMesh = 0;
+ fBaseDrawToken = GrDeferredUploadToken::AlreadyFlushedToken();
+}
+
void GrOpFlushState::doUpload(GrDeferredTextureUploadFn& upload) {
GrDeferredTextureUploadWritePixelsFn wp = [this](GrTextureProxy* proxy, int left, int top,
int width, int height, GrPixelConfig config,
@@ -62,18 +123,12 @@
}
GrDeferredUploadToken GrOpFlushState::addInlineUpload(GrDeferredTextureUploadFn&& upload) {
- SkASSERT(fOpArgs);
- SkASSERT(fOpArgs->fOp);
- // Here we're dangerously relying on only GrDrawOps calling this method. This gets fixed by
- // storing inline uploads on GrOpFlushState and removing GrDrawOp::FlushStateAccess.
- auto op = static_cast<GrDrawOp*>(fOpArgs->fOp);
- auto token = this->nextDrawToken();
- GrDrawOp::FlushStateAccess(op).addInlineUpload(std::move(upload), token);
- return token;
+ return fInlineUploads.append(&fArena, std::move(upload), this->nextDrawToken())
+ .fUploadBeforeToken;
}
GrDeferredUploadToken GrOpFlushState::addASAPUpload(GrDeferredTextureUploadFn&& upload) {
- fASAPUploads.emplace_back(std::move(upload));
+ fAsapUploads.append(&fArena, std::move(upload));
return this->nextTokenToFlush();
}
@@ -81,30 +136,29 @@
const GrMesh& mesh) {
SkASSERT(fOpArgs);
SkASSERT(fOpArgs->fOp);
- // Here we're dangerously relying on only GrMeshDrawOps calling this method. This gets fixed by
- // storing draw data on GrOpFlushState and removing GrMeshDrawOp::FlushStateAccess.
- auto op = static_cast<GrMeshDrawOp*>(fOpArgs->fOp);
- GrMeshDrawOp::FlushStateAccess fsa(op);
-
- fsa.addMesh(mesh);
- GrMeshDrawOp::FlushStateAccess::QueuedDraw* lastDraw = fsa.lastDraw();
- if (lastDraw) {
+ fMeshes.push_back(mesh);
+ bool firstDraw = fDraws.begin() == fDraws.end();
+ if (!firstDraw) {
+ Draw& lastDraw = *fDraws.begin();
// If the last draw shares a geometry processor and pipeline and there are no intervening
// uploads, add this mesh to it.
- if (lastDraw->fGeometryProcessor == gp && lastDraw->fPipeline == pipeline &&
- (fsa.lastUploadToken() != this->nextDrawToken())) {
- ++lastDraw->fMeshCnt;
- return;
+ if (lastDraw.fGeometryProcessor == gp && lastDraw.fPipeline == pipeline) {
+ if (fInlineUploads.begin() == fInlineUploads.end() ||
+ fInlineUploads.tail()->fUploadBeforeToken != this->nextDrawToken()) {
+ ++lastDraw.fMeshCnt;
+ return;
+ }
}
}
- GrMeshDrawOp::FlushStateAccess::QueuedDraw* draw = fsa.addDraw();
+ auto& draw = fDraws.append(&fArena);
GrDeferredUploadToken token = this->issueDrawToken();
- draw->fGeometryProcessor.reset(gp);
- draw->fPipeline = pipeline;
- draw->fMeshCnt = 1;
- if (!lastDraw) {
- fsa.setBaseDrawToken(token);
+ draw.fGeometryProcessor.reset(gp);
+ draw.fPipeline = pipeline;
+ draw.fMeshCnt = 1;
+ draw.fOpID = fOpArgs->fOp->uniqueID();
+ if (firstDraw) {
+ fBaseDrawToken = token;
}
}
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 947fd14..b20098a 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -8,6 +8,7 @@
#ifndef GrOpFlushState_DEFINED
#define GrOpFlushState_DEFINED
+#include <utility>
#include "GrAppliedClip.h"
#include "GrBufferAllocPool.h"
#include "GrDeferredUpload.h"
@@ -19,48 +20,6 @@
class GrGpuRTCommandBuffer;
class GrResourceProvider;
-// TODO: Store uploads on GrOpFlushState rather than GrDrawOp and remove this.
-class GrDrawOp::FlushStateAccess {
-private:
- friend class GrOpFlushState;
-
- explicit FlushStateAccess(GrDrawOp* op) : fOp(op) {}
-
- void addInlineUpload(GrDeferredTextureUploadFn&& upload, GrDeferredUploadToken token) {
- fOp->fInlineUploads.emplace_back(std::move(upload), token);
- }
-
- GrDrawOp* fOp;
-};
-
-// TODO: Store draw related data on GrOpFlushState rather than GrMeshDrawOp and remove this.
-class GrMeshDrawOp::FlushStateAccess {
-private:
- friend class GrOpFlushState;
- using QueuedDraw = GrMeshDrawOp::QueuedDraw;
-
- explicit FlushStateAccess(GrMeshDrawOp* op) : fOp(op) {}
-
- void addMesh(const GrMesh& mesh) { fOp->fMeshes.push_back(mesh); }
-
- QueuedDraw* lastDraw() {
- return fOp->fQueuedDraws.empty() ? nullptr : &fOp->fQueuedDraws.back();
- }
-
- QueuedDraw* addDraw() { return &fOp->fQueuedDraws.push_back(); }
-
- GrDeferredUploadToken lastUploadToken() const {
- if (fOp->fInlineUploads.empty()) {
- return GrDeferredUploadToken::AlreadyFlushedToken();
- }
- return fOp->fInlineUploads.back().fUploadBeforeToken;
- }
-
- void setBaseDrawToken(GrDeferredUploadToken token) { fOp->fBaseDrawToken = token; }
-
- GrMeshDrawOp* fOp;
-};
-
/** Tracks the state across all the GrOps (really just the GrDrawOps) in a GrOpList flush. */
class GrOpFlushState final : public GrDeferredUploadTarget, public GrMeshDrawOp::Target {
public:
@@ -69,20 +28,14 @@
~GrOpFlushState() final { this->reset(); }
/** This is called after each op has a chance to prepare its draws and before the draws are
- issued. */
- void preIssueDraws() {
- fVertexPool.unmap();
- fIndexPool.unmap();
- int uploadCount = fASAPUploads.count();
-
- for (int i = 0; i < uploadCount; i++) {
- this->doUpload(fASAPUploads[i]);
- }
- fASAPUploads.reset();
- }
+ executed. */
+ void preExecuteDraws();
void doUpload(GrDeferredTextureUploadFn&);
+ /** Called as ops are executed. Must be called in the same order as the ops were prepared. */
+ void executeDrawsAndUploadsForMeshDrawOp(uint32_t opID, const SkRect& opBounds);
+
GrGpuCommandBuffer* commandBuffer() { return fCommandBuffer; }
// Helper function used by Ops that are only called via RenderTargetOpLists
GrGpuRTCommandBuffer* rtCommandBuffer();
@@ -90,11 +43,7 @@
GrGpu* gpu() { return fGpu; }
- void reset() {
- fVertexPool.reset();
- fIndexPool.reset();
- fPipelines.reset();
- }
+ void reset();
/** Additional data required on a per-op basis when executing GrOps. */
struct OpArgs {
@@ -115,14 +64,6 @@
return *fOpArgs;
}
- /** Expose base class methods for incrementing the last flushed and next draw token. */
-
- void flushToken() { this->GrDeferredUploadTarget::flushToken(); }
-
- GrDeferredUploadToken issueDrawToken() {
- return this->GrDeferredUploadTarget::issueDrawToken();
- }
-
/** Overrides of GrDeferredUploadTarget. */
GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final;
@@ -149,16 +90,104 @@
private:
/** GrMeshDrawOp::Target override. */
- SkArenaAlloc* pipelineArena() override { return &fPipelines; }
+ SkArenaAlloc* pipelineArena() override { return &fArena; }
+
+ struct InlineUpload {
+ InlineUpload(GrDeferredTextureUploadFn&& upload, GrDeferredUploadToken token)
+ : fUpload(std::move(upload)), fUploadBeforeToken(token) {}
+ GrDeferredTextureUploadFn fUpload;
+ GrDeferredUploadToken fUploadBeforeToken;
+ };
+
+ // A set of contiguous draws that share a draw token, geometry processor, and pipeline. The
+ // meshes for the draw are stored in the fMeshes array. The reason for coalescing meshes
+ // that share a geometry processor into a Draw is that it allows the Gpu object to setup
+ // the shared state once and then issue draws for each mesh.
+ struct Draw {
+ int fMeshCnt = 0;
+ GrPendingProgramElement<const GrGeometryProcessor> fGeometryProcessor;
+ const GrPipeline* fPipeline;
+ uint32_t fOpID;
+ };
+
+ /**
+ * A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns
+ * the elements. This supports forward iteration and range based for loops.
+ */
+ template <typename T>
+ class List {
+ private:
+ struct Node;
+
+ public:
+ List() = default;
+
+ void reset() { fHead = fTail = nullptr; }
+
+ template <typename... Args>
+ T& append(SkArenaAlloc* arena, Args... args);
+
+ class Iter {
+ public:
+ Iter() = default;
+ Iter& operator++();
+ T& operator*() const { return fCurr->fT; }
+ T* operator->() const { return &fCurr->fT; }
+ bool operator==(const Iter& that) const { return fCurr == that.fCurr; }
+ bool operator!=(const Iter& that) const { return !(*this == that); }
+
+ private:
+ friend class List;
+ explicit Iter(Node* node) : fCurr(node) {}
+ Node* fCurr = nullptr;
+ };
+
+ Iter begin() { return Iter(fHead); }
+ Iter end() { return Iter(); }
+ Iter tail() { return Iter(fTail); }
+
+ private:
+ struct Node {
+ template <typename... Args>
+ Node(Args... args) : fT(std::forward<Args>(args)...) {}
+ T fT;
+ Node* fNext = nullptr;
+ };
+ Node* fHead = nullptr;
+ Node* fTail = nullptr;
+ };
+
+ // Storage for ops' pipelines, draws, and inline uploads.
+ SkArenaAlloc fArena{sizeof(GrPipeline) * 100};
+
+ // Store vertex and index data on behalf of ops that are flushed.
+ GrVertexBufferAllocPool fVertexPool;
+ GrIndexBufferAllocPool fIndexPool;
+
+ // Data stored on behalf of the ops being flushed.
+ List<GrDeferredTextureUploadFn> fAsapUploads;
+ List<InlineUpload> fInlineUploads;
+ List<Draw> fDraws;
+ // TODO: These should go in the arena. However, GrGpuCommandBuffer and other classes currently
+ // accept contiguous arrays of meshes.
+ SkSTArray<16, GrMesh> fMeshes;
+
+ // All draws we store have an implicit draw token. This is the draw token for the first draw
+ // in fDraws.
+ GrDeferredUploadToken fBaseDrawToken = GrDeferredUploadToken::AlreadyFlushedToken();
+
+ // Info about the op that is currently preparing or executing using the flush state or null if
+ // an op is not currently preparing of executing.
+ OpArgs* fOpArgs = nullptr;
GrGpu* fGpu;
GrResourceProvider* fResourceProvider;
- GrGpuCommandBuffer* fCommandBuffer;
- GrVertexBufferAllocPool fVertexPool;
- GrIndexBufferAllocPool fIndexPool;
- SkSTArray<4, GrDeferredTextureUploadFn> fASAPUploads;
- OpArgs* fOpArgs;
- SkArenaAlloc fPipelines{sizeof(GrPipeline) * 100};
+ GrGpuCommandBuffer* fCommandBuffer = nullptr;
+
+ // Variables that are used to track where we are in lists as ops are executed
+ List<Draw>::Iter fCurrDraw;
+ int fCurrMesh;
+ List<InlineUpload>::Iter fCurrUpload;
};
#endif
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index f4a7102..7871d9e 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -20,9 +20,6 @@
*/
class GrDrawOp : public GrOp {
public:
- /** Provides GrOpFlushState with privileged access to GrDrawOp. */
- class FlushStateAccess;
-
GrDrawOp(uint32_t classID) : INHERITED(classID) {}
/**
@@ -50,16 +47,6 @@
virtual RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
GrPixelConfigIsClamped) = 0;
-protected:
- struct QueuedUpload {
- QueuedUpload(GrDeferredTextureUploadFn&& upload, GrDeferredUploadToken token)
- : fUpload(std::move(upload)), fUploadBeforeToken(token) {}
- GrDeferredTextureUploadFn fUpload;
- GrDeferredUploadToken fUploadBeforeToken;
- };
-
- SkTArray<QueuedUpload> fInlineUploads;
-
private:
typedef GrOp INHERITED;
};
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index a00af15..0bc782c 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -10,8 +10,7 @@
#include "GrOpFlushState.h"
#include "GrResourceProvider.h"
-GrMeshDrawOp::GrMeshDrawOp(uint32_t classID)
- : INHERITED(classID), fBaseDrawToken(GrDeferredUploadToken::AlreadyFlushedToken()) {}
+GrMeshDrawOp::GrMeshDrawOp(uint32_t classID) : INHERITED(classID) {}
void GrMeshDrawOp::onPrepare(GrOpFlushState* state) { this->onPrepareDraws(state); }
@@ -57,28 +56,5 @@
}
void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
- int currUploadIdx = 0;
- int currMeshIdx = 0;
-
- SkASSERT(fQueuedDraws.empty() || fBaseDrawToken == state->nextTokenToFlush());
- SkASSERT(state->rtCommandBuffer());
-
- for (int currDrawIdx = 0; currDrawIdx < fQueuedDraws.count(); ++currDrawIdx) {
- GrDeferredUploadToken drawToken = state->nextTokenToFlush();
- while (currUploadIdx < fInlineUploads.count() &&
- fInlineUploads[currUploadIdx].fUploadBeforeToken == drawToken) {
- state->rtCommandBuffer()->inlineUpload(state, fInlineUploads[currUploadIdx++].fUpload);
- }
- const QueuedDraw& draw = fQueuedDraws[currDrawIdx];
- SkASSERT(draw.fPipeline->proxy() == state->drawOpArgs().fProxy);
- state->rtCommandBuffer()->draw(*draw.fPipeline, *draw.fGeometryProcessor.get(),
- fMeshes.begin() + currMeshIdx, nullptr, draw.fMeshCnt,
- this->bounds());
- currMeshIdx += draw.fMeshCnt;
- state->flushToken();
- }
- SkASSERT(currUploadIdx == fInlineUploads.count());
- SkASSERT(currMeshIdx == fMeshes.count());
- fQueuedDraws.reset();
- fInlineUploads.reset();
+ state->executeDrawsAndUploadsForMeshDrawOp(this->uniqueID(), this->bounds());
}
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 232adb2..1b8831c 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -25,8 +25,6 @@
public:
/** Abstract interface that represents a destination for a GrMeshDrawOp. */
class Target;
- /** Provides GrOpFlushState with privileged access to GrMeshDrawOp. */
- class FlushStateAccess;
protected:
GrMeshDrawOp(uint32_t classID);
@@ -69,27 +67,7 @@
private:
void onPrepare(GrOpFlushState* state) final;
void onExecute(GrOpFlushState* state) final;
-
virtual void onPrepareDraws(Target*) = 0;
-
- // A set of contiguous draws that share a draw token and primitive processor. The draws all use
- // the op's pipeline. The meshes for the draw are stored in the fMeshes array and each
- // Queued draw uses fMeshCnt meshes from the fMeshes array. The reason for coallescing meshes
- // that share a primitive processor into a QueuedDraw is that it allows the Gpu object to setup
- // the shared state once and then issue draws for each mesh.
- struct QueuedDraw {
- int fMeshCnt = 0;
- GrPendingProgramElement<const GrGeometryProcessor> fGeometryProcessor;
- const GrPipeline* fPipeline;
- };
-
- // All draws in all the GrMeshDrawOps have implicit tokens based on the order they are enqueued
- // globally across all ops. This is the offset of the first entry in fQueuedDraws.
- // fQueuedDraws[i]'s token is fBaseDrawToken + i.
- GrDeferredUploadToken fBaseDrawToken;
- SkSTArray<4, GrMesh> fMeshes;
- SkSTArray<4, QueuedDraw, true> fQueuedDraws;
-
typedef GrDrawOp INHERITED;
};
@@ -154,7 +132,7 @@
/**
* Helper that makes a pipeline targeting the op's render target that incorporates the op's
* GrAppliedClip.
- * */
+ */
GrPipeline* makePipeline(uint32_t pipelineFlags, GrProcessorSet&& processorSet,
GrAppliedClip&& clip) {
GrPipeline::InitArgs pipelineArgs;