1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrOpFlushState_DEFINED
9 #define GrOpFlushState_DEFINED
10 
11 #include "GrBufferAllocPool.h"
12 #include "GrGpu.h"
13 #include "ops/GrMeshDrawOp.h"
14 
15 class GrGpuCommandBuffer;
16 class GrResourceProvider;
17 
18 /** Tracks the state across all the GrOps (really just the GrDrawOps) in a GrOpList flush. */
19 class GrOpFlushState {
20 public:
21     GrOpFlushState(GrGpu*, GrResourceProvider*);
22 
~GrOpFlushState()23     ~GrOpFlushState() { this->reset(); }
24 
25     /** Inserts an upload to be executed after all ops in the flush prepared their draws but before
26         the draws are executed to the backend 3D API. */
addASAPUpload(GrDrawOp::DeferredUploadFn && upload)27     void addASAPUpload(GrDrawOp::DeferredUploadFn&& upload) {
28         fAsapUploads.emplace_back(std::move(upload));
29     }
30 
caps()31     const GrCaps& caps() const { return *fGpu->caps(); }
resourceProvider()32     GrResourceProvider* resourceProvider() const { return fResourceProvider; }
33 
34     /** Has the token been flushed to the backend 3D API. */
hasDrawBeenFlushed(GrDrawOpUploadToken token)35     bool hasDrawBeenFlushed(GrDrawOpUploadToken token) const {
36         return token.fSequenceNumber <= fLastFlushedToken.fSequenceNumber;
37     }
38 
39     /** Issue a token to an operation that is being enqueued. */
issueDrawToken()40     GrDrawOpUploadToken issueDrawToken() {
41         return GrDrawOpUploadToken(++fLastIssuedToken.fSequenceNumber);
42     }
43 
44     /** Call every time a draw that was issued a token is flushed */
flushToken()45     void flushToken() { ++fLastFlushedToken.fSequenceNumber; }
46 
47     /** Gets the next draw token that will be issued. */
nextDrawToken()48     GrDrawOpUploadToken nextDrawToken() const {
49         return GrDrawOpUploadToken(fLastIssuedToken.fSequenceNumber + 1);
50     }
51 
52     /** The last token flushed to all the way to the backend API. */
nextTokenToFlush()53     GrDrawOpUploadToken nextTokenToFlush() const {
54         return GrDrawOpUploadToken(fLastFlushedToken.fSequenceNumber + 1);
55     }
56 
57     void* makeVertexSpace(size_t vertexSize, int vertexCount,
58                           const GrBuffer** buffer, int* startVertex);
59     uint16_t* makeIndexSpace(int indexCount, const GrBuffer** buffer, int* startIndex);
60 
61     /** This is called after each op has a chance to prepare its draws and before the draws are
62         issued. */
preIssueDraws()63     void preIssueDraws() {
64         fVertexPool.unmap();
65         fIndexPool.unmap();
66         int uploadCount = fAsapUploads.count();
67 
68         for (int i = 0; i < uploadCount; i++) {
69             this->doUpload(fAsapUploads[i]);
70         }
71         fAsapUploads.reset();
72     }
73 
doUpload(GrDrawOp::DeferredUploadFn & upload)74     void doUpload(GrDrawOp::DeferredUploadFn& upload) {
75         GrDrawOp::WritePixelsFn wp = [this] (GrSurface* surface,
76                 int left, int top, int width, int height,
77                 GrPixelConfig config, const void* buffer,
78                 size_t rowBytes) -> bool {
79             return this->fGpu->writePixels(surface, left, top, width, height, config, buffer,
80                                            rowBytes);
81         };
82         upload(wp);
83     }
84 
putBackIndices(size_t indices)85     void putBackIndices(size_t indices) { fIndexPool.putBack(indices * sizeof(uint16_t)); }
86 
putBackVertexSpace(size_t sizeInBytes)87     void putBackVertexSpace(size_t sizeInBytes) { fVertexPool.putBack(sizeInBytes); }
88 
commandBuffer()89     GrGpuCommandBuffer* commandBuffer() { return fCommandBuffer; }
setCommandBuffer(GrGpuCommandBuffer * buffer)90     void setCommandBuffer(GrGpuCommandBuffer* buffer) { fCommandBuffer = buffer; }
91 
gpu()92     GrGpu* gpu() { return fGpu; }
93 
reset()94     void reset() {
95         fVertexPool.reset();
96         fIndexPool.reset();
97     }
98 
99     /** Additional data required on a per-op basis when executing GrDrawOps. */
100     struct DrawOpArgs {
101         GrRenderTarget* fRenderTarget;
102         const GrAppliedClip* fAppliedClip;
103         GrXferProcessor::DstTexture fDstTexture;
104     };
105 
setDrawOpArgs(DrawOpArgs * opArgs)106     void setDrawOpArgs(DrawOpArgs* opArgs) { fOpArgs = opArgs; }
107 
drawOpArgs()108     const DrawOpArgs& drawOpArgs() const {
109         SkASSERT(fOpArgs);
110         return *fOpArgs;
111     }
112 
113 private:
114     GrGpu*                                      fGpu;
115     GrResourceProvider*                         fResourceProvider;
116     GrGpuCommandBuffer*                         fCommandBuffer;
117     GrVertexBufferAllocPool                     fVertexPool;
118     GrIndexBufferAllocPool                      fIndexPool;
119     SkSTArray<4, GrDrawOp::DeferredUploadFn>    fAsapUploads;
120     GrDrawOpUploadToken                         fLastIssuedToken;
121     GrDrawOpUploadToken                         fLastFlushedToken;
122     DrawOpArgs*                                 fOpArgs;
123 };
124 
125 /**
126  * A word about uploads and tokens: Ops should usually schedule their uploads to occur at the
127  * begining of a frame whenever possible. These are called ASAP uploads. Of course, this requires
128  * that there are no draws that have yet to be flushed that rely on the old texture contents. In
129  * that case the ASAP upload would happen prior to the previous draw causing the draw to read the
130  * new (wrong) texture data. In that case they should schedule an inline upload.
131  *
132  * Ops, in conjunction with helpers such as GrDrawOpAtlas, can use the token system to know
133  * what the most recent draw was that referenced a resource (or portion of a resource). Each draw
134  * is assigned a token. A resource (or portion) can be tagged with the most recent draw's
135  * token. The target provides a facility for testing whether the draw corresponding to the token
136  * has been flushed. If it has not been flushed then the op must perform an inline upload instead.
137  * When scheduling an inline upload the op provides the token of the draw that the upload must occur
138  * before. The upload will then occur between the draw that requires the new data but after the
139  * token that requires the old data.
140  *
141  * TODO: Currently the token/upload interface is spread over GrDrawOp, GrMeshDrawOp,
142  * GrDrawOp::Target, and GrMeshDrawOp::Target. However, the interface at the GrDrawOp level is not
143  * complete and isn't useful. We should push it down to GrMeshDrawOp until it is required at the
144  * GrDrawOp level.
145  */
146 
147 /**
148  * GrDrawOp instances use this object to allocate space for their geometry and to issue the draws
149  * that render their op.
150  */
151 class GrDrawOp::Target {
152 public:
Target(GrOpFlushState * state,GrDrawOp * op)153     Target(GrOpFlushState* state, GrDrawOp* op) : fState(state), fOp(op) {}
154 
155     /** Returns the token of the draw that this upload will occur before. */
addInlineUpload(DeferredUploadFn && upload)156     GrDrawOpUploadToken addInlineUpload(DeferredUploadFn&& upload) {
157         fOp->fInlineUploads.emplace_back(std::move(upload), fState->nextDrawToken());
158         return fOp->fInlineUploads.back().fUploadBeforeToken;
159     }
160 
161     /** Returns the token of the draw that this upload will occur before. Since ASAP uploads
162         are done first during a flush, this will be the first token since the most recent
163         flush. */
addAsapUpload(DeferredUploadFn && upload)164     GrDrawOpUploadToken addAsapUpload(DeferredUploadFn&& upload) {
165         fState->addASAPUpload(std::move(upload));
166         return fState->nextTokenToFlush();
167     }
168 
hasDrawBeenFlushed(GrDrawOpUploadToken token)169     bool hasDrawBeenFlushed(GrDrawOpUploadToken token) const {
170         return fState->hasDrawBeenFlushed(token);
171     }
172 
173     /** Gets the next draw token that will be issued by this target. This can be used by an op
174         to record that the next draw it issues will use a resource (e.g. texture) while preparing
175         that draw. */
nextDrawToken()176     GrDrawOpUploadToken nextDrawToken() const { return fState->nextDrawToken(); }
177 
caps()178     const GrCaps& caps() const { return fState->caps(); }
179 
resourceProvider()180     GrResourceProvider* resourceProvider() const { return fState->resourceProvider(); }
181 
182 protected:
op()183     GrDrawOp* op() { return fOp; }
state()184     GrOpFlushState* state() { return fState; }
185 
186 private:
187     GrOpFlushState* fState;
188     GrDrawOp* fOp;
189 };
190 
191 /** Extension of GrDrawOp::Target for use by GrMeshDrawOp. Adds the ability to create vertex
192     draws. */
193 class GrMeshDrawOp::Target : public GrDrawOp::Target {
194 public:
Target(GrOpFlushState * state,GrMeshDrawOp * op)195     Target(GrOpFlushState* state, GrMeshDrawOp* op) : INHERITED(state, op) {}
196 
197     void draw(const GrGeometryProcessor* gp, const GrMesh& mesh);
198 
makeVertexSpace(size_t vertexSize,int vertexCount,const GrBuffer ** buffer,int * startVertex)199     void* makeVertexSpace(size_t vertexSize, int vertexCount,
200                           const GrBuffer** buffer, int* startVertex) {
201         return this->state()->makeVertexSpace(vertexSize, vertexCount, buffer, startVertex);
202     }
203 
makeIndexSpace(int indexCount,const GrBuffer ** buffer,int * startIndex)204     uint16_t* makeIndexSpace(int indexCount, const GrBuffer** buffer, int* startIndex) {
205         return this->state()->makeIndexSpace(indexCount, buffer, startIndex);
206     }
207 
208     /** Helpers for ops which over-allocate and then return data to the pool. */
putBackIndices(int indices)209     void putBackIndices(int indices) { this->state()->putBackIndices(indices); }
putBackVertices(int vertices,size_t vertexStride)210     void putBackVertices(int vertices, size_t vertexStride) {
211         this->state()->putBackVertexSpace(vertices * vertexStride);
212     }
213 
214 private:
meshDrawOp()215     GrMeshDrawOp* meshDrawOp() { return static_cast<GrMeshDrawOp*>(this->op()); }
216     typedef GrDrawOp::Target INHERITED;
217 };
218 
219 #endif
220