Reland "Use a custom FP for tessellation atlas clips"
This is a reland of 90a0d9f61879940d3c632d886af9f15c311e18fe
Original change's description:
> Use a custom FP for tessellation atlas clips
>
> Something about GrTextureEffect::MakeSubset was upsetting NVIDIA
> Vulkan. This is cleaner anyway though since we only have to create 2
> fps instead of 3, and since we don't need to make new shaders for
> inverting coverage anymore.
>
> Bug: skia:12102
> Change-Id: I5d03ed12abba5c4053e08062c75ac8d40933b422
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419150
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
Bug: skia:12102
Change-Id: I32c3f698ee22179caee196ef884ae64456595c96
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419563
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 94519b6..e2b8bae 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -447,6 +447,8 @@
"$_src/gpu/tessellate/GrVectorXform.h",
# tessellate/shaders
+ "$_src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp",
+ "$_src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h",
"$_src/gpu/tessellate/shaders/GrPathTessellationShader.cpp",
"$_src/gpu/tessellate/shaders/GrPathTessellationShader.h",
"$_src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp",
diff --git a/src/gpu/GrDynamicAtlas.cpp b/src/gpu/GrDynamicAtlas.cpp
index 80818fd..ea44c8c 100644
--- a/src/gpu/GrDynamicAtlas.cpp
+++ b/src/gpu/GrDynamicAtlas.cpp
@@ -109,6 +109,11 @@
return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
}
+GrSurfaceProxyView GrDynamicAtlas::surfaceProxyView(const GrCaps& caps) const {
+ return {fTextureProxy, kTextureOrigin,
+ caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
+}
+
bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
// This can't be called anymore once instantiate() has been called.
SkASSERT(!this->isInstantiated());
diff --git a/src/gpu/GrDynamicAtlas.h b/src/gpu/GrDynamicAtlas.h
index 6191648..38ba3c7 100644
--- a/src/gpu/GrDynamicAtlas.h
+++ b/src/gpu/GrDynamicAtlas.h
@@ -56,6 +56,7 @@
int maxAtlasSize() const { return fMaxAtlasSize; }
GrTextureProxy* textureProxy() const { return fTextureProxy.get(); }
+ GrSurfaceProxyView surfaceProxyView(const GrCaps&) const;
bool isInstantiated() const { return fTextureProxy->isInstantiated(); }
int currentWidth() const { return fWidth; }
int currentHeight() const { return fHeight; }
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index d5628d6..8eec222 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -128,6 +128,7 @@
kFwidthSquircleTestProcessor_ClassID,
kSwizzleFragmentProcessor_ClassID,
kTessellate_BoundingBoxShader_ClassID,
+ kTessellate_GrModulateAtlasCoverageFP_ClassID,
kTessellate_GrStrokeTessellationShader_ClassID,
kTessellate_HardwareCurveShader_ClassID,
kTessellate_HardwareWedgeShader_ClassID,
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
index 9854ff0..c20cfa5 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
@@ -25,6 +25,7 @@
#include "src/gpu/tessellate/GrPathStencilCoverOp.h"
#include "src/gpu/tessellate/GrPathTessellateOp.h"
#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
+#include "src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h"
constexpr static SkISize kAtlasInitialSize{512, 512};
constexpr static int kMaxAtlasSize = 2048;
@@ -210,11 +211,13 @@
surfaceDrawContext->addDrawOp(args.fClip, std::move(op));
}
-GrFPResult GrTessellationPathRenderer::makeAtlasClipFP(
- const SkIRect& drawBounds, const SkMatrix& viewMatrix, const SkPath& path, GrAA aa,
- std::unique_ptr<GrFragmentProcessor> inputCoverage, const GrCaps& caps) {
+GrFPResult GrTessellationPathRenderer::makeAtlasClipFP(const SkIRect& drawBounds,
+ const SkMatrix& viewMatrix,
+ const SkPath& path, GrAA aa,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrCaps& caps) {
if (viewMatrix.hasPerspective()) {
- return GrFPFailure(std::move(inputCoverage));
+ return GrFPFailure(std::move(inputFP));
}
SkIRect devIBounds;
SkIPoint16 locationInAtlas;
@@ -224,63 +227,31 @@
aa != GrAA::kNo, &devIBounds, &locationInAtlas,
&transposedInAtlas)) {
// The path is too big, or the atlas ran out of room.
- return GrFPFailure(std::move(inputCoverage));
+ return GrFPFailure(std::move(inputFP));
}
- GrSurfaceProxyView atlasView(sk_ref_sp(fAtlas.textureProxy()), GrDynamicAtlas::kTextureOrigin,
- caps.getReadSwizzle(fAtlas.textureProxy()->backendFormat(),
- GrColorType::kAlpha_8));
SkMatrix atlasMatrix;
- SkRect atlasSubset, atlasDomain;
auto [atlasX, atlasY] = locationInAtlas;
if (!transposedInAtlas) {
- auto atlasOffset = SkVector::Make(atlasX - devIBounds.left(), atlasY - devIBounds.top());
- atlasMatrix = SkMatrix::Translate(atlasOffset);
- atlasSubset = SkRect::Make(devIBounds).makeOffset(atlasOffset);
- atlasDomain = SkRect::Make(drawBounds).makeOffset(atlasOffset);
+ atlasMatrix = SkMatrix::Translate(atlasX - devIBounds.left(), atlasY - devIBounds.top());
} else {
atlasMatrix.setAll(0, 1, atlasX - devIBounds.top(),
1, 0, atlasY - devIBounds.left(),
0, 0, 1);
- atlasSubset = SkRect::MakeXYWH(atlasX, atlasY, devIBounds.height(), devIBounds.width());
- atlasDomain = atlasMatrix.mapRect(SkRect::Make(drawBounds));
}
-#ifdef SK_DEBUG
- if (!path.isInverseFillType()) {
- // At this point in time we expect callers to tighten the scissor for "kIntersect" clips, as
- // opposed to us having to enforce the texture subset. Feel free to remove this assert if
- // that ever changes.
- SkASSERT(atlasDomain.isEmpty() || atlasSubset.contains(atlasDomain));
- }
-#endif
- // Inset the domain because if it is equal to the subset, then it falls on an exact boundary
- // between pixels, the "nearest" filter becomes undefined, and GrTextureEffect is forced to
- // manually enforce the subset. This inset is justifiable because textures are sampled at pixel
- // center, unless sample shading is enabled, in which case we assume standard sample locations
- // (https://www.khronos.org/registry/vulkan/specs/1.2/html/chap25.html).
- // NOTE: At MSAA16, standard sample locations begin falling on actual pixel boundaries. If this
- // happens then we simply have to rely on the fact that the atlas has a 1px padding between
- // entries.
- constexpr static float kMinInsetOfStandardMSAA8Locations = 1/16.f;
- atlasDomain.inset(kMinInsetOfStandardMSAA8Locations, kMinInsetOfStandardMSAA8Locations);
- // Look up clip coverage in the atlas.
- GrSamplerState samplerState(GrSamplerState::WrapMode::kClampToBorder,
- GrSamplerState::Filter::kNearest);
- auto fp = GrTextureEffect::MakeSubset(std::move(atlasView), kPremul_SkAlphaType, atlasMatrix,
- samplerState, atlasSubset, atlasDomain, caps);
- // Feed sk_FragCoord into the above texture lookup.
- fp = GrDeviceSpaceEffect::Make(std::move(fp));
+ auto flags = GrModulateAtlasCoverageFP::Flags::kNone;
if (path.isInverseFillType()) {
- // outputCoverage = inputCoverage * (1 - atlasAlpha)
- fp = GrBlendFragmentProcessor::Make(
- std::move(fp), std::move(inputCoverage), SkBlendMode::kDstOut,
- GrBlendFragmentProcessor::BlendBehavior::kSkModeBehavior);
- } else {
- // outputCoverage = inputCoverage * atlasAlpha
- fp = GrBlendFragmentProcessor::Make(
- std::move(fp), std::move(inputCoverage), SkBlendMode::kDstIn,
- GrBlendFragmentProcessor::BlendBehavior::kSkModeBehavior);
+ flags |= GrModulateAtlasCoverageFP::Flags::kInvertCoverage;
}
- return GrFPSuccess(std::move(fp));
+ if (!devIBounds.contains(drawBounds)) {
+ flags |= GrModulateAtlasCoverageFP::Flags::kCheckBounds;
+ // At this point in time we expect callers to tighten the scissor for "kIntersect" clips, as
+ // opposed to us having to check the path bounds. Feel free to remove this assert if that
+ // ever changes.
+ SkASSERT(path.isInverseFillType());
+ }
+ return GrFPSuccess(std::make_unique<GrModulateAtlasCoverageFP>(flags, std::move(inputFP),
+ fAtlas.surfaceProxyView(caps),
+ atlasMatrix, devIBounds));
}
void GrTessellationPathRenderer::AtlasPathKey::set(const SkMatrix& m, bool antialias,
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.h b/src/gpu/tessellate/GrTessellationPathRenderer.h
index 5850f6a..8ba1f9c 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.h
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.h
@@ -42,16 +42,16 @@
bool onDrawPath(const DrawPathArgs&) override;
void onStencilPath(const StencilPathArgs&) override;
- // Returns a fragment processor that modulates inputCoverage by the given deviceSpacePath's
- // coverage, implemented using an internal atlas.
+ // Returns a fragment processor that modulates inputFP by the given deviceSpacePath's coverage,
+ // implemented using an internal atlas.
//
- // Returns 'inputCoverage' wrapped in GrFPFailure() if the path was too big, or if the atlas was
- // out of room. (Currently, "too big" means more than 128*128 total pixels, or larger than half
- // the atlas size in either dimension.)
+ // Returns 'inputFP' wrapped in GrFPFailure() if the path was too big, or if the atlas was out
+ // of room. (Currently, "too big" means more than 128*128 total pixels, or larger than half the
+ // atlas size in either dimension.)
//
- // Also return GrFPFailure() if the view matrix has perspective.
+ // Also returns GrFPFailure() if the view matrix has perspective.
GrFPResult makeAtlasClipFP(const SkIRect& drawBounds, const SkMatrix&, const SkPath&, GrAA,
- std::unique_ptr<GrFragmentProcessor> inputCoverage, const GrCaps&);
+ std::unique_ptr<GrFragmentProcessor> inputFP, const GrCaps&);
void preFlush(GrOnFlushResourceProvider*, SkSpan<const uint32_t> taskIDs) override;
diff --git a/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp
new file mode 100644
index 0000000..8c1b153
--- /dev/null
+++ b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h"
+
+#include "src/gpu/GrDynamicAtlas.h"
+#include "src/gpu/effects/GrTextureEffect.h"
+
+GrModulateAtlasCoverageFP::GrModulateAtlasCoverageFP(Flags flags,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrSurfaceProxyView atlasView,
+ const SkMatrix& devToAtlasMatrix,
+ const SkIRect& devIBounds)
+ : GrFragmentProcessor(kTessellate_GrModulateAtlasCoverageFP_ClassID,
+ kCompatibleWithCoverageAsAlpha_OptimizationFlag)
+ , fFlags(flags)
+ , fBounds((fFlags & Flags::kCheckBounds) ? devIBounds : SkIRect{0,0,0,0}) {
+ this->registerChild(std::move(inputFP));
+ this->registerChild(GrTextureEffect::Make(std::move(atlasView), kUnknown_SkAlphaType,
+ devToAtlasMatrix, GrSamplerState::Filter::kNearest),
+ SkSL::SampleUsage::Explicit());
+}
+
+GrModulateAtlasCoverageFP::GrModulateAtlasCoverageFP(const GrModulateAtlasCoverageFP& that)
+ : GrFragmentProcessor(kTessellate_GrModulateAtlasCoverageFP_ClassID,
+ kCompatibleWithCoverageAsAlpha_OptimizationFlag)
+ , fFlags(that.fFlags)
+ , fBounds(that.fBounds) {
+ this->cloneAndRegisterAllChildProcessors(that);
+}
+
+std::unique_ptr<GrGLSLFragmentProcessor> GrModulateAtlasCoverageFP::onMakeProgramImpl() const {
+ class Impl : public GrGLSLFragmentProcessor {
+ void emitCode(EmitArgs& args) override {
+ auto fp = args.fFp.cast<GrModulateAtlasCoverageFP>();
+ auto f = args.fFragBuilder;
+ auto uniHandler = args.fUniformHandler;
+ SkString inputColor = this->invokeChild(0, args);
+ f->codeAppend("half coverage = 0;");
+ if (fp.fFlags & Flags::kCheckBounds) {
+ const char* boundsName;
+ fBoundsUniform = uniHandler->addUniform(&fp, kFragment_GrShaderFlag,
+ kFloat4_GrSLType, "bounds", &boundsName);
+ // Are we inside the path's valid atlas bounds?
+ f->codeAppendf("if (all(greaterThan(sk_FragCoord.xy, %s.xy)) && "
+ "all(lessThan(sk_FragCoord.xy, %s.zw))) ",
+ boundsName, boundsName);
+ }
+ f->codeAppendf("{");
+ SkString atlasCoverage = this->invokeChild(1, args, "sk_FragCoord.xy");
+ f->codeAppendf("coverage = %s.a;", atlasCoverage.c_str());
+ f->codeAppendf("}");
+ const char* coverageMaybeInvertName;
+ fCoverageMaybeInvertUniform = uniHandler->addUniform(&fp, kFragment_GrShaderFlag,
+ kHalf2_GrSLType, "coverageInvert",
+ &coverageMaybeInvertName);
+ // Invert coverage, if needed.
+ f->codeAppendf("coverage = coverage * %s.x + %s.y;",
+ coverageMaybeInvertName, coverageMaybeInvertName);
+ f->codeAppendf("return %s * coverage;", inputColor.c_str());
+ }
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& processor) override {
+ auto fp = processor.cast<GrModulateAtlasCoverageFP>();
+ if (fp.fFlags & Flags::kCheckBounds) {
+ pdman.set4fv(fBoundsUniform, 1, SkRect::Make(fp.fBounds).asScalars());
+ }
+ if (fp.fFlags & Flags::kInvertCoverage) {
+ pdman.set2f(fCoverageMaybeInvertUniform, -1, 1); // -1*coverage + 1 = 1 - coverage.
+ } else {
+ pdman.set2f(fCoverageMaybeInvertUniform, 1, 0); // 1*coverage + 0 = coverage.
+ }
+ }
+ UniformHandle fBoundsUniform;
+ UniformHandle fCoverageMaybeInvertUniform;
+ };
+ return std::make_unique<Impl>();
+}
diff --git a/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h
new file mode 100644
index 0000000..9ac146d
--- /dev/null
+++ b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGrModulateAtlasCoverageFP_DEFINED
+#define GrGrModulateAtlasCoverageFP_DEFINED
+
+#include "src/gpu/GrFragmentProcessor.h"
+
+// Multiplies 'inputFP' by the coverage value in an atlas, optionally inverting or clamping to 0.
+class GrModulateAtlasCoverageFP : public GrFragmentProcessor {
+public:
+ enum class Flags {
+ kNone = 0,
+ kInvertCoverage = 1 << 0, // Return inputColor * (1 - atlasCoverage).
+ kCheckBounds = 1 << 1 // Clamp atlasCoverage to 0 if outside the path's valid atlas bounds.
+ };
+
+ GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags);
+
+ GrModulateAtlasCoverageFP(Flags flags, std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrSurfaceProxyView atlasView, const SkMatrix& devToAtlasMatrix,
+ const SkIRect& devIBounds);
+
+ GrModulateAtlasCoverageFP(const GrModulateAtlasCoverageFP& that);
+
+ const char* name() const override {
+ return "GrModulateAtlasCoverageFP";
+ }
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
+ b->add32(fFlags & Flags::kCheckBounds);
+ }
+ std::unique_ptr<GrFragmentProcessor> clone() const override {
+ return std::make_unique<GrModulateAtlasCoverageFP>(*this);
+ }
+ bool onIsEqual(const GrFragmentProcessor& that) const override {
+ auto fp = that.cast<GrModulateAtlasCoverageFP>();
+ return fFlags == fp.fFlags && fBounds == fp.fBounds;
+ }
+ std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
+
+private:
+ const Flags fFlags;
+ const SkIRect fBounds;
+};
+
+GR_MAKE_BITFIELD_CLASS_OPS(GrModulateAtlasCoverageFP::Flags);
+
+#endif