Add a writer class for indirect draw commands

This enforces write-only access to the mapped buffers, will enable
chaining of indirect strokes, and gives us the ability to reorder the
fields for Metal.

Bug: chromium:1172543
Bug: skia:11291
Bug: skia:10419
Change-Id: I4449ff85dd0019f6d6d6781ede52bcf26dee8b02
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/367416
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrBufferAllocPool.h b/src/gpu/GrBufferAllocPool.h
index ce4b02c..ad57668 100644
--- a/src/gpu/GrBufferAllocPool.h
+++ b/src/gpu/GrBufferAllocPool.h
@@ -14,6 +14,7 @@
 #include "include/private/SkTArray.h"
 #include "include/private/SkTDArray.h"
 #include "src/gpu/GrCpuBuffer.h"
+#include "src/gpu/GrDrawIndirectCommand.h"
 #include "src/gpu/GrNonAtomicRef.h"
 
 class GrGpu;
@@ -319,23 +320,23 @@
     GrDrawIndirectBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
             : GrBufferAllocPool(gpu, GrGpuBufferType::kDrawIndirect, std::move(cpuBufferCache)) {}
 
-    GrDrawIndirectCommand* makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
-        return static_cast<GrDrawIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
-                (size_t)drawCount * sizeof(GrDrawIndirectCommand), 4, buffer, offset));
+    GrDrawIndirectWriter makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
+        return this->GrBufferAllocPool::makeSpace(drawCount * sizeof(GrDrawIndirectCommand), 4,
+                                                  buffer, offset);
     }
 
     void putBack(int drawCount) {
-        this->GrBufferAllocPool::putBack((size_t)drawCount * sizeof(GrDrawIndirectCommand));
+        this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndirectCommand));
     }
 
-    GrDrawIndexedIndirectCommand* makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
-                                                   size_t* offset) {
-        return static_cast<GrDrawIndexedIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
-                (size_t)drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset));
+    GrDrawIndexedIndirectWriter makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
+                                                 size_t* offset) {
+        return this->GrBufferAllocPool::makeSpace(
+                drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset);
     }
 
     void putBackIndexed(int drawCount) {
-        this->GrBufferAllocPool::putBack((size_t)drawCount * sizeof(GrDrawIndexedIndirectCommand));
+        this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndexedIndirectCommand));
     }
 
     using GrBufferAllocPool::unmap;
diff --git a/src/gpu/GrDrawIndirectCommand.h b/src/gpu/GrDrawIndirectCommand.h
new file mode 100644
index 0000000..ba7cb05
--- /dev/null
+++ b/src/gpu/GrDrawIndirectCommand.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDrawIndirectCommand_DEFINED
+#define GrDrawIndirectCommand_DEFINED
+
+#include "src/gpu/GrCaps.h"
+#include <array>
+
+// Draw commands on the GPU are simple tuples of uint32_t. The ordering is backend-specific.
+using GrDrawIndirectCommand = std::array<uint32_t, 4>;
+using GrDrawIndexedIndirectCommand = std::array<uint32_t, 5>;
+
+// Helper for writing commands to an indirect draw buffer. Usage:
+//
+//    GrDrawIndirectWriter indirectWriter = target->makeDrawIndirectSpace(...);
+//    indirectWriter.write(...);
+//    indirectWriter.write(...);
+struct GrDrawIndirectWriter {
+public:
+    GrDrawIndirectWriter() = default;
+    GrDrawIndirectWriter(void* data) : fData(static_cast<GrDrawIndirectCommand*>(data)) {}
+    GrDrawIndirectWriter(const GrDrawIndirectWriter&) = delete;
+    GrDrawIndirectWriter(GrDrawIndirectWriter&& that) { *this = std::move(that); }
+
+    GrDrawIndirectWriter& operator=(const GrDrawIndirectWriter&) = delete;
+    GrDrawIndirectWriter& operator=(GrDrawIndirectWriter&& that) {
+        fData = that.fData;
+        that.fData = nullptr;
+        return *this;
+    }
+
+    bool isValid() const { return fData != nullptr; }
+
+    inline void write(uint32_t instanceCount, uint32_t baseInstance, uint32_t vertexCount,
+                      uint32_t baseVertex, const GrCaps&) {
+        *fData++ = {vertexCount, instanceCount, baseVertex, baseInstance};
+    }
+
+private:
+    GrDrawIndirectCommand* fData;
+};
+
+// Helper for writing commands to an indexed indirect draw buffer. Usage:
+//
+//    GrDrawIndexedIndirectWriter indirectWriter = target->makeDrawIndexedIndirectSpace(...);
+//    indirectWriter.writeIndexed(...);
+//    indirectWriter.writeIndexed(...);
+struct GrDrawIndexedIndirectWriter {
+public:
+    GrDrawIndexedIndirectWriter() = default;
+    GrDrawIndexedIndirectWriter(void* data)
+            : fData(static_cast<GrDrawIndexedIndirectCommand*>(data)) {}
+    GrDrawIndexedIndirectWriter(const GrDrawIndexedIndirectWriter&) = delete;
+    GrDrawIndexedIndirectWriter(GrDrawIndexedIndirectWriter&& that) { *this = std::move(that); }
+
+    GrDrawIndexedIndirectWriter& operator=(const GrDrawIndexedIndirectWriter&) = delete;
+    GrDrawIndexedIndirectWriter& operator=(GrDrawIndexedIndirectWriter&& that) {
+        fData = that.fData;
+        that.fData = nullptr;
+        return *this;
+    }
+
+    bool isValid() const { return fData != nullptr; }
+
+    inline void writeIndexed(uint32_t indexCount, uint32_t baseIndex, uint32_t instanceCount,
+                             uint32_t baseInstance, uint32_t baseVertex, const GrCaps&) {
+        *fData++ = {indexCount, instanceCount, baseIndex, baseVertex, baseInstance};
+    }
+
+private:
+    GrDrawIndexedIndirectCommand* fData;
+};
+
+#endif
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 5065fec..77e85d0 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -134,12 +134,13 @@
     uint16_t* makeIndexSpaceAtLeast(int minIndexCount, int fallbackIndexCount,
                                     sk_sp<const GrBuffer>*, int* startIndex,
                                     int* actualIndexCount) final;
-    GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
-                                                 size_t* offset) override {
+    GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
+                                               size_t* offset) override {
         return fDrawIndirectPool.makeSpace(drawCount, buffer, offset);
     }
-    GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
-            int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) override {
+    GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
+                                                             sk_sp<const GrBuffer>* buffer,
+                                                             size_t* offset) override {
         return fDrawIndirectPool.makeIndexedSpace(drawCount, buffer, offset);
     }
     void putBackIndices(int indexCount) final;
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index e48c14c..88468af 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -10,6 +10,7 @@
 #include "include/core/SkRect.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrCpuBuffer.h"
+#include "src/gpu/GrDrawIndirectCommand.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrPrimitiveProcessor.h"
 #include "src/gpu/GrProgramInfo.h"
@@ -284,13 +285,13 @@
     if (!this->gpu()->caps()->nativeDrawIndirectSupport()) {
         // Polyfill indirect draws with looping instanced calls.
         SkASSERT(drawIndirectBuffer->isCpuBuffer());
-        auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
-        auto cmd = reinterpret_cast<const GrDrawIndirectCommand*>(
+        auto* cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
+        auto* cmds = reinterpret_cast<const GrDrawIndirectCommand*>(
                 cpuIndirectBuffer->data() + bufferOffset);
-        auto end = cmd + drawCount;
-        for (; cmd != end; ++cmd) {
-            this->onDrawInstanced(cmd->fInstanceCount, cmd->fBaseInstance, cmd->fVertexCount,
-                                  cmd->fBaseVertex);
+        for (int i = 0; i < drawCount; ++i) {
+            // TODO: SkASSERT(caps.drawIndirectSignature() == standard);
+            auto [vertexCount, instanceCount, baseVertex, baseInstance] = cmds[i];
+            this->onDrawInstanced(instanceCount, baseInstance, vertexCount, baseVertex);
         }
         return;
     }
@@ -312,13 +313,14 @@
         this->gpu()->caps()->nativeDrawIndexedIndirectIsBroken()) {
         // Polyfill indexedIndirect draws with looping indexedInstanced calls.
         SkASSERT(drawIndirectBuffer->isCpuBuffer());
-        auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
-        auto cmd = reinterpret_cast<const GrDrawIndexedIndirectCommand*>(
+        auto* cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
+        auto* cmds = reinterpret_cast<const GrDrawIndexedIndirectCommand*>(
                 cpuIndirectBuffer->data() + bufferOffset);
-        auto end = cmd + drawCount;
-        for (; cmd != end; ++cmd) {
-            this->onDrawIndexedInstanced(cmd->fIndexCount, cmd->fBaseIndex, cmd->fInstanceCount,
-                                         cmd->fBaseInstance, cmd->fBaseVertex);
+        for (int i = 0; i < drawCount; ++i) {
+            // TODO: SkASSERT(caps.drawIndirectSignature() == standard);
+            auto [indexCount, instanceCount, baseIndex, baseVertex, baseInstance] = cmds[i];
+            this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance,
+                                         baseVertex);
         }
         return;
     }
diff --git a/src/gpu/gl/GrGLOpsRenderPass.cpp b/src/gpu/gl/GrGLOpsRenderPass.cpp
index be69a2b..812ad42 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.cpp
+++ b/src/gpu/gl/GrGLOpsRenderPass.cpp
@@ -286,11 +286,12 @@
     while (drawCount) {
         int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
         for (int i = 0; i < countInBatch; ++i) {
-            const auto& cmd = cmds[i];
-            fFirsts[i] = cmd.fBaseVertex;
-            fCounts[i] = cmd.fVertexCount;
-            fInstanceCounts[i] = cmd.fInstanceCount;
-            fBaseInstances[i] = cmd.fBaseInstance;
+            // TODO: SkASSERT(caps.drawIndirectSignature() == standard);
+            auto [vertexCount, instanceCount, baseVertex, baseInstance] = cmds[i];
+            fFirsts[i] = baseVertex;
+            fCounts[i] = vertexCount;
+            fInstanceCounts[i] = instanceCount;
+            fBaseInstances[i] = baseInstance;
         }
         if (countInBatch == 1) {
             GL_CALL(DrawArraysInstancedBaseInstance(glPrimType, fFirsts[0], fCounts[0],
@@ -359,12 +360,13 @@
     while (drawCount) {
         int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
         for (int i = 0; i < countInBatch; ++i) {
-            const auto& cmd = cmds[i];
-            fCounts[i] = cmd.fIndexCount;
-            fIndices[i] = this->offsetForBaseIndex(cmd.fBaseIndex);
-            fInstanceCounts[i] = cmd.fInstanceCount;
-            fBaseVertices[i] = cmd.fBaseVertex;
-            fBaseInstances[i] = cmd.fBaseInstance;
+            // TODO: SkASSERT(caps.drawIndirectSignature() == standard);
+            auto [indexCount, instanceCount, baseIndex, baseVertex, baseInstance] = cmds[i];
+            fCounts[i] = indexCount;
+            fIndices[i] = this->offsetForBaseIndex(baseIndex);
+            fInstanceCounts[i] = instanceCount;
+            fBaseVertices[i] = baseVertex;
+            fBaseInstances[i] = baseInstance;
         }
         if (countInBatch == 1) {
             GL_CALL(DrawElementsInstancedBaseVertexBaseInstance(glPrimType, fCounts[0],
diff --git a/src/gpu/mock/GrMockOpTarget.h b/src/gpu/mock/GrMockOpTarget.h
index a8cc8c5..9564cf3 100644
--- a/src/gpu/mock/GrMockOpTarget.h
+++ b/src/gpu/mock/GrMockOpTarget.h
@@ -56,32 +56,35 @@
         return fStaticVertexData;
     }
 
-    GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
-                                                 size_t* offsetInBytes) override {
-        int staticBufferCount = (int)SK_ARRAY_COUNT(fStaticDrawIndirectData);
-        if (drawCount > staticBufferCount) {
-            SK_ABORT("FATAL: wanted %i static drawIndirect elements; only have %i.\n",
-                     drawCount, staticBufferCount);
+    GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
+                                               size_t* offsetInBytes) override {
+        if (sizeof(GrDrawIndirectCommand) * drawCount > sizeof(fStaticIndirectData)) {
+            SK_ABORT("FATAL: wanted %zu bytes of static indirect data; only have %zu.\n",
+                     sizeof(GrDrawIndirectCommand) * drawCount, sizeof(fStaticIndirectData));
         }
         *offsetInBytes = 0;
-        return fStaticDrawIndirectData;
+        return fStaticIndirectData;
     }
 
     void putBackIndirectDraws(int count) override { /* no-op */ }
 
-    GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
-            int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offsetInBytes) override {
-        int staticBufferCount = (int)SK_ARRAY_COUNT(fStaticDrawIndexedIndirectData);
-        if (drawCount > staticBufferCount) {
-            SK_ABORT("FATAL: wanted %i static drawIndexedIndirect elements; only have %i.\n",
-                     drawCount, staticBufferCount);
+    GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
+                                                             sk_sp<const GrBuffer>* buffer,
+                                                             size_t* offsetInBytes) override {
+        if (sizeof(GrDrawIndexedIndirectCommand) * drawCount > sizeof(fStaticIndirectData)) {
+            SK_ABORT("FATAL: wanted %zu bytes of static indirect data; only have %zu.\n",
+                     sizeof(GrDrawIndexedIndirectCommand) * drawCount, sizeof(fStaticIndirectData));
         }
         *offsetInBytes = 0;
-        return fStaticDrawIndexedIndirectData;
+        return fStaticIndirectData;
     }
 
     void putBackIndexedIndirectDraws(int count) override { /* no-op */ }
 
+    // Call these methods to see what got written after the previous call to make*Space.
+    const void* peekStaticVertexData() const { return fStaticVertexData; }
+    const void* peekStaticIndirectData() const { return fStaticIndirectData; }
+
 #define UNIMPL(...) __VA_ARGS__ override { SK_ABORT("unimplemented."); }
     UNIMPL(void recordDraw(const GrGeometryProcessor*, const GrSimpleMesh[], int,
                            const GrSurfaceProxy* const[], GrPrimitiveType))
@@ -100,8 +103,7 @@
 private:
     sk_sp<GrDirectContext> fMockContext;
     char fStaticVertexData[6 * 1024 * 1024];
-    GrDrawIndirectCommand fStaticDrawIndirectData[32];
-    GrDrawIndexedIndirectCommand fStaticDrawIndexedIndirectData[32];
+    char fStaticIndirectData[sizeof(GrDrawIndexedIndirectCommand) * 32];
     SkSTArenaAllocWithReset<1024 * 1024> fAllocator;
     GrXferProcessor::DstProxyView fDstProxyView;
 };
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index b8e6c06..4704b46 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -10,6 +10,7 @@
 
 #include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrAppliedClip.h"
+#include "src/gpu/GrDrawIndirectCommand.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrSimpleMesh.h"
 #include "src/gpu/ops/GrDrawOp.h"
@@ -199,16 +200,16 @@
      * Makes space for elements in a draw-indirect buffer. Upon success, the returned pointer is a
      * CPU mapping where the data should be written.
      */
-    virtual GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount,
-                                                         sk_sp<const GrBuffer>* buffer,
-                                                         size_t* offsetInBytes) = 0;
+    virtual GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
+                                                       size_t* offsetInBytes) = 0;
 
     /**
      * Makes space for elements in a draw-indexed-indirect buffer. Upon success, the returned
      * pointer is a CPU mapping where the data should be written.
      */
-    virtual GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
-            int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offsetInBytes) = 0;
+    virtual GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
+                                                                     sk_sp<const GrBuffer>*,
+                                                                     size_t* offsetInBytes) = 0;
 
     /** Helpers for ops which over-allocate and then return excess data to the pool. */
     virtual void putBackIndices(int indices) = 0;
diff --git a/src/gpu/tessellate/GrPathTessellator.cpp b/src/gpu/tessellate/GrPathTessellator.cpp
index adeceb9..f3dc4fb 100644
--- a/src/gpu/tessellate/GrPathTessellator.cpp
+++ b/src/gpu/tessellate/GrPathTessellator.cpp
@@ -49,9 +49,10 @@
 void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkMatrix& viewMatrix,
                                         const SkPath& path,
                                         const BreadcrumbTriangleList* breadcrumbTriangleList) {
+    const GrCaps& caps = target->caps();
     SkASSERT(fTotalInstanceCount == 0);
     SkASSERT(fIndirectDrawCount == 0);
-    SkASSERT(target->caps().drawInstancedSupport());
+    SkASSERT(caps.drawInstancedSupport());
 
     int instanceLockCount = fOuterCurveInstanceCount;
     if (fDrawInnerFan) {
@@ -110,9 +111,9 @@
     // possible resolve level (kMaxResolveLevel; resolveLevel=0 never has any instances), plus one
     // more for the optional inner fan triangles.
     int indirectLockCnt = kMaxResolveLevel + 1;
-    GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
+    GrDrawIndexedIndirectWriter indirectWriter = target->makeDrawIndexedIndirectSpace(
             indirectLockCnt, &fIndirectDrawBuffer, &fIndirectDrawOffset);
-    if (!indirectData) {
+    if (!indirectWriter.isValid()) {
         SkASSERT(!fIndirectDrawBuffer);
         return;
     }
@@ -126,8 +127,10 @@
         // at the beginning of the instance buffer. Add a special-case indirect draw here that will
         // emit the triangles [P0, P1, P2] from these 4-point instances.
         SkASSERT(fIndirectDrawCount < indirectLockCnt);
-        indirectData[fIndirectDrawCount++] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
-                numTrianglesAtBeginningOfData, fBaseInstance);
+        GrMiddleOutCubicShader::WriteDrawTrianglesIndirectCmd(&indirectWriter,
+                                                              numTrianglesAtBeginningOfData,
+                                                              fBaseInstance, caps);
+        ++fIndirectDrawCount;
         runningInstanceCount = numTrianglesAtBeginningOfData;
     }
     SkASSERT(fResolveLevelCounts[0] == 0);
@@ -139,8 +142,11 @@
         }
         instanceLocations[resolveLevel] = instanceData + runningInstanceCount * 4;
         SkASSERT(fIndirectDrawCount < indirectLockCnt);
-        indirectData[fIndirectDrawCount++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
-                resolveLevel, instanceCountAtCurrLevel, fBaseInstance + runningInstanceCount);
+        GrMiddleOutCubicShader::WriteDrawCubicsIndirectCmd(&indirectWriter, resolveLevel,
+                                                           instanceCountAtCurrLevel,
+                                                           fBaseInstance + runningInstanceCount,
+                                                           caps);
+        ++fIndirectDrawCount;
         runningInstanceCount += instanceCountAtCurrLevel;
     }
 
diff --git a/src/gpu/tessellate/GrStencilPathShader.h b/src/gpu/tessellate/GrStencilPathShader.h
index 9e44b82..87a01c9 100644
--- a/src/gpu/tessellate/GrStencilPathShader.h
+++ b/src/gpu/tessellate/GrStencilPathShader.h
@@ -8,6 +8,7 @@
 #ifndef GrStencilPathShader_DEFINED
 #define GrStencilPathShader_DEFINED
 
+#include "src/gpu/GrDrawIndirectCommand.h"
 #include "src/gpu/tessellate/GrPathShader.h"
 #include "src/gpu/tessellate/GrTessellationPathRenderer.h"
 
@@ -165,25 +166,26 @@
 
     // Configures an indirect draw to render cubic instances with 2^resolveLevel evenly-spaced (in
     // the parametric sense) line segments.
-    static GrDrawIndexedIndirectCommand MakeDrawCubicsIndirectCmd(int resolveLevel,
-                                                                  uint32_t instanceCount,
-                                                                  uint32_t baseInstance) {
+    static void WriteDrawCubicsIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
+                                           int resolveLevel, uint32_t instanceCount,
+                                           uint32_t baseInstance, const GrCaps& caps) {
         SkASSERT(resolveLevel > 0 && resolveLevel <= GrTessellationPathRenderer::kMaxResolveLevel);
         // Starting at baseIndex=3, the index buffer triangulates a cubic with 2^kMaxResolveLevel
         // line segments. Each index value corresponds to a parametric T value on the curve. Since
         // the triangles are arranged in "middle-out" order, we can conveniently control the
         // resolveLevel by changing only the indexCount.
         uint32_t indexCount = NumVerticesAtResolveLevel(resolveLevel);
-        return {indexCount, instanceCount, 3, 0, baseInstance};
+        indirectWriter->writeIndexed(indexCount, 3, instanceCount, baseInstance, 0, caps);
     }
 
     // For performance reasons we can often express triangles as an indirect cubic draw and sneak
     // them in alongside the other indirect draws. This method configures an indirect draw to emit
     // the triangle [P0, P1, P2] from a 4-point instance.
-    static GrDrawIndexedIndirectCommand MakeDrawTrianglesIndirectCmd(uint32_t instanceCount,
-                                                                     uint32_t baseInstance) {
+    static void WriteDrawTrianglesIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
+                                              uint32_t instanceCount, uint32_t baseInstance,
+                                              const GrCaps& caps) {
         // Indices 0,1,2 have special index values that emit points P0, P1, and P2 respectively.
-        return {3, instanceCount, 0, 0, baseInstance};
+        indirectWriter->writeIndexed(3, 0, instanceCount, baseInstance, 0, caps);
     }
 
     // Returns the index buffer that should be bound when drawing with this shader.
diff --git a/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp b/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
index 1e8080c..7ca6467 100644
--- a/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
+++ b/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
@@ -599,11 +599,14 @@
         return;
     }
 
+    const GrCaps& caps = target->caps();
+
     // Allocate enough indirect commands for every resolve level. We will putBack the unused ones
     // at the end.
-    GrDrawIndirectCommand* drawIndirectData = target->makeDrawIndirectSpace(
-            kMaxResolveLevel + 1, &fDrawIndirectBuffer, &fDrawIndirectOffset);
-    if (!drawIndirectData) {
+    GrDrawIndirectWriter indirectWriter = target->makeDrawIndirectSpace(kMaxResolveLevel + 1,
+                                                                        &fDrawIndirectBuffer,
+                                                                        &fDrawIndirectOffset);
+    if (!indirectWriter.isValid()) {
         SkASSERT(!fDrawIndirectBuffer);
         return;
     }
@@ -627,11 +630,9 @@
     for (int i = 0; i <= kMaxResolveLevel; ++i) {
         if (fResolveLevelCounts[i]) {
             int numEdges = numExtraEdgesInJoin + num_edges_in_resolve_level(i);
-            auto& cmd = drawIndirectData[fDrawIndirectCount++];
-            cmd.fVertexCount = numEdges * 2;
-            cmd.fInstanceCount = fResolveLevelCounts[i];
-            cmd.fBaseVertex = 0;
-            cmd.fBaseInstance = baseInstance + currentInstanceIdx;
+            indirectWriter.write(fResolveLevelCounts[i], baseInstance + currentInstanceIdx,
+                                 numEdges * 2, 0, caps);
+            ++fDrawIndirectCount;
             numEdgesPerResolveLevel[i] = numEdges;
             nextInstanceLocations[i] = instanceData + currentInstanceIdx;
 #ifdef SK_DEBUG
diff --git a/src/gpu/tessellate/GrStrokeIndirectTessellator.h b/src/gpu/tessellate/GrStrokeIndirectTessellator.h
index 54bb7ed..c91bfc2 100644
--- a/src/gpu/tessellate/GrStrokeIndirectTessellator.h
+++ b/src/gpu/tessellate/GrStrokeIndirectTessellator.h
@@ -61,9 +61,9 @@
 
 #if GR_TEST_UTILS
 public:
-    void verifyResolveLevels(skiatest::Reporter*, GrMeshDrawOp::Target*, const SkMatrix&,
+    void verifyResolveLevels(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
                              const SkPath&, const SkStrokeRec&);
-    void verifyBuffers(skiatest::Reporter*, GrMeshDrawOp::Target*, const SkMatrix&,
+    void verifyBuffers(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
                        const SkStrokeRec&);
     class Benchmark;
 #endif