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 <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 1a251b9..3e0da52 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -57,15 +57,15 @@
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 a95319b..5f78d58 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -443,8 +443,8 @@
}
}
- 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 91fcf3f..3f217e7 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -470,11 +470,11 @@
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 ceadf58..2bd4df5 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -1429,8 +1429,8 @@
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 @@
auto addSameFormat = [&](const SkZip<SkGlyphVariant, SkPoint>& 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 @@
, fInitialLuminance{initialLuminance}
, fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
-void GrTextBlob::insertSubRun(GrSubRun* subRun) {
- fSubRunList.addToTail(subRun);
-}
-
void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) {
@@ -1501,7 +1497,7 @@
strikeSpec,
*this,
&fAlloc);
- this->insertSubRun(subRun);
+ fSubRunList.append(subRun);
}
void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
@@ -1511,7 +1507,7 @@
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<SkGlyphVariant, SkPoint>& drawables,
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index 3807101..bdd59cb 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -37,125 +37,6 @@
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<GrTextBlob>, 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<GrTextBlob> 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<SkScalar, SkScalar> scaleBounds() const {
- return {fMaxMinScale, fMinMaxScale};
- }
-
- bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix);
-
- const Key& key() const;
- size_t size() const;
-
- template<typename AddSingleMaskFormat>
- void addMultiMaskFormat(
- AddSingleMaskFormat addSingle,
- const SkZip<SkGlyphVariant, SkPoint>& drawables,
- const SkStrikeSpec& strikeSpec);
-
- const SkTInternalLList<GrSubRun>& 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<SkGlyphVariant, SkPoint>& drawables,
- const SkStrikeSpec& strikeSpec) override;
- void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
- const SkFont& runFont,
- const SkStrikeSpec& strikeSpec) override;
- void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
- const SkStrikeSpec& strikeSpec,
- const SkFont& runFont,
- SkScalar minScale,
- SkScalar maxScale) override;
- void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& 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<GrSubRun> 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 @@
// * 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<GrTextBlob>, 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<GrTextBlob> 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<SkScalar, SkScalar> scaleBounds() const {
+ return {fMaxMinScale, fMinMaxScale};
+ }
+
+ bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix);
+
+ const Key& key() const;
+ size_t size() const;
+
+ template<typename AddSingleMaskFormat>
+ void addMultiMaskFormat(
+ AddSingleMaskFormat addSingle,
+ const SkZip<SkGlyphVariant, SkPoint>& 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<SkGlyphVariant, SkPoint>& drawables,
+ const SkStrikeSpec& strikeSpec) override;
+ void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+ const SkFont& runFont,
+ const SkStrikeSpec& strikeSpec) override;
+ void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+ const SkStrikeSpec& strikeSpec,
+ const SkFont& runFont,
+ SkScalar minScale,
+ SkScalar maxScale) override;
+ void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& 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