Extract a GrVertexChunkArray class with a builder
This will be used by the new stroke tessellator. All the other
tessellators should start chopping and chunking too. That will allow us
to quit cropping paths if we are afraid they might need more segments
than are supported.
Bug: chromium:1172543
Change-Id: I30f0ebb581f56cac099d8c05e0e181c4657c3db8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/390096
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrVertexChunkArray.h b/src/gpu/GrVertexChunkArray.h
new file mode 100644
index 0000000..febde51
--- /dev/null
+++ b/src/gpu/GrVertexChunkArray.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVertexChunkArray_DEFINED
+#define GrVertexChunkArray_DEFINED
+
+#include "include/private/SkNoncopyable.h"
+#include "include/private/SkTArray.h"
+#include "src/gpu/GrBuffer.h"
+#include "src/gpu/GrVertexWriter.h"
+#include "src/gpu/ops/GrMeshDrawOp.h"
+
+// Represents a chunk of vertex data. Use with GrVertexChunkArray and GrVertexChunkBuilder. We write
+// the data out in chunks when we don't start out knowing exactly how many vertices (or instances)
+// we will end up writing.
+struct GrVertexChunk {
+ sk_sp<const GrBuffer> fBuffer;
+ int fVertexCount = 0;
+ int fBaseVertex;
+};
+
+// Represents an array of GrVertexChunks.
+//
+// We only preallocate 1 chunk because if the array needs to grow, then we're also allocating a
+// brand new GPU buffer anyway.
+using GrVertexChunkArray = SkSTArray<1, GrVertexChunk>;
+
+// Builds a GrVertexChunkArray. The provided Target must not be used externally throughout the
+// entire lifetime of this object.
+class GrVertexChunkBuilder : SkNoncopyable {
+public:
+ GrVertexChunkBuilder(GrMeshDrawOp::Target* target, GrVertexChunkArray* chunks, size_t stride,
+ int minVerticesPerChunk)
+ : fTarget(target)
+ , fChunks(chunks)
+ , fStride(stride)
+ , fMinVerticesPerChunk(minVerticesPerChunk) {
+ SkASSERT(fMinVerticesPerChunk > 0);
+ }
+
+ ~GrVertexChunkBuilder() {
+ if (!fChunks->empty()) {
+ fTarget->putBackVertices(fCurrChunkVertexCapacity - fCurrChunkVertexCount, fStride);
+ fChunks->back().fVertexCount = fCurrChunkVertexCount;
+ }
+ }
+
+ // Appends 'count' contiguous vertices. These vertices are not guaranteed to be contiguous with
+ // previous or future calls to appendVertices.
+ SK_ALWAYS_INLINE GrVertexWriter appendVertices(int count) {
+ SkASSERT(count > 0);
+ if (fCurrChunkVertexCount + count > fCurrChunkVertexCapacity && !this->allocChunk(count)) {
+ return {nullptr};
+ }
+ SkASSERT(fCurrChunkVertexCount + count <= fCurrChunkVertexCapacity);
+ fCurrChunkVertexCount += count;
+ return std::exchange(fCurrChunkVertexWriter,
+ fCurrChunkVertexWriter.makeOffset(fStride * count));
+ }
+
+ SK_ALWAYS_INLINE GrVertexWriter appendVertex() { return this->appendVertices(1); }
+
+private:
+ bool allocChunk(int minCount) {
+ if (!fChunks->empty()) {
+ // No need to put back vertices; the buffer is full.
+ fChunks->back().fVertexCount = fCurrChunkVertexCount;
+ }
+ fCurrChunkVertexCount = 0;
+ GrVertexChunk* chunk = &fChunks->push_back();
+ fCurrChunkVertexWriter = {fTarget->makeVertexSpaceAtLeast(fStride,
+ fMinVerticesPerChunk * minCount,
+ fMinVerticesPerChunk * minCount,
+ &chunk->fBuffer,
+ &chunk->fBaseVertex,
+ &fCurrChunkVertexCapacity)};
+ if (!fCurrChunkVertexWriter || !chunk->fBuffer || fCurrChunkVertexCapacity < minCount) {
+ SkDebugf("WARNING: Failed to allocate vertex buffer for GrVertexChunk.\n");
+ fChunks->pop_back();
+ SkASSERT(fCurrChunkVertexCount == 0);
+ fCurrChunkVertexCapacity = 0;
+ return false;
+ }
+ fMinVerticesPerChunk *= 2;
+ return true;
+ }
+
+ GrMeshDrawOp::Target* const fTarget;
+ GrVertexChunkArray* const fChunks;
+ const size_t fStride;
+ size_t fMinVerticesPerChunk;
+
+ GrVertexWriter fCurrChunkVertexWriter;
+ int fCurrChunkVertexCount = 0;
+ int fCurrChunkVertexCapacity = 0;
+};
+
+#endif