Make GrAtlasTextBlob return to caller when a flush is required during subrun tessellation.
The old code used a helper object in the tessellation code that called flush() on GrAtlasTextOp.
A confusing aspect of this was that the pre-flush vertex data generated for the sub run was copied
to the op's vertex buffer after flush() had already recorded the draw that read the data.
The new code adds a tessellator nested helper class to GrAtlasTextBlob. The helper exits early if
a flush is required, the op performs the flush, and then the helper is invoked again until
tessellation is complete.
This also changes the blob object to use char* instead of unsigned char* for its vertex pointers.
Change-Id: I31bed251435f13b2172e6f5829ba437b882dd44d
Reviewed-on: https://skia-review.googlesource.com/67856
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 2383510..9e98466 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -76,12 +76,12 @@
return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
}
-static void clip_quads(const SkIRect& clipRect,
- unsigned char* currVertex, unsigned char* blobVertices,
+static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
size_t vertexStride, int glyphCount) {
for (int i = 0; i < glyphCount; ++i) {
- SkPoint* blobPositionLT = reinterpret_cast<SkPoint*>(blobVertices);
- SkPoint* blobPositionRB = reinterpret_cast<SkPoint*>(blobVertices + 3*vertexStride);
+ const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
+ const SkPoint* blobPositionRB =
+ reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);
// positions for bitmap glyphs are pixel boundary aligned
SkIRect positionRect = SkIRect::MakeLTRB(SkScalarFloorToInt(blobPositionLT->fX),
@@ -95,11 +95,11 @@
// Pull out some more data that we'll need.
// In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
// and it avoids a lot of conditionals.
- SkColor color = *reinterpret_cast<SkColor*>(blobVertices + sizeof(SkPoint));
+ auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
- uint16_t* blobCoordsLT = reinterpret_cast<uint16_t*>(blobVertices + coordOffset);
- uint16_t* blobCoordsRB = reinterpret_cast<uint16_t*>(blobVertices + 3*vertexStride +
- coordOffset);
+ auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
+ auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
+ coordOffset);
// Pull out the texel coordinates and texture index bits
uint16_t coordsRectL = blobCoordsLT[0] >> 1;
uint16_t coordsRectT = blobCoordsLT[1] >> 1;
@@ -224,32 +224,34 @@
return;
}
- unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
+ char* currVertex = reinterpret_cast<char*>(vertices);
- GrBlobRegenHelper helper(this, target, &flushInfo);
SkAutoGlyphCache glyphCache;
// each of these is a SubRun
for (int i = 0; i < fGeoCount; i++) {
const Geometry& args = fGeoData[i];
Blob* blob = args.fBlob;
- size_t byteCount;
- void* blobVertices;
- int subRunGlyphCount;
- blob->regenInOp(target->deferredUploadTarget(), fFontCache, &helper, args.fRun,
- args.fSubRun, &glyphCache, vertexStride, args.fViewMatrix, args.fX, args.fY,
- args.fColor, &blobVertices, &byteCount, &subRunGlyphCount);
-
- // now copy all vertices
- if (args.fClipRect.isEmpty()) {
- memcpy(currVertex, blobVertices, byteCount);
- } else {
- clip_quads(args.fClipRect, currVertex, reinterpret_cast<unsigned char*>(blobVertices),
- vertexStride, subRunGlyphCount);
- }
-
- currVertex += byteCount;
+ GrAtlasTextBlob::VertexRegenerator regenerator(
+ blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY, args.fColor,
+ target->deferredUploadTarget(), fFontCache, &glyphCache, vertexStride);
+ GrAtlasTextBlob::VertexRegenerator::Result result;
+ do {
+ result = regenerator.regenerate();
+ // Copy regenerated vertices from the blob to our vertex buffer.
+ size_t vertexBytes = result.fGlyphsRegenerated * kVerticesPerGlyph * vertexStride;
+ if (args.fClipRect.isEmpty()) {
+ memcpy(currVertex, result.fFirstVertex, vertexBytes);
+ } else {
+ clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
+ result.fGlyphsRegenerated);
+ }
+ flushInfo.fGlyphsToFlush += result.fGlyphsRegenerated;
+ if (!result.fFinished) {
+ this->flush(target, &flushInfo);
+ }
+ currVertex += vertexBytes;
+ } while (!result.fFinished);
}
-
this->flush(target, &flushInfo);
}
@@ -406,4 +408,3 @@
}
}
-void GrBlobRegenHelper::flush() { fOp->flush(fTarget, fFlushInfo); }
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 1814676..240b98b 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -206,29 +206,7 @@
SkColor fLuminanceColor;
bool fUseGammaCorrectDistanceTable;
- friend class GrBlobRegenHelper; // Needs to trigger flushes
-
typedef GrMeshDrawOp INHERITED;
};
-/*
- * A simple helper class to abstract the interface GrAtlasTextBlob needs to regenerate itself.
- * It'd be nicer if this was nested, but we need to forward declare it in GrAtlasTextBlob.h
- */
-class GrBlobRegenHelper {
-public:
- GrBlobRegenHelper(const GrAtlasTextOp* op, GrMeshDrawOp::Target* target,
- GrAtlasTextOp::FlushInfo* flushInfo)
- : fOp(op), fTarget(target), fFlushInfo(flushInfo) {}
-
- void flush();
-
- void incGlyphCount(int glyphCount = 1) { fFlushInfo->fGlyphsToFlush += glyphCount; }
-
-private:
- const GrAtlasTextOp* fOp;
- GrMeshDrawOp::Target* fTarget;
- GrAtlasTextOp::FlushInfo* fFlushInfo;
-};
-
#endif
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 17f81a2..0b25a34 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -34,8 +34,7 @@
cacheBlob->fSize = size;
// setup offsets for vertices / glyphs
- cacheBlob->fVertices = sizeof(GrAtlasTextBlob) +
- reinterpret_cast<unsigned char*>(cacheBlob.get());
+ cacheBlob->fVertices = sizeof(GrAtlasTextBlob) + reinterpret_cast<char*>(cacheBlob.get());
cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount);
cacheBlob->fRuns = reinterpret_cast<GrAtlasTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount);
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index 495e72a..403cbbf 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -21,7 +21,6 @@
#include "SkSurfaceProps.h"
#include "SkTInternalLList.h"
-class GrBlobRegenHelper;
struct GrDistanceFieldAdjustTable;
class GrMemoryPool;
class SkDrawFilter;
@@ -50,6 +49,8 @@
public:
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrAtlasTextBlob);
+ class VertexRegenerator;
+
static sk_sp<GrAtlasTextBlob> Make(GrMemoryPool* pool, int glyphCount, int runCount);
struct Key {
@@ -250,16 +251,6 @@
this->setupViewMatrix(viewMatrix, x, y);
}
- /**
- * Consecutive calls to regenInOp often use the same SkGlyphCache. If the same instance of
- * SkAutoGlyphCache is passed to multiple calls of regenInOp then it can save the cost of
- * multiple detach/attach operations of SkGlyphCache.
- */
- void regenInOp(GrDeferredUploadTarget*, GrAtlasGlyphCache* fontCache, GrBlobRegenHelper* helper,
- int run, int subRun, SkAutoGlyphCache*, size_t vertexStride,
- const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
- void** vertices, size_t* byteCount, int* glyphCount);
-
const Key& key() const { return fKey; }
~GrAtlasTextBlob() {
@@ -492,11 +483,6 @@
bool fDrawAsPaths;
}; // Run
- template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
- void regenInOp(GrDeferredUploadTarget*, GrAtlasGlyphCache* fontCache, GrBlobRegenHelper* helper,
- Run* run, Run::SubRunInfo* info, SkAutoGlyphCache*, int glyphCount,
- size_t vertexStride, GrColor color, SkScalar transX, SkScalar transY) const;
-
inline std::unique_ptr<GrAtlasTextOp> makeOp(
const Run::SubRunInfo& info, int glyphCount, uint16_t run, uint16_t subRun,
const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
@@ -530,7 +516,7 @@
};
// all glyph / vertex offsets are into these pools.
- unsigned char* fVertices;
+ char* fVertices;
GrGlyph** fGlyphs;
Run* fRuns;
GrMemoryPool* fPool;
@@ -554,4 +540,65 @@
uint8_t fTextType;
};
+/**
+ * Used to produce vertices for a subrun of a blob. The vertices are cached in the blob itself.
+ * This is invoked each time a sub run is drawn. It regenerates the vertex data as required either
+ * because of changes to the atlas or because of different draw parameters (e.g. color change). In
+ * rare cases the draw may have to interrupted and flushed in the middle of the sub run in order to
+ * free up atlas space. Thus, this generator is stateful and should be invoked in a loop until the
+ * entire sub run has been completed.
+ */
+class GrAtlasTextBlob::VertexRegenerator {
+public:
+ /**
+ * Consecutive VertexRegenerators often use the same SkGlyphCache. If the same instance of
+ * SkAutoGlyphCache is reused then it can save the cost of multiple detach/attach operations of
+ * SkGlyphCache.
+ */
+ VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx, const SkMatrix& viewMatrix,
+ SkScalar x, SkScalar y, GrColor color, GrDeferredUploadTarget*,
+ GrAtlasGlyphCache*, SkAutoGlyphCache*, size_t vertexStride);
+
+ struct Result {
+ /**
+ * Was regenerate() able to draw all the glyphs from the sub run? If not flush all glyph
+ * draws and call regenerate() again.
+ */
+ bool fFinished = true;
+
+ /**
+ * How many glyphs were regenerated. Will be equal to the sub run's glyph count if
+ * fType is kFinished.
+ */
+ int fGlyphsRegenerated = 0;
+
+ /**
+ * Pointer where the caller finds the first regenerated vertex.
+ */
+ const char* fFirstVertex;
+ };
+
+ Result regenerate();
+
+private:
+ template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
+ Result doRegen();
+
+ const SkMatrix& fViewMatrix;
+ GrAtlasTextBlob* fBlob;
+ GrDeferredUploadTarget* fUploadTarget;
+ GrAtlasGlyphCache* fGlyphCache;
+ SkAutoGlyphCache* fLazyCache;
+ Run* fRun;
+ Run::SubRunInfo* fSubRun;
+ size_t fVertexStride;
+ GrColor fColor;
+ SkScalar fTransX;
+ SkScalar fTransY;
+
+ uint32_t fRegenFlags = 0;
+ int fCurrGlyph = 0;
+ bool fBrokenRun = false;
+};
+
#endif
diff --git a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
new file mode 100644
index 0000000..6b242ad
--- /dev/null
+++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAtlasTextBlob.h"
+#include "GrTextUtils.h"
+#include "SkDistanceFieldGen.h"
+#include "SkGlyphCache.h"
+#include "ops/GrAtlasTextOp.h"
+
+using Regenerator = GrAtlasTextBlob::VertexRegenerator;
+
+enum RegenMask {
+ kNoRegen = 0x0,
+ kRegenPos = 0x1,
+ kRegenCol = 0x2,
+ kRegenTex = 0x4,
+ kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
+
+ // combinations
+ kRegenPosCol = kRegenPos | kRegenCol,
+ kRegenPosTex = kRegenPos | kRegenTex,
+ kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
+ kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
+ kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
+ kRegenColTex = kRegenCol | kRegenTex,
+ kRegenColTexGlyph = kRegenCol | kRegenGlyph,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// A large template to handle regenerating the vertices of a textblob with as few branches as
+// possible
+template <bool regenPos, bool regenCol, bool regenTexCoords>
+inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStride,
+ bool useDistanceFields, SkScalar transX, SkScalar transY,
+ GrColor color) {
+ uint16_t u0, v0, u1, v1;
+ if (regenTexCoords) {
+ SkASSERT(glyph);
+ int width = glyph->fBounds.width();
+ int height = glyph->fBounds.height();
+
+ if (useDistanceFields) {
+ u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
+ v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
+ u1 = u0 + width - 2 * SK_DistanceFieldInset;
+ v1 = v0 + height - 2 * SK_DistanceFieldInset;
+ } else {
+ u0 = glyph->fAtlasLocation.fX;
+ v0 = glyph->fAtlasLocation.fY;
+ u1 = u0 + width;
+ v1 = v0 + height;
+ }
+ // We pack the 2bit page index in the low bit of the u and v texture coords
+ uint32_t pageIndex = glyph->pageIndex();
+ SkASSERT(pageIndex < 4);
+ uint16_t uBit = (pageIndex >> 1) & 0x1;
+ uint16_t vBit = pageIndex & 0x1;
+ u0 <<= 1;
+ u0 |= uBit;
+ v0 <<= 1;
+ v0 |= vBit;
+ u1 <<= 1;
+ u1 |= uBit;
+ v1 <<= 1;
+ v1 |= vBit;
+ }
+
+ // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
+ // vertices, hence vertexStride - sizeof(SkIPoint16)
+ intptr_t colorOffset = sizeof(SkPoint);
+ intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
+
+ // V0
+ if (regenPos) {
+ SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
+ point->fX += transX;
+ point->fY += transY;
+ }
+
+ if (regenCol) {
+ SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
+ *vcolor = color;
+ }
+
+ if (regenTexCoords) {
+ uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
+ textureCoords[0] = u0;
+ textureCoords[1] = v0;
+ }
+ vertex += vertexStride;
+
+ // V1
+ if (regenPos) {
+ SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
+ point->fX += transX;
+ point->fY += transY;
+ }
+
+ if (regenCol) {
+ SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
+ *vcolor = color;
+ }
+
+ if (regenTexCoords) {
+ uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
+ textureCoords[0] = u0;
+ textureCoords[1] = v1;
+ }
+ vertex += vertexStride;
+
+ // V2
+ if (regenPos) {
+ SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
+ point->fX += transX;
+ point->fY += transY;
+ }
+
+ if (regenCol) {
+ SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
+ *vcolor = color;
+ }
+
+ if (regenTexCoords) {
+ uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
+ textureCoords[0] = u1;
+ textureCoords[1] = v0;
+ }
+ vertex += vertexStride;
+
+ // V3
+ if (regenPos) {
+ SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
+ point->fX += transX;
+ point->fY += transY;
+ }
+
+ if (regenCol) {
+ SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
+ *vcolor = color;
+ }
+
+ if (regenTexCoords) {
+ uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
+ textureCoords[0] = u1;
+ textureCoords[1] = v1;
+ }
+}
+
+Regenerator::VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx,
+ const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
+ GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache,
+ SkAutoGlyphCache* lazyCache, size_t vertexStride)
+ : fViewMatrix(viewMatrix)
+ , fBlob(blob)
+ , fUploadTarget(uploadTarget)
+ , fGlyphCache(glyphCache)
+ , fLazyCache(lazyCache)
+ , fRun(&blob->fRuns[runIdx])
+ , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
+ , fVertexStride(vertexStride)
+ , fColor(color) {
+ // Compute translation if any
+ fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
+
+ // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
+ // generating its texture coords, we have to track whether or not the strike has
+ // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
+ // otherwise we have to get the new strike, and use that to get the correct glyphs.
+ // Because we do not have the packed ids, and thus can't look up our glyphs in the
+ // new strike, we instead keep our ref to the old strike and use the packed ids from
+ // it. These ids will still be valid as long as we hold the ref. When we are done
+ // updating our cache of the GrGlyph*s, we drop our ref on the old strike
+ if (fSubRun->strike()->isAbandoned()) {
+ fRegenFlags |= kRegenGlyph;
+ fRegenFlags |= kRegenTex;
+ }
+ if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
+ fRegenFlags |= kRegenCol;
+ }
+ if (0.f != fTransX || 0.f != fTransY) {
+ fRegenFlags |= kRegenPos;
+ }
+}
+
+template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
+Regenerator::Result Regenerator::doRegen() {
+ static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
+ GrAtlasTextStrike* strike = nullptr;
+ if (regenTexCoords) {
+ fSubRun->resetBulkUseToken();
+
+ const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
+ ? fRun->fOverrideDescriptor->getDesc()
+ : fRun->fDescriptor.getDesc();
+
+ if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
+ SkScalerContextEffects effects;
+ effects.fPathEffect = fRun->fPathEffect.get();
+ effects.fRasterizer = fRun->fRasterizer.get();
+ effects.fMaskFilter = fRun->fMaskFilter.get();
+ fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
+ }
+
+ if (regenGlyphs) {
+ strike = fGlyphCache->getStrike(fLazyCache->get());
+ } else {
+ strike = fSubRun->strike();
+ }
+ }
+
+ Result result;
+ char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
+ fCurrGlyph * kVerticesPerGlyph * fVertexStride;
+ result.fFirstVertex = currVertex;
+
+ for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
+ GrGlyph* glyph = nullptr;
+ if (regenTexCoords) {
+ size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
+
+ if (regenGlyphs) {
+ // Get the id from the old glyph, and use the new strike to lookup
+ // the glyph.
+ GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
+ fBlob->fGlyphs[glyphOffset] =
+ strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
+ SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
+ }
+ glyph = fBlob->fGlyphs[glyphOffset];
+ SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
+
+ if (!fGlyphCache->hasGlyph(glyph) &&
+ !strike->addGlyphToAtlas(fUploadTarget, glyph, fLazyCache->get(),
+ fSubRun->maskFormat())) {
+ fBrokenRun = glyphIdx > 0;
+ result.fFinished = false;
+ return result;
+ }
+ fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
+ fUploadTarget->nextDrawToken());
+ }
+
+ regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, fVertexStride,
+ fSubRun->drawAsDistanceFields(), fTransX,
+ fTransY, fColor);
+ currVertex += fVertexStride * GrAtlasTextOp::kVerticesPerGlyph;
+ ++result.fGlyphsRegenerated;
+ ++fCurrGlyph;
+ }
+
+ // We may have changed the color so update it here
+ fSubRun->setColor(fColor);
+ if (regenTexCoords) {
+ if (regenGlyphs) {
+ fSubRun->setStrike(strike);
+ }
+ fSubRun->setAtlasGeneration(fBrokenRun
+ ? GrDrawOpAtlas::kInvalidAtlasGeneration
+ : fGlyphCache->atlasGeneration(fSubRun->maskFormat()));
+ }
+ return result;
+}
+
+Regenerator::Result Regenerator::regenerate() {
+ uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat());
+ // If regenerate() is called multiple times then the atlas gen may have changed. So we check
+ // this each time.
+ if (fSubRun->atlasGeneration() != currentAtlasGen) {
+ fRegenFlags |= kRegenTex;
+ }
+
+ switch (static_cast<RegenMask>(fRegenFlags)) {
+ case kRegenPos:
+ return this->doRegen<true, false, false, false>();
+ case kRegenCol:
+ return this->doRegen<false, true, false, false>();
+ case kRegenTex:
+ return this->doRegen<false, false, true, false>();
+ case kRegenGlyph:
+ return this->doRegen<false, false, true, true>();
+
+ // combinations
+ case kRegenPosCol:
+ return this->doRegen<true, true, false, false>();
+ case kRegenPosTex:
+ return this->doRegen<true, false, true, false>();
+ case kRegenPosTexGlyph:
+ return this->doRegen<true, false, true, true>();
+ case kRegenPosColTex:
+ return this->doRegen<true, true, true, false>();
+ case kRegenPosColTexGlyph:
+ return this->doRegen<true, true, true, true>();
+ case kRegenColTex:
+ return this->doRegen<false, true, true, false>();
+ case kRegenColTexGlyph:
+ return this->doRegen<false, true, true, true>();
+ case kNoRegen: {
+ Result result;
+ result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
+ result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
+ fCurrGlyph * kVerticesPerGlyph * fVertexStride;
+ fCurrGlyph = fSubRun->glyphCount();
+
+ // set use tokens for all of the glyphs in our subrun. This is only valid if we
+ // have a valid atlas generation
+ fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(), fUploadTarget->nextDrawToken(),
+ fSubRun->maskFormat());
+ return result;
+ }
+ }
+ SK_ABORT("Should not get here");
+ return Result();
+}
diff --git a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
deleted file mode 100644
index e081158..0000000
--- a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrAtlasTextBlob.h"
-
-#include "GrOpFlushState.h"
-#include "GrTextUtils.h"
-
-#include "SkDistanceFieldGen.h"
-#include "SkGlyphCache.h"
-
-#include "ops/GrAtlasTextOp.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// A large template to handle regenerating the vertices of a textblob with as few branches as
-// possible
-template <bool regenPos, bool regenCol, bool regenTexCoords>
-inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexStride,
- bool useDistanceFields, SkScalar transX, SkScalar transY,
- GrColor color) {
- uint16_t u0, v0, u1, v1;
- if (regenTexCoords) {
- SkASSERT(glyph);
- int width = glyph->fBounds.width();
- int height = glyph->fBounds.height();
-
- if (useDistanceFields) {
- u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
- v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
- u1 = u0 + width - 2 * SK_DistanceFieldInset;
- v1 = v0 + height - 2 * SK_DistanceFieldInset;
- } else {
- u0 = glyph->fAtlasLocation.fX;
- v0 = glyph->fAtlasLocation.fY;
- u1 = u0 + width;
- v1 = v0 + height;
- }
- // We pack the 2bit page index in the low bit of the u and v texture coords
- uint32_t pageIndex = glyph->pageIndex();
- SkASSERT(pageIndex < 4);
- uint16_t uBit = (pageIndex >> 1) & 0x1;
- uint16_t vBit = pageIndex & 0x1;
- u0 <<= 1;
- u0 |= uBit;
- v0 <<= 1;
- v0 |= vBit;
- u1 <<= 1;
- u1 |= uBit;
- v1 <<= 1;
- v1 |= vBit;
- }
-
- // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
- // vertices, hence vertexStride - sizeof(SkIPoint16)
- intptr_t colorOffset = sizeof(SkPoint);
- intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
-
- // V0
- if (regenPos) {
- SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
- point->fX += transX;
- point->fY += transY;
- }
-
- if (regenCol) {
- SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
- *vcolor = color;
- }
-
- if (regenTexCoords) {
- uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
- textureCoords[0] = u0;
- textureCoords[1] = v0;
- }
- vertex += vertexStride;
-
- // V1
- if (regenPos) {
- SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
- point->fX += transX;
- point->fY += transY;
- }
-
- if (regenCol) {
- SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
- *vcolor = color;
- }
-
- if (regenTexCoords) {
- uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
- textureCoords[0] = u0;
- textureCoords[1] = v1;
- }
- vertex += vertexStride;
-
- // V2
- if (regenPos) {
- SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
- point->fX += transX;
- point->fY += transY;
- }
-
- if (regenCol) {
- SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
- *vcolor = color;
- }
-
- if (regenTexCoords) {
- uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
- textureCoords[0] = u1;
- textureCoords[1] = v0;
- }
- vertex += vertexStride;
-
- // V3
- if (regenPos) {
- SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
- point->fX += transX;
- point->fY += transY;
- }
-
- if (regenCol) {
- SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
- *vcolor = color;
- }
-
- if (regenTexCoords) {
- uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
- textureCoords[0] = u1;
- textureCoords[1] = v1;
- }
-}
-
-template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
-void GrAtlasTextBlob::regenInOp(GrDeferredUploadTarget* target, GrAtlasGlyphCache* fontCache,
- GrBlobRegenHelper* helper, Run* run, Run::SubRunInfo* info,
- SkAutoGlyphCache* lazyCache, int glyphCount, size_t vertexStride,
- GrColor color, SkScalar transX, SkScalar transY) const {
- SkASSERT(lazyCache);
- static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
- GrAtlasTextStrike* strike = nullptr;
- if (regenTexCoords) {
- info->resetBulkUseToken();
-
- const SkDescriptor* desc = (run->fOverrideDescriptor && !info->drawAsDistanceFields())
- ? run->fOverrideDescriptor->getDesc()
- : run->fDescriptor.getDesc();
-
- if (!*lazyCache || (*lazyCache)->getDescriptor() != *desc) {
- SkScalerContextEffects effects;
- effects.fPathEffect = run->fPathEffect.get();
- effects.fRasterizer = run->fRasterizer.get();
- effects.fMaskFilter = run->fMaskFilter.get();
- lazyCache->reset(SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc));
- }
-
- if (regenGlyphs) {
- strike = fontCache->getStrike(lazyCache->get());
- } else {
- strike = info->strike();
- }
- }
-
- bool brokenRun = false;
- intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
- vertex += info->vertexStartIndex();
- for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
- GrGlyph* glyph = nullptr;
- if (regenTexCoords) {
- size_t glyphOffset = glyphIdx + info->glyphStartIndex();
-
- if (regenGlyphs) {
- // Get the id from the old glyph, and use the new strike to lookup
- // the glyph.
- GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
- fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), lazyCache->get());
- SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
- }
- glyph = fGlyphs[glyphOffset];
- SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());
-
- if (!fontCache->hasGlyph(glyph) &&
- !strike->addGlyphToAtlas(target, glyph, lazyCache->get(), info->maskFormat())) {
- helper->flush();
- brokenRun = glyphIdx > 0;
-
- SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
- glyph,
- lazyCache->get(),
- info->maskFormat());
- SkASSERT(success);
- }
- fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
- target->nextDrawToken());
- }
-
- regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
- info->drawAsDistanceFields(),
- transX, transY, color);
- vertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
- helper->incGlyphCount();
- }
-
- // We may have changed the color so update it here
- info->setColor(color);
- if (regenTexCoords) {
- if (regenGlyphs) {
- info->setStrike(strike);
- }
- info->setAtlasGeneration(brokenRun ? GrDrawOpAtlas::kInvalidAtlasGeneration
- : fontCache->atlasGeneration(info->maskFormat()));
- }
-}
-
-enum RegenMask {
- kNoRegen = 0x0,
- kRegenPos = 0x1,
- kRegenCol = 0x2,
- kRegenTex = 0x4,
- kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
-
- // combinations
- kRegenPosCol = kRegenPos | kRegenCol,
- kRegenPosTex = kRegenPos | kRegenTex,
- kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
- kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
- kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
- kRegenColTex = kRegenCol | kRegenTex,
- kRegenColTexGlyph = kRegenCol | kRegenGlyph,
-};
-
-#define REGEN_ARGS target, fontCache, helper, &run, &info, lazyCache, \
- *glyphCount, vertexStride, color, transX, transY
-
-void GrAtlasTextBlob::regenInOp(GrDeferredUploadTarget* target, GrAtlasGlyphCache* fontCache,
- GrBlobRegenHelper* helper, int runIndex, int subRunIndex,
- SkAutoGlyphCache* lazyCache, size_t vertexStride,
- const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
- void** vertices, size_t* byteCount, int* glyphCount) {
- Run& run = fRuns[runIndex];
- Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex];
-
- uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat());
-
- // Compute translation if any
- SkScalar transX, transY;
- info.computeTranslation(viewMatrix, x, y, &transX, &transY);
-
- // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
- // generating its texture coords, we have to track whether or not the strike has
- // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
- // otherwise we have to get the new strike, and use that to get the correct glyphs.
- // Because we do not have the packed ids, and thus can't look up our glyphs in the
- // new strike, we instead keep our ref to the old strike and use the packed ids from
- // it. These ids will still be valid as long as we hold the ref. When we are done
- // updating our cache of the GrGlyph*s, we drop our ref on the old strike
- bool regenerateGlyphs = info.strike()->isAbandoned();
- bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen ||
- regenerateGlyphs;
- bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() &&
- info.color() != color;
- bool regeneratePositions = transX != 0.f || transY != 0.f;
- *glyphCount = info.glyphCount();
-
- uint32_t regenMaskBits = kNoRegen;
- regenMaskBits |= regeneratePositions ? kRegenPos : 0;
- regenMaskBits |= regenerateColors ? kRegenCol : 0;
- regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0;
- regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0;
- RegenMask regenMask = (RegenMask)regenMaskBits;
-
- switch (regenMask) {
- case kRegenPos:
- this->regenInOp<true, false, false, false>(REGEN_ARGS);
- break;
- case kRegenCol:
- this->regenInOp<false, true, false, false>(REGEN_ARGS);
- break;
- case kRegenTex:
- this->regenInOp<false, false, true, false>(REGEN_ARGS);
- break;
- case kRegenGlyph:
- this->regenInOp<false, false, true, true>(REGEN_ARGS);
- break;
-
- // combinations
- case kRegenPosCol:
- this->regenInOp<true, true, false, false>(REGEN_ARGS);
- break;
- case kRegenPosTex:
- this->regenInOp<true, false, true, false>(REGEN_ARGS);
- break;
- case kRegenPosTexGlyph:
- this->regenInOp<true, false, true, true>(REGEN_ARGS);
- break;
- case kRegenPosColTex:
- this->regenInOp<true, true, true, false>(REGEN_ARGS);
- break;
- case kRegenPosColTexGlyph:
- this->regenInOp<true, true, true, true>(REGEN_ARGS);
- break;
- case kRegenColTex:
- this->regenInOp<false, true, true, false>(REGEN_ARGS);
- break;
- case kRegenColTexGlyph:
- this->regenInOp<false, true, true, true>(REGEN_ARGS);
- break;
- case kNoRegen:
- helper->incGlyphCount(*glyphCount);
-
- // set use tokens for all of the glyphs in our subrun. This is only valid if we
- // have a valid atlas generation
- fontCache->setUseTokenBulk(*info.bulkUseToken(), target->nextDrawToken(),
- info.maskFormat());
- break;
- }
-
- *byteCount = info.byteCount();
- *vertices = fVertices + info.vertexStartIndex();
-}