Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Unified Diff: src/gpu/batches/GrAtlasTextBatch.cpp

Issue 1466333003: Move GrAtlasTextBatch blob regeneration to template (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: add inline qualifier Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/batches/GrAtlasTextBatch.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/batches/GrAtlasTextBatch.cpp
diff --git a/src/gpu/batches/GrAtlasTextBatch.cpp b/src/gpu/batches/GrAtlasTextBatch.cpp
index 91088c0c59068811a94fea0a48c9ba6dca51d39a..9d408f997ab7e24c749d1406a09e196d1640065d 100644
--- a/src/gpu/batches/GrAtlasTextBatch.cpp
+++ b/src/gpu/batches/GrAtlasTextBatch.cpp
@@ -18,6 +18,210 @@
#include "effects/GrBitmapTextGeoProc.h"
#include "effects/GrDistanceFieldGeoProc.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) {
+ int 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;
+ }
+ }
+
+ // 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) {
+ SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
+ textureCoords->set(u0, 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) {
+ SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
+ textureCoords->set(u0, 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) {
+ SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
+ textureCoords->set(u1, v1);
+ }
+ 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) {
+ SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
+ textureCoords->set(u1, v0);
+ }
+}
+
+typedef GrAtlasTextBlob Blob;
+typedef Blob::Run Run;
+typedef Run::SubRunInfo TextInfo;
+
+template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
+inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Blob* blob, Run* run,
+ TextInfo* info, SkGlyphCache** cache,
+ SkTypeface** typeface, GrFontScaler** scaler,
+ const SkDescriptor** desc, const GrGeometryProcessor* gp,
+ int glyphCount, size_t vertexStride,
+ GrColor color, SkScalar transX, SkScalar transY) {
+ static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
+ GrBatchTextStrike* strike = nullptr;
+ if (regenTexCoords) {
+ info->fBulkUseToken.reset();
+
+ // We can reuse if we have a valid strike and our descriptors / typeface are the
+ // same. The override descriptor is only for the non distance field text within
+ // a run
+ const SkDescriptor* newDesc = (run->fOverrideDescriptor && !this->usesDistanceFields()) ?
+ run->fOverrideDescriptor->getDesc() :
+ run->fDescriptor.getDesc();
+ if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) ||
+ !((*desc)->equals(*newDesc))) {
+ if (*cache) {
+ SkGlyphCache::AttachCache(*cache);
+ }
+ *desc = newDesc;
+ *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc);
+ *scaler = GrTextContext::GetGrFontScaler(*cache);
+ strike = info->fStrike;
+ *typeface = run->fTypeface;
+ }
+
+ if (regenGlyphs) {
+ strike = fFontCache->getStrike(*scaler);
+ } else {
+ strike = info->fStrike;
+ }
+ }
+
+ bool brokenRun = false;
+ for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
+ GrGlyph* glyph = nullptr;
+ if (regenTexCoords) {
+ size_t glyphOffset = glyphIdx + info->fGlyphStartIndex;
+
+ glyph = blob->fGlyphs[glyphOffset];
+ GrGlyph::PackedID id = glyph->fPackedID;
+ const SkGlyph& skGlyph = (*scaler)->grToSkGlyph(id);
+ if (regenGlyphs) {
+ // Get the id from the old glyph, and use the new strike to lookup
+ // the glyph.
+ blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, this->maskFormat(),
+ *scaler);
+ }
+ glyph = blob->fGlyphs[glyphOffset];
+ SkASSERT(glyph);
+ SkASSERT(id == glyph->fPackedID);
+ // We want to be able to assert this but cannot for testing purposes.
+ // once skbug:4143 has landed we can revist this assert
+ //SkASSERT(glyph->fMaskFormat == this->maskFormat());
+
+ if (!fFontCache->hasGlyph(glyph) &&
+ !strike->addGlyphToAtlas(target, glyph, *scaler, skGlyph, this->maskFormat())) {
+ this->flush(target, flushInfo);
+ target->initDraw(gp, this->pipeline());
+ brokenRun = glyphIdx > 0;
+
+ SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
+ glyph,
+ *scaler,
+ skGlyph,
+ this->maskFormat());
+ SkASSERT(success);
+ }
+ fFontCache->addGlyphToBulkAndSetUseToken(&info->fBulkUseToken, glyph,
+ target->currentToken());
+ }
+
+ intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
+ vertex += info->fVertexStartIndex;
+ vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
+ regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
+ this->usesDistanceFields(), transX,
+ transY, color);
+ flushInfo->fGlyphsToFlush++;
+ }
+
+ // We my have changed the color so update it here
+ run->fColor = color;
+ if (regenTexCoords) {
+ if (regenGlyphs) {
+ info->fStrike.reset(SkRef(strike));
+ }
+ info->fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
+ fFontCache->atlasGeneration(this->maskFormat());
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
unsigned r = SkColorGetR(c);
unsigned g = SkColorGetG(c);
@@ -81,6 +285,26 @@ void GrAtlasTextBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
fBatch.fCoverageIgnored = !opt.readsCoverage();
}
+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, &flushInfo, blob, &run, &info, &cache, &typeface, &scaler, &desc, gp, \
+ glyphCount, vertexStride, args.fColor, args.fTransX, args.fTransY
+
void GrAtlasTextBatch::onPrepareDraws(Target* target) {
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
// TODO actually only invert if we don't have RGBA
@@ -153,8 +377,18 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) {
TextInfo& info = run.fSubRunInfo[args.fSubRun];
uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
+
+ // Because the GrBatchFontCache 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.fStrike->isAbandoned();
bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
- info.fStrike->isAbandoned();
+ regenerateGlyphs;
bool regenerateColors;
if (usesDistanceFields) {
regenerateColors = !isLCD && run.fColor != args.fColor;
@@ -164,136 +398,34 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) {
bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
- // We regenerate both texture coords and colors in the blob itself, and update the
- // atlas generation. If we don't end up purging any unused plots, we can avoid
- // regenerating the coords. We could take a finer grained approach to updating texture
- // coords but its not clear if the extra bookkeeping would offset any gains.
- // To avoid looping over the glyphs twice, we do one loop and conditionally update color
- // or coords as needed. One final note, if we have to break a run for an atlas eviction
- // then we can't really trust the atlas has all of the correct data. Atlas evictions
- // should be pretty rare, so we just always regenerate in those cases
- if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
- // first regenerate texture coordinates / colors if need be
- bool brokenRun = false;
-
- // Because the GrBatchFontCache 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 = false;
- GrBatchTextStrike* strike = nullptr;
- if (regenerateTextureCoords) {
- info.fBulkUseToken.reset();
-
- // We can reuse if we have a valid strike and our descriptors / typeface are the
- // same. The override descriptor is only for the non distance field text within
- // a run
- const SkDescriptor* newDesc = (run.fOverrideDescriptor && !usesDistanceFields) ?
- run.fOverrideDescriptor->getDesc() :
- run.fDescriptor.getDesc();
- if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
- !(desc->equals(*newDesc))) {
- if (cache) {
- SkGlyphCache::AttachCache(cache);
- }
- desc = newDesc;
- cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
- scaler = GrTextContext::GetGrFontScaler(cache);
- strike = info.fStrike;
- typeface = run.fTypeface;
- }
-
- if (info.fStrike->isAbandoned()) {
- regenerateGlyphs = true;
- strike = fFontCache->getStrike(scaler);
- } else {
- strike = info.fStrike;
- }
- }
-
- for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
- if (regenerateTextureCoords) {
- size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
-
- GrGlyph* glyph = blob->fGlyphs[glyphOffset];
- GrGlyph::PackedID id = glyph->fPackedID;
- const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
- if (regenerateGlyphs) {
- // Get the id from the old glyph, and use the new strike to lookup
- // the glyph.
- blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
- scaler);
- }
- glyph = blob->fGlyphs[glyphOffset];
- SkASSERT(glyph);
- SkASSERT(id == glyph->fPackedID);
- // We want to be able to assert this but cannot for testing purposes.
- // once skbug:4143 has landed we can revist this assert
- //SkASSERT(glyph->fMaskFormat == this->maskFormat());
-
- if (!fFontCache->hasGlyph(glyph) &&
- !strike->addGlyphToAtlas(target, glyph, scaler, skGlyph, maskFormat)) {
- this->flush(target, &flushInfo);
- target->initDraw(gp, this->pipeline());
- brokenRun = glyphIdx > 0;
-
- SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
- glyph,
- scaler,
- skGlyph,
- maskFormat);
- SkASSERT(success);
- }
- fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
- target->currentToken());
-
- // Texture coords are the last vertex attribute so we get a pointer to the
- // first one and then map with stride in regenerateTextureCoords
- intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
- vertex += info.fVertexStartIndex;
- vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
- vertex += vertexStride - sizeof(SkIPoint16);
-
- this->regenerateTextureCoords(glyph, vertex, vertexStride);
- }
-
- if (regenerateColors) {
- intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
- vertex += info.fVertexStartIndex;
- vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
- this->regenerateColors(vertex, vertexStride, args.fColor);
- }
-
- if (regeneratePositions) {
- intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
- vertex += info.fVertexStartIndex;
- vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
- SkScalar transX = args.fTransX;
- SkScalar transY = args.fTransY;
- this->regeneratePositions(vertex, vertexStride, transX, transY);
- }
- flushInfo.fGlyphsToFlush++;
- }
-
- // We my have changed the color so update it here
- run.fColor = args.fColor;
- if (regenerateTextureCoords) {
- if (regenerateGlyphs) {
- info.fStrike.reset(SkRef(strike));
- }
- info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
- fFontCache->atlasGeneration(maskFormat);
- }
- } else {
- flushInfo.fGlyphsToFlush += glyphCount;
-
- // set use tokens for all of the glyphs in our subrun. This is only valid if we
- // have a valid atlas generation
- fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
+ 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->regenBlob<true, false, false, false>(REGEN_ARGS); break;
+ case kRegenCol: this->regenBlob<false, true, false, false>(REGEN_ARGS); break;
+ case kRegenTex: this->regenBlob<false, false, true, false>(REGEN_ARGS); break;
+ case kRegenGlyph: this->regenBlob<false, false, true, true>(REGEN_ARGS); break;
+
+ // combinations
+ case kRegenPosCol: this->regenBlob<true, true, false, false>(REGEN_ARGS); break;
+ case kRegenPosTex: this->regenBlob<true, false, true, false>(REGEN_ARGS); break;
+ case kRegenPosTexGlyph: this->regenBlob<true, false, true, true>(REGEN_ARGS); break;
+ case kRegenPosColTex: this->regenBlob<true, true, true, false>(REGEN_ARGS); break;
+ case kRegenPosColTexGlyph: this->regenBlob<true, true, true, true>(REGEN_ARGS); break;
+ case kRegenColTex: this->regenBlob<false, true, true, false>(REGEN_ARGS); break;
+ case kRegenColTexGlyph: this->regenBlob<false, true, true, true>(REGEN_ARGS); break;
+ case kNoRegen:
+ flushInfo.fGlyphsToFlush += glyphCount;
+
+ // set use tokens for all of the glyphs in our subrun. This is only valid if we
+ // have a valid atlas generation
+ fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
+ break;
}
// now copy all vertices
@@ -302,6 +434,7 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) {
currVertex += byteCount;
}
+
// Make sure to attach the last cache if applicable
if (cache) {
SkGlyphCache::AttachCache(cache);
@@ -309,63 +442,6 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) {
this->flush(target, &flushInfo);
}
-void GrAtlasTextBatch::regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex,
- size_t vertexStride) {
- int width = glyph->fBounds.width();
- int height = glyph->fBounds.height();
-
- int u0, v0, u1, v1;
- if (this->usesDistanceFields()) {
- 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;
- }
-
- SkIPoint16* textureCoords;
- // V0
- textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
- textureCoords->set(u0, v0);
- vertex += vertexStride;
-
- // V1
- textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
- textureCoords->set(u0, v1);
- vertex += vertexStride;
-
- // V2
- textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
- textureCoords->set(u1, v1);
- vertex += vertexStride;
-
- // V3
- textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
- textureCoords->set(u1, v0);
-}
-
-void GrAtlasTextBatch::regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
- for (int i = 0; i < kVerticesPerGlyph; i++) {
- SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
- *vcolor = color;
- vertex += vertexStride;
- }
-}
-
-void GrAtlasTextBatch::regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
- SkScalar transY) {
- for (int i = 0; i < kVerticesPerGlyph; i++) {
- SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
- point->fX += transX;
- point->fY += transY;
- vertex += vertexStride;
- }
-}
-
void GrAtlasTextBatch::flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) {
GrVertices vertices;
int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
« no previous file with comments | « src/gpu/batches/GrAtlasTextBatch.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698