From 22c5deaf755223111cb055462ac5bbce623f3a08 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Thu, 7 Jul 2011 14:38:03 +0000 Subject: [PATCH] Fix div by zero in GPU degenerate radials (and add sample and gm baselines) 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 | 3 + gpu/src/GrGLProgram.cpp | 66 ++++++++++++++++------ gpu/src/GrGLProgram.h | 7 ++- gpu/src/GrGpuGLShaders.cpp | 28 +++++++--- gyp/SampleApp.gyp | 1 + samplecode/SampleDegenerateTwoPtRadials.cpp | 85 +++++++++++++++++++++++++++++ samplecode/SampleXfermodesBlur.cpp | 50 +---------------- 7 files changed, 165 insertions(+), 75 deletions(-) create mode 100644 samplecode/SampleDegenerateTwoPtRadials.cpp diff --git a/gpu/include/GrSamplerState.h b/gpu/include/GrSamplerState.h index 6d343d0..d10d8c4 100644 --- a/gpu/include/GrSamplerState.h +++ b/gpu/include/GrSamplerState.h @@ -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 diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp index 31571ab..d6a832c 100644 --- a/gpu/src/GrGLProgram.cpp +++ b/gpu/src/GrGLProgram.cpp @@ -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. diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h index 2c4152d..edd4737 100644 --- a/gpu/src/GrGLProgram.h +++ b/gpu/src/GrGLProgram.h @@ -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, diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp index 2de9950..1309802 100644 --- a/gpu/src/GrGpuGLShaders.cpp +++ b/gpu/src/GrGpuGLShaders.cpp @@ -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; } diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp index a0c98a8..084ae71 100644 --- a/gyp/SampleApp.gyp +++ b/gyp/SampleApp.gyp @@ -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 index 0000000..ed10ce2 --- /dev/null +++ b/samplecode/SampleDegenerateTwoPtRadials.cpp @@ -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); diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp index 0167a6d..03b2968 100644 --- a/samplecode/SampleXfermodesBlur.cpp +++ b/samplecode/SampleXfermodesBlur.cpp @@ -24,50 +24,6 @@ #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 { -- 2.7.4