xref: /aosp_15_r20/external/skia/src/gpu/graphite/DrawWriter.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_DrawWriter_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_DrawWriter_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkAutoMalloc.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/BufferWriter.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/BufferManager.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker namespace DrawPassCommands {
19*c8dee2aaSAndroid Build Coastguard Worker class List;
20*c8dee2aaSAndroid Build Coastguard Worker }
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker /**
23*c8dee2aaSAndroid Build Coastguard Worker  * DrawWriter is a helper around recording draws (to a temporary buffer or directly to a
24*c8dee2aaSAndroid Build Coastguard Worker  * CommandBuffer), particularly when the number of draws is not known ahead of time, or the vertex
25*c8dee2aaSAndroid Build Coastguard Worker  * and instance data is computed at record time and does not have a known size.
26*c8dee2aaSAndroid Build Coastguard Worker  *
27*c8dee2aaSAndroid Build Coastguard Worker  * To use, construct the DrawWriter with the current pipeline layout or call newPipelineState() on
28*c8dee2aaSAndroid Build Coastguard Worker  * an existing DrawWriter and then bind that matching pipeline. When other dynamic state needs to
29*c8dee2aaSAndroid Build Coastguard Worker  * change between draw calls, notify the DrawWriter using newDynamicState() before recording the
30*c8dee2aaSAndroid Build Coastguard Worker  * modifications. See the listing below for how to append dynamic data or draw with existing buffers
31*c8dee2aaSAndroid Build Coastguard Worker  *
32*c8dee2aaSAndroid Build Coastguard Worker  * CommandBuffer::draw(vertices)
33*c8dee2aaSAndroid Build Coastguard Worker  *  - dynamic vertex data     -> DrawWriter::Vertices(writer) verts;
34*c8dee2aaSAndroid Build Coastguard Worker  *                               verts.append(n) << ...;
35*c8dee2aaSAndroid Build Coastguard Worker  *  - fixed vertex data       -> writer.draw(vertices, {}, vertexCount)
36*c8dee2aaSAndroid Build Coastguard Worker  *
37*c8dee2aaSAndroid Build Coastguard Worker  * CommandBuffer::drawIndexed(vertices, indices)
38*c8dee2aaSAndroid Build Coastguard Worker  *  - dynamic vertex data     -> unsupported
39*c8dee2aaSAndroid Build Coastguard Worker  *  - fixed vertex,index data -> writer.drawIndexed(vertices, indices, indexCount)
40*c8dee2aaSAndroid Build Coastguard Worker  *
41*c8dee2aaSAndroid Build Coastguard Worker  * CommandBuffer::drawInstances(vertices, instances)
42*c8dee2aaSAndroid Build Coastguard Worker  *  - dynamic instance data + fixed vertex data        ->
43*c8dee2aaSAndroid Build Coastguard Worker  *        DrawWriter::Instances instances(writer, vertices, {}, vertexCount);
44*c8dee2aaSAndroid Build Coastguard Worker  *        instances.append(n) << ...;
45*c8dee2aaSAndroid Build Coastguard Worker  *  - fixed vertex and instance data                   ->
46*c8dee2aaSAndroid Build Coastguard Worker  *        writer.drawInstanced(vertices, vertexCount, instances, instanceCount)
47*c8dee2aaSAndroid Build Coastguard Worker  *
48*c8dee2aaSAndroid Build Coastguard Worker  * CommandBuffer::drawIndexedInstanced(vertices, indices, instances)
49*c8dee2aaSAndroid Build Coastguard Worker  *  - dynamic instance data + fixed vertex, index data ->
50*c8dee2aaSAndroid Build Coastguard Worker  *        DrawWriter::Instances instances(writer, vertices, indices, indexCount);
51*c8dee2aaSAndroid Build Coastguard Worker  *        instances.append(n) << ...;
52*c8dee2aaSAndroid Build Coastguard Worker  *  - fixed vertex, index, and instance data           ->
53*c8dee2aaSAndroid Build Coastguard Worker  *        writer.drawIndexedInstanced(vertices, indices, indexCount, instances, instanceCount)
54*c8dee2aaSAndroid Build Coastguard Worker  *
55*c8dee2aaSAndroid Build Coastguard Worker  * NOTE: DrawWriter automatically handles failures to find or create a GPU buffer or map it to
56*c8dee2aaSAndroid Build Coastguard Worker  * be writable. All returned VertexWriters will have a non-null pointer to write to, even if it will
57*c8dee2aaSAndroid Build Coastguard Worker  * be discarded due to GPU failure at Recorder::snap() time.
58*c8dee2aaSAndroid Build Coastguard Worker  */
59*c8dee2aaSAndroid Build Coastguard Worker class DrawWriter {
60*c8dee2aaSAndroid Build Coastguard Worker public:
61*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: This constructor creates a writer that defaults 0 vertex and instance stride, so
62*c8dee2aaSAndroid Build Coastguard Worker     // 'newPipelineState()' must be called once the pipeline properties are known before it's used.
63*c8dee2aaSAndroid Build Coastguard Worker     DrawWriter(DrawPassCommands::List*, DrawBufferManager*);
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker     // Cannot move or copy
66*c8dee2aaSAndroid Build Coastguard Worker     DrawWriter(const DrawWriter&) = delete;
67*c8dee2aaSAndroid Build Coastguard Worker     DrawWriter(DrawWriter&&) = delete;
68*c8dee2aaSAndroid Build Coastguard Worker 
69*c8dee2aaSAndroid Build Coastguard Worker     // flush() should be called before the writer is destroyed
~DrawWriter()70*c8dee2aaSAndroid Build Coastguard Worker     ~DrawWriter() { SkASSERT(fPendingCount == 0); }
71*c8dee2aaSAndroid Build Coastguard Worker 
bufferManager()72*c8dee2aaSAndroid Build Coastguard Worker     DrawBufferManager* bufferManager() { return fManager; }
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker     // Issue draw calls for any pending vertex and instance data collected by the writer.
75*c8dee2aaSAndroid Build Coastguard Worker     // Use either flush() or newDynamicState() based on context and readability.
76*c8dee2aaSAndroid Build Coastguard Worker     void flush();
newDynamicState()77*c8dee2aaSAndroid Build Coastguard Worker     void newDynamicState() { this->flush(); }
78*c8dee2aaSAndroid Build Coastguard Worker 
79*c8dee2aaSAndroid Build Coastguard Worker     // Notify the DrawWriter that a new pipeline needs to be bound, providing the primitive type and
80*c8dee2aaSAndroid Build Coastguard Worker     // attribute strides of that pipeline. This issues draw calls for pending data that relied on
81*c8dee2aaSAndroid Build Coastguard Worker     // the old pipeline, so this must be called *before* binding the new pipeline.
newPipelineState(PrimitiveType type,size_t vertexStride,size_t instanceStride)82*c8dee2aaSAndroid Build Coastguard Worker     void newPipelineState(PrimitiveType type, size_t vertexStride, size_t instanceStride) {
83*c8dee2aaSAndroid Build Coastguard Worker         this->flush();
84*c8dee2aaSAndroid Build Coastguard Worker         fPrimitiveType = type;
85*c8dee2aaSAndroid Build Coastguard Worker         fVertexStride = vertexStride;
86*c8dee2aaSAndroid Build Coastguard Worker         fInstanceStride = instanceStride;
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: resetting pending base is sufficient to redo bindings for vertex/instance data that
89*c8dee2aaSAndroid Build Coastguard Worker         // is later appended but doesn't invalidate bindings for fixed buffers that might not need
90*c8dee2aaSAndroid Build Coastguard Worker         // to change between pipelines.
91*c8dee2aaSAndroid Build Coastguard Worker         fPendingBase = 0;
92*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fPendingCount == 0);
93*c8dee2aaSAndroid Build Coastguard Worker     }
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
96*c8dee2aaSAndroid Build Coastguard Worker     // Query current pipeline state for validation
instanceStride()97*c8dee2aaSAndroid Build Coastguard Worker     size_t        instanceStride() const { return fInstanceStride; }
vertexStride()98*c8dee2aaSAndroid Build Coastguard Worker     size_t        vertexStride()   const { return fVertexStride;   }
primitiveType()99*c8dee2aaSAndroid Build Coastguard Worker     PrimitiveType primitiveType()  const { return fPrimitiveType;  }
100*c8dee2aaSAndroid Build Coastguard Worker #endif
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker     // Collects new vertex data for a call to CommandBuffer::draw(). Automatically accumulates
103*c8dee2aaSAndroid Build Coastguard Worker     // vertex data into a buffer, issuing draw and bind calls as needed when a new buffer is
104*c8dee2aaSAndroid Build Coastguard Worker     // required, so that it is seamless to the caller. The draws do not use instances or indices.
105*c8dee2aaSAndroid Build Coastguard Worker     //
106*c8dee2aaSAndroid Build Coastguard Worker     // Usage (assuming writer has already had 'newPipelineState()' called with correct strides):
107*c8dee2aaSAndroid Build Coastguard Worker     //    DrawWriter::Vertices verts{writer};
108*c8dee2aaSAndroid Build Coastguard Worker     //    verts.append(n) << x << y << ...;
109*c8dee2aaSAndroid Build Coastguard Worker     //
110*c8dee2aaSAndroid Build Coastguard Worker     // This should not be used when the vertex stride is 0.
111*c8dee2aaSAndroid Build Coastguard Worker     class Vertices;
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     // Collects new instance data for a call to CommandBuffer::drawInstanced() or
114*c8dee2aaSAndroid Build Coastguard Worker     // drawIndexedInstanced(). The specific draw call that's issued depends on if a non-null index
115*c8dee2aaSAndroid Build Coastguard Worker     // buffer is provided for the template. Like DrawWriter::Vertices, this automatically merges
116*c8dee2aaSAndroid Build Coastguard Worker     // the appended data into as few buffer binds and draw calls as possible, while remaining
117*c8dee2aaSAndroid Build Coastguard Worker     // seamless to the caller.
118*c8dee2aaSAndroid Build Coastguard Worker     //
119*c8dee2aaSAndroid Build Coastguard Worker     // Usage for drawInstanced (assuming writer has correct strides):
120*c8dee2aaSAndroid Build Coastguard Worker     //    DrawWriter::Instances instances{writer, fixedVerts, {}, fixedVertexCount};
121*c8dee2aaSAndroid Build Coastguard Worker     //    instances.append(n) << foo << bar << ...;
122*c8dee2aaSAndroid Build Coastguard Worker     //
123*c8dee2aaSAndroid Build Coastguard Worker     // Usage for drawIndexedInstanced:
124*c8dee2aaSAndroid Build Coastguard Worker     //    DrawWriter::Instances instances{writer, fixedVerts, fixedIndices, fixedIndexCount};
125*c8dee2aaSAndroid Build Coastguard Worker     //    instances.append(n) << foo << bar << ...;
126*c8dee2aaSAndroid Build Coastguard Worker     //
127*c8dee2aaSAndroid Build Coastguard Worker     // This should not be used when the instance stride is 0. However, the fixed vertex buffer can
128*c8dee2aaSAndroid Build Coastguard Worker     // be null (or have a stride of 0) if the vertex shader only relies on the vertex ID and no
129*c8dee2aaSAndroid Build Coastguard Worker     // other per-vertex data.
130*c8dee2aaSAndroid Build Coastguard Worker     class Instances;
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     // Collects new instance data for a call to CommandBuffer::drawInstanced() or
133*c8dee2aaSAndroid Build Coastguard Worker     // drawIndexedInstanced() (depending on presence of index data in the template). Unlike the
134*c8dee2aaSAndroid Build Coastguard Worker     // Instances mode, the template's index or vertex count is not provided at the time of creation.
135*c8dee2aaSAndroid Build Coastguard Worker     // Instead, DynamicInstances can be used with pipeline programs that can have a flexible number
136*c8dee2aaSAndroid Build Coastguard Worker     // of vertices per instance. Appended instances specify a proxy object that can be converted
137*c8dee2aaSAndroid Build Coastguard Worker     // to the minimum index/vertex count they must be drawn with; but if they are later batched with
138*c8dee2aaSAndroid Build Coastguard Worker     // instances that would use more, the pipeline's vertex shader knows how to handle it.
139*c8dee2aaSAndroid Build Coastguard Worker     //
140*c8dee2aaSAndroid Build Coastguard Worker     // The proxy object serves as a useful point of indirection when the actual index count is
141*c8dee2aaSAndroid Build Coastguard Worker     // expensive to compute, but can be derived from correlated geometric properties. The proxy
142*c8dee2aaSAndroid Build Coastguard Worker     // can store those properties and accumulate a "worst-case" and then calculate the index count
143*c8dee2aaSAndroid Build Coastguard Worker     // when DrawWriter has to flush.
144*c8dee2aaSAndroid Build Coastguard Worker     //
145*c8dee2aaSAndroid Build Coastguard Worker     // The VertexCountProxy type must provide:
146*c8dee2aaSAndroid Build Coastguard Worker     //  - a default constructor and copy assignment, where the initial value represents the minimum
147*c8dee2aaSAndroid Build Coastguard Worker     //    supported vertex count.
148*c8dee2aaSAndroid Build Coastguard Worker     //  - an 'unsigned int' operator that converts the proxy to the actual index count that is
149*c8dee2aaSAndroid Build Coastguard Worker     //    needed in order to dispatch a draw call.
150*c8dee2aaSAndroid Build Coastguard Worker     //  - operator <<(const V&) where V is any type the caller wants to pass to append() that
151*c8dee2aaSAndroid Build Coastguard Worker     //    represents the proxy for the about-to-be-written instances. This operator then updates its
152*c8dee2aaSAndroid Build Coastguard Worker     //    internal state to represent the worst case between what had previously been recorded and
153*c8dee2aaSAndroid Build Coastguard Worker     //    the latest V value.
154*c8dee2aaSAndroid Build Coastguard Worker     //
155*c8dee2aaSAndroid Build Coastguard Worker     // Usage for drawInstanced (fixedIndices == {}) or drawIndexedInstanced:
156*c8dee2aaSAndroid Build Coastguard Worker     //    DrawWriter::DynamicInstances<ProxyType> instances(writer, fixedVerts, fixedIndices);
157*c8dee2aaSAndroid Build Coastguard Worker     //    instances.append(minIndexProxy1, n1) << ...;
158*c8dee2aaSAndroid Build Coastguard Worker     //    instances.append(minIndexProxy2, n2) << ...;
159*c8dee2aaSAndroid Build Coastguard Worker     //
160*c8dee2aaSAndroid Build Coastguard Worker     // In this example, if the two sets of instances were contiguous, a single draw call with
161*c8dee2aaSAndroid Build Coastguard Worker     // (n1 + n2) instances would still be made using max(minIndexCount1, minIndexCount2) as the
162*c8dee2aaSAndroid Build Coastguard Worker     // index/vertex count, 'minIndexCountX' was derived from 'minIndexProxyX'. If the available
163*c8dee2aaSAndroid Build Coastguard Worker     // vertex data from the DrawBufferManager forced a flush after the first, then the second would
164*c8dee2aaSAndroid Build Coastguard Worker     // use minIndexCount2 unless a subsequent compatible DynamicInstances template appended more
165*c8dee2aaSAndroid Build Coastguard Worker     // contiguous data.
166*c8dee2aaSAndroid Build Coastguard Worker     template <typename VertexCountProxy>
167*c8dee2aaSAndroid Build Coastguard Worker     class DynamicInstances;
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker     // Issues a draws with fully specified data. This can be used when all instance data has already
170*c8dee2aaSAndroid Build Coastguard Worker     // been written to known buffers, or when the vertex shader only depends on the vertex or
171*c8dee2aaSAndroid Build Coastguard Worker     // instance IDs. To keep things simple, these helpers do not accept parameters for base vertices
172*c8dee2aaSAndroid Build Coastguard Worker     // or instances; if needed, this can be accounted for in the BindBufferInfos provided.
173*c8dee2aaSAndroid Build Coastguard Worker     //
174*c8dee2aaSAndroid Build Coastguard Worker     // This will not merge with any already appended instance or vertex data, pending data is issued
175*c8dee2aaSAndroid Build Coastguard Worker     // in its own draw call first.
draw(BindBufferInfo vertices,unsigned int vertexCount)176*c8dee2aaSAndroid Build Coastguard Worker     void draw(BindBufferInfo vertices, unsigned int vertexCount) {
177*c8dee2aaSAndroid Build Coastguard Worker         this->bindAndFlush(vertices, {}, {}, 0, vertexCount);
178*c8dee2aaSAndroid Build Coastguard Worker     }
drawIndexed(BindBufferInfo vertices,BindBufferInfo indices,unsigned int indexCount)179*c8dee2aaSAndroid Build Coastguard Worker     void drawIndexed(BindBufferInfo vertices, BindBufferInfo indices, unsigned int indexCount) {
180*c8dee2aaSAndroid Build Coastguard Worker         this->bindAndFlush(vertices, indices, {}, 0, indexCount);
181*c8dee2aaSAndroid Build Coastguard Worker     }
drawInstanced(BindBufferInfo vertices,unsigned int vertexCount,BindBufferInfo instances,unsigned int instanceCount)182*c8dee2aaSAndroid Build Coastguard Worker     void drawInstanced(BindBufferInfo vertices, unsigned int vertexCount,
183*c8dee2aaSAndroid Build Coastguard Worker                        BindBufferInfo instances, unsigned int instanceCount) {
184*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(vertexCount > 0);
185*c8dee2aaSAndroid Build Coastguard Worker         this->bindAndFlush(vertices, {}, instances, vertexCount, instanceCount);
186*c8dee2aaSAndroid Build Coastguard Worker     }
drawIndexedInstanced(BindBufferInfo vertices,BindBufferInfo indices,unsigned int indexCount,BindBufferInfo instances,unsigned int instanceCount)187*c8dee2aaSAndroid Build Coastguard Worker     void drawIndexedInstanced(BindBufferInfo vertices, BindBufferInfo indices,
188*c8dee2aaSAndroid Build Coastguard Worker                               unsigned int indexCount, BindBufferInfo instances,
189*c8dee2aaSAndroid Build Coastguard Worker                               unsigned int instanceCount) {
190*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(indexCount > 0);
191*c8dee2aaSAndroid Build Coastguard Worker         this->bindAndFlush(vertices, indices, instances, indexCount, instanceCount);
192*c8dee2aaSAndroid Build Coastguard Worker     }
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker private:
195*c8dee2aaSAndroid Build Coastguard Worker     // Both of these pointers must outlive the DrawWriter.
196*c8dee2aaSAndroid Build Coastguard Worker     DrawPassCommands::List* fCommandList;
197*c8dee2aaSAndroid Build Coastguard Worker     DrawBufferManager* fManager;
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMalloc fFailureStorage; // storage address for VertexWriter when GPU buffer mapping fails
200*c8dee2aaSAndroid Build Coastguard Worker 
201*c8dee2aaSAndroid Build Coastguard Worker     // Pipeline state matching currently bound pipeline
202*c8dee2aaSAndroid Build Coastguard Worker     PrimitiveType fPrimitiveType;
203*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fVertexStride;
204*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fInstanceStride;
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker     /// Draw buffer binding state for pending draws
207*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo fVertices;
208*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo fIndices;
209*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo fInstances;
210*c8dee2aaSAndroid Build Coastguard Worker     // Vertex/index count for [pseudo]-instanced rendering:
211*c8dee2aaSAndroid Build Coastguard Worker     // == 0 is vertex-only drawing; > 0 is regular instanced drawing; < 0 is dynamic index count
212*c8dee2aaSAndroid Build Coastguard Worker     // instanced drawing, where real index count = max(-fTemplateCount-1)
213*c8dee2aaSAndroid Build Coastguard Worker     int fTemplateCount;
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fPendingCount; // # of vertices or instances (depending on mode) to be drawn
216*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fPendingBase; // vertex/instance offset (depending on mode) applied to buffer
217*c8dee2aaSAndroid Build Coastguard Worker     bool fPendingBufferBinds; // true if {fVertices,fIndices,fInstances} has changed since last draw
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker     void setTemplate(BindBufferInfo vertices, BindBufferInfo indices, BindBufferInfo instances,
220*c8dee2aaSAndroid Build Coastguard Worker                      int templateCount);
221*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: bindAndFlush's templateCount is unsigned because dynamic index count instancing
222*c8dee2aaSAndroid Build Coastguard Worker     // isn't applicable.
bindAndFlush(BindBufferInfo vertices,BindBufferInfo indices,BindBufferInfo instances,unsigned int templateCount,unsigned int drawCount)223*c8dee2aaSAndroid Build Coastguard Worker     void bindAndFlush(BindBufferInfo vertices, BindBufferInfo indices, BindBufferInfo instances,
224*c8dee2aaSAndroid Build Coastguard Worker                       unsigned int templateCount, unsigned int drawCount) {
225*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(drawCount > 0);
226*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fAppender); // shouldn't be appending and manually drawing at the same time.
227*c8dee2aaSAndroid Build Coastguard Worker         this->setTemplate(vertices, indices, instances, SkTo<int>(templateCount));
228*c8dee2aaSAndroid Build Coastguard Worker         fPendingBase = 0;
229*c8dee2aaSAndroid Build Coastguard Worker         fPendingCount = drawCount;
230*c8dee2aaSAndroid Build Coastguard Worker         this->flush();
231*c8dee2aaSAndroid Build Coastguard Worker     }
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker     // RAII - Sets the DrawWriter's template and marks the writer in append mode (disabling direct
234*c8dee2aaSAndroid Build Coastguard Worker     // draws until the Appender is destructed).
235*c8dee2aaSAndroid Build Coastguard Worker     class Appender;
236*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(const Appender* fAppender = nullptr;)
237*c8dee2aaSAndroid Build Coastguard Worker };
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker // Appender implementations for DrawWriter that set the template on creation and provide a
240*c8dee2aaSAndroid Build Coastguard Worker // template-specific API to accumulate vertex/instance data.
241*c8dee2aaSAndroid Build Coastguard Worker class DrawWriter::Appender {
242*c8dee2aaSAndroid Build Coastguard Worker public:
243*c8dee2aaSAndroid Build Coastguard Worker     enum class Target { kVertices, kInstances };
244*c8dee2aaSAndroid Build Coastguard Worker 
Appender(DrawWriter & w,Target target)245*c8dee2aaSAndroid Build Coastguard Worker     Appender(DrawWriter& w, Target target)
246*c8dee2aaSAndroid Build Coastguard Worker             : fDrawer(w)
247*c8dee2aaSAndroid Build Coastguard Worker             , fTarget(target == Target::kVertices ? w.fVertices     : w.fInstances)
248*c8dee2aaSAndroid Build Coastguard Worker             , fStride(target == Target::kVertices ? w.fVertexStride : w.fInstanceStride)
249*c8dee2aaSAndroid Build Coastguard Worker             , fReservedCount(0)
250*c8dee2aaSAndroid Build Coastguard Worker             , fNextWriter() {
251*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fStride > 0);
252*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!w.fAppender);
253*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(w.fAppender = this;)
254*c8dee2aaSAndroid Build Coastguard Worker     }
255*c8dee2aaSAndroid Build Coastguard Worker 
~Appender()256*c8dee2aaSAndroid Build Coastguard Worker     virtual ~Appender() {
257*c8dee2aaSAndroid Build Coastguard Worker         if (fReservedCount > 0) {
258*c8dee2aaSAndroid Build Coastguard Worker             fDrawer.fManager->returnVertexBytes(fReservedCount * fStride);
259*c8dee2aaSAndroid Build Coastguard Worker         }
260*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fDrawer.fAppender == this);
261*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fDrawer.fAppender = nullptr;)
262*c8dee2aaSAndroid Build Coastguard Worker     }
263*c8dee2aaSAndroid Build Coastguard Worker 
264*c8dee2aaSAndroid Build Coastguard Worker protected:
265*c8dee2aaSAndroid Build Coastguard Worker     DrawWriter&     fDrawer;
266*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo& fTarget;
267*c8dee2aaSAndroid Build Coastguard Worker     uint32_t        fStride;
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     uint32_t     fReservedCount; // in target stride units
270*c8dee2aaSAndroid Build Coastguard Worker     VertexWriter fNextWriter;    // writing to the target buffer binding
271*c8dee2aaSAndroid Build Coastguard Worker 
onFlush()272*c8dee2aaSAndroid Build Coastguard Worker     virtual void onFlush() {}
273*c8dee2aaSAndroid Build Coastguard Worker 
reserve(unsigned int count)274*c8dee2aaSAndroid Build Coastguard Worker     void reserve(unsigned int count) {
275*c8dee2aaSAndroid Build Coastguard Worker         if (fReservedCount >= count) {
276*c8dee2aaSAndroid Build Coastguard Worker             return;
277*c8dee2aaSAndroid Build Coastguard Worker         } else if (fReservedCount > 0) {
278*c8dee2aaSAndroid Build Coastguard Worker             // Have contiguous bytes that can't satisfy request, so return them in the event the
279*c8dee2aaSAndroid Build Coastguard Worker             // DBM has additional contiguous bytes after the prior reserved range. The byte count
280*c8dee2aaSAndroid Build Coastguard Worker             // multiply should be safe here: if it would have overflowed, the original allocation
281*c8dee2aaSAndroid Build Coastguard Worker             // should have failed and not increased fReservedCount.
282*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(SkTFitsIn<uint32_t>((uint64_t)fReservedCount*(uint64_t)fStride));
283*c8dee2aaSAndroid Build Coastguard Worker             const uint32_t returnedBytes = fReservedCount * fStride;
284*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fTarget.fSize >= returnedBytes);
285*c8dee2aaSAndroid Build Coastguard Worker             fDrawer.fManager->returnVertexBytes(returnedBytes);
286*c8dee2aaSAndroid Build Coastguard Worker             fTarget.fSize -= returnedBytes;
287*c8dee2aaSAndroid Build Coastguard Worker             fReservedCount = 0;
288*c8dee2aaSAndroid Build Coastguard Worker         }
289*c8dee2aaSAndroid Build Coastguard Worker 
290*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: Cannot bind tuple directly to fNextWriter, compilers don't produce the right
291*c8dee2aaSAndroid Build Coastguard Worker         // move assignment.
292*c8dee2aaSAndroid Build Coastguard Worker         auto [writer, reservedChunk] = fDrawer.fManager->getVertexWriter(count, fStride);
293*c8dee2aaSAndroid Build Coastguard Worker         if (writer) {
294*c8dee2aaSAndroid Build Coastguard Worker             fReservedCount = count;
295*c8dee2aaSAndroid Build Coastguard Worker 
296*c8dee2aaSAndroid Build Coastguard Worker             if (reservedChunk.fBuffer != fTarget.fBuffer ||
297*c8dee2aaSAndroid Build Coastguard Worker                 reservedChunk.fOffset !=
298*c8dee2aaSAndroid Build Coastguard Worker                         (fTarget.fOffset + (fDrawer.fPendingBase+fDrawer.fPendingCount)*fStride)) {
299*c8dee2aaSAndroid Build Coastguard Worker                 // Not contiguous, so flush and update binding to 'reservedChunk'
300*c8dee2aaSAndroid Build Coastguard Worker                 this->onFlush();
301*c8dee2aaSAndroid Build Coastguard Worker                 fDrawer.flush();
302*c8dee2aaSAndroid Build Coastguard Worker 
303*c8dee2aaSAndroid Build Coastguard Worker                 fTarget = reservedChunk;
304*c8dee2aaSAndroid Build Coastguard Worker                 fDrawer.fPendingBase = 0;
305*c8dee2aaSAndroid Build Coastguard Worker                 fDrawer.fPendingBufferBinds = true;
306*c8dee2aaSAndroid Build Coastguard Worker             } else {
307*c8dee2aaSAndroid Build Coastguard Worker                 fTarget.fSize += reservedChunk.fSize;
308*c8dee2aaSAndroid Build Coastguard Worker             }
309*c8dee2aaSAndroid Build Coastguard Worker         }
310*c8dee2aaSAndroid Build Coastguard Worker         fNextWriter = std::move(writer);
311*c8dee2aaSAndroid Build Coastguard Worker     }
312*c8dee2aaSAndroid Build Coastguard Worker 
append(unsigned int count)313*c8dee2aaSAndroid Build Coastguard Worker     VertexWriter append(unsigned int count) {
314*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(count > 0);
315*c8dee2aaSAndroid Build Coastguard Worker         this->reserve(count);
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker         if (!fNextWriter) SK_UNLIKELY {
318*c8dee2aaSAndroid Build Coastguard Worker             // If the GPU mapped buffer failed, ensure we have a sufficiently large CPU address to
319*c8dee2aaSAndroid Build Coastguard Worker             // write to so that RenderSteps don't have to worry about error handling. The Recording
320*c8dee2aaSAndroid Build Coastguard Worker             // will fail since the map failure is tracked by BufferManager.
321*c8dee2aaSAndroid Build Coastguard Worker             // Since one of the reasons for GPU mapping failure is that count*stride does not fit
322*c8dee2aaSAndroid Build Coastguard Worker             // in 32-bits, we calculate the CPU-side size carefully.
323*c8dee2aaSAndroid Build Coastguard Worker             uint64_t size = (uint64_t)count * (uint64_t)fStride;
324*c8dee2aaSAndroid Build Coastguard Worker             if (!SkTFitsIn<size_t>(size)) {
325*c8dee2aaSAndroid Build Coastguard Worker                 sk_report_container_overflow_and_die();
326*c8dee2aaSAndroid Build Coastguard Worker             }
327*c8dee2aaSAndroid Build Coastguard Worker             return VertexWriter(fDrawer.fFailureStorage.reset(size, SkAutoMalloc::kReuse_OnShrink),
328*c8dee2aaSAndroid Build Coastguard Worker                                 SkTo<size_t>(size));
329*c8dee2aaSAndroid Build Coastguard Worker         }
330*c8dee2aaSAndroid Build Coastguard Worker 
331*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fReservedCount >= count);
332*c8dee2aaSAndroid Build Coastguard Worker         fReservedCount -= count;
333*c8dee2aaSAndroid Build Coastguard Worker         fDrawer.fPendingCount += count;
334*c8dee2aaSAndroid Build Coastguard Worker         // Since we have a writer, we know count*stride is valid.
335*c8dee2aaSAndroid Build Coastguard Worker         return std::exchange(fNextWriter, fNextWriter.makeOffset(count * fStride));
336*c8dee2aaSAndroid Build Coastguard Worker     }
337*c8dee2aaSAndroid Build Coastguard Worker };
338*c8dee2aaSAndroid Build Coastguard Worker 
339*c8dee2aaSAndroid Build Coastguard Worker class DrawWriter::Vertices : private DrawWriter::Appender {
340*c8dee2aaSAndroid Build Coastguard Worker public:
Vertices(DrawWriter & w)341*c8dee2aaSAndroid Build Coastguard Worker     Vertices(DrawWriter& w) : Appender(w, Target::kVertices) {
342*c8dee2aaSAndroid Build Coastguard Worker         w.setTemplate(w.fVertices, {}, {}, 0);
343*c8dee2aaSAndroid Build Coastguard Worker     }
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker     using Appender::reserve;
346*c8dee2aaSAndroid Build Coastguard Worker     using Appender::append;
347*c8dee2aaSAndroid Build Coastguard Worker };
348*c8dee2aaSAndroid Build Coastguard Worker 
349*c8dee2aaSAndroid Build Coastguard Worker class DrawWriter::Instances : private DrawWriter::Appender {
350*c8dee2aaSAndroid Build Coastguard Worker public:
Instances(DrawWriter & w,BindBufferInfo vertices,BindBufferInfo indices,unsigned int vertexCount)351*c8dee2aaSAndroid Build Coastguard Worker     Instances(DrawWriter& w,
352*c8dee2aaSAndroid Build Coastguard Worker               BindBufferInfo vertices,
353*c8dee2aaSAndroid Build Coastguard Worker               BindBufferInfo indices,
354*c8dee2aaSAndroid Build Coastguard Worker               unsigned int vertexCount)
355*c8dee2aaSAndroid Build Coastguard Worker             : Appender(w, Target::kInstances) {
356*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(vertexCount > 0);
357*c8dee2aaSAndroid Build Coastguard Worker         w.setTemplate(vertices, indices, w.fInstances, SkTo<int>(vertexCount));
358*c8dee2aaSAndroid Build Coastguard Worker     }
359*c8dee2aaSAndroid Build Coastguard Worker 
360*c8dee2aaSAndroid Build Coastguard Worker     using Appender::reserve;
361*c8dee2aaSAndroid Build Coastguard Worker     using Appender::append;
362*c8dee2aaSAndroid Build Coastguard Worker };
363*c8dee2aaSAndroid Build Coastguard Worker 
364*c8dee2aaSAndroid Build Coastguard Worker template <typename VertexCountProxy>
365*c8dee2aaSAndroid Build Coastguard Worker class DrawWriter::DynamicInstances : private DrawWriter::Appender {
366*c8dee2aaSAndroid Build Coastguard Worker public:
DynamicInstances(DrawWriter & w,BindBufferInfo vertices,BindBufferInfo indices)367*c8dee2aaSAndroid Build Coastguard Worker     DynamicInstances(DrawWriter& w,
368*c8dee2aaSAndroid Build Coastguard Worker                      BindBufferInfo vertices,
369*c8dee2aaSAndroid Build Coastguard Worker                      BindBufferInfo indices)
370*c8dee2aaSAndroid Build Coastguard Worker             : Appender(w, Target::kInstances) {
371*c8dee2aaSAndroid Build Coastguard Worker         w.setTemplate(vertices, indices, w.fInstances, -1);
372*c8dee2aaSAndroid Build Coastguard Worker     }
373*c8dee2aaSAndroid Build Coastguard Worker 
~DynamicInstances()374*c8dee2aaSAndroid Build Coastguard Worker     ~DynamicInstances() override {
375*c8dee2aaSAndroid Build Coastguard Worker         // Persist the template count since the DrawWriter could continue batching if a new
376*c8dee2aaSAndroid Build Coastguard Worker         // compatible DynamicInstances object is created for the next draw.
377*c8dee2aaSAndroid Build Coastguard Worker         this->updateTemplateCount();
378*c8dee2aaSAndroid Build Coastguard Worker     }
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker     using Appender::reserve;
381*c8dee2aaSAndroid Build Coastguard Worker 
382*c8dee2aaSAndroid Build Coastguard Worker     template <typename V>
append(const V & vertexCount,unsigned int instanceCount)383*c8dee2aaSAndroid Build Coastguard Worker     VertexWriter append(const V& vertexCount, unsigned int instanceCount) {
384*c8dee2aaSAndroid Build Coastguard Worker         VertexWriter w = this->Appender::append(instanceCount);
385*c8dee2aaSAndroid Build Coastguard Worker         // Record index count after appending instance data in case the append triggered a flush
386*c8dee2aaSAndroid Build Coastguard Worker         // and the max index count is reset. However, the contents of 'w' will not have been flushed
387*c8dee2aaSAndroid Build Coastguard Worker         // so 'fProxy' will account for 'vertexCount' when it is actually drawn.
388*c8dee2aaSAndroid Build Coastguard Worker         fProxy << vertexCount;
389*c8dee2aaSAndroid Build Coastguard Worker         return w;
390*c8dee2aaSAndroid Build Coastguard Worker     }
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker private:
updateTemplateCount()393*c8dee2aaSAndroid Build Coastguard Worker     void updateTemplateCount() {
394*c8dee2aaSAndroid Build Coastguard Worker         const unsigned int count = static_cast<unsigned int>(fProxy);
395*c8dee2aaSAndroid Build Coastguard Worker         fDrawer.fTemplateCount = std::min(fDrawer.fTemplateCount, -SkTo<int>(count) - 1);
396*c8dee2aaSAndroid Build Coastguard Worker         // By resetting the proxy after updating the template count, the next batch will start over
397*c8dee2aaSAndroid Build Coastguard Worker         // with the minimum required vertex count and grow from there.
398*c8dee2aaSAndroid Build Coastguard Worker         fProxy = {};
399*c8dee2aaSAndroid Build Coastguard Worker     }
400*c8dee2aaSAndroid Build Coastguard Worker 
onFlush()401*c8dee2aaSAndroid Build Coastguard Worker     void onFlush() override {
402*c8dee2aaSAndroid Build Coastguard Worker         // Update the DrawWriter's template count before its flush() is invoked and the appender
403*c8dee2aaSAndroid Build Coastguard Worker         // starts recording to a new buffer, which ensures the flush's draw call uses the most
404*c8dee2aaSAndroid Build Coastguard Worker         // up-to-date vertex count derived from fProxy.
405*c8dee2aaSAndroid Build Coastguard Worker         this->updateTemplateCount();
406*c8dee2aaSAndroid Build Coastguard Worker     }
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker     VertexCountProxy fProxy = {};
409*c8dee2aaSAndroid Build Coastguard Worker };
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_DrawWriter_DEFINED
414