Handle insane number of glyphs in GrAtlasTextOp.
Cap size of draws by vertex buffer size. Remove size limit in combining since we now enforce a limit when preparing draws. (This limit wasn't effective in bug repro scenario as the large number of glyphs are in a single run). Fix another bug discovered along the way: We increase the number of proxies in a FixedDynamicState after having recorded draws. However, the draws take refs on the proxies in FDS and unref them when destroyed. By adding the proxies late we cause the ref count to underflow. (This needs a more systematic fix outside the scope of this change). Bug: chromium:1018511 Change-Id: I79fe446d97c573196653a18792462bd46b9ac4c6 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/262001 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
c0724ec00a
commit
43cbd7229f
@ -326,12 +326,14 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
|
||||
samplerState, maskFormat, localMatrix, vmPerspective);
|
||||
}
|
||||
|
||||
flushInfo.fGlyphsToFlush = 0;
|
||||
size_t vertexStride = flushInfo.fGeometryProcessor->vertexStride();
|
||||
|
||||
int glyphCount = this->numGlyphs();
|
||||
|
||||
void* vertices = target->makeVertexSpace(vertexStride, glyphCount * kVerticesPerGlyph,
|
||||
// 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);
|
||||
flushInfo.fIndexBuffer = resourceProvider->refNonAAQuadIndexBuffer();
|
||||
if (!vertices || !flushInfo.fVertexBuffer) {
|
||||
@ -349,46 +351,65 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
|
||||
resourceProvider, args.fSubRunPtr, args.fDrawMatrix, args.fDrawOrigin,
|
||||
args.fColor.toBytes_RGBA(), target->deferredUploadTarget(), glyphCache,
|
||||
atlasManager);
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
// This loop issues draws until regenerator says we're done with this geo. Regenerator
|
||||
// breaks things up if inline uploads are necessary.
|
||||
while (true) {
|
||||
GrTextBlob::VertexRegenerator::Result result;
|
||||
if (!regenerator.regenerate(&result)) {
|
||||
// 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;
|
||||
if (args.fClipRect.isEmpty()) {
|
||||
memcpy(currVertex, result.fFirstVertex, vertexBytes);
|
||||
} else {
|
||||
SkASSERT(!vmPerspective);
|
||||
clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
|
||||
glyphCount);
|
||||
}
|
||||
if (fNeedsGlyphTransform && !args.fDrawMatrix.isIdentity()) {
|
||||
// We always do the distance field view matrix transformation after copying
|
||||
// rather than during blob vertex generation time in the blob as handling
|
||||
// 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);
|
||||
} else {
|
||||
auto* pos = reinterpret_cast<SkPoint*>(currVertex);
|
||||
SkMatrixPriv::MapPointsWithStride(args.fDrawMatrix, pos, vertexStride,
|
||||
vertexCount);
|
||||
}
|
||||
}
|
||||
flushInfo.fGlyphsToFlush += glyphCount;
|
||||
currVertex += vertexBytes;
|
||||
result.fFirstVertex += vertexBytes;
|
||||
result.fGlyphsRegenerated -= glyphCount;
|
||||
bufferGlyphCount -= glyphCount;
|
||||
totalGlyphCount -= glyphCount;
|
||||
} while (result.fGlyphsRegenerated);
|
||||
if (result.fFinished) {
|
||||
break;
|
||||
}
|
||||
done = result.fFinished;
|
||||
|
||||
// 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 {
|
||||
SkASSERT(!vmPerspective);
|
||||
clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
|
||||
result.fGlyphsRegenerated);
|
||||
}
|
||||
if (fNeedsGlyphTransform && !args.fDrawMatrix.isIdentity()) {
|
||||
// We always do the distance field view matrix transformation after copying rather
|
||||
// than during blob vertex generation time in the blob as handling 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,
|
||||
result.fGlyphsRegenerated * kVerticesPerGlyph);
|
||||
} else {
|
||||
auto* pos = reinterpret_cast<SkPoint*>(currVertex);
|
||||
SkMatrixPriv::MapPointsWithStride(
|
||||
args.fDrawMatrix, pos, vertexStride,
|
||||
result.fGlyphsRegenerated * kVerticesPerGlyph);
|
||||
}
|
||||
}
|
||||
flushInfo.fGlyphsToFlush += result.fGlyphsRegenerated;
|
||||
if (!result.fFinished) {
|
||||
this->flush(target, &flushInfo);
|
||||
}
|
||||
currVertex += vertexBytes;
|
||||
this->flush(target, &flushInfo);
|
||||
}
|
||||
}
|
||||
SkASSERT(!bufferGlyphCount);
|
||||
SkASSERT(!totalGlyphCount);
|
||||
this->flush(target, &flushInfo);
|
||||
}
|
||||
|
||||
@ -425,6 +446,10 @@ void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) co
|
||||
// This op does not know its atlas proxies when it is added to a GrOpsTasks, so the
|
||||
// proxies don't get added during the visitProxies call. Thus we add them here.
|
||||
target->sampledProxyArray()->push_back(views[i].proxy());
|
||||
// These will get unreffed when the previously recorded draws destruct.
|
||||
for (int d = 0; d < flushInfo->fNumDraws; ++d) {
|
||||
flushInfo->fFixedDynamicState->fPrimitiveProcessorTextures[i]->ref();
|
||||
}
|
||||
}
|
||||
if (this->usesDistanceFields()) {
|
||||
if (this->isLCD()) {
|
||||
@ -450,6 +475,7 @@ void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) co
|
||||
nullptr, GrPrimitiveType::kTriangles);
|
||||
flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
|
||||
flushInfo->fGlyphsToFlush = 0;
|
||||
++flushInfo->fNumDraws;
|
||||
}
|
||||
|
||||
GrOp::CombineResult GrAtlasTextOp::onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
|
||||
@ -493,14 +519,6 @@ GrOp::CombineResult GrAtlasTextOp::onCombineIfPossible(GrOp* t, GrRecordingConte
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the batch vertex buffer size below 32K so we don't have to create a special one
|
||||
// We use the largest possible vertex size for this
|
||||
static const int kVertexSize = sizeof(SkPoint) + sizeof(SkColor) + 2 * sizeof(uint16_t);
|
||||
static const int kMaxGlyphs = 32768 / (kVerticesPerGlyph * kVertexSize);
|
||||
if (this->fNumGlyphs + that->fNumGlyphs > kMaxGlyphs) {
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
|
||||
fNumGlyphs += that->numGlyphs();
|
||||
|
||||
// Reallocate space for geo data if necessary and then import that geo's data.
|
||||
|
@ -106,8 +106,9 @@ private:
|
||||
sk_sp<const GrBuffer> fIndexBuffer;
|
||||
GrGeometryProcessor* fGeometryProcessor;
|
||||
GrPipeline::FixedDynamicState* fFixedDynamicState;
|
||||
int fGlyphsToFlush;
|
||||
int fVertexOffset;
|
||||
int fGlyphsToFlush = 0;
|
||||
int fVertexOffset = 0;
|
||||
int fNumDraws = 0;
|
||||
};
|
||||
|
||||
void onPrepareDraws(Target*) override;
|
||||
|
@ -872,7 +872,8 @@ GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourcePro
|
||||
fSubRun->translateVerticesIfNeeded(drawMatrix, drawOrigin);
|
||||
}
|
||||
|
||||
bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result) {
|
||||
bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
|
||||
int maxGlyphs) {
|
||||
SkASSERT(!fActions.regenStrike || fActions.regenTextureCoordinates);
|
||||
if (fActions.regenTextureCoordinates) {
|
||||
fSubRun->resetBulkUseToken();
|
||||
@ -907,8 +908,12 @@ bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Resul
|
||||
auto vertexStride = fSubRun->vertexStride();
|
||||
char* currVertex = fSubRun->fVertexData.data() + fCurrGlyph * kVerticesPerGlyph * vertexStride;
|
||||
result->fFirstVertex = currVertex;
|
||||
|
||||
for (; fCurrGlyph < (int)fSubRun->fGlyphs.size(); fCurrGlyph++) {
|
||||
int glyphLimit = (int)fSubRun->fGlyphs.size();
|
||||
if (glyphLimit > fCurrGlyph + maxGlyphs) {
|
||||
glyphLimit = fCurrGlyph + maxGlyphs;
|
||||
result->fFinished = false;
|
||||
}
|
||||
for (; fCurrGlyph < glyphLimit; fCurrGlyph++) {
|
||||
if (fActions.regenTextureCoordinates) {
|
||||
GrGlyph* glyph = fSubRun->fGlyphs[fCurrGlyph];
|
||||
SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
|
||||
@ -956,7 +961,8 @@ bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Resul
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
|
||||
bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result,
|
||||
int maxGlyphs) {
|
||||
uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
|
||||
// If regenerate() is called multiple times then the atlas gen may have changed. So we check
|
||||
// this each time.
|
||||
@ -964,25 +970,29 @@ bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Re
|
||||
|
||||
if (fActions.regenStrike
|
||||
|fActions.regenTextureCoordinates) {
|
||||
return this->doRegen(result);
|
||||
return this->doRegen(result, maxGlyphs);
|
||||
} else {
|
||||
auto vertexStride = fSubRun->vertexStride();
|
||||
result->fFinished = true;
|
||||
result->fGlyphsRegenerated = fSubRun->fGlyphs.size() - fCurrGlyph;
|
||||
int glyphsLeft = fSubRun->fGlyphs.size() - fCurrGlyph;
|
||||
if (glyphsLeft <= maxGlyphs) {
|
||||
result->fFinished = true;
|
||||
result->fGlyphsRegenerated = glyphsLeft;
|
||||
} else {
|
||||
result->fFinished = false;
|
||||
result->fGlyphsRegenerated = maxGlyphs;
|
||||
}
|
||||
result->fFirstVertex = fSubRun->fVertexData.data() +
|
||||
fCurrGlyph * kVerticesPerGlyph * vertexStride;
|
||||
fCurrGlyph = fSubRun->fGlyphs.size();
|
||||
fCurrGlyph += result->fGlyphsRegenerated;
|
||||
|
||||
// set use tokens for all of the glyphs in our subrun. This is only valid if we
|
||||
// have a valid atlas generation
|
||||
fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
|
||||
fUploadTarget->tokenTracker()->nextDrawToken(),
|
||||
fSubRun->maskFormat());
|
||||
if (result->fFinished) {
|
||||
// set use tokens for all of the glyphs in our subrun. This is only valid if we
|
||||
// have a valid atlas generation
|
||||
fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
|
||||
fUploadTarget->tokenTracker()->nextDrawToken(),
|
||||
fSubRun->maskFormat());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SK_ABORT("Should not get here");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "src/gpu/text/GrTextContext.h"
|
||||
#include "src/gpu/text/GrTextTarget.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
class GrAtlasManager;
|
||||
class GrAtlasTextOp;
|
||||
struct GrDistanceFieldAdjustTable;
|
||||
@ -314,13 +316,13 @@ public:
|
||||
/**
|
||||
* Pointer where the caller finds the first regenerated vertex.
|
||||
*/
|
||||
const char* fFirstVertex;
|
||||
const char* fFirstVertex = nullptr;
|
||||
};
|
||||
|
||||
bool regenerate(Result*);
|
||||
bool regenerate(Result*, int maxGlyphs = std::numeric_limits<int>::max());
|
||||
|
||||
private:
|
||||
bool doRegen(Result* result);
|
||||
bool doRegen(Result* result, int maxGlyphs);
|
||||
|
||||
GrResourceProvider* fResourceProvider;
|
||||
GrDeferredUploadTarget* fUploadTarget;
|
||||
|
Loading…
Reference in New Issue
Block a user