• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
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 skgpu_DrawWriter_DEFINED
9 #define skgpu_DrawWriter_DEFINED
10 
11 #include "experimental/graphite/src/DrawTypes.h"
12 #include "src/gpu/BufferWriter.h"
13 
14 namespace skgpu {
15 
16 class DrawBufferManager;
17 
18 class DrawDispatcher; // Forward declaration, handles virtual dispatch of binds/draws
19 
20 /**
21  * DrawWriter is a helper around recording draws (to a temporary buffer or directly to a
22  * CommandBuffer), particularly when the number of draws is not known ahead of time, or the vertex
23  * and instance data is computed at record time and does not have a known size.
24  *
25  * To use, construct the DrawWriter with the current pipeline layout or call newPipelineState() on
26  * an existing DrawWriter and then bind that matching pipeline. When other dynamic state needs to
27  * change between draw calls, notify the DrawWriter using newDynamicState() before recording the
28  * modifications. See the listing below for how to append dynamic data or draw with existing buffers
29  *
30  * CommandBuffer::draw(vertices)
31  *  - dynamic vertex data     -> DrawWriter::Vertices(writer) verts;
32  *                               verts.append(n) << ...;
33  *  - fixed vertex data       -> writer.draw(vertices, {}, vertexCount)
34  *
35  * CommandBuffer::drawIndexed(vertices, indices)
36  *  - dynamic vertex data     -> unsupported
37  *  - fixed vertex,index data -> writer.drawIndexed(vertices, indices, indexCount)
38  *
39  * CommandBuffer::drawInstances(vertices, instances)
40  *  - dynamic instance data + fixed vertex data        ->
41  *        DrawWriter::Instances instances(writer, vertices, {}, vertexCount);
42  *        instances.append(n) << ...;
43  *  - fixed vertex and instance data                   ->
44  *        writer.drawInstanced(vertices, vertexCount, instances, instanceCount)
45  *
46  * CommandBuffer::drawIndexedInstanced(vertices, indices, instances)
47  *  - dynamic instance data + fixed vertex, index data ->
48  *        DrawWriter::Instances instances(writer, vertices, indices, indexCount);
49  *        instances.append(n) << ...;
50  *  - fixed vertex, index, and instance data           ->
51  *        writer.drawIndexedInstanced(vertices, indices, indexCount, instances, instanceCount)
52  */
53 class DrawWriter {
54 public:
55     // NOTE: This constructor creates a writer that defaults 0 vertex and instance stride, so
56     // 'newPipelineState()' must be called once the pipeline properties are known before it's used.
57     DrawWriter(DrawDispatcher*, DrawBufferManager*);
58 
59     DrawWriter(DrawDispatcher*, DrawBufferManager*,
60                PrimitiveType type, size_t vertexStride, size_t instanceStride);
61 
62     // Cannot move or copy
63     DrawWriter(const DrawWriter&) = delete;
64     DrawWriter(DrawWriter&&) = delete;
65 
66     // flush() should be called before the writer is destroyed
~DrawWriter()67     ~DrawWriter() { SkASSERT(fPendingCount == 0); }
68 
bufferManager()69     DrawBufferManager* bufferManager() { return fManager; }
70 
71     // Issue draw calls for any pending vertex and instance data collected by the writer.
72     // Use either flush() or newDynamicState() based on context and readability.
73     void flush();
newDynamicState()74     void newDynamicState() { this->flush(); }
75 
76     // Notify the DrawWriter that a new pipeline needs to be bound, providing the primitive type and
77     // attribute strides of that pipeline. This issues draw calls for pending data that relied on
78     // the old pipeline, so this must be called *before* binding the new pipeline.
newPipelineState(PrimitiveType type,size_t vertexStride,size_t instanceStride)79     void newPipelineState(PrimitiveType type, size_t vertexStride, size_t instanceStride) {
80         this->flush();
81         fPrimitiveType = type;
82         fVertexStride = vertexStride;
83         fInstanceStride = instanceStride;
84 
85         // NOTE: resetting pending base is sufficient to redo bindings for vertex/instance data that
86         // is later appended but doesn't invalidate bindings for fixed buffers that might not need
87         // to change between pipelines.
88         fPendingBase = 0;
89         SkASSERT(fPendingCount == 0);
90     }
91 
92     // Collects new vertex data for a call to CommandBuffer::draw(). Automatically accumulates
93     // vertex data into a buffer, issuing draw and bind calls as needed when a new buffer is
94     // required, so that it is seamless to the caller. The draws do not use instances or indices.
95     //
96     // Usage (assuming writer has already had 'newPipelineState()' called with correct strides):
97     //    DrawWriter::Vertices verts{writer};
98     //    verts.append(n) << x << y << ...;
99     //
100     // This should not be used when the vertex stride is 0.
101     class Vertices;
102 
103     // Collects new instance data for a call to CommandBuffer::drawInstanced() or
104     // drawIndexedInstanced(). The specific draw call that's issued depends on if a non-null index
105     // buffer is provided for the template. Like DrawWriter::Vertices, this automatically merges
106     // the appended data into as few buffer binds and draw calls as possible, while remaining
107     // seamless to the caller.
108     //
109     // Usage for drawInstanced (assuming writer has correct strides):
110     //    DrawWriter::Instances instances{writer, fixedVerts, {}, fixedVertexCount};
111     //    instances.append(n) << foo << bar << ...;
112     //
113     // Usage for drawIndexedInstanced:
114     //    DrawWriter::Instances instances{writer, fixedVerts, fixedIndices, fixedIndexCount};
115     //    instances.append(n) << foo << bar << ...;
116     //
117     // This should not be used when the instance stride is 0. However, the fixed vertex buffer can
118     // be null (or have a stride of 0) if the vertex shader only relies on the vertex ID and no
119     // other per-vertex data.
120     class Instances;
121 
122     // Issues a draws with fully specified data. This can be used when all instance data has already
123     // been written to known buffers, or when the vertex shader only depends on the vertex or
124     // instance IDs. To keep things simple, these helpers do not accept parameters for base vertices
125     // or instances; if needed, this can be accounted for in the BindBufferInfos provided.
126     //
127     // This will not merge with any already appended instance or vertex data, pending data is issued
128     // in its own draw call first.
draw(BindBufferInfo vertices,unsigned int vertexCount)129     void draw(BindBufferInfo vertices, unsigned int vertexCount) {
130         this->bindAndFlush(vertices, {}, {}, 0, vertexCount);
131     }
drawIndexed(BindBufferInfo vertices,BindBufferInfo indices,unsigned int indexCount)132     void drawIndexed(BindBufferInfo vertices, BindBufferInfo indices, unsigned int indexCount) {
133         this->bindAndFlush(vertices, indices, {}, 0, indexCount);
134     }
drawInstanced(BindBufferInfo vertices,unsigned int vertexCount,BindBufferInfo instances,unsigned int instanceCount)135     void drawInstanced(BindBufferInfo vertices, unsigned int vertexCount,
136                        BindBufferInfo instances, unsigned int instanceCount) {
137         this->bindAndFlush(vertices, {}, instances, vertexCount, instanceCount);
138     }
drawIndexedInstanced(BindBufferInfo vertices,BindBufferInfo indices,unsigned int indexCount,BindBufferInfo instances,unsigned int instanceCount)139     void drawIndexedInstanced(BindBufferInfo vertices, BindBufferInfo indices,
140                               unsigned int indexCount, BindBufferInfo instances,
141                               unsigned int instanceCount) {
142         this->bindAndFlush(vertices, indices, instances, indexCount, instanceCount);
143     }
144 
145 private:
146     // Both of these pointers must outlive the DrawWriter.
147     DrawDispatcher*    fDispatcher;
148     DrawBufferManager* fManager;
149 
150     // Pipeline state matching currently bound pipeline
151     PrimitiveType fPrimitiveType;
152     size_t fVertexStride;
153     size_t fInstanceStride;
154 
155     /// Draw buffer binding state for pending draws
156     BindBufferInfo fVertices;
157     BindBufferInfo fIndices;
158     BindBufferInfo fInstances;
159     // Vertex/index count for [pseudo]-instanced rendering:
160     // == 0 is vertex-only drawing; > 0 is regular instanced drawing
161     unsigned int fTemplateCount;
162 
163     unsigned int fPendingCount; // # of vertices or instances (depending on mode) to be drawn
164     unsigned int fPendingBase; // vertex/instance offset (depending on mode) applied to buffer
165     bool fPendingBufferBinds; // true if {fVertices,fIndices,fInstances} has changed since last draw
166 
167     void setTemplate(BindBufferInfo vertices, BindBufferInfo indices, BindBufferInfo instances,
168                      unsigned int templateCount);
bindAndFlush(BindBufferInfo vertices,BindBufferInfo indices,BindBufferInfo instances,unsigned int templateCount,unsigned int drawCount)169     void bindAndFlush(BindBufferInfo vertices, BindBufferInfo indices, BindBufferInfo instances,
170                       unsigned int templateCount, unsigned int drawCount) {
171         this->setTemplate(vertices, indices, instances, templateCount);
172         fPendingBase = 0;
173         fPendingCount = drawCount;
174         this->flush();
175     }
176 
177     // RAII - Sets the DrawWriter's template and marks the writer in append mode (disabling direct
178     // draws until the Appender is destructed).
179     class Appender;
180     SkDEBUGCODE(const Appender* fAppender = nullptr;)
181 };
182 
183 // Mirrors the CommandBuffer API, since a DrawWriter is meant to aggregate and then map onto
184 // CommandBuffer commands, although these are virtual to allow for recording to intermediate
185 // storage before a CommandBuffer is available.
186 class DrawDispatcher {
187 public:
188     virtual ~DrawDispatcher() = default;
189 
190     virtual void bindDrawBuffers(BindBufferInfo vertexAttribs,
191                                  BindBufferInfo instanceAttribs,
192                                  BindBufferInfo indices) = 0;
193 
194     virtual void draw(PrimitiveType type, unsigned int baseVertex, unsigned int vertexCount) = 0;
195     virtual void drawIndexed(PrimitiveType type, unsigned int baseIndex,
196                              unsigned int indexCount, unsigned int baseVertex) = 0;
197     virtual void drawInstanced(PrimitiveType type,
198                                unsigned int baseVertex, unsigned int vertexCount,
199                                unsigned int baseInstance, unsigned int instanceCount) = 0;
200     virtual void drawIndexedInstanced(PrimitiveType type,
201                                       unsigned int baseIndex, unsigned int indexCount,
202                                       unsigned int baseVertex, unsigned int baseInstance,
203                                       unsigned int instanceCount) = 0;
204 };
205 
206 // Appender implementations for DrawWriter that set the template on creation and provide a
207 // template-specific API to accumulate vertex/instance data.
208 class DrawWriter::Appender {
209 public:
Appender(DrawWriter & w)210     Appender(DrawWriter& w) : fWriter(w) {
211         SkASSERT(!w.fAppender);
212         SkDEBUGCODE(w.fAppender = this;)
213     }
214 
~Appender()215     ~Appender() {
216         SkASSERT(fWriter.fAppender == this);
217         SkDEBUGCODE(fWriter.fAppender = nullptr;)
218     }
219 
220 protected:
221     DrawWriter& fWriter;
222 
223     VertexWriter append(unsigned int count, size_t stride, BindBufferInfo& target);
224 };
225 
226 class DrawWriter::Vertices : private DrawWriter::Appender {
227 public:
Vertices(DrawWriter & w)228     Vertices(DrawWriter& w) : Appender(w) {
229         SkASSERT(w.fVertexStride > 0);
230         w.setTemplate(w.fVertices, {}, {}, 0);
231     }
232 
append(unsigned int count)233     VertexWriter append(unsigned int count) {
234         return this->Appender::append(count, fWriter.fVertexStride, fWriter.fVertices);
235     }
236 };
237 
238 class DrawWriter::Instances : private DrawWriter::Appender {
239 public:
Instances(DrawWriter & w,BindBufferInfo vertices,BindBufferInfo indices,unsigned int vertexCount)240     Instances(DrawWriter& w,
241               BindBufferInfo vertices,
242               BindBufferInfo indices,
243               unsigned int vertexCount)
244             : Appender(w) {
245         SkASSERT(w.fInstanceStride > 0);
246         w.setTemplate(vertices, indices, w.fInstances, vertexCount);
247     }
248 
append(unsigned int count)249     VertexWriter append(unsigned int count) {
250         return this->Appender::append(count, fWriter.fInstanceStride, fWriter.fInstances);
251     }
252 };
253 
254 } // namespace skgpu
255 
256 #endif // skgpu_DrawWriter_DEFINED
257