Redo glyph quad regeneration
Lift all the invariants for the loop into the onPrepare loops
from regenerate and updateTextureCoordinatesMaybeStrike.
* Simplify looping over geos and subRuns
* remove VertexRegenerator::Result
* remove fCurrGlyph
Change-Id: I75445c6d7113207a3b1544154b605af2c4cfcb31
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/263778
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 7a41cac..a50d4e8 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -331,58 +331,75 @@
numActiveViews, filter, maskFormat, localMatrix, vmPerspective);
}
- size_t vertexStride = flushInfo.fGeometryProcessor->vertexStride();
+ int vertexStride = (int)flushInfo.fGeometryProcessor->vertexStride();
// Ensure we don't request an insanely large contiguous vertex allocation.
- static const size_t kMaxVertexBytes = GrBufferAllocPool::kDefaultBufferSize;
- int maxGlyphsInBuffer = kMaxVertexBytes / (vertexStride * kVerticesPerGlyph);
- int totalGlyphCount = this->numGlyphs();
- int bufferGlyphCount = std::min(totalGlyphCount, maxGlyphsInBuffer);
- void* vertices = target->makeVertexSpace(vertexStride, bufferGlyphCount * kVerticesPerGlyph,
- &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset);
+ static const int kMaxVertexBytes = GrBufferAllocPool::kDefaultBufferSize;
+ const int quadSize = vertexStride * kVerticesPerGlyph;
+ const int maxQuadsPerBuffer = kMaxVertexBytes / quadSize;
+
+ // Where the quad buffer begins and ends relative to totalGlyphsRegened.
+ int quadBufferBegin = 0;
+ int quadBufferEnd = std::min(this->numGlyphs(), maxQuadsPerBuffer);
+
flushInfo.fIndexBuffer = resourceProvider->refNonAAQuadIndexBuffer();
+ void* vertices = target->makeVertexSpace(
+ vertexStride,
+ kVerticesPerGlyph * (quadBufferEnd - quadBufferBegin),
+ &flushInfo.fVertexBuffer,
+ &flushInfo.fVertexOffset);
if (!vertices || !flushInfo.fVertexBuffer) {
SkDebugf("Could not allocate vertices\n");
return;
}
- char* currVertex = reinterpret_cast<char*>(vertices);
-
- // each of these is a SubRun
+ // totalGlyphsRegened is all the glyphs for the op [0, this->numGlyphs()). The subRun glyph and
+ // quad buffer indices are calculated from this.
+ int totalGlyphsRegened = 0;
for (int i = 0; i < fGeoCount; i++) {
const Geometry& args = fGeoData[i];
+ auto subRun = args.fSubRunPtr;
+ SkASSERT((int)subRun->vertexStride() == vertexStride);
+
// TODO4F: Preserve float colors
GrTextBlob::VertexRegenerator regenerator(
resourceProvider, args.fSubRunPtr, args.fDrawMatrix, args.fDrawOrigin,
args.fColor.toBytes_RGBA(), target->deferredUploadTarget(), glyphCache,
atlasManager);
- // This loop issues draws until regenerator says we're done with this geo. Regenerator
- // breaks things up if inline uploads are necessary.
- GrTextBlob::VertexRegenerator::Result result;
- while (!result.fFinished) {
- // Copy regenerated vertices from the blob to our vertex buffer. If we overflow our
- // vertex buffer we'll issue a draw and then get more vertex buffer space.
- do {
- if (!bufferGlyphCount) {
- this->flush(target, &flushInfo);
- bufferGlyphCount = std::min(totalGlyphCount, maxGlyphsInBuffer);
- vertices = target->makeVertexSpace(
- vertexStride, bufferGlyphCount * kVerticesPerGlyph,
- &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset);
- currVertex = reinterpret_cast<char*>(vertices);
- }
- if (!regenerator.regenerate(&result, bufferGlyphCount)) {
- return;
- }
- int glyphCount = std::min(result.fGlyphsRegenerated, bufferGlyphCount);
- int vertexCount = glyphCount * kVerticesPerGlyph;
- size_t vertexBytes = vertexCount * vertexStride;
+
+ // Where the subRun begins and ends relative to totalGlyphsRegened.
+ int subRunBegin = totalGlyphsRegened;
+ int subRunEnd = subRunBegin + (int)subRun->fGlyphs.size();
+
+ // Draw all the glyphs in the subRun.
+ while (totalGlyphsRegened < subRunEnd) {
+ // drawBegin and drawEnd are indices for the subRun on the
+ // interval [0, subRun->fGlyphs.size()).
+ int drawBegin = totalGlyphsRegened - subRunBegin;
+ // drawEnd is either the end of the subRun or the end of the current quad buffer.
+ int drawEnd = std::min(subRunEnd, quadBufferEnd) - subRunBegin;
+ auto[ok, glyphsRegenerated] = regenerator.regenerate(drawBegin, drawEnd);
+
+ // There was a problem allocating the glyph in the atlas. Bail.
+ if(!ok) { return; }
+
+ // Update all the vertices for glyphsRegenerate glyphs.
+ if (glyphsRegenerated > 0) {
+ int quadBufferIndex = totalGlyphsRegened - quadBufferBegin;
+ int subRunIndex = totalGlyphsRegened - subRunBegin;
+ auto regeneratedQuadBuffer =
+ SkTAddOffset<char>(vertices, subRun->quadOffset(quadBufferIndex));
if (args.fClipRect.isEmpty()) {
- memcpy(currVertex, result.fFirstVertex, vertexBytes);
+ memcpy(regeneratedQuadBuffer,
+ subRun->quadStart(subRunIndex),
+ glyphsRegenerated * quadSize);
} else {
SkASSERT(!vmPerspective);
- clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
- glyphCount);
+ clip_quads(args.fClipRect,
+ regeneratedQuadBuffer,
+ subRun->quadStart(subRunIndex),
+ vertexStride,
+ glyphsRegenerated);
}
if (fNeedsGlyphTransform && !args.fDrawMatrix.isIdentity()) {
// We always do the distance field view matrix transformation after copying
@@ -390,30 +407,54 @@
// successive arbitrary transformations would be complicated and accumulate
// error.
if (args.fDrawMatrix.hasPerspective()) {
- auto* pos = reinterpret_cast<SkPoint3*>(currVertex);
- SkMatrixPriv::MapHomogeneousPointsWithStride(args.fDrawMatrix, pos,
- vertexStride, pos,
- vertexStride, vertexCount);
+ auto* pos = reinterpret_cast<SkPoint3*>(regeneratedQuadBuffer);
+ SkMatrixPriv::MapHomogeneousPointsWithStride(
+ args.fDrawMatrix, pos,
+ vertexStride, pos,
+ vertexStride,
+ glyphsRegenerated * kVerticesPerGlyph);
} else {
- auto* pos = reinterpret_cast<SkPoint*>(currVertex);
+ auto* pos = reinterpret_cast<SkPoint*>(regeneratedQuadBuffer);
SkMatrixPriv::MapPointsWithStride(args.fDrawMatrix, pos, vertexStride,
- vertexCount);
+ glyphsRegenerated * kVerticesPerGlyph);
}
}
- flushInfo.fGlyphsToFlush += glyphCount;
- currVertex += vertexBytes;
- result.fFirstVertex += vertexBytes;
- result.fGlyphsRegenerated -= glyphCount;
- bufferGlyphCount -= glyphCount;
- totalGlyphCount -= glyphCount;
- } while (result.fGlyphsRegenerated);
- if (!result.fFinished) {
- this->flush(target, &flushInfo);
}
- } // for all vertices
+
+ totalGlyphsRegened += glyphsRegenerated;
+ flushInfo.fGlyphsToFlush += glyphsRegenerated;
+
+ // regenerate() has stopped part way through a SubRun. This means that either the atlas
+ // or the quad buffer is full or both. There is a case were the flow through
+ // the loop is strange. If we run out of quad buffer space at the same time the
+ // SubRun ends, then this is not triggered which is the right result for the last
+ // SubRun. But, if this is not the last SubRun, then advance to the next SubRun which
+ // will process no glyphs, and return to this point where the quad buffer will be
+ // expanded.
+ if (totalGlyphsRegened != subRunEnd) {
+ // Flush if not all glyphs drawn because either the quad buffer is full or the
+ // atlas is out of space.
+ this->flush(target, &flushInfo);
+ if (totalGlyphsRegened == quadBufferEnd) {
+ // Quad buffer is full. Get more buffer.
+ quadBufferBegin = totalGlyphsRegened;
+ int quadBufferSize =
+ std::min(maxQuadsPerBuffer, this->numGlyphs() - totalGlyphsRegened);
+ quadBufferEnd = quadBufferBegin + quadBufferSize;
+
+ vertices = target->makeVertexSpace(
+ vertexStride,
+ kVerticesPerGlyph * quadBufferSize,
+ &flushInfo.fVertexBuffer,
+ &flushInfo.fVertexOffset);
+ if (!vertices || !flushInfo.fVertexBuffer) {
+ SkDebugf("Could not allocate vertices\n");
+ return;
+ }
+ }
+ }
+ }
} // for all geometries
- SkASSERT(!bufferGlyphCount);
- SkASSERT(!totalGlyphCount);
this->flush(target, &flushInfo);
}