From cb71889318451a4c91442d8bcddce9de2a40eb9c Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Sat, 7 Dec 2019 00:07:42 -0500 Subject: [PATCH] Put SubRuns in an alloc on GrTextBlob This improves performance when there are multiple runs. Bug: chromium:1029972 Change-Id: If32ddb2baf974ee1af7833710bd10a60e39c0169 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/258736 Reviewed-by: Ben Wagner Commit-Queue: Herb Derby --- src/gpu/text/GrTextBlob.cpp | 101 +++++++++++++++++++++--------------- src/gpu/text/GrTextBlob.h | 12 +++-- 2 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp index e8f08bb7ff..7ae8d52b06 100644 --- a/src/gpu/text/GrTextBlob.cpp +++ b/src/gpu/text/GrTextBlob.cpp @@ -20,10 +20,6 @@ #include -template static size_t sk_align(size_t s) { - return ((s + (N-1)) / N) * N; -} - static void calculate_translation(bool applyVM, const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY, const SkMatrix& currentViewMatrix, SkScalar currentX, @@ -82,6 +78,7 @@ GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrike , fY{textBlob->fInitialOrigin.y()} , fCurrentViewMatrix{textBlob->fInitialViewMatrix} { SkASSERT(type != kTransformedPath); + textBlob->insertSubRun(this); } GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec) @@ -95,7 +92,9 @@ GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec) , fStrikeSpec{strikeSpec} , fStrike{nullptr} , fColor{textBlob->fColor} - , fPaths{} { } + , fPaths{} { + textBlob->insertSubRun(this); +} void GrTextBlob::SubRun::appendGlyphs(const SkZip& drawables) { GrTextStrike* grStrike = fStrike.get(); @@ -223,32 +222,34 @@ sk_sp GrTextBlob::Make(const SkGlyphRunList& glyphRunList, bool forceWForDistanceFields) { static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex)); - size_t vertexSize = sizeof(Mask2DVertex); + static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex)); + size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph; if (viewMatrix.hasPerspective() || forceWForDistanceFields) { static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex)); - vertexSize = sizeof(SDFT3DVertex); + static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex)); + quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph; } - size_t quadSize = kVerticesPerGlyph * vertexSize; + // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments. + static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex)); - size_t glyphCount = glyphRunList.totalGlyphCount(); - // We allocate size for the GrTextBlob itself, plus size for the vertices array, - // and size for the glyphIds array. - size_t verticesCount = glyphCount * quadSize; + size_t subRunsOffset = sizeof(GrTextBlob); + size_t subRunsSize = glyphRunList.runCount() * sizeof(SubRun); + static_assert(alignof(SubRun) >= alignof(GrGlyph*)); + size_t glyphsOffset = subRunsOffset + subRunsSize; + static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex)); + size_t vertexOffset = glyphsOffset + sizeof(GrGlyph*) * glyphRunList.totalGlyphCount(); + size_t allocationSize = vertexOffset + quadSize * glyphRunList.totalGlyphCount(); - size_t blobStart = 0; - size_t vertex = sk_align (blobStart + sizeof(GrTextBlob) * 1); - size_t glyphs = sk_align (vertex + sizeof(char) * verticesCount); - size_t size = (glyphs + sizeof(GrGlyph*) * glyphCount); - - void* allocation = ::operator new (size); + void* allocation = ::operator new (allocationSize); sk_sp blob{new (allocation) GrTextBlob{ - size, strikeCache, viewMatrix, glyphRunList.origin(), color, forceWForDistanceFields}}; + subRunsSize, strikeCache, viewMatrix, glyphRunList.origin(), + color, forceWForDistanceFields}}; // setup offsets for vertices / glyphs - blob->fVertices = SkTAddOffset(blob.get(), vertex); - blob->fGlyphs = SkTAddOffset(blob.get(), glyphs); + blob->fVertices = SkTAddOffset(blob.get(), vertexOffset); + blob->fGlyphs = SkTAddOffset(blob.get(), glyphsOffset); return blob; } @@ -286,7 +287,9 @@ void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::Bl const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; } uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); } -bool GrTextBlob::hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); } +bool GrTextBlob::hasDistanceField() const { + return SkToBool(fTextType & kHasDistanceField_TextType); +} bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); } bool GrTextBlob::hasPerspective() const { return fInitialViewMatrix.hasPerspective(); } @@ -401,10 +404,10 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { - for (auto& subRun : fSubRuns) { - if (subRun.drawAsPaths()) { + for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) { + if (subRun->drawAsPaths()) { SkPaint runPaint{paint}; - runPaint.setAntiAlias(subRun.isAntiAliased()); + runPaint.setAntiAlias(subRun->isAntiAliased()); // If there are shaders, blurs or styles, the path must be scaled into source // space independently of the CTM. This allows the CTM to be correct for the // different effects. @@ -417,9 +420,10 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, // The origin for the blob may have changed, so figure out the delta. SkVector originShift = SkPoint{x, y} - fInitialOrigin; - for (const auto& pathGlyph : subRun.fPaths) { + for (const auto& pathGlyph : subRun->fPaths) { SkMatrix ctm{viewMatrix}; - SkMatrix pathMatrix = SkMatrix::MakeScale(subRun.fStrikeSpec.strikeToSourceRatio()); + SkMatrix pathMatrix = SkMatrix::MakeScale( + subRun->fStrikeSpec.strikeToSourceRatio()); // Shift the original glyph location in source space to the position of the new // blob. pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(), @@ -447,7 +451,7 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, target->drawShape(clip, runPaint, ctm, shape); } } else { - int glyphCount = subRun.glyphCount(); + int glyphCount = subRun->glyphCount(); if (0 == glyphCount) { continue; } @@ -460,13 +464,13 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, GrAA aa; // We can clip geometrically if we're not using SDFs or transformed glyphs, // and we have an axis-aligned rectangular non-AA clip - if (!subRun.drawAsDistanceFields() && !subRun.needsTransform() && + if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() && clip.isRRect(rtBounds, &clipRRect, &aa) && clipRRect.isRect() && GrAA::kNo == aa) { skipClip = true; // We only need to do clipping work if the subrun isn't contained by the clip SkRect subRunBounds; - this->computeSubRunBounds(&subRunBounds, subRun, viewMatrix, x, y, false); + this->computeSubRunBounds(&subRunBounds, *subRun, viewMatrix, x, y, false); if (!clipRRect.getBounds().contains(subRunBounds)) { // If the subrun is completely outside, don't add an op for it if (!clipRRect.getBounds().intersects(subRunBounds)) { @@ -479,7 +483,7 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, } if (submitOp) { - auto op = this->makeOp(subRun, glyphCount, viewMatrix, x, y, + auto op = this->makeOp(*subRun, glyphCount, viewMatrix, x, y, clipRect, paint, filteredColor, props, distanceAdjustTable, target); if (op) { @@ -538,9 +542,9 @@ std::unique_ptr GrTextBlob::test_makeOp( SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) { - GrTextBlob::SubRun& info = fSubRuns[0]; + SubRun* info = fFirstSubRun; SkIRect emptyRect = SkIRect::MakeEmpty(); - return this->makeOp(info, glyphCount, viewMatrix, x, y, emptyRect, + return this->makeOp(*info, glyphCount, viewMatrix, x, y, emptyRect, paint, filteredColor, props, distanceAdjustTable, target); } @@ -573,12 +577,12 @@ GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type, sk_sp grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache); - SubRun& subRun = fSubRuns.emplace_back( + SubRun* subRun = fAlloc.make( type, this, strikeSpec, format, bufferSpec, std::move(grStrike)); - subRun.appendGlyphs(drawables); + subRun->appendGlyphs(drawables); - return &subRun; + return subRun; } void GrTextBlob::addSingleMaskFormat( @@ -627,21 +631,32 @@ void GrTextBlob::addSDFT(const SkZip& drawables, subRun->setAntiAliased(runFont.hasSomeAntiAliasing()); } -GrTextBlob::GrTextBlob(size_t size, +GrTextBlob::GrTextBlob(size_t allocSize, GrStrikeCache* strikeCache, const SkMatrix& viewMatrix, SkPoint origin, GrColor color, bool forceWForDistanceFields) - : fSize{size} + : fSize{allocSize} , fStrikeCache{strikeCache} , fInitialViewMatrix{viewMatrix} , fInitialViewMatrixInverse{make_inverse(viewMatrix)} , fInitialOrigin{origin} , fForceWForDistanceFields{forceWForDistanceFields} - , fColor{color} { } + , fColor{color} + , fAlloc{SkTAddOffset(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { } -inline std::unique_ptr GrTextBlob::makeOp( +void GrTextBlob::insertSubRun(SubRun* subRun) { + if (fFirstSubRun == nullptr) { + fFirstSubRun = subRun; + fLastSubRun = subRun; + } else { + fLastSubRun->fNextSubRun = subRun; + fLastSubRun = subRun; + } +} + +std::unique_ptr GrTextBlob::makeOp( SubRun& info, int glyphCount, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect, const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props, @@ -682,10 +697,10 @@ void GrTextBlob::processSourcePaths(const SkZip& drawab const SkFont& runFont, const SkStrikeSpec& strikeSpec) { this->setHasBitmap(); - SubRun& subRun = fSubRuns.emplace_back(this, strikeSpec); - subRun.setAntiAliased(runFont.hasSomeAntiAliasing()); + SubRun* subRun = fAlloc.make(this, strikeSpec); + subRun->setAntiAliased(runFont.hasSomeAntiAliasing()); for (auto [variant, pos] : drawables) { - subRun.fPaths.emplace_back(*variant.path(), pos); + subRun->fPaths.emplace_back(*variant.path(), pos); } } diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h index 3ecfd12ad2..cd5cc25b25 100644 --- a/src/gpu/text/GrTextBlob.h +++ b/src/gpu/text/GrTextBlob.h @@ -138,6 +138,7 @@ public: const SkStrikeSpec& strikeSpec() const; + SubRun* fNextSubRun{nullptr}; const SubRunType fType; GrTextBlob* const fBlob; const GrMaskFormat fMaskFormat; @@ -292,13 +293,15 @@ private: SkPaint::Join fJoin; }; - GrTextBlob(size_t size, + GrTextBlob(size_t allocSize, GrStrikeCache* strikeCache, const SkMatrix& viewMatrix, SkPoint origin, GrColor color, bool forceWForDistanceFields); + void insertSubRun(SubRun* subRun); + std::unique_ptr makeOp( SubRun& info, int glyphCount, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect, @@ -352,9 +355,6 @@ private: // Number of glyphs stored in fGlyphs while accumulating SubRuns. uint32_t fGlyphsCursor{0}; - // Assume one run per text blob. - SkSTArray<1, SubRun> fSubRuns; - SkMaskFilterBase::BlurRec fBlurRec; StrokeInfo fStrokeInfo; Key fKey; @@ -367,6 +367,10 @@ private: SkScalar fMinMaxScale{SK_ScalarMax}; uint8_t fTextType{0}; + + SubRun* fFirstSubRun{nullptr}; + SubRun* fLastSubRun{nullptr}; + SkArenaAlloc fAlloc; }; /**