From 1e0b3b797388e3e3222bce8c2db81e7ed9338e42 Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Tue, 1 Feb 2022 11:51:21 -0500 Subject: [PATCH] codify SDFT matrix range Since the introduction of canReuse on a per-run basis, we have not revisited how SDFT reuse is handled. Abandon the less precise per GrTextBlob calculation for handling matrix range calculations on the SubRun. Change-Id: Ib943b97b2184da1abedbe6851200e6487908bad0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/502696 Reviewed-by: Robert Phillips Commit-Queue: Herb Derby --- src/core/SkGlyphRunPainter.cpp | 4 +- src/core/SkGlyphRunPainter.h | 4 +- src/core/SkStrikeSpec.cpp | 13 ++--- src/core/SkStrikeSpec.h | 2 +- src/gpu/text/GrSDFTControl.cpp | 87 ++++++++++++---------------------- src/gpu/text/GrSDFTControl.h | 23 +++++++-- src/gpu/text/GrTextBlob.cpp | 46 ++++++++---------- src/gpu/text/GrTextBlob.h | 12 +---- 8 files changed, 79 insertions(+), 112 deletions(-) diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp index 9e857eb699..dfc2a97888 100644 --- a/src/core/SkGlyphRunPainter.cpp +++ b/src/core/SkGlyphRunPainter.cpp @@ -287,7 +287,7 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process, if (control.isSDFT(approximateDeviceTextSize, runPaint)) { // Process SDFT - This should be the .009% case. - const auto& [strikeSpec, strikeToSourceScale, minScale, maxScale] = + const auto& [strikeSpec, strikeToSourceScale, matrixRange] = SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, drawMatrix, control); #if defined(SK_TRACE_GLYPH_RUN_PROCESS) @@ -311,7 +311,7 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process, strike->getUnderlyingStrike(), strikeToSourceScale, runFont, - minScale, maxScale); + matrixRange); } } } diff --git a/src/core/SkGlyphRunPainter.h b/src/core/SkGlyphRunPainter.h index 21939c55ee..0692df95a1 100644 --- a/src/core/SkGlyphRunPainter.h +++ b/src/core/SkGlyphRunPainter.h @@ -23,6 +23,7 @@ namespace skgpu { namespace v1 { class SurfaceDrawContext; }} class SkGlyphRunPainterInterface; class SkStrikeSpec; +class GrSDFTMatrixRange; // round and ignorePositionMask are used to calculate the subpixel position of a glyph. // The per component (x or y) calculation is: @@ -149,8 +150,7 @@ public: sk_sp&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, - SkScalar maxScale) = 0; + const GrSDFTMatrixRange& matrixRange) = 0; }; #endif // SkGlyphRunPainter_DEFINED diff --git a/src/core/SkStrikeSpec.cpp b/src/core/SkStrikeSpec.cpp index 6507635ec8..6147cf3681 100644 --- a/src/core/SkStrikeSpec.cpp +++ b/src/core/SkStrikeSpec.cpp @@ -171,25 +171,22 @@ SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) } #if SK_SUPPORT_GPU -std::tuple +std::tuple SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix, const GrSDFTControl& control) { + // Add filter to the paint which creates the SDFT data for A8 masks. SkPaint dfPaint{paint}; dfPaint.setMaskFilter(GrSDFMaskFilter::Make()); - SkScalar strikeToSourceScale; - SkFont dfFont = control.getSDFFont(font, deviceMatrix, &strikeToSourceScale); + + auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix); // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). SkScalerContextFlags flags = SkScalerContextFlags::kNone; - - SkScalar minScale, maxScale; - std::tie(minScale, maxScale) = control.computeSDFMinMaxScale(font.getSize(), deviceMatrix); - SkStrikeSpec strikeSpec(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I()); - return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, minScale, maxScale); + return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange); } sk_sp SkStrikeSpec::findOrCreateGrStrike(GrStrikeCache* cache) const { diff --git a/src/core/SkStrikeSpec.h b/src/core/SkStrikeSpec.h index a544483a7f..530eb1bb6d 100644 --- a/src/core/SkStrikeSpec.h +++ b/src/core/SkStrikeSpec.h @@ -71,7 +71,7 @@ public: #if SK_SUPPORT_GPU // Create a strike spec for scaled distance field text. - static std::tuple MakeSDFT( + static std::tuple MakeSDFT( const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, diff --git a/src/gpu/text/GrSDFTControl.cpp b/src/gpu/text/GrSDFTControl.cpp index a428b1d686..8eb695aca9 100644 --- a/src/gpu/text/GrSDFTControl.cpp +++ b/src/gpu/text/GrSDFTControl.cpp @@ -20,19 +20,16 @@ // DF sizes and thresholds for usage of the small and medium sizes. For example, above // kSmallDFFontLimit we will use the medium size. The large size is used up until the size at // which we switch over to drawing as paths as controlled by Control. -static const int kSmallDFFontSize = 32; static const int kSmallDFFontLimit = 32; -static const int kMediumDFFontSize = 72; static const int kMediumDFFontLimit = 72; -static const int kLargeDFFontSize = 162; -#ifdef SK_BUILD_FOR_MAC static const int kLargeDFFontLimit = 162; -static const int kExtraLargeDFFontSize = 256; +#ifdef SK_BUILD_FOR_MAC +static const int kExtraLargeDFFontLimit = 256; #endif SkScalar GrSDFTControl::MinSDFTRange(bool useSDFTForSmallText, SkScalar min) { if (!useSDFTForSmallText) { - return kLargeDFFontSize; + return kLargeDFFontLimit; } return min; } @@ -78,51 +75,13 @@ SkScalar scaled_text_size(const SkScalar textSize, const SkMatrix& viewMatrix) { return scaledTextSize; } -SkFont GrSDFTControl::getSDFFont(const SkFont& font, - const SkMatrix& viewMatrix, - SkScalar* textRatio) const { +std::tuple +GrSDFTControl::getSDFFont(const SkFont& font, const SkMatrix& viewMatrix) const { SkScalar textSize = font.getSize(); SkScalar scaledTextSize = scaled_text_size(textSize, viewMatrix); SkFont dfFont{font}; - if (scaledTextSize <= kSmallDFFontLimit) { - *textRatio = textSize / kSmallDFFontSize; - dfFont.setSize(SkIntToScalar(kSmallDFFontSize)); - } else if (scaledTextSize <= kMediumDFFontLimit) { - *textRatio = textSize / kMediumDFFontSize; - dfFont.setSize(SkIntToScalar(kMediumDFFontSize)); -#ifdef SK_BUILD_FOR_MAC - } else if (scaledTextSize <= kLargeDFFontLimit) { - *textRatio = textSize / kLargeDFFontSize; - dfFont.setSize(SkIntToScalar(kLargeDFFontSize)); - } else { - *textRatio = textSize / kExtraLargeDFFontSize; - dfFont.setSize(SkIntToScalar(kExtraLargeDFFontSize)); - } -#else - } else { - *textRatio = textSize / kLargeDFFontSize; - dfFont.setSize(SkIntToScalar(kLargeDFFontSize)); - } -#endif - - dfFont.setEdging(SkFont::Edging::kAntiAlias); - dfFont.setForceAutoHinting(false); - dfFont.setHinting(SkFontHinting::kNormal); - - // The sub-pixel position will always happen when transforming to the screen. - dfFont.setSubpixel(false); - return dfFont; -} - -std::pair GrSDFTControl::computeSDFMinMaxScale( - SkScalar textSize, const SkMatrix& viewMatrix) const { - - SkScalar scaledTextSize = scaled_text_size(textSize, viewMatrix); - - // We have three sizes of distance field text, and within each size 'bucket' there is a floor - // and ceiling. A scale outside of this range would require regenerating the distance fields SkScalar dfMaskScaleFloor; SkScalar dfMaskScaleCeil; if (scaledTextSize <= kSmallDFFontLimit) { @@ -131,21 +90,35 @@ std::pair GrSDFTControl::computeSDFMinMaxScale( } else if (scaledTextSize <= kMediumDFFontLimit) { dfMaskScaleFloor = kSmallDFFontLimit; dfMaskScaleCeil = kMediumDFFontLimit; +#ifdef SK_BUILD_FOR_MAC + } else if (scaledTextSize <= kLargeDFFontLimit) { + dfMaskScaleFloor = kMediumDFFontLimit; + dfMaskScaleCeil = kLargeDFFontLimit; + } else { + dfMaskScaleFloor = kLargeDFFontLimit; + dfMaskScaleCeil = kExtraLargeDFFontLimit; + } +#else } else { dfMaskScaleFloor = kMediumDFFontLimit; - dfMaskScaleCeil = fMaxDistanceFieldFontSize; + dfMaskScaleCeil = kLargeDFFontLimit; } +#endif - // Because there can be multiple runs in the blob, we want the overall maxMinScale, and - // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale - // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can - // tolerate before we'd have to move to a large mip size. When we actually test these values - // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test - // against these values to decide if we can reuse or not(ie, will a given scale change our mip - // level) - SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); + dfFont.setSize(SkIntToScalar(dfMaskScaleCeil)); + dfFont.setEdging(SkFont::Edging::kAntiAlias); + dfFont.setForceAutoHinting(false); + dfFont.setHinting(SkFontHinting::kNormal); - return std::make_pair(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize); + // The sub-pixel position will always happen when transforming to the screen. + dfFont.setSubpixel(false); + + SkScalar minMatrixScale = dfMaskScaleFloor / textSize, + maxMatrixScale = dfMaskScaleCeil / textSize; + return {dfFont, textSize / dfMaskScaleCeil, {minMatrixScale, maxMatrixScale}}; } - +bool GrSDFTMatrixRange::matrixInRange(const SkMatrix& matrix) const { + SkScalar maxScale = matrix.getMaxScale(); + return fMatrixMin < maxScale && maxScale <= fMatrixMax; +} diff --git a/src/gpu/text/GrSDFTControl.h b/src/gpu/text/GrSDFTControl.h index 38990f13e3..a36042377b 100644 --- a/src/gpu/text/GrSDFTControl.h +++ b/src/gpu/text/GrSDFTControl.h @@ -11,18 +11,31 @@ #include "include/core/SkFont.h" #include "include/core/SkScalar.h" +#include + class SkMatrix; class SkSurfaceProps; +// Two numbers fMatrixMin and fMatrixMax such that if viewMatrix.getMaxScale() is between them then +// this SDFT size can be reused. +class GrSDFTMatrixRange { +public: + GrSDFTMatrixRange(SkScalar min, SkScalar max) : fMatrixMin{min}, fMatrixMax{max} {} + bool matrixInRange(const SkMatrix& matrix) const; + +private: + const SkScalar fMatrixMin, + fMatrixMax; +}; + class GrSDFTControl { public: GrSDFTControl(bool ableToUseSDFT, bool useSDFTForSmallText, SkScalar min, SkScalar max); - SkFont getSDFFont(const SkFont& font, - const SkMatrix& viewMatrix, - SkScalar* textRatio) const; - std::pair computeSDFMinMaxScale( - SkScalar textSize, const SkMatrix& viewMatrix) const; + // Produce a font, a scale factor from the nominal size to the source space size, and matrix + // range where this font can be reused. + std::tuple + getSDFFont(const SkFont& font, const SkMatrix& viewMatrix) const; bool isDirect(SkScalar approximateDeviceTextSize, const SkPaint& paint) const; bool isSDFT(SkScalar approximateDeviceTextSize, const SkPaint& paint) const; diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp index 47e3ed36d5..481e81f453 100644 --- a/src/gpu/text/GrTextBlob.cpp +++ b/src/gpu/text/GrTextBlob.cpp @@ -1277,12 +1277,14 @@ public: SkSpan vertexData, GlyphVector&& glyphs, bool useLCDText, - bool antiAliased); + bool antiAliased, + const GrSDFTMatrixRange& matrixRange); static GrSubRunOwner Make(const SkZip& drawables, const SkFont& runFont, sk_sp&& strike, SkScalar strikeToSourceScale, + const GrSDFTMatrixRange& matrixRange, GrTextBlob* blob, GrSubRunAllocator* alloc); @@ -1339,6 +1341,7 @@ private: const bool fUseLCDText; const bool fAntiAliased; + const GrSDFTMatrixRange fMatrixRange; }; SDFTSubRun::SDFTSubRun(GrMaskFormat format, @@ -1348,7 +1351,8 @@ SDFTSubRun::SDFTSubRun(GrMaskFormat format, SkSpan vertexData, GlyphVector&& glyphs, bool useLCDText, - bool antiAliased) + bool antiAliased, + const GrSDFTMatrixRange& matrixRange) : fMaskFormat{format} , fBlob{textBlob} , fStrikeToSourceScale{strikeToSource} @@ -1356,7 +1360,8 @@ SDFTSubRun::SDFTSubRun(GrMaskFormat format, , fVertexData{vertexData} , fGlyphs{std::move(glyphs)} , fUseLCDText{useLCDText} - , fAntiAliased{antiAliased} {} + , fAntiAliased{antiAliased} + , fMatrixRange{matrixRange} {} bool has_some_antialiasing(const SkFont& font ) { SkFont::Edging edging = font.getEdging(); @@ -1368,6 +1373,7 @@ GrSubRunOwner SDFTSubRun::Make(const SkZip& drawables, const SkFont& runFont, sk_sp&& strike, SkScalar strikeToSourceScale, + const GrSDFTMatrixRange& matrixRange, GrTextBlob* blob, GrSubRunAllocator* alloc) { SkRect bounds = SkRectPriv::MakeLargestInverted(); @@ -1395,7 +1401,8 @@ GrSubRunOwner SDFTSubRun::Make(const SkZip& drawables, vertexData, GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc), runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias, - has_some_antialiasing(runFont)); + has_some_antialiasing(runFont), + matrixRange); } void SDFTSubRun::draw(SkCanvas*, @@ -1481,18 +1488,7 @@ SDFTSubRun::makeAtlasTextOp(const GrClip* clip, } bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const { - const SkMatrix& initialPositionMatrix = fBlob->initialPositionMatrix(); - - // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different - // distance field being generated, so we have to regenerate in those cases - SkScalar newMaxScale = positionMatrix.getMaxScale(); - SkScalar oldMaxScale = initialPositionMatrix.getMaxScale(); - SkScalar scaleAdjust = newMaxScale / oldMaxScale; - auto [maxMinScale, minMaxScale] = fBlob->scaleBounds(); - if (scaleAdjust < maxMinScale || scaleAdjust > minMaxScale) { - return false; - } - return true; + return fMatrixRange.matrixInRange(positionMatrix); } void SDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const { @@ -1848,13 +1844,10 @@ void GrTextBlob::processSourceSDFT(const SkZip& drawabl sk_sp&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, - SkScalar maxScale) { - - fMaxMinScale = std::max(minScale, fMaxMinScale); - fMinMaxScale = std::min(maxScale, fMinMaxScale); + const GrSDFTMatrixRange& matrixRange) { fSubRunList.append( - SDFTSubRun::Make(drawables, runFont, std::move(strike), strikeToSourceScale,this, &fAlloc)); + SDFTSubRun::Make( + drawables, runFont, std::move(strike), strikeToSourceScale, matrixRange, this,&fAlloc)); } void GrTextBlob::processSourceMasks(const SkZip& drawables, @@ -2560,7 +2553,7 @@ void GrSubRunNoCachePainter::processSourceSDFT(const SkZip&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, SkScalar maxScale) { + const GrSDFTMatrixRange&) { if (drawables.empty()) { return; } @@ -2615,8 +2608,8 @@ public: SkScalar strikeToSourceScale) override; void processSourceSDFT( const SkZip& drawables, sk_sp&& strike, - SkScalar strikeToSourceScale, const SkFont& runFont, SkScalar minScale, - SkScalar maxScale) override; + SkScalar strikeToSourceScale, const SkFont& runFont, + const GrSDFTMatrixRange& matrixRange) override; const SkMatrix& initialPositionMatrix() const override { return fInitialPositionMatrix; } SkPoint origin() const { return fOrigin; } @@ -3321,8 +3314,7 @@ void Slug::processSourceSDFT(const SkZip& drawables, sk_sp&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, - SkScalar maxScale) { + const GrSDFTMatrixRange&) { fSubRuns.append(SDFTSubRunSlug::Make( this, drawables, runFont, std::move(strike), strikeToSourceScale, &fAlloc)); diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h index ad57bd9029..680539e0d5 100644 --- a/src/gpu/text/GrTextBlob.h +++ b/src/gpu/text/GrTextBlob.h @@ -224,7 +224,6 @@ public: const SkMatrix& initialPositionMatrix() const override { return fInitialPositionMatrix; } bool supportBilerpAtlas() const { return fSupportBilerpAtlas; } - std::tuple scaleBounds() const { return {fMaxMinScale, fMinMaxScale}; } bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const; const Key& key() const; @@ -254,8 +253,7 @@ private: sk_sp&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, - SkScalar maxScale) override; + const GrSDFTMatrixRange& matrixRange) override; void processSourceMasks(const SkZip& drawables, sk_sp&& strike, SkScalar strikeToSourceScale) override; @@ -281,12 +279,6 @@ private: 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}; }; @@ -311,7 +303,7 @@ public: sk_sp&& strike, SkScalar strikeToSourceScale, const SkFont& runFont, - SkScalar minScale, SkScalar maxScale) override; + const GrSDFTMatrixRange& matrixRange) override; private: