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