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