Report optimizations in master gradient effects
All of the FPs for the gradient components have had to be updated to
include the @optimizationFlags section. Layout FPs now can use the
opacity preserving optimization to report whether or not they might
reject a fragment (e.g. 2 point conical gradients).
The previous composition logic that handled ensuring the gradient shader
output a premul color was removing optimizations that should have been
valid for gradients, so the make premul logic has been inlined into
the top-level effect FPs.
Bug: skia:
Change-Id: I73e4224d8dc0e3420a215b0aa805d829b08f6c76
Reviewed-on: https://skia-review.googlesource.com/151547
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gradients/GrClampedGradientEffect.cpp b/src/gpu/gradients/GrClampedGradientEffect.cpp
index d3b1d28..409549c 100644
--- a/src/gpu/gradients/GrClampedGradientEffect.cpp
+++ b/src/gpu/gradients/GrClampedGradientEffect.cpp
@@ -26,6 +26,10 @@
(void)leftBorderColor;
auto rightBorderColor = _outer.rightBorderColor();
(void)rightBorderColor;
+ auto makePremul = _outer.makePremul();
+ (void)makePremul;
+ auto colorsAreOpaque = _outer.colorsAreOpaque();
+ (void)colorsAreOpaque;
fLeftBorderColorVar = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "leftBorderColor");
fRightBorderColorVar =
@@ -34,15 +38,20 @@
SkString _child1("_child1");
this->emitChild(1, &_child1, args);
fragBuilder->codeAppendf(
- "half4 t = %s;\nif (t.y < 0.0) {\n %s = half4(0.0);\n} else if (t.x < 0.0) {\n "
- " %s = %s;\n} else if (float(t.x) > 1.0) {\n %s = %s;\n} else {",
- _child1.c_str(), args.fOutputColor, args.fOutputColor,
+ "half4 t = %s;\nif (!%s && t.y < 0.0) {\n %s = half4(0.0);\n} else if (t.x < "
+ "0.0) {\n %s = %s;\n} else if (float(t.x) > 1.0) {\n %s = %s;\n} else {",
+ _child1.c_str(),
+ (_outer.childProcessor(1).preservesOpaqueInput() ? "true" : "false"),
+ args.fOutputColor, args.fOutputColor,
args.fUniformHandler->getUniformCStr(fLeftBorderColorVar), args.fOutputColor,
args.fUniformHandler->getUniformCStr(fRightBorderColorVar));
SkString _input0("t");
SkString _child0("_child0");
this->emitChild(0, _input0.c_str(), &_child0, args);
- fragBuilder->codeAppendf("\n %s = %s;\n}\n", args.fOutputColor, _child0.c_str());
+ fragBuilder->codeAppendf("\n %s = %s;\n}\n@if (%s) {\n %s.xyz *= %s.w;\n}\n",
+ args.fOutputColor, _child0.c_str(),
+ (_outer.makePremul() ? "true" : "false"), args.fOutputColor,
+ args.fOutputColor);
}
private:
@@ -71,18 +80,24 @@
return new GrGLSLClampedGradientEffect();
}
void GrClampedGradientEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const {}
+ GrProcessorKeyBuilder* b) const {
+ b->add32((int32_t)fMakePremul);
+}
bool GrClampedGradientEffect::onIsEqual(const GrFragmentProcessor& other) const {
const GrClampedGradientEffect& that = other.cast<GrClampedGradientEffect>();
(void)that;
if (fLeftBorderColor != that.fLeftBorderColor) return false;
if (fRightBorderColor != that.fRightBorderColor) return false;
+ if (fMakePremul != that.fMakePremul) return false;
+ if (fColorsAreOpaque != that.fColorsAreOpaque) return false;
return true;
}
GrClampedGradientEffect::GrClampedGradientEffect(const GrClampedGradientEffect& src)
: INHERITED(kGrClampedGradientEffect_ClassID, src.optimizationFlags())
, fLeftBorderColor(src.fLeftBorderColor)
- , fRightBorderColor(src.fRightBorderColor) {
+ , fRightBorderColor(src.fRightBorderColor)
+ , fMakePremul(src.fMakePremul)
+ , fColorsAreOpaque(src.fColorsAreOpaque) {
this->registerChildProcessor(src.childProcessor(0).clone());
this->registerChildProcessor(src.childProcessor(1).clone());
}
diff --git a/src/gpu/gradients/GrClampedGradientEffect.fp b/src/gpu/gradients/GrClampedGradientEffect.fp
index 67a21e5..79ede3f 100644
--- a/src/gpu/gradients/GrClampedGradientEffect.fp
+++ b/src/gpu/gradients/GrClampedGradientEffect.fp
@@ -20,15 +20,18 @@
layout(ctype=GrColor4f, tracked) in uniform half4 leftBorderColor; // t < 0.0
layout(ctype=GrColor4f, tracked) in uniform half4 rightBorderColor; // t > 1.0
+layout(key) in bool makePremul;
+// Trust the creator that this matches the color spec of the gradient
+in bool colorsAreOpaque;
+
void main() {
half4 t = process(gradLayout);
// If t.x is below 0, use the left border color without invoking the child processor. If any t.x
// is above 1, use the right border color. Otherwise, t is in the [0, 1] range assumed by the
// colorizer FP, so delegate to the child processor.
- if (t.y < 0) {
- // layout has rejected this fragment
- // FIXME: only 2pt conic does this, can we add an optimization flag
- // that lets us assume t.y >= 0 in many cases?
+ if (!gradLayout.preservesOpaqueInput && t.y < 0) {
+ // layout has rejected this fragment (rely on sksl to remove this branch if the layout FP
+ // preserves opacity is false)
sk_OutColor = half4(0);
} else if (t.x < 0) {
sk_OutColor = leftBorderColor;
@@ -37,4 +40,19 @@
} else {
sk_OutColor = process(colorizer, t);
}
+
+ @if(makePremul) {
+ sk_OutColor.xyz *= sk_OutColor.w;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// If the layout does not preserve opacity, remove the opaque optimization,
+// but otherwise respect the provided color opacity state (which should take
+// into account the opacity of the border colors).
+@optimizationFlags {
+ kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+ (colorsAreOpaque && gradLayout->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags)
}
diff --git a/src/gpu/gradients/GrClampedGradientEffect.h b/src/gpu/gradients/GrClampedGradientEffect.h
index af3e11d..ce07d64 100644
--- a/src/gpu/gradients/GrClampedGradientEffect.h
+++ b/src/gpu/gradients/GrClampedGradientEffect.h
@@ -17,12 +17,15 @@
public:
const GrColor4f& leftBorderColor() const { return fLeftBorderColor; }
const GrColor4f& rightBorderColor() const { return fRightBorderColor; }
+ bool makePremul() const { return fMakePremul; }
+ bool colorsAreOpaque() const { return fColorsAreOpaque; }
static std::unique_ptr<GrFragmentProcessor> Make(
std::unique_ptr<GrFragmentProcessor> colorizer,
std::unique_ptr<GrFragmentProcessor> gradLayout, GrColor4f leftBorderColor,
- GrColor4f rightBorderColor) {
+ GrColor4f rightBorderColor, bool makePremul, bool colorsAreOpaque) {
return std::unique_ptr<GrFragmentProcessor>(new GrClampedGradientEffect(
- std::move(colorizer), std::move(gradLayout), leftBorderColor, rightBorderColor));
+ std::move(colorizer), std::move(gradLayout), leftBorderColor, rightBorderColor,
+ makePremul, colorsAreOpaque));
}
GrClampedGradientEffect(const GrClampedGradientEffect& src);
std::unique_ptr<GrFragmentProcessor> clone() const override;
@@ -31,10 +34,17 @@
private:
GrClampedGradientEffect(std::unique_ptr<GrFragmentProcessor> colorizer,
std::unique_ptr<GrFragmentProcessor> gradLayout,
- GrColor4f leftBorderColor, GrColor4f rightBorderColor)
- : INHERITED(kGrClampedGradientEffect_ClassID, kNone_OptimizationFlags)
+ GrColor4f leftBorderColor, GrColor4f rightBorderColor, bool makePremul,
+ bool colorsAreOpaque)
+ : INHERITED(kGrClampedGradientEffect_ClassID,
+ (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+ (colorsAreOpaque && gradLayout->preservesOpaqueInput()
+ ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags))
, fLeftBorderColor(leftBorderColor)
- , fRightBorderColor(rightBorderColor) {
+ , fRightBorderColor(rightBorderColor)
+ , fMakePremul(makePremul)
+ , fColorsAreOpaque(colorsAreOpaque) {
this->registerChildProcessor(std::move(colorizer));
this->registerChildProcessor(std::move(gradLayout));
}
@@ -44,6 +54,8 @@
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
GrColor4f fLeftBorderColor;
GrColor4f fRightBorderColor;
+ bool fMakePremul;
+ bool fColorsAreOpaque;
typedef GrFragmentProcessor INHERITED;
};
#endif
diff --git a/src/gpu/gradients/GrDualIntervalGradientColorizer.fp b/src/gpu/gradients/GrDualIntervalGradientColorizer.fp
index 6838f05..d624e82 100644
--- a/src/gpu/gradients/GrDualIntervalGradientColorizer.fp
+++ b/src/gpu/gradients/GrDualIntervalGradientColorizer.fp
@@ -42,8 +42,7 @@
@cppEnd {
std::unique_ptr<GrFragmentProcessor> GrDualIntervalGradientColorizer::Make(
- const GrColor4f& c0, const GrColor4f& c1, const GrColor4f& c2,
- const GrColor4f& c3, float threshold) {
+ const GrColor4f& c0, const GrColor4f& c1, const GrColor4f& c2, const GrColor4f& c3, float threshold) {
// Derive scale and biases from the 4 colors and threshold
auto vc0 = Sk4f::Load(c0.fRGBA);
auto vc1 = Sk4f::Load(c1.fRGBA);
diff --git a/src/gpu/gradients/GrGradientShader.cpp b/src/gpu/gradients/GrGradientShader.cpp
index 7e14025..b4d2b65 100644
--- a/src/gpu/gradients/GrGradientShader.cpp
+++ b/src/gpu/gradients/GrGradientShader.cpp
@@ -115,6 +115,7 @@
// Convert all colors into destination space and into GrColor4fs, and handle
// premul issues depending on the interpolation mode
bool inputPremul = shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag;
+ bool allOpaque = true;
SkAutoSTMalloc<4, GrColor4f> colors(shader.fColorCount);
SkColor4fXformer xformedColors(shader.fOrigColors4f, shader.fColorCount,
shader.fColorSpace.get(), args.fDstColorSpaceInfo->colorSpace());
@@ -123,6 +124,9 @@
if (inputPremul) {
colors[i] = colors[i].premul();
}
+ if (allOpaque && !SkScalarNearlyEqual(colors[i].fRGBA[3], 1.0)) {
+ allOpaque = false;
+ }
}
// SkGradientShader stores positions implicitly when they are evenly spaced, but the getPos()
@@ -148,16 +152,23 @@
return nullptr;
}
+ // The master effect has to export premul colors, but under certain conditions it doesn't need
+ // to do anything to achieve that: i.e. its interpolating already premul colors (inputPremul)
+ // or all the colors have a = 1, in which case premul is a no op. Note that this allOpaque
+ // check is more permissive than SkGradientShaderBase's isOpaque(), since we can optimize away
+ // the make-premul op for two point conical gradients (which report false for isOpaque).
+ bool makePremul = !inputPremul && !allOpaque;
+
// All tile modes are supported (unless something was added to SkShader)
std::unique_ptr<GrFragmentProcessor> master;
switch(shader.getTileMode()) {
case SkShader::kRepeat_TileMode:
master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
- /* mirror */ false);
+ /* mirror */ false, makePremul, allOpaque);
break;
case SkShader::kMirror_TileMode:
master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
- /* mirror */ true);
+ /* mirror */ true, makePremul, allOpaque);
break;
case SkShader::kClamp_TileMode:
// For the clamped mode, the border colors are the first and last colors, corresponding
@@ -165,12 +176,14 @@
// appropriate. If there is a hard stop, this grabs the expected outer colors for the
// border.
master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
- colors[0], colors[shader.fColorCount - 1]);
+ colors[0], colors[shader.fColorCount - 1], makePremul, allOpaque);
break;
case SkShader::kDecal_TileMode:
+ // Even if the gradient colors are opaque, the decal borders are transparent so
+ // disable that optimization
master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
- GrColor4f::TransparentBlack(),
- GrColor4f::TransparentBlack());
+ GrColor4f::TransparentBlack(), GrColor4f::TransparentBlack(),
+ makePremul, /* colorsAreOpaque */ false);
break;
}
@@ -179,14 +192,6 @@
return nullptr;
}
- if (!inputPremul) {
- // When interpolating unpremul colors, the output of the gradient
- // effect fp's will also be unpremul, so wrap it to ensure its premul.
- // - this is unnecessary when interpolating premul colors since the
- // output color is premul by nature
- master = GrFragmentProcessor::PremulOutput(std::move(master));
- }
-
return GrFragmentProcessor::MulChildByInputAlpha(std::move(master));
}
diff --git a/src/gpu/gradients/GrLinearGradientLayout.fp b/src/gpu/gradients/GrLinearGradientLayout.fp
index e1d62f7..6eaf8b1 100644
--- a/src/gpu/gradients/GrLinearGradientLayout.fp
+++ b/src/gpu/gradients/GrLinearGradientLayout.fp
@@ -22,6 +22,11 @@
#include "SkLinearGradient.h"
}
+// The linear gradient never rejects a pixel so it doesn't change opacity
+@optimizationFlags {
+ kPreservesOpaqueInput_OptimizationFlag
+}
+
@make {
static std::unique_ptr<GrFragmentProcessor> Make(const SkLinearGradient& gradient,
const GrFPArgs& args);
diff --git a/src/gpu/gradients/GrLinearGradientLayout.h b/src/gpu/gradients/GrLinearGradientLayout.h
index 19c9d8c..f128f34 100644
--- a/src/gpu/gradients/GrLinearGradientLayout.h
+++ b/src/gpu/gradients/GrLinearGradientLayout.h
@@ -27,7 +27,8 @@
private:
GrLinearGradientLayout(SkMatrix44 gradientMatrix)
- : INHERITED(kGrLinearGradientLayout_ClassID, kNone_OptimizationFlags)
+ : INHERITED(kGrLinearGradientLayout_ClassID,
+ (OptimizationFlags)kPreservesOpaqueInput_OptimizationFlag)
, fGradientMatrix(gradientMatrix)
, fCoordTransform0(gradientMatrix) {
this->addCoordTransform(&fCoordTransform0);
diff --git a/src/gpu/gradients/GrRadialGradientLayout.fp b/src/gpu/gradients/GrRadialGradientLayout.fp
index b59b347..7d6c5c8 100644
--- a/src/gpu/gradients/GrRadialGradientLayout.fp
+++ b/src/gpu/gradients/GrRadialGradientLayout.fp
@@ -22,6 +22,11 @@
#include "SkRadialGradient.h"
}
+// The radial gradient never rejects a pixel so it doesn't change opacity
+@optimizationFlags {
+ kPreservesOpaqueInput_OptimizationFlag
+}
+
@make {
static std::unique_ptr<GrFragmentProcessor> Make(const SkRadialGradient& gradient,
const GrFPArgs& args);
diff --git a/src/gpu/gradients/GrRadialGradientLayout.h b/src/gpu/gradients/GrRadialGradientLayout.h
index 3fa034c..7af6a64 100644
--- a/src/gpu/gradients/GrRadialGradientLayout.h
+++ b/src/gpu/gradients/GrRadialGradientLayout.h
@@ -27,7 +27,8 @@
private:
GrRadialGradientLayout(SkMatrix44 gradientMatrix)
- : INHERITED(kGrRadialGradientLayout_ClassID, kNone_OptimizationFlags)
+ : INHERITED(kGrRadialGradientLayout_ClassID,
+ (OptimizationFlags)kPreservesOpaqueInput_OptimizationFlag)
, fGradientMatrix(gradientMatrix)
, fCoordTransform0(gradientMatrix) {
this->addCoordTransform(&fCoordTransform0);
diff --git a/src/gpu/gradients/GrSweepGradientLayout.fp b/src/gpu/gradients/GrSweepGradientLayout.fp
index eff712f..d704d9f 100644
--- a/src/gpu/gradients/GrSweepGradientLayout.fp
+++ b/src/gpu/gradients/GrSweepGradientLayout.fp
@@ -39,6 +39,11 @@
#include "SkSweepGradient.h"
}
+// The sweep gradient never rejects a pixel so it doesn't change opacity
+@optimizationFlags {
+ kPreservesOpaqueInput_OptimizationFlag
+}
+
@make {
static std::unique_ptr<GrFragmentProcessor> Make(const SkSweepGradient& gradient,
const GrFPArgs& args);
diff --git a/src/gpu/gradients/GrSweepGradientLayout.h b/src/gpu/gradients/GrSweepGradientLayout.h
index ff4e143..293ada1 100644
--- a/src/gpu/gradients/GrSweepGradientLayout.h
+++ b/src/gpu/gradients/GrSweepGradientLayout.h
@@ -29,7 +29,8 @@
private:
GrSweepGradientLayout(SkMatrix44 gradientMatrix, float bias, float scale)
- : INHERITED(kGrSweepGradientLayout_ClassID, kNone_OptimizationFlags)
+ : INHERITED(kGrSweepGradientLayout_ClassID,
+ (OptimizationFlags)kPreservesOpaqueInput_OptimizationFlag)
, fGradientMatrix(gradientMatrix)
, fBias(bias)
, fScale(scale)
diff --git a/src/gpu/gradients/GrTiledGradientEffect.cpp b/src/gpu/gradients/GrTiledGradientEffect.cpp
index 887b921..8789d4c 100644
--- a/src/gpu/gradients/GrTiledGradientEffect.cpp
+++ b/src/gpu/gradients/GrTiledGradientEffect.cpp
@@ -24,21 +24,30 @@
(void)_outer;
auto mirror = _outer.mirror();
(void)mirror;
+ auto makePremul = _outer.makePremul();
+ (void)makePremul;
+ auto colorsAreOpaque = _outer.colorsAreOpaque();
+ (void)colorsAreOpaque;
SkString _child1("_child1");
this->emitChild(1, &_child1, args);
fragBuilder->codeAppendf(
- "half4 t = %s;\nif (t.y < 0.0) {\n %s = half4(0.0);\n} else {\n @if (%s) {\n "
- " half t_1 = t.x - 1.0;\n half tiled_t = (float(t_1) - 2.0 * "
+ "half4 t = %s;\nif (!%s && t.y < 0.0) {\n %s = half4(0.0);\n} else {\n @if "
+ "(%s) {\n half t_1 = t.x - 1.0;\n half tiled_t = (float(t_1) - 2.0 * "
"floor(float(float(t_1) * 0.5))) - 1.0;\n if "
"(sk_Caps.mustDoOpBetweenFloorAndAbs) {\n tiled_t = "
"half(clamp(float(tiled_t), -1.0, 1.0));\n }\n t.x = "
"half(abs(float(tiled_t)));\n } else {\n t.x = "
"half(fract(float(t.x)));\n }",
- _child1.c_str(), args.fOutputColor, (_outer.mirror() ? "true" : "false"));
+ _child1.c_str(),
+ (_outer.childProcessor(1).preservesOpaqueInput() ? "true" : "false"),
+ args.fOutputColor, (_outer.mirror() ? "true" : "false"));
SkString _input0("t");
SkString _child0("_child0");
this->emitChild(0, _input0.c_str(), &_child0, args);
- fragBuilder->codeAppendf("\n %s = %s;\n}\n", args.fOutputColor, _child0.c_str());
+ fragBuilder->codeAppendf("\n %s = %s;\n}\n@if (%s) {\n %s.xyz *= %s.w;\n}\n",
+ args.fOutputColor, _child0.c_str(),
+ (_outer.makePremul() ? "true" : "false"), args.fOutputColor,
+ args.fOutputColor);
}
private:
@@ -51,15 +60,21 @@
void GrTiledGradientEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const {
b->add32((int32_t)fMirror);
+ b->add32((int32_t)fMakePremul);
}
bool GrTiledGradientEffect::onIsEqual(const GrFragmentProcessor& other) const {
const GrTiledGradientEffect& that = other.cast<GrTiledGradientEffect>();
(void)that;
if (fMirror != that.fMirror) return false;
+ if (fMakePremul != that.fMakePremul) return false;
+ if (fColorsAreOpaque != that.fColorsAreOpaque) return false;
return true;
}
GrTiledGradientEffect::GrTiledGradientEffect(const GrTiledGradientEffect& src)
- : INHERITED(kGrTiledGradientEffect_ClassID, src.optimizationFlags()), fMirror(src.fMirror) {
+ : INHERITED(kGrTiledGradientEffect_ClassID, src.optimizationFlags())
+ , fMirror(src.fMirror)
+ , fMakePremul(src.fMakePremul)
+ , fColorsAreOpaque(src.fColorsAreOpaque) {
this->registerChildProcessor(src.childProcessor(0).clone());
this->registerChildProcessor(src.childProcessor(1).clone());
}
diff --git a/src/gpu/gradients/GrTiledGradientEffect.fp b/src/gpu/gradients/GrTiledGradientEffect.fp
index cd22f88..b6f6595 100644
--- a/src/gpu/gradients/GrTiledGradientEffect.fp
+++ b/src/gpu/gradients/GrTiledGradientEffect.fp
@@ -11,14 +11,16 @@
in fragmentProcessor gradLayout;
layout(key) in bool mirror;
+layout(key) in bool makePremul;
+// Trust the creator that this matches the color spec of the gradient
+in bool colorsAreOpaque;
void main() {
half4 t = process(gradLayout);
- if (t.y < 0) {
- // layout has rejected this fragment
- // FIXME: only 2pt conic does this, can we add an optimization flag
- // that lets us assume t.y >= 0 in many cases?
+ if (!gradLayout.preservesOpaqueInput && t.y < 0) {
+ // layout has rejected this fragment (rely on sksl to remove this branch if the layout FP
+ // preserves opacity is false)
sk_OutColor = half4(0);
} else {
@if(mirror) {
@@ -40,4 +42,18 @@
// unmodified.
sk_OutColor = process(colorizer, t);
}
+
+ @if (makePremul) {
+ sk_OutColor.xyz *= sk_OutColor.w;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// If the layout does not preserve opacity, remove the opaque optimization,
+// but otherwise respect the provided color opacity state.
+@optimizationFlags {
+ kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+ (colorsAreOpaque && gradLayout->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags)
}
diff --git a/src/gpu/gradients/GrTiledGradientEffect.h b/src/gpu/gradients/GrTiledGradientEffect.h
index 8216742..ffaca8e 100644
--- a/src/gpu/gradients/GrTiledGradientEffect.h
+++ b/src/gpu/gradients/GrTiledGradientEffect.h
@@ -16,11 +16,14 @@
class GrTiledGradientEffect : public GrFragmentProcessor {
public:
bool mirror() const { return fMirror; }
+ bool makePremul() const { return fMakePremul; }
+ bool colorsAreOpaque() const { return fColorsAreOpaque; }
static std::unique_ptr<GrFragmentProcessor> Make(
std::unique_ptr<GrFragmentProcessor> colorizer,
- std::unique_ptr<GrFragmentProcessor> gradLayout, bool mirror) {
- return std::unique_ptr<GrFragmentProcessor>(
- new GrTiledGradientEffect(std::move(colorizer), std::move(gradLayout), mirror));
+ std::unique_ptr<GrFragmentProcessor> gradLayout, bool mirror, bool makePremul,
+ bool colorsAreOpaque) {
+ return std::unique_ptr<GrFragmentProcessor>(new GrTiledGradientEffect(
+ std::move(colorizer), std::move(gradLayout), mirror, makePremul, colorsAreOpaque));
}
GrTiledGradientEffect(const GrTiledGradientEffect& src);
std::unique_ptr<GrFragmentProcessor> clone() const override;
@@ -28,8 +31,16 @@
private:
GrTiledGradientEffect(std::unique_ptr<GrFragmentProcessor> colorizer,
- std::unique_ptr<GrFragmentProcessor> gradLayout, bool mirror)
- : INHERITED(kGrTiledGradientEffect_ClassID, kNone_OptimizationFlags), fMirror(mirror) {
+ std::unique_ptr<GrFragmentProcessor> gradLayout, bool mirror,
+ bool makePremul, bool colorsAreOpaque)
+ : INHERITED(kGrTiledGradientEffect_ClassID,
+ (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+ (colorsAreOpaque && gradLayout->preservesOpaqueInput()
+ ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags))
+ , fMirror(mirror)
+ , fMakePremul(makePremul)
+ , fColorsAreOpaque(colorsAreOpaque) {
this->registerChildProcessor(std::move(colorizer));
this->registerChildProcessor(std::move(gradLayout));
}
@@ -38,6 +49,8 @@
bool onIsEqual(const GrFragmentProcessor&) const override;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
bool fMirror;
+ bool fMakePremul;
+ bool fColorsAreOpaque;
typedef GrFragmentProcessor INHERITED;
};
#endif
diff --git a/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp b/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
index 133cf7b..3aebd3b 100644
--- a/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
+++ b/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
@@ -122,6 +122,12 @@
#include "SkTwoPointConicalGradient.h"
}
+// The 2 point conical gradient can reject a pixel so it does change opacity
+// even if the input was opaque, so disable that optimization
+@optimizationFlags {
+ kNone_OptimizationFlags
+}
+
@make {
static std::unique_ptr<GrFragmentProcessor> Make(const SkTwoPointConicalGradient& gradient,
const GrFPArgs& args);
diff --git a/src/gpu/gradients/GrTwoPointConicalGradientLayout.h b/src/gpu/gradients/GrTwoPointConicalGradientLayout.h
index 4bccdea..0c1233c 100644
--- a/src/gpu/gradients/GrTwoPointConicalGradientLayout.h
+++ b/src/gpu/gradients/GrTwoPointConicalGradientLayout.h
@@ -34,15 +34,11 @@
const char* name() const override { return "TwoPointConicalGradientLayout"; }
private:
- GrTwoPointConicalGradientLayout(SkMatrix44 gradientMatrix,
- Type type,
- bool isRadiusIncreasing,
- bool isFocalOnCircle,
- bool isWellBehaved,
- bool isSwapped,
- bool isNativelyFocal,
- SkPoint focalParams)
- : INHERITED(kGrTwoPointConicalGradientLayout_ClassID, kNone_OptimizationFlags)
+ GrTwoPointConicalGradientLayout(SkMatrix44 gradientMatrix, Type type, bool isRadiusIncreasing,
+ bool isFocalOnCircle, bool isWellBehaved, bool isSwapped,
+ bool isNativelyFocal, SkPoint focalParams)
+ : INHERITED(kGrTwoPointConicalGradientLayout_ClassID,
+ (OptimizationFlags)kNone_OptimizationFlags)
, fGradientMatrix(gradientMatrix)
, fType(type)
, fIsRadiusIncreasing(isRadiusIncreasing)
diff --git a/src/gpu/gradients/README.md b/src/gpu/gradients/README.md
index 5cad0b6..72ed05a 100644
--- a/src/gpu/gradients/README.md
+++ b/src/gpu/gradients/README.md
@@ -18,7 +18,13 @@
GrClampedGradientEffect handles clamped and decal tile modes, while
-GrTiledGradientEffect implements repeat and mirror tile modes. The GrClampedGradientEffect requires border colors to be specified outside of its colorizer child, but these border colors may be defined by the gradient color stops. Both of these master effects delegate calculating a t interpolant to a child process, perform their respective tile mode operations, and possibly convert the tiled t value (guaranteed to be within 0 and 1) into an output color using their child colorizer process.
+GrTiledGradientEffect implements repeat and mirror tile modes. The
+GrClampedGradientEffect requires border colors to be specified outside of its
+colorizer child, but these border colors may be defined by the gradient color
+stops. Both of these master effects delegate calculating a t interpolant to a
+child process, perform their respective tile mode operations, and possibly
+convert the tiled t value (guaranteed to be within 0 and 1) into an output
+color using their child colorizer process.
Because of how child processors are currently defined, where they have a single
half4 input and a single half4 output, their is a type mismatch between the 1D
@@ -26,7 +32,7 @@
now, the master effect assumes an untiled t is output in sk_OutColor.x by the
layout and it tiles solely off of that value.
-However, layouts can output a negative value in the w component to invalidate
+However, layouts can output a negative value in the y component to invalidate
the gradient location (currently on the two point conical gradient does this).
When invalidated, the master effect outputs transparent black and does not
invoke the child processor. Other than this condition, any value in y, z, or w
@@ -38,3 +44,28 @@
GrGradientShader provides static factory functions to create
GrFragmentProcessor graphs that reproduce a particular SkGradientShader.
+
+Optimization Flags
+==================
+
+At an abstract level, gradient shaders are compatible with coverage as alpha
+and, under certain conditions, preserve opacity when the inputs are opaque. To
+reduce the amount of duplicate code and boilerplate, these optimization
+decisions are implemented in the master effects and not in the colorizers. It
+is assumed that all colorizer FPs will be compatible with coverage as alpha and
+will preserve opacity if input colors are opaque. Since this is assumed by the
+master effects, they do not need to report these optimizations or check input
+opacity (this does mean if the colorizers are used independently from the
+master effect shader that the reported flags might not be optimal, but since
+that is unlikely, this convention really simplifies the colorizer
+implementations).
+
+Unlike colorizers, which do not need to report any optimization flags, layout
+FPs should report opacity preserving optimizations because they can impact the
+opacity of a pixel outside of how the gradient would otherwise color it.
+Layouts that potentially reject pixels (i.e. could output a negative y value)
+must not report kPreservesOpaqueInput_OptimizationFlag. Layouts that never
+reject a pixel should report kPreservesOpaqueInput_OptimizationFlag since the
+master effects can optimize away checking if the layout rejects a pixel.
+
+