Fix div by zero in GPU degenerate radials (and add sample and gm baselines)
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 7 Jul 2011 14:38:03 +0000 (14:38 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 7 Jul 2011 14:38:03 +0000 (14:38 +0000)
Review URL: http://codereview.appspot.com/4675062/

git-svn-id: http://skia.googlecode.com/svn/trunk@1817 2bbb7eff-a529-9590-31e7-b0007b416f81

gpu/include/GrSamplerState.h
gpu/src/GrGLProgram.cpp
gpu/src/GrGLProgram.h
gpu/src/GrGpuGLShaders.cpp
gyp/SampleApp.gyp
samplecode/SampleDegenerateTwoPtRadials.cpp [new file with mode: 0644]
samplecode/SampleXfermodesBlur.cpp

index 6d343d0..d10d8c4 100644 (file)
@@ -203,6 +203,9 @@ public:
     GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
     GrScalar getRadial2Radius0() const { return fRadial2Radius0; }
     bool     isRadial2PosRoot() const { return fRadial2PosRoot; }
+    // do the radial gradient params lead to a linear (rather than quadratic)
+    // equation.
+    bool radial2IsDegenerate() const { return GR_Scalar1 == fRadial2CenterX1; }
 
     /**
      * Sets the parameters for kRadial2_SampleMode. The texture
index 31571ab..d6a832c 100644 (file)
@@ -966,7 +966,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
 //============================================================================
 
 void GrGLProgram::genStageCode(int stageNum,
-                               const GrGLProgram::ProgramDesc::StageDesc& desc,
+                               const GrGLProgram::StageDesc& desc,
                                const char* fsInColor, // NULL means no incoming color
                                const char* fsOutColor,
                                const char* vsInCoord,
@@ -989,7 +989,7 @@ void GrGLProgram::genStageCode(int stageNum,
     // and whether the varying needs a perspective coord.
     GrStringBuilder texMName;
     tex_matrix_name(stageNum, &texMName);
-    if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
         varyingDims = coordDims;
     } else {
     #if GR_GL_ATTRIBUTE_MATRICES
@@ -999,7 +999,7 @@ void GrGLProgram::genStageCode(int stageNum,
         segments->fVSUnis.appendf("uniform mat3 %s;\n", texMName.c_str());
         locations->fTextureMatrixUni = kUseUniform;
     #endif
-        if (desc.fOptFlags & ProgramDesc::StageDesc::kNoPerspective_OptFlagBit) {
+        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
             varyingDims = coordDims;
         } else {
             varyingDims = coordDims + 1;
@@ -1012,7 +1012,7 @@ void GrGLProgram::genStageCode(int stageNum,
     locations->fSamplerUni = kUseUniform;
 
     GrStringBuilder texelSizeName;
-    if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
+    if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
         normalized_texel_size_name(stageNum, &texelSizeName);
         segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
     }
@@ -1020,7 +1020,7 @@ void GrGLProgram::genStageCode(int stageNum,
     segments->fVaryings.appendf("varying %s %s;\n",
                                 float_vector_type(varyingDims), varyingName.c_str());
 
-    if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
         GrAssert(varyingDims == coordDims);
         segments->fVSCode.appendf("\t%s = %s;\n", varyingName.c_str(), vsInCoord);
     } else {
@@ -1037,7 +1037,8 @@ void GrGLProgram::genStageCode(int stageNum,
     GrStringBuilder radial2VaryingName;
     radial2_varying_name(stageNum, &radial2VaryingName);
 
-    if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
+    if (StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping || 
+        StageDesc::kRadial2GradientDegenerate_CoordMapping == desc.fCoordMapping) {
 
         segments->fVSUnis.appendf("uniform %s float %s[6];\n",
                                   GrPrecision(), radial2ParamsName.c_str());
@@ -1061,16 +1062,16 @@ void GrGLProgram::genStageCode(int stageNum,
     GrStringBuilder fsCoordName;
     // function used to access the shader, may be made projective
     GrStringBuilder texFunc("texture2D");
-    if (desc.fOptFlags & (ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit |
-                          ProgramDesc::StageDesc::kNoPerspective_OptFlagBit)) {
+    if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
+                          StageDesc::kNoPerspective_OptFlagBit)) {
         GrAssert(varyingDims == coordDims);
         fsCoordName = varyingName;
     } else {
         // if we have to do some special op on the varyings to get
         // our final tex coords then when in perspective we have to
         // do an explicit divide. Otherwise, we can use a Proj func.
-        if  (ProgramDesc::StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
-             ProgramDesc::StageDesc::kSingle_FetchMode == desc.fFetchMode) {
+        if  (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
+             StageDesc::kSingle_FetchMode == desc.fFetchMode) {
             texFunc.append("Proj");
             fsCoordName = varyingName;
         } else {
@@ -1089,18 +1090,18 @@ void GrGLProgram::genStageCode(int stageNum,
     GrStringBuilder sampleCoords;
     bool complexCoord = false;
     switch (desc.fCoordMapping) {
-    case ProgramDesc::StageDesc::kIdentity_CoordMapping:
+    case StageDesc::kIdentity_CoordMapping:
         sampleCoords = fsCoordName;
         break;
-    case ProgramDesc::StageDesc::kSweepGradient_CoordMapping:
+    case StageDesc::kSweepGradient_CoordMapping:
         sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str());
         complexCoord = true;
         break;
-    case ProgramDesc::StageDesc::kRadialGradient_CoordMapping:
+    case StageDesc::kRadialGradient_CoordMapping:
         sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
         complexCoord = true;
         break;
-    case ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping: {
+    case StageDesc::kRadial2Gradient_CoordMapping: {
         GrStringBuilder cName("c");
         GrStringBuilder ac4Name("ac4");
         GrStringBuilder rootName("root");
@@ -1147,10 +1148,41 @@ void GrGLProgram::genStageCode(int stageNum,
                             rootName.c_str(), radial2ParamsName.c_str());
         complexCoord = true;
         break;}
+    case StageDesc::kRadial2GradientDegenerate_CoordMapping: {
+        GrStringBuilder cName("c");
+
+        cName.appendS32(stageNum);
+
+        // if we were able to interpolate the linear component bVar is the varying
+        // otherwise compute it
+        GrStringBuilder bVar;
+        if (coordDims == varyingDims) {
+            bVar = radial2VaryingName;
+            GrAssert(2 == varyingDims);
+        } else {
+            GrAssert(3 == varyingDims);
+            bVar = "b";
+            bVar.appendS32(stageNum);
+            segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n",
+                                        bVar.c_str(), radial2ParamsName.c_str(),
+                                        fsCoordName.c_str(), radial2ParamsName.c_str());
+        }
+
+        // c = (x^2)+(y^2) - params[4]
+        segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n",
+                                  cName.c_str(), fsCoordName.c_str(),
+                                  fsCoordName.c_str(),
+                                  radial2ParamsName.c_str());
+
+        // x coord is: -c/b
+        // y coord is 0.5 (texture is effectively 1D)
+        sampleCoords.printf("vec2((-%s / %s), 0.5)", cName.c_str(), bVar.c_str());
+        complexCoord = true;
+        break;}
     };
 
     const char* smear;
-    if (desc.fModulation == ProgramDesc::StageDesc::kAlpha_Modulation) {
+    if (desc.fModulation == StageDesc::kAlpha_Modulation) {
         smear = ".aaaa";
     } else {
         smear = "";
@@ -1161,7 +1193,7 @@ void GrGLProgram::genStageCode(int stageNum,
     }
 
     if (desc.fOptFlags &
-        ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) {
+        StageDesc::kCustomTextureDomain_OptFlagBit) {
         GrStringBuilder texDomainName;
         tex_domain_name(stageNum, &texDomainName);
         segments->fFSUnis.appendf("uniform %s %s;\n",
@@ -1178,7 +1210,7 @@ void GrGLProgram::genStageCode(int stageNum,
         locations->fTexDomUni = kUseUniform;
     }
 
-    if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
+    if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
         locations->fNormalizedTexelSizeUni = kUseUniform;
         if (complexCoord) {
             // assign the coord to a var rather than compute 4x.
index 2c4152d..edd4737 100644 (file)
@@ -122,7 +122,9 @@ private:
                 kRadialGradient_CoordMapping,
                 kSweepGradient_CoordMapping,
                 kRadial2Gradient_CoordMapping,
-
+                // need different shader computation when quadratic
+                // eq describing the gradient degenerates to a linear eq.
+                kRadial2GradientDegenerate_CoordMapping,
                 kCoordMappingCnt
             };
 
@@ -180,6 +182,9 @@ private:
 
     const ProgramDesc& getDesc() { return fProgramDesc; }
 
+    // for code readability
+    typedef ProgramDesc::StageDesc StageDesc;
+
 public:
     enum {
         kUnusedUniform = -1,
index 2de9950..1309802 100644 (file)
@@ -433,6 +433,11 @@ void GrGpuGLShaders::flushRadial2(int s) {
 
         GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
 
+        // when were in the degenerate (linear) case the second
+        // value will be INF but the program doesn't read it. (We
+        // use the same 6 uniforms even though we don't need them
+        // all in the linear case just to keep the code complexity
+        // down).
         float values[6] = {
             GrScalarToFloat(a),
             1 / (2.f * values[0]),
@@ -737,14 +742,15 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
             lastEnabledStage = s;
             GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
             GrAssert(NULL != texture);
+            const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
             // we matrix to invert when orientation is TopDown, so make sure
             // we aren't in that case before flagging as identity.
-            if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
+            if (TextureMatrixIsIdentity(texture, sampler)) {
                 stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit;
             } else if (!getSamplerMatrix(s).hasPerspective()) {
                 stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
             }
-            switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
+            switch (sampler.getSampleMode()) {
                 case GrSamplerState::kNormal_SampleMode:
                     stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
                     break;
@@ -752,7 +758,13 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
                     stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
                     break;
                 case GrSamplerState::kRadial2_SampleMode:
-                    stage.fCoordMapping = StageDesc::kRadial2Gradient_CoordMapping;
+                    if (sampler.radial2IsDegenerate()) {
+                        stage.fCoordMapping =
+                            StageDesc::kRadial2GradientDegenerate_CoordMapping;
+                    } else {
+                        stage.fCoordMapping =
+                            StageDesc::kRadial2Gradient_CoordMapping;
+                    }
                     break;
                 case GrSamplerState::kSweep_SampleMode:
                     stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
@@ -762,7 +774,7 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
                     break;
             }
 
-            switch (fCurrDrawState.fSamplerStates[s].getFilter()) {
+            switch (sampler.getFilter()) {
                 // these both can use a regular texture2D()
                 case GrSamplerState::kNearest_Filter:
                 case GrSamplerState::kBilinear_Filter:
@@ -777,11 +789,11 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
                     break;
             }
 
-            if (fCurrDrawState.fSamplerStates[s].hasTextureDomain()) {
+            if (sampler.hasTextureDomain()) {
                 GrAssert(GrSamplerState::kClamp_WrapMode ==
-                    fCurrDrawState.fSamplerStates[s].getWrapX() &&
-                    GrSamplerState::kClamp_WrapMode ==
-                    fCurrDrawState.fSamplerStates[s].getWrapY());
+                            sampler.getWrapX() &&
+                         GrSamplerState::kClamp_WrapMode ==
+                            sampler.getWrapY());
                 stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit;
             }
 
index a0c98a8..084ae71 100644 (file)
@@ -48,6 +48,7 @@
         '../samplecode/SampleConcavePaths.cpp',
         '../samplecode/SampleCull.cpp',
         '../samplecode/SampleDecode.cpp',
+        '../samplecode/SampleDegenerateTwoPtRadials.cpp',
         '../samplecode/SampleDither.cpp',
         '../samplecode/SampleDitherBitmap.cpp',
         '../samplecode/SampleDrawLooper.cpp',
diff --git a/samplecode/SampleDegenerateTwoPtRadials.cpp b/samplecode/SampleDegenerateTwoPtRadials.cpp
new file mode 100644 (file)
index 0000000..ed10ce2
--- /dev/null
@@ -0,0 +1,85 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+
+static void draw_gradient2(SkCanvas* canvas, const SkRect& rect, SkScalar delta) {
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorMAGENTA };
+    SkScalar pos[] = { 0, SkFloatToScalar(0.25f), SkFloatToScalar(0.75f), SK_Scalar1 };
+
+    SkScalar l = rect.fLeft;
+    SkScalar t = rect.fTop;
+    SkScalar w = rect.width();
+    SkScalar h = rect.height();
+
+    SkASSERT(0 == SkScalarMod(w, SK_Scalar1 * 5));
+
+    SkPoint c0 = { l + 2 * w / 5 + delta, t + h / 2 };
+    SkPoint c1 = { l + 3 * w / 5, t + h / 2 };
+    SkScalar r0 = w / 5;
+    SkScalar r1 = 2 * w / 5;
+    SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
+                                                         pos, SK_ARRAY_COUNT(pos),
+                                                         SkShader::kClamp_TileMode);
+    SkPaint paint;
+    paint.setShader(s)->unref();
+
+    canvas->drawRect(rect, paint);
+}
+
+
+class DegenerateTwoPtRadialsView : public SampleView {
+
+public:
+    DegenerateTwoPtRadialsView() {
+        fTime = 0;
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "DegenerateTwoPtRadials");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        fTime += SampleCode::GetAnimSecondsDelta();
+        SkScalar delta = fTime / 15.f;
+        int intPart = SkScalarFloor(delta);
+        delta = delta - SK_Scalar1 * intPart;
+        if (intPart % 2) {
+            delta = SK_Scalar1 - delta;
+        }
+        delta -= SK_ScalarHalf;
+        static const int DELTA_SCALE = 500;
+        delta /= DELTA_SCALE;
+
+        SkRect rect;
+        SkScalar w = SK_Scalar1 * 500;
+        SkScalar h = SK_Scalar1 * 500;
+        SkScalar l = SK_Scalar1 * 100;
+        SkScalar t = SK_Scalar1 * 100;
+        draw_gradient2(canvas, SkRect::MakeXYWH(l, t, w, h), delta);
+        char txt[512];
+        sprintf(txt, "gap at \"tangent\" pt = %f", SkScalarToFloat(delta));
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorBLACK);
+        canvas->drawText(txt, strlen(txt), l + w/2 + w*DELTA_SCALE*delta, t + h + SK_Scalar1 * 10, paint);
+        this->inval(NULL);
+    }
+
+private:
+    SkScalar           fTime;
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DegenerateTwoPtRadialsView; }
+static SkViewRegister reg(MyFactory);
index 0167a6d..03b2968 100644 (file)
 #include "SkImageDecoder.h"
 #include "SkBlurMaskFilter.h"
 
-static void test_gradient2(SkCanvas* canvas) {
-#if 1
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
-    bm.allocPixels();
-    *bm.getAddr32(0, 0) = SkPackARGB32(0xFF, 0, 0xFF, 0);
-
-    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-#else
-/*
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    
-    var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
-    g.addColorStop(0, '#f00');
-    g.addColorStop(0.01, '#0f0');
-    g.addColorStop(0.99, '#0f0');
-    g.addColorStop(1, '#f00');
-    ctx.fillStyle = g;
-    ctx.fillRect(0, 0, 100, 50);
-*/
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
-    SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 };
-    SkPoint c0 = { -80, 25 };
-    SkScalar r0 = 70;
-    SkPoint c1 = { 0, 25 };
-    SkScalar r1 = 150;
-    SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
-                                                         pos, SK_ARRAY_COUNT(pos),
-                                                         SkShader::kClamp_TileMode);
-#endif
-
-    SkPaint paint;
-    paint.setShader(s)->unref();
-
-    canvas->drawPaint(paint);
-
-    paint.setShader(NULL);
-    paint.setStyle(SkPaint::kStroke_Style);
-    SkRect r = { 0, 0, 100, 50 };
-    canvas->drawRect(r, paint);
-}
-
 static void setNamedTypeface(SkPaint* paint, const char name[]) {
     SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
     paint->setTypeface(face);
@@ -110,7 +66,7 @@ class XfermodesBlurView : public SampleView {
 public:
     const static int W = 64;
     const static int H = 64;
-       XfermodesBlurView() {
+    XfermodesBlurView() {
         const int W = 64;
         const int H = 64;
 
@@ -130,10 +86,6 @@ protected:
     }
 
     virtual void onDrawContent(SkCanvas* canvas) {
-        if (false) {
-            test_gradient2(canvas);
-            return;
-        }
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const struct {