aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/shaders
diff options
context:
space:
mode:
authorGravatar Yuqian Li <liyuqian@google.com>2018-01-17 12:05:20 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-01-17 17:27:16 +0000
commit33a86560ce12b549c2d69e3f891e0cae3173aed9 (patch)
tree80c213d1ea4296338431def2bd8cd613c8ce4caa /src/shaders
parent777707be8445b7d2f9cb235cd040cd1994dd2996 (diff)
Reuse the CPU 2pt conical code in the GPU backend
Bug: skia: Change-Id: I7e58c4faee018138ae6f93e92850a4e0a0126854 Reviewed-on: https://skia-review.googlesource.com/94041 Commit-Queue: Yuqian Li <liyuqian@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
Diffstat (limited to 'src/shaders')
-rw-r--r--src/shaders/gradients/SkTwoPointConicalGradient.h16
-rw-r--r--src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp187
2 files changed, 88 insertions, 115 deletions
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.h b/src/shaders/gradients/SkTwoPointConicalGradient.h
index 623e69f95a..37c0471c22 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.h
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.h
@@ -37,6 +37,12 @@ public:
bool isNativelyFocal() const { return SkScalarNearlyZero(fFocalX); }
};
+ enum class Type {
+ kRadial,
+ kStrip,
+ kFocal
+ };
+
static sk_sp<SkShader> Create(const SkPoint& start, SkScalar startRadius,
const SkPoint& end, SkScalar endRadius,
const Descriptor&);
@@ -54,6 +60,10 @@ public:
const SkPoint& getEndCenter() const { return fCenter2; }
SkScalar getEndRadius() const { return fRadius2; }
+ Type getType() const { return fType; }
+ const SkMatrix& getGradientMatrix() const { return fPtsToUnit; }
+ const FocalData& getFocalData() const { return fFocalData; }
+
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
@@ -67,12 +77,6 @@ protected:
bool onIsRasterPipelineOnly(const SkMatrix&) const override { return true; }
private:
- enum class Type {
- kRadial,
- kStrip,
- kFocal
- };
-
SkTwoPointConicalGradient(const SkPoint& c0, SkScalar r0,
const SkPoint& c1, SkScalar r1,
const Descriptor&, Type, const SkMatrix&, const FocalData&);
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
index b6f569de82..38e4495c84 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -20,35 +20,56 @@ typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
// Please see https://skia.org/dev/design/conical for how our shader works.
class TwoPointConicalEffect : public GrGradientEffect {
public:
+ using Type = SkTwoPointConicalGradient::Type;
class DegeneratedGLSLProcessor; // radial (center0 == center1) or strip (r0 == r1) case
class FocalGLSLProcessor; // all other cases where we can derive a focal point
- enum Type {
- kRadial_Type,
- kStrip_Type,
- kFocal_Type
- };
-
struct Data {
+ Type fType;
SkScalar fRadius0;
SkScalar fDiffRadius;
- Type fType;
- bool fIsSwapped;
+ SkTwoPointConicalGradient::FocalData fFocalData;
// Construct from the shader, and set the matrix accordingly
Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix);
bool operator== (const Data& d) const {
- return fRadius0 == d.fRadius0 && fDiffRadius == d.fDiffRadius && fType == d.fType &&
- fIsSwapped == d.fIsSwapped;
+ if (fType != d.fType) {
+ return false;
+ }
+ switch (fType) {
+ case Type::kRadial:
+ case Type::kStrip:
+ return fRadius0 == d.fRadius0 && fDiffRadius == d.fDiffRadius;
+ case Type::kFocal:
+ return fFocalData.fR1 == d.fFocalData.fR1 &&
+ fFocalData.fFocalX == d.fFocalData.fFocalX &&
+ fFocalData.fIsSwapped == d.fFocalData.fIsSwapped;
+ }
+ SkDEBUGFAIL("This return should be unreachable; it's here just for compile warning");
+ return false;
}
};
static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, const Data& data);
- SkScalar diffRadius() const { return fData.fDiffRadius; }
- SkScalar r0() const { return fData.fRadius0; }
- SkScalar r1() const { return fData.fRadius0 + fData.fDiffRadius; }
+ SkScalar diffRadius() const {
+ SkASSERT(!this->isFocal()); // fDiffRadius is uninitialized for focal cases
+ return fData.fDiffRadius;
+ }
+ SkScalar r0() const {
+ SkASSERT(!this->isFocal()); // fRadius0 is uninitialized for focal cases
+ return fData.fRadius0;
+ }
+
+ SkScalar r1() const {
+ SkASSERT(this->isFocal()); // fFocalData is uninitialized for non-focal cases
+ return fData.fFocalData.fR1;
+ }
+ SkScalar focalX() const {
+ SkASSERT(this->isFocal()); // fFocalData is uninitialized for non-focal cases
+ return fData.fFocalData.fFocalX;
+ }
const char* name() const override { return "Two-Point Conical Gradient"; }
@@ -58,24 +79,26 @@ public:
// edge case where the inside circle touches the outside circle (on the focal point). If we were
// to solve for t bruteforcely using a quadratic equation, this case implies that the quadratic
// equation degenerates to a linear equation.
- bool isFocalOnCircle() const { return SkScalarNearlyZero(1 - this->r1()); }
- bool isSwapped() const { return fData.fIsSwapped; }
+ bool isFocalOnCircle() const { return this->isFocal() && fData.fFocalData.isFocalOnCircle(); }
+ bool isSwapped() const { return this->isFocal() && fData.fFocalData.isSwapped(); }
Type getType() const { return fData.fType; }
+ bool isFocal() const { return fData.fType == Type::kFocal; }
// Whether the t we solved is always valid (so we don't need to check r(t) > 0).
- bool isWellBehaved() const { return !this->isFocalOnCircle() && this->r1() > 1; }
+ bool isWellBehaved() const { return this->isFocal() && fData.fFocalData.isWellBehaved(); }
// Whether r0 == 0 so it's focal without any transformation
- bool isNativelyFocal() const { return SkScalarNearlyZero(fData.fRadius0); }
+ bool isNativelyFocal() const { return this->isFocal() && fData.fFocalData.isNativelyFocal(); }
- bool isRadiusIncreasing() const { return fData.fDiffRadius > 0; }
+ // Note that focalX = f = r0 / (r0 - r1), so 1 - focalX > 0 == r0 < r1
+ bool isRadiusIncreasing() const { return this->isFocal() && 1 - fData.fFocalData.fFocalX > 0; }
protected:
void onGetGLSLProcessorKey(const GrShaderCaps& c, GrProcessorKeyBuilder* b) const override {
INHERITED::onGetGLSLProcessorKey(c, b);
uint32_t key = 0;
- key |= fData.fType;
+ key |= static_cast<int>(fData.fType);
SkASSERT(key < (1 << 2));
key |= (this->isFocalOnCircle() << 2);
key |= (this->isWellBehaved() << 3);
@@ -135,13 +158,13 @@ std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::TestCreate(
int mask = d->fRandom->nextU();
int type = mask & kTestTypeMask;
- if (type == TwoPointConicalEffect::kRadial_Type) {
+ if (type == static_cast<int>(TwoPointConicalEffect::Type::kRadial)) {
center2 = center1;
// Make sure that the radii are different
if (SkScalarNearlyZero(radius1 - radius2)) {
radius2 += .1f;
}
- } else if (type == TwoPointConicalEffect::kStrip_Type) {
+ } else if (type == static_cast<int>(TwoPointConicalEffect::Type::kStrip)) {
radius1 = SkTMax(radius1, .1f); // Make sure that the radius is non-zero
radius2 = radius1;
// Make sure that the centers are different
@@ -199,9 +222,9 @@ std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::TestCreate(
class TwoPointConicalEffect::DegeneratedGLSLProcessor : public GrGradientEffect::GLSLProcessor {
protected:
void emitCode(EmitArgs& args) override {
- const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
+ const TwoPointConicalEffect& effect = args.fFp.cast<TwoPointConicalEffect>();
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
+ this->emitUniforms(uniformHandler, effect);
fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
"Conical2FSParams");
@@ -213,7 +236,7 @@ protected:
SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
const char* p = coords2D.c_str();
- if (ge.getType() == kRadial_Type) {
+ if (effect.getType() == Type::kRadial) {
fragBuilder->codeAppendf("half %s = length(%s) - %s;", tName, p, p0.c_str());
} else {
// output will default to transparent black (we simply won't write anything
@@ -226,23 +249,24 @@ protected:
this->emitColor(fragBuilder,
uniformHandler,
args.fShaderCaps,
- ge,
+ effect,
tName,
args.fOutputColor,
args.fInputColor,
args.fTexSamplers);
- if (ge.getType() != kRadial_Type) {
+ if (effect.getType() != Type::kRadial) {
fragBuilder->codeAppendf("}");
}
}
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
INHERITED::onSetData(pdman, p);
- const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
+ const TwoPointConicalEffect& effect = p.cast<TwoPointConicalEffect>();
// kRadialType should imply r1 - r0 = 1 (after our transformation) so r0 = r0 / (r1 - r0)
- SkASSERT(data.getType() == kStrip_Type || SkScalarNearlyZero(data.r1() - data.r0() - 1));
- pdman.set1f(fParamUni, data.getType() == kRadial_Type ? data.r0() : data.r0() * data.r0());
+ SkASSERT(effect.getType() == Type::kStrip || SkScalarNearlyZero(effect.diffRadius() - 1));
+ pdman.set1f(fParamUni, effect.getType() == Type::kRadial ? effect.r0()
+ : effect.r0() * effect.r0());
}
UniformHandle fParamUni;
@@ -259,14 +283,14 @@ private:
class TwoPointConicalEffect::FocalGLSLProcessor : public GrGradientEffect::GLSLProcessor {
protected:
void emitCode(EmitArgs& args) override {
- const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
+ const TwoPointConicalEffect& effect = args.fFp.cast<TwoPointConicalEffect>();
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
+ this->emitUniforms(uniformHandler, effect);
fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
"Conical2FSParams");
SkString p0; // 1 / r1
- SkString p1; // r0 / (r1 - r0)
+ SkString p1; // f = focalX = r0 / (r0 - r1)
p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
const char* tName = "t"; // the gradient
@@ -275,12 +299,12 @@ protected:
SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
const char* p = coords2D.c_str();
- if (ge.isFocalOnCircle()) {
+ if (effect.isFocalOnCircle()) {
fragBuilder->codeAppendf("half x_t = dot(%s, %s) / %s.x;", p, p, p);
- } else if (ge.isWellBehaved()) {
+ } else if (effect.isWellBehaved()) {
fragBuilder->codeAppendf("half x_t = length(%s) - %s.x * %s;", p, p, p0.c_str());
} else {
- char sign = (ge.isSwapped() || !ge.isRadiusIncreasing()) ? '-' : ' ';
+ char sign = (effect.isSwapped() || !effect.isRadiusIncreasing()) ? '-' : ' ';
fragBuilder->codeAppendf("half temp = %s.x * %s.x - %s.y * %s.y;", p, p, p, p);
// Initialize x_t to illegal state
fragBuilder->codeAppendf("half x_t = -1;");
@@ -297,42 +321,40 @@ protected:
}
// empty sign is positive
- char sign = ge.isRadiusIncreasing() ? ' ' : '-';
+ char sign = effect.isRadiusIncreasing() ? ' ' : '-';
- // "- 0" is much faster than "- p1" so we specialize the natively focal case where p1 = 0.
- fragBuilder->codeAppendf("half %s = %cx_t - %s;", tName, sign,
- ge.isNativelyFocal() ? "0" : p1.c_str());
+ // "+ 0" is much faster than "+ p1" so we specialize the natively focal case where p1 = 0.
+ fragBuilder->codeAppendf("half %s = %cx_t + %s;", tName, sign,
+ effect.isNativelyFocal() ? "0" : p1.c_str());
- if (!ge.isWellBehaved()) {
+ if (!effect.isWellBehaved()) {
// output will default to transparent black (we simply won't write anything
// else to it if invalid, instead of discarding or returning prematurely)
fragBuilder->codeAppendf("%s = half4(0.0,0.0,0.0,0.0);", args.fOutputColor);
fragBuilder->codeAppendf("if (x_t > 0.0) {");
}
- if (ge.isSwapped()) {
+ if (effect.isSwapped()) {
fragBuilder->codeAppendf("%s = 1 - %s;", tName, tName);
}
this->emitColor(fragBuilder,
uniformHandler,
args.fShaderCaps,
- ge,
+ effect,
tName,
args.fOutputColor,
args.fInputColor,
args.fTexSamplers);
- if (!ge.isWellBehaved()) {
+ if (!effect.isWellBehaved()) {
fragBuilder->codeAppend("};");
}
}
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
INHERITED::onSetData(pdman, p);
- const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
- SkScalar r0 = data.r0();
- SkScalar r1 = data.r1();
- pdman.set2f(fParamUni, 1 / r1, r0 / (r1 - r0));
+ const TwoPointConicalEffect& effect = p.cast<TwoPointConicalEffect>();
+ pdman.set2f(fParamUni, 1 / effect.r1(), effect.focalX());
}
UniformHandle fParamUni;
@@ -344,7 +366,7 @@ private:
//////////////////////////////////////////////////////////////////////////////
GrGLSLFragmentProcessor* TwoPointConicalEffect::onCreateGLSLInstance() const {
- if (fData.fType == kRadial_Type || fData.fType == kStrip_Type) {
+ if (fData.fType == Type::kRadial || fData.fType == Type::kStrip) {
return new DegeneratedGLSLProcessor;
}
return new FocalGLSLProcessor;
@@ -383,74 +405,21 @@ std::unique_ptr<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
}
TwoPointConicalEffect::Data::Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix) {
- fIsSwapped = false;
- if (SkScalarNearlyZero(shader.getCenterX1())) {
- fType = kRadial_Type;
+ fType = shader.getType();
+ if (fType == Type::kRadial) {
SkScalar dr = shader.getDiffRadius();
// Map center to (0, 0) and scale dr to 1
matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);
matrix.postScale(1 / dr, 1 / dr);
fRadius0 = shader.getStartRadius() / dr;
fDiffRadius = 1;
- } else {
- // Map centers to (0, 0), (1, 0)
- const SkPoint centers[2] = { shader.getStartCenter(), shader.getEndCenter() };
- const SkPoint unitvec[2] = { { 0, 0 },{ 1, 0 } };
- SkMatrix gradientMatrix;
- // The radial case is already handled so this must succeed
- SkAssertResult(gradientMatrix.setPolyToPoly(centers, unitvec, 2));
- matrix.postConcat(gradientMatrix);
+ } else if (fType == Type::kStrip) {
fRadius0 = shader.getStartRadius() / shader.getCenterX1();
- fDiffRadius = shader.getDiffRadius() / shader.getCenterX1();
-
- if (SkScalarNearlyZero(shader.getDiffRadius())) {
- fType = kStrip_Type;
- } else { // focal case
- fType = kFocal_Type;
- SkScalar focalX = - fRadius0 / fDiffRadius;
-
- // We want to check if the end radius is zero. If so, dr = -r0 and focalX will be 1.
- // That makes our mapping from {focal_point, (1, 0)} to {(0, 0), {1, 0)} invalid. Hence
- // we have to swap r0 and r1 to make sure focalX != 1. Due to precision limit, checking
- // focalX == 1 is better than checking r1 == 0 or r0 + (r1 - r0) for large r0 (e.g., r0
- // = 1e6, r1 = 1).
- if (SkScalarNearlyZero(focalX - 1)) {
- // swap r0, r1
- matrix.postTranslate(-1, 0);
- matrix.postScale(-1, 1);
- fRadius0 = 0;
- fDiffRadius = -fDiffRadius;
- fIsSwapped = true;
- focalX = 0; // - fRadius0 / fDiffRadius;
- }
-
- // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
- const SkPoint from[2] = { {focalX, 0}, {1, 0} };
- const SkPoint to[2] = { {0, 0}, {1, 0} };
- SkMatrix focalMatrix;
- if (!focalMatrix.setPolyToPoly(from, to, 2)) {
- SkDEBUGFAILF("Mapping focal point failed unexpectedly for focalX = %f.\n", focalX);
- // We won't be able to draw the gradient; at least make sure that we initialize the
- // memory to prevent security issues.
- focalMatrix = SkMatrix::MakeScale(1, 1);
- }
- matrix.postConcat(focalMatrix);
- fRadius0 /= SkScalarAbs(1 - focalX);
- fDiffRadius /= SkScalarAbs(1 - focalX);
-
- SkScalar r0 = fRadius0;
- SkScalar r1 = fRadius0 + fDiffRadius;
- // The following transformations are not reflected on data; they're just to accelerate
- // the shader computation by saving some arithmatic operations.
- bool isFocalOnCircle = SkScalarNearlyZero(1 - r1);
- if (isFocalOnCircle) {
- matrix.postScale(0.5, 0.5); // r1 = 1 so r1 + 1 = 2 and 0.5 = 1 / (r1 + 1)
- } else {
- matrix.postScale(r1 / (r1 * r1 - 1), 1 / sqrt(SkScalarAbs(r1 * r1 - 1)));
- }
- SkScalar scale = SkScalarAbs(r1 / (r1 - r0)); // |1 - f|
- matrix.postScale(scale, scale);
- }
+ fDiffRadius = 0;
+ matrix.postConcat(shader.getGradientMatrix());
+ } else if (fType == Type::kFocal) {
+ fFocalData = shader.getFocalData();
+ matrix.postConcat(shader.getGradientMatrix());
}
}