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>
This commit is contained in:
Herb Derby 2021-02-05 13:45:05 -05:00 committed by Skia Commit-Bot
parent 990a0d8b65
commit 55f795e696
5 changed files with 164 additions and 136 deletions

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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<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 @@ GrTextBlob::GrTextBlob(size_t allocSize,
, 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 @@ void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawab
strikeSpec,
*this,
&fAlloc);
this->insertSubRun(subRun);
fSubRunList.append(subRun);
}
void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
@ -1511,7 +1507,7 @@ void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& 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<SkGlyphVariant, SkPoint>& drawables,

View File

@ -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<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 @@ 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<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