From 55f795e696a96033b593e39f8c18463108ceed62 Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Fri, 5 Feb 2021 13:45:05 -0500 Subject: [PATCH] introduce a subrun linked list Create a simple linked list to sequence the subruns preparing the way for a more focused allocator for managing subruns at the end of the GrTextBlob. Change-Id: I595e2ce2810d161332a23405e4615724d2953471 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/366957 Reviewed-by: Robert Phillips Commit-Queue: Herb Derby --- bench/GlyphQuadFillBench.cpp | 6 +- src/gpu/GrSurfaceDrawContext.cpp | 4 +- src/gpu/ops/GrAtlasTextOp.cpp | 4 +- src/gpu/text/GrTextBlob.cpp | 14 +- src/gpu/text/GrTextBlob.h | 272 +++++++++++++++++-------------- 5 files changed, 164 insertions(+), 136 deletions(-) diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp index 1a251b9164..3e0da52f54 100644 --- a/bench/GlyphQuadFillBench.cpp +++ b/bench/GlyphQuadFillBench.cpp @@ -57,15 +57,15 @@ class DirectMaskGlyphVertexFillBenchmark : public Benchmark { glyphRun, view, drawOrigin, drawPaint, props, false, options, fBlob.get()); } - SkASSERT(fBlob->subRunList().head() != nullptr); - GrAtlasSubRun* subRun = fBlob->subRunList().head()->testingOnly_atlasSubRun(); + SkASSERT(!fBlob->subRunList().isEmpty()); + GrAtlasSubRun* subRun = fBlob->subRunList().front().testingOnly_atlasSubRun(); SkASSERT(subRun); subRun->testingOnly_packedGlyphIDToGrGlyph(&fCache); fVertices.reset(new char[subRun->vertexStride(view) * subRun->glyphCount() * 4]); } void onDraw(int loops, SkCanvas* canvas) override { - GrAtlasSubRun* subRun = fBlob->subRunList().head()->testingOnly_atlasSubRun(); + GrAtlasSubRun* subRun = fBlob->subRunList().front().testingOnly_atlasSubRun(); SkASSERT(subRun); SkIRect clip = SkIRect::MakeEmpty(); diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp index a95319b538..5f78d58770 100644 --- a/src/gpu/GrSurfaceDrawContext.cpp +++ b/src/gpu/GrSurfaceDrawContext.cpp @@ -443,8 +443,8 @@ void GrSurfaceDrawContext::drawGlyphRunList(const GrClip* clip, } } - for (GrSubRun* subRun : blob->subRunList()) { - subRun->draw(clip, viewMatrix, glyphRunList, this); + for (GrSubRun& subRun : blob->subRunList()) { + subRun.draw(clip, viewMatrix, glyphRunList, this); } } diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp index 91fcf3fe11..3f217e7739 100644 --- a/src/gpu/ops/GrAtlasTextOp.cpp +++ b/src/gpu/ops/GrAtlasTextOp.cpp @@ -470,11 +470,11 @@ GrOp::Owner GrAtlasTextOp::CreateOpTestingOnly(GrSurfaceDrawContext* rtc, rtc->surfaceProps(), rContext->priv().caps()->shaderCaps()->supportsDistanceFieldText(), SDFOptions, blob.get()); - if (!blob->subRunList().head()) { + if (blob->subRunList().isEmpty()) { return nullptr; } - GrAtlasSubRun* subRun = blob->subRunList().head()->testingOnly_atlasSubRun(); + GrAtlasSubRun* subRun = blob->subRunList().front().testingOnly_atlasSubRun(); SkASSERT(subRun); GrOp::Owner op; std::tie(std::ignore, op) = subRun->makeAtlasTextOp(nullptr, mtxProvider, glyphRunList, rtc); diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp index ceadf581f5..2bd4df55ff 100644 --- a/src/gpu/text/GrTextBlob.cpp +++ b/src/gpu/text/GrTextBlob.cpp @@ -1429,8 +1429,8 @@ bool GrTextBlob::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) { return false; } - for (GrSubRun* subRun : this->subRunList()) { - if (!subRun->canReuse(paint, drawMatrix)) { + for (GrSubRun& subRun : this->fSubRunList) { + if (!subRun.canReuse(paint, drawMatrix)) { return false; } } @@ -1451,7 +1451,7 @@ void GrTextBlob::addMultiMaskFormat( auto addSameFormat = [&](const SkZip& drawable, GrMaskFormat format) { GrSubRun* subRun = addSingle(drawable, strikeSpec, format, this, &fAlloc); if (subRun != nullptr) { - this->insertSubRun(subRun); + fSubRunList.append(subRun); } else { fSomeGlyphsExcluded = true; } @@ -1483,10 +1483,6 @@ GrTextBlob::GrTextBlob(size_t allocSize, , fInitialLuminance{initialLuminance} , fAlloc{SkTAddOffset(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { } -void GrTextBlob::insertSubRun(GrSubRun* subRun) { - fSubRunList.addToTail(subRun); -} - void GrTextBlob::processDeviceMasks(const SkZip& drawables, const SkStrikeSpec& strikeSpec) { @@ -1501,7 +1497,7 @@ void GrTextBlob::processSourcePaths(const SkZip& drawab strikeSpec, *this, &fAlloc); - this->insertSubRun(subRun); + fSubRunList.append(subRun); } void GrTextBlob::processSourceSDFT(const SkZip& drawables, @@ -1511,7 +1507,7 @@ void GrTextBlob::processSourceSDFT(const SkZip& drawabl SkScalar maxScale) { this->setMinAndMaxScale(minScale, maxScale); GrSubRun* subRun = SDFTSubRun::Make(drawables, runFont, strikeSpec, this, &fAlloc); - this->insertSubRun(subRun); + fSubRunList.append(subRun); } void GrTextBlob::processSourceMasks(const SkZip& drawables, diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h index 3807101153..bdd59cb8f8 100644 --- a/src/gpu/text/GrTextBlob.h +++ b/src/gpu/text/GrTextBlob.h @@ -37,125 +37,6 @@ class SkSurfaceProps; class SkTextBlob; class SkTextBlobRunIterator; -// A GrTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing -// on the GPU. These are initially created with valid positions and colors, but invalid -// texture coordinates. -// -// A GrTextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun -// tracks its own GrGlyph* and vertex data. The memory is organized in the arena in the following -// way so that the pointers for the GrGlyph* and vertex data are known before creating the SubRun. -// -// GrGlyph*... | vertexData... | SubRun | GrGlyph*... | vertexData... | SubRun etc. -// -// In these classes, I'm trying to follow the convention about matrices and origins. -// * draw Matrix|Origin - describes the current draw command. -// * initial Matrix - describes the combined initial matrix and origin the GrTextBlob was created -// with. -// -// -class GrTextBlob final : public SkNVRefCnt, public SkGlyphRunPainterInterface { -public: - struct Key { - Key(); - uint32_t fUniqueID; - // Color may affect the gamma of the mask we generate, but in a fairly limited way. - // Each color is assigned to on of a fixed number of buckets based on its - // luminance. For each luminance bucket there is a "canonical color" that - // represents the bucket. This functionality is currently only supported for A8 - SkColor fCanonicalColor; - SkPaint::Style fStyle; - SkScalar fFrameWidth; - SkScalar fMiterLimit; - SkPaint::Join fJoin; - SkPixelGeometry fPixelGeometry; - bool fHasBlur; - SkMaskFilterBase::BlurRec fBlurRec; - uint32_t fScalerContextFlags; - - bool operator==(const Key& other) const; - }; - - SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob); - - // Change memory management to handle the data after GrTextBlob, but in the same allocation - // of memory. Only allow placement new. - void operator delete(void* p); - void* operator new(size_t); - void* operator new(size_t, void* p); - - ~GrTextBlob() override; - - // Make an empty GrTextBlob, with all the invariants set to make the right decisions when - // adding SubRuns. - static sk_sp Make(const SkGlyphRunList& glyphRunList, - const SkMatrix& drawMatrix); - - static const Key& GetKey(const GrTextBlob& blob); - static uint32_t Hash(const Key& key); - - void addKey(const Key& key); - bool hasPerspective() const; - const SkMatrix& initialMatrix() const { return fInitialMatrix; } - - void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax); - std::tuple scaleBounds() const { - return {fMaxMinScale, fMinMaxScale}; - } - - bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix); - - const Key& key() const; - size_t size() const; - - template - void addMultiMaskFormat( - AddSingleMaskFormat addSingle, - const SkZip& drawables, - const SkStrikeSpec& strikeSpec); - - const SkTInternalLList& subRunList() const { return fSubRunList; } - -private: - GrTextBlob(size_t allocSize, const SkMatrix& drawMatrix, SkColor initialLuminance); - - void insertSubRun(GrSubRun* subRun); - - // Methods to satisfy SkGlyphRunPainterInterface - void processDeviceMasks(const SkZip& drawables, - const SkStrikeSpec& strikeSpec) override; - void processSourcePaths(const SkZip& drawables, - const SkFont& runFont, - const SkStrikeSpec& strikeSpec) override; - void processSourceSDFT(const SkZip& drawables, - const SkStrikeSpec& strikeSpec, - const SkFont& runFont, - SkScalar minScale, - SkScalar maxScale) override; - void processSourceMasks(const SkZip& drawables, - const SkStrikeSpec& strikeSpec) override; - - // Overall size of this struct plus vertices and glyphs at the end. - const size_t fSize; - - // The initial view matrix combined with the initial origin. Used to determine if a cached - // subRun can be used in this draw situation. - const SkMatrix fInitialMatrix; - - const SkColor fInitialLuminance; - - Key fKey; - - // We can reuse distance field text, but only if the new view matrix would not result in - // a mip change. Because there can be multiple runs in a blob, we track the overall - // maximum minimum scale, and minimum maximum scale, we can support before we need to regen - SkScalar fMaxMinScale{-SK_ScalarMax}; - SkScalar fMinMaxScale{SK_ScalarMax}; - - bool fSomeGlyphsExcluded{false}; - SkTInternalLList fSubRunList; - SkArenaAlloc fAlloc; -}; - // -- GrAtlasSubRun -------------------------------------------------------------------------------- // GrAtlasSubRun is the API that GrAtlasTextOp uses to generate vertex data for drawing. // There are three different ways GrAtlasSubRun is specialized. @@ -222,7 +103,158 @@ public: // * Don't use this API. It is only to support testing. virtual GrAtlasSubRun* testingOnly_atlasSubRun() = 0; + GrSubRun* fNext{nullptr}; +}; + +struct GrSubRunList { + class Iterator { + public: + using value_type = GrSubRun; + using difference_type = ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + constexpr Iterator(GrSubRun* subRun) : fPtr{subRun} { } + constexpr Iterator& operator++() { fPtr = fPtr->fNext; return *this; } + constexpr Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; } + constexpr bool operator==(const Iterator& rhs) const { return fPtr == rhs.fPtr; } + constexpr bool operator!=(const Iterator& rhs) const { return fPtr != rhs.fPtr; } + constexpr reference operator*() { return *fPtr; } + + private: + GrSubRun* fPtr; + }; + + void append(GrSubRun* subRun) { + GrSubRun** newTail = &subRun->fNext; + *fTail = subRun; + fTail = newTail; + } + bool isEmpty() const { return fHead == nullptr; } + Iterator begin() { return Iterator{fHead}; } + Iterator end() { return Iterator{nullptr}; } + GrSubRun& front() const {return *fHead; } + + GrSubRun* fHead{nullptr}; + GrSubRun** fTail{&fHead}; +}; + +// A GrTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing +// on the GPU. These are initially created with valid positions and colors, but invalid +// texture coordinates. +// +// A GrTextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun +// tracks its own GrGlyph* and vertex data. The memory is organized in the arena in the following +// way so that the pointers for the GrGlyph* and vertex data are known before creating the SubRun. +// +// GrGlyph*... | vertexData... | SubRun | GrGlyph*... | vertexData... | SubRun etc. +// +// In these classes, I'm trying to follow the convention about matrices and origins. +// * draw Matrix|Origin - describes the current draw command. +// * initial Matrix - describes the combined initial matrix and origin the GrTextBlob was created +// with. +// +// +class GrTextBlob final : public SkNVRefCnt, public SkGlyphRunPainterInterface { +public: + struct Key { + Key(); + uint32_t fUniqueID; + // Color may affect the gamma of the mask we generate, but in a fairly limited way. + // Each color is assigned to on of a fixed number of buckets based on its + // luminance. For each luminance bucket there is a "canonical color" that + // represents the bucket. This functionality is currently only supported for A8 + SkColor fCanonicalColor; + SkPaint::Style fStyle; + SkScalar fFrameWidth; + SkScalar fMiterLimit; + SkPaint::Join fJoin; + SkPixelGeometry fPixelGeometry; + bool fHasBlur; + SkMaskFilterBase::BlurRec fBlurRec; + uint32_t fScalerContextFlags; + + bool operator==(const Key& other) const; + }; + + SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob); + + // Make an empty GrTextBlob, with all the invariants set to make the right decisions when + // adding SubRuns. + static sk_sp Make(const SkGlyphRunList& glyphRunList, + const SkMatrix& drawMatrix); + + ~GrTextBlob() override; + + // Change memory management to handle the data after GrTextBlob, but in the same allocation + // of memory. Only allow placement new. + void operator delete(void* p); + void* operator new(size_t); + void* operator new(size_t, void* p); + + static const Key& GetKey(const GrTextBlob& blob); + static uint32_t Hash(const Key& key); + + void addKey(const Key& key); + bool hasPerspective() const; + const SkMatrix& initialMatrix() const { return fInitialMatrix; } + + void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax); + std::tuple scaleBounds() const { + return {fMaxMinScale, fMinMaxScale}; + } + + bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix); + + const Key& key() const; + size_t size() const; + + template + void addMultiMaskFormat( + AddSingleMaskFormat addSingle, + const SkZip& drawables, + const SkStrikeSpec& strikeSpec); + + GrSubRunList& subRunList() { + return fSubRunList; + } + private: - SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrSubRun); + GrTextBlob(size_t allocSize, const SkMatrix& drawMatrix, SkColor initialLuminance); + + // Methods to satisfy SkGlyphRunPainterInterface + void processDeviceMasks(const SkZip& drawables, + const SkStrikeSpec& strikeSpec) override; + void processSourcePaths(const SkZip& drawables, + const SkFont& runFont, + const SkStrikeSpec& strikeSpec) override; + void processSourceSDFT(const SkZip& drawables, + const SkStrikeSpec& strikeSpec, + const SkFont& runFont, + SkScalar minScale, + SkScalar maxScale) override; + void processSourceMasks(const SkZip& drawables, + const SkStrikeSpec& strikeSpec) override; + + // Overall size of this struct plus vertices and glyphs at the end. + const size_t fSize; + + // The initial view matrix combined with the initial origin. Used to determine if a cached + // subRun can be used in this draw situation. + const SkMatrix fInitialMatrix; + + const SkColor fInitialLuminance; + + Key fKey; + + // We can reuse distance field text, but only if the new view matrix would not result in + // a mip change. Because there can be multiple runs in a blob, we track the overall + // maximum minimum scale, and minimum maximum scale, we can support before we need to regen + SkScalar fMaxMinScale{-SK_ScalarMax}; + SkScalar fMinMaxScale{SK_ScalarMax}; + + bool fSomeGlyphsExcluded{false}; + GrSubRunList fSubRunList; + SkArenaAlloc fAlloc; }; #endif // GrTextBlob_DEFINED