From 894790d77c56cd4bae8070331d275c6d2897e33c Mon Sep 17 00:00:00 2001 From: "senorblanco@chromium.org" Date: Wed, 11 Jul 2012 16:01:22 +0000 Subject: [PATCH] This patch implements the diffuse and specular lighting filters in Ganesh. There are three light types for each: distant, point and spot, whose code generation lives in a GrGLLight class hierarchy. This similar to the CPU implementation, where each light type provides a function to compute the vector from the surface plane to the light (surfaceToLight) and to compute the light colour (emitLightColour). Instead of templated member functions, as in the CPU implementation, these are virtual functions to emit the light-specific GLSL code. All of the code for the GPU path lives in the same file as that for the CPU path, SkLightingImageFilter.cpp. In order to provide Ganesh a hook to access it, SkImageFilter now has a asNewCustomStage() virtual, which allows an image filter to return a GrCustomStage representing that filter. Note that this patch does not handle the border conditions correctly (the [top|bottom][Left|Right]Normal() functions in the CPU implementation). That will come in a future patch. Review URL: http://codereview.appspot.com/6345081/ git-svn-id: http://skia.googlecode.com/svn/trunk@4535 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/effects.gyp | 2 + gyp/gpu.gyp | 3 + include/core/SkImageFilter.h | 12 + include/effects/SkLightingImageFilter.h | 3 + include/gpu/GrProgramStageFactory.h | 1 + src/core/SkPaint.cpp | 4 + src/effects/SkLightingImageFilter.cpp | 730 +++++++++++++++++++++++- src/gpu/SkGpuDevice.cpp | 41 +- src/gpu/gl/GrGpuGL_unittest.cpp | 91 +++ 9 files changed, 883 insertions(+), 4 deletions(-) diff --git a/gyp/effects.gyp b/gyp/effects.gyp index fbccb401b..cb15a2ac2 100644 --- a/gyp/effects.gyp +++ b/gyp/effects.gyp @@ -7,6 +7,8 @@ '../include/config', '../include/core', '../include/effects', + '../include/gpu', + '../src/gpu', ], 'sources': [ '../include/effects/Sk1DPathEffect.h', diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 91e7385fd..2874054bd 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -163,6 +163,9 @@ 'include_dirs': [ '../include/core', '../include/config', + '../include/effects', # This is only to get Sk effects into + # GrGpuGL_unittest.cpp, not for general + # consumption in src/gpu. '../include/gpu', '../src/core', # SkRasterClip.h '../src/gpu' diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index c27f69820..b7bf8bcfe 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -14,6 +14,7 @@ class SkBitmap; class SkDevice; class SkMatrix; struct SkPoint; +class GrCustomStage; /** * Experimental. @@ -76,6 +77,17 @@ public: */ bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst); + /** + * Returns true if the filter can be expressed a single-pass + * GrCustomStage, used to process this filter on the GPU, or false if + * not. + * + * If stage is non-NULL, a new GrCustomStage instance is stored + * in it. The caller assumes ownership of the stage, and it is up to the + * caller to unref it. + */ + virtual bool asNewCustomStage(GrCustomStage** stage) const; + /** * Experimental. * diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h index 5406e7e62..c1ef234ea 100644 --- a/include/effects/SkLightingImageFilter.h +++ b/include/effects/SkLightingImageFilter.h @@ -39,6 +39,9 @@ public: SkPoint3 operator-(const SkPoint3& other) const { return SkPoint3(fX - other.fX, fY - other.fY, fZ - other.fZ); } + bool operator==(const SkPoint3& other) const { + return fX == other.fX && fY == other.fY && fZ == other.fZ; + } SkScalar fX, fY, fZ; }; diff --git a/include/gpu/GrProgramStageFactory.h b/include/gpu/GrProgramStageFactory.h index d133beb9f..f2b4f5fc9 100644 --- a/include/gpu/GrProgramStageFactory.h +++ b/include/gpu/GrProgramStageFactory.h @@ -10,6 +10,7 @@ #include "GrTypes.h" #include "SkTemplates.h" +#include "GrNoncopyable.h" /** Given a GrCustomStage of a particular type, creates the corresponding graphics-backend-specific GrProgramStage. Also tracks equivalence diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index cba7930af..08e642cfb 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -2193,6 +2193,10 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, return true; } +bool SkImageFilter::asNewCustomStage(GrCustomStage**) const { + return false; +} + bool SkImageFilter::asABlur(SkSize* sigma) const { return false; } diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp index 5625d0363..ef45968f5 100644 --- a/src/effects/SkLightingImageFilter.cpp +++ b/src/effects/SkLightingImageFilter.cpp @@ -8,6 +8,14 @@ #include "SkLightingImageFilter.h" #include "SkBitmap.h" #include "SkColorPriv.h" +#include "GrProgramStageFactory.h" +#include "gl/GrGLProgramStage.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLTexture.h" +#include "GrCustomStage.h" + +class GrGLDiffuseLightingEffect; +class GrGLSpecularLightingEffect; // FIXME: Eventually, this should be implemented properly, and put in // SkScalar.h. @@ -19,6 +27,13 @@ const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3)); const SkScalar gOneHalf = SkFloatToScalar(0.5f); const SkScalar gOneQuarter = SkFloatToScalar(0.25f); +void setUniformPoint3(const GrGLInterface* gl, GrGLint location, const SkPoint3& point) { + float x = SkScalarToFloat(point.fX); + float y = SkScalarToFloat(point.fY); + float z = SkScalarToFloat(point.fZ); + GR_GL_CALL(gl, Uniform3f(location, x, y, z)); +} + // Shift matrix components to the left, as we advance pixels to the right. inline void shiftMatrixLeft(int m[9]) { m[0] = m[1]; @@ -236,6 +251,7 @@ public: SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter) + virtual bool asNewCustomStage(GrCustomStage** stage) const SK_OVERRIDE; SkScalar kd() const { return fKD; } protected: @@ -255,6 +271,10 @@ public: SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter) + virtual bool asNewCustomStage(GrCustomStage** stage) const SK_OVERRIDE; + SkScalar ks() const { return fKS; } + SkScalar shininess() const { return fShininess; } + protected: explicit SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer); virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE; @@ -267,8 +287,144 @@ private: SkScalar fShininess; }; + +class GrLightingEffect : public GrCustomStage { +public: + GrLightingEffect(const SkLight* light, SkScalar surfaceScale); + virtual ~GrLightingEffect(); + + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + const SkLight* light() const { return fLight; } + SkScalar surfaceScale() const { return fSurfaceScale; } +private: + typedef GrCustomStage INHERITED; + const SkLight* fLight; + SkScalar fSurfaceScale; }; +class GrDiffuseLightingEffect : public GrLightingEffect { +public: + GrDiffuseLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar kd); + + static const char* Name() { return "DiffuseLighting"; } + + typedef GrGLDiffuseLightingEffect GLProgramStage; + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + SkScalar kd() const { return fKD; } +private: + typedef GrLightingEffect INHERITED; + SkScalar fKD; +}; + +class GrSpecularLightingEffect : public GrLightingEffect { +public: + GrSpecularLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess); + + static const char* Name() { return "SpecularLighting"; } + + typedef GrGLSpecularLightingEffect GLProgramStage; + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + SkScalar ks() const { return fKS; } + SkScalar shininess() const { return fShininess; } + +private: + typedef GrLightingEffect INHERITED; + SkScalar fKS; + SkScalar fShininess; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void emitVS(SkString* builder) const {} + virtual void emitFuncs(SkString* builder) const {} + virtual void emitSurfaceToLight(SkString* builder, const char* z) const = 0; + virtual void emitLightColor(SkString* builder, const char *surfaceToLight) const; + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface*, const SkLight* light) const; + +private: + typedef SkRefCnt INHERITED; + +protected: + const GrGLShaderVar* fColorVar; + int fColorVarLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLDistantLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface* gl, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + +private: + typedef GrGLLight INHERITED; + const GrGLShaderVar* fDirectionVar; + int fDirectionLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLPointLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitVS(SkString* builder) const; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + +private: + typedef GrGLLight INHERITED; + SkPoint3 fLocation; + const GrGLShaderVar* fLocationVar; + int fLocationLocation; + const char* fHeightVaryingName; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLSpotLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitVS(SkString* builder) const; + virtual void emitFuncs(SkString* builder) const; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + virtual void emitLightColor(SkString* builder, const char *surfaceToLight) const; + +private: + typedef GrGLLight INHERITED; + + const GrGLShaderVar* fLocationVar; + int fLocationLocation; + const GrGLShaderVar* fExponentVar; + int fExponentLocation; + const GrGLShaderVar* fCosOuterConeAngleVar; + int fCosOuterConeAngleLocation; + const GrGLShaderVar* fCosInnerConeAngleVar; + int fCosInnerConeAngleLocation; + const GrGLShaderVar* fConeScaleVar; + int fConeScaleLocation; + const GrGLShaderVar* fSVar; + int fSLocation; + const char* fHeightVaryingName; +}; + +}; + +/////////////////////////////////////////////////////////////////////////////// + class SkLight : public SkFlattenable { public: SK_DECLARE_INST_COUNT(SkLight) @@ -280,6 +436,10 @@ public: }; virtual LightType type() const = 0; const SkPoint3& color() const { return fColor; } + virtual GrGLLight* createGLLight() const = 0; + virtual bool isEqual(const SkLight& other) const { + return fColor == other.fColor; + } protected: SkLight(SkColor color) @@ -302,6 +462,8 @@ private: SK_DEFINE_INST_COUNT(SkLight) +/////////////////////////////////////////////////////////////////////////////// + class SkDistantLight : public SkLight { public: SkDistantLight(const SkPoint3& direction, SkColor color) @@ -313,7 +475,8 @@ public: }; SkPoint3 lightColor(const SkPoint3&) const { return color(); } virtual LightType type() const { return kDistant_LightType; } - SkPoint3 direction() const { return fDirection; } + const SkPoint3& direction() const { return fDirection; } + virtual GrGLLight* createGLLight() const SK_OVERRIDE; SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDistantLight) @@ -331,6 +494,8 @@ private: SkPoint3 fDirection; }; +/////////////////////////////////////////////////////////////////////////////// + class SkPointLight : public SkLight { public: SkPointLight(const SkPoint3& location, SkColor color) @@ -345,6 +510,18 @@ public: }; SkPoint3 lightColor(const SkPoint3&) const { return color(); } virtual LightType type() const { return kPoint_LightType; } + const SkPoint3& location() const { return fLocation; } + virtual GrGLLight* createGLLight() const SK_OVERRIDE { + return new GrGLPointLight(); + } + bool isEqual(const SkLight& other) SK_OVERRIDE { + if (other.type() != kPoint_LightType) { + return false; + } + const SkPointLight& o = static_cast(other); + return INHERITED::isEqual(other) && + fLocation == o.fLocation; + } SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPointLight) @@ -362,6 +539,8 @@ private: SkPoint3 fLocation; }; +/////////////////////////////////////////////////////////////////////////////// + class SkSpotLight : public SkLight { public: SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor color) @@ -397,8 +576,17 @@ public: } return color() * scale; } - + virtual GrGLLight* createGLLight() const SK_OVERRIDE { + return new GrGLSpotLight(); + } virtual LightType type() const { return kSpot_LightType; } + const SkPoint3& location() const { return fLocation; } + const SkPoint3& target() const { return fTarget; } + SkScalar specularExponent() const { return fSpecularExponent; } + SkScalar cosInnerConeAngle() const { return fCosInnerConeAngle; } + SkScalar cosOuterConeAngle() const { return fCosOuterConeAngle; } + SkScalar coneScale() const { return fConeScale; } + SkPoint3 s() const { return fS; } SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotLight) @@ -423,6 +611,19 @@ protected: writePoint3(fS, buffer); } + virtual bool isEqual(const SkLight& other) SK_OVERRIDE { + if (other.type() != kSpot_LightType) { + return false; + } + + const SkSpotLight& o = static_cast(other); + return INHERITED::isEqual(other) && + fLocation == o.fLocation && + fTarget == o.fTarget && + fSpecularExponent == o.fSpecularExponent && + fCosOuterConeAngle == o.fCosOuterConeAngle; + } + private: typedef SkLight INHERITED; SkPoint3 fLocation; @@ -434,6 +635,8 @@ private: SkPoint3 fS; }; +/////////////////////////////////////////////////////////////////////////////// + SkLightingImageFilter::SkLightingImageFilter(SkLight* light, SkScalar surfaceScale) : fLight(light), fSurfaceScale(SkScalarDiv(surfaceScale, SkIntToScalar(255))) @@ -507,6 +710,8 @@ void SkLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeScalar(fSurfaceScale); } +/////////////////////////////////////////////////////////////////////////////// + SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd) : SkLightingImageFilter(light, surfaceScale), fKD(kd) @@ -557,6 +762,16 @@ bool SkDiffuseLightingImageFilter::onFilterImage(Proxy*, return true; } +bool SkDiffuseLightingImageFilter::asNewCustomStage(GrCustomStage** stage) const { + if (stage) { + SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255)); + *stage = new GrDiffuseLightingEffect(light(), scale, kd()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess) : SkLightingImageFilter(light, surfaceScale), fKS(ks), @@ -610,6 +825,517 @@ bool SkSpecularLightingImageFilter::onFilterImage(Proxy*, return true; } +bool SkSpecularLightingImageFilter::asNewCustomStage(GrCustomStage** stage) const { + if (stage) { + SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255)); + *stage = new GrSpecularLightingEffect(light(), scale, ks(), shininess()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLLightingEffect : public GrGLProgramStage { +public: + GrGLLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual ~GrGLLightingEffect(); + + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE; + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + + virtual void emitLightFunc(SkString* funcs) = 0; + + static inline StageKey GenKey(const GrCustomStage& s); + + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLProgramStage INHERITED; + + const GrGLShaderVar* fImageIncrementVar; + GrGLint fImageIncrementLocation; + const GrGLShaderVar* fSurfaceScaleVar; + GrGLint fSurfaceScaleLocation; + GrGLLight* fLight; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLDiffuseLightingEffect : public GrGLLightingEffect { +public: + GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitLightFunc(SkString* funcs) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLLightingEffect INHERITED; + + const GrGLShaderVar* fKDVar; + GrGLint fKDLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLSpecularLightingEffect : public GrGLLightingEffect { +public: + GrGLSpecularLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitLightFunc(SkString* funcs) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLLightingEffect INHERITED; + + const GrGLShaderVar* fKSVar; + GrGLint fKSLocation; + const GrGLShaderVar* fShininessVar; + GrGLint fShininessLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +GrLightingEffect::GrLightingEffect(const SkLight* light, SkScalar surfaceScale) + : fLight(light) + , fSurfaceScale(surfaceScale) { + fLight->ref(); +} + +GrLightingEffect::~GrLightingEffect() { + fLight->unref(); +} + +bool GrLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrLightingEffect& s = + static_cast(sBase); + return fLight->isEqual(*s.fLight) && + fSurfaceScale == s.fSurfaceScale; +} + +/////////////////////////////////////////////////////////////////////////////// + +GrDiffuseLightingEffect::GrDiffuseLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar kd) + : INHERITED(light, surfaceScale), fKD(kd) { +} + +const GrProgramStageFactory& GrDiffuseLightingEffect::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrDiffuseLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrDiffuseLightingEffect& s = + static_cast(sBase); + return INHERITED::isEqual(sBase) && + this->kd() == s.kd(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrGLLightingEffect::GrGLLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLProgramStage(factory) + , fImageIncrementVar(NULL) + , fImageIncrementLocation(0) + , fSurfaceScaleVar(NULL) + , fSurfaceScaleLocation(0) { + const GrLightingEffect& m = static_cast(stage); + fLight = m.light()->createGLLight(); +} + +GrGLLightingEffect::~GrGLLightingEffect() { + delete fLight; +} + +void GrGLLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + fImageIncrementVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec2f_GrSLType, "uImageIncrement", stage); + fSurfaceScaleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uSurfaceScale", stage); + fLight->setupVariables(state, stage); +} + +void GrGLLightingEffect::emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) { + fLight->emitVS(&state->fVSCode); +} + +void GrGLLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + GR_GL_CALL_RET(gl, fSurfaceScaleLocation, + GetUniformLocation(programID, + fSurfaceScaleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fImageIncrementLocation, + GetUniformLocation(programID, + fImageIncrementVar->getName().c_str())); + fLight->initUniforms(gl, programID); +} + +void GrGLLightingEffect::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + SkString* code = &state->fFSCode; + SkString* funcs = &state->fFSFunctions; + fLight->emitFuncs(funcs); + emitLightFunc(funcs); + funcs->appendf("float sobel(float a, float b, float c, float d, float e, float f, float scale) {\n"); + funcs->appendf("\treturn (-a + b - 2 * c + 2 * d -e + f) * scale;\n"); + funcs->appendf("}\n"); + funcs->appendf("vec3 pointToNormal(float x, float y, float scale) {\n"); + funcs->appendf("\treturn normalize(vec3(-x * scale, -y * scale, 1));\n"); + funcs->appendf("}\n"); + funcs->append("\n\ +vec3 interiorNormal(float m[9], float surfaceScale) {\n\ + return pointToNormal(sobel(m[0], m[2], m[3], m[5], m[6], m[8], 0.25),\n\ + sobel(m[0], m[6], m[1], m[7], m[2], m[8], 0.25),\n\ + surfaceScale);\n}\n"); + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tfloat m[9];\n"); + int index = 0; + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + SkString texCoords; + texCoords.appendf("coord + vec2(%d, %d) * %s", dx, dy, fImageIncrementVar->getName().c_str()); + code->appendf("\t\tm[%d] = ", index++); + state->emitTextureLookup(samplerName, texCoords.c_str()); + code->appendf(".a;\n"); + } + } + code->appendf("\t\tvec3 surfaceToLight = "); + SkString arg; + arg.appendf("%s * m[4]", fSurfaceScaleVar->getName().c_str()); + fLight->emitSurfaceToLight(code, arg.c_str()); + code->appendf(";\n"); + code->appendf("\t\t%s = light(interiorNormal(m, %s), surfaceToLight, ", outputColor, fSurfaceScaleVar->getName().c_str()); + fLight->emitLightColor(code, "surfaceToLight"); + code->appendf(")%s;\n", state->fModulate.c_str()); +} + +GrGLProgramStage::StageKey GrGLLightingEffect::GenKey( + const GrCustomStage& s) { + return static_cast(s).light()->type(); +} + +void GrGLLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + const GrLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform2f(fImageIncrementLocation, 1.0f / texture.width(), 1.0f / texture.height())); + GR_GL_CALL(gl, Uniform1f(fSurfaceScaleLocation, effect.surfaceScale())); + fLight->setData(gl, effect.light()); +} + +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// + +GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : INHERITED(factory, stage) + , fKDVar(NULL) + , fKDLocation(0) { +} + +void GrGLDiffuseLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fKDVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uKD", stage); +} + +void GrGLDiffuseLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fKDLocation, + GetUniformLocation(programID, + fKDVar->getName().c_str())); +} + +void GrGLDiffuseLightingEffect::emitLightFunc(SkString* funcs) { + funcs->appendf("vec4 light(vec3 normal, vec3 surfaceToLight, vec3 lightColor) {\n"); + funcs->appendf("\tfloat colorScale = %s * dot(normal, surfaceToLight);\n", fKDVar->getName().c_str()); + funcs->appendf("\treturn vec4(lightColor * clamp(colorScale, 0, 1), 1);\n"); + funcs->appendf("}\n"); +} + +void GrGLDiffuseLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + INHERITED::setData(gl, texture, data, stageNum); + const GrDiffuseLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform1f(fKDLocation, effect.kd())); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrSpecularLightingEffect::GrSpecularLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess) + : INHERITED(light, surfaceScale), + fKS(ks), + fShininess(shininess) { +} + +const GrProgramStageFactory& GrSpecularLightingEffect::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrSpecularLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrSpecularLightingEffect& s = + static_cast(sBase); + return INHERITED::isEqual(sBase) && + this->ks() == s.ks() && + this->shininess() == s.shininess(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLLightingEffect(factory, stage) + , fKSVar(NULL) + , fKSLocation(0) + , fShininessVar(NULL) + , fShininessLocation(0) { +} + +void GrGLSpecularLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fKSVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uKS", stage); + fShininessVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uShininess", stage); +} + +void GrGLSpecularLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fKSLocation, + GetUniformLocation(programID, fKSVar->getName().c_str())); + GR_GL_CALL_RET(gl, fShininessLocation, + GetUniformLocation(programID, fShininessVar->getName().c_str())); +} + +void GrGLSpecularLightingEffect::emitLightFunc(SkString* funcs) { + funcs->appendf("vec4 light(vec3 normal, vec3 surfaceToLight, vec3 lightColor) {\n"); + funcs->appendf("\tvec3 halfDir = vec3(normalize(surfaceToLight + vec3(0, 0, 1)));\n"); + + funcs->appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n", + fKSVar->getName().c_str(), fShininessVar->getName().c_str()); + funcs->appendf("\treturn vec4(lightColor * clamp(colorScale, 0, 1), 1);\n"); + funcs->appendf("}\n"); +} + +void GrGLSpecularLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + INHERITED::setData(gl, texture, data, stageNum); + const GrSpecularLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform1f(fKSLocation, effect.ks())); + GR_GL_CALL(gl, Uniform1f(fShininessLocation, effect.shininess())); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLLight::emitLightColor(SkString* builder, const char *surfaceToLight) const { + builder->append(fColorVar->getName().c_str()); +} + +void GrGLLight::setupVariables(GrGLShaderBuilder* state, int stage) { + fColorVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uLightColor", stage); +} + +void GrGLLight::initUniforms(const GrGLInterface* gl, int programID) { + GR_GL_CALL_RET(gl, fColorVarLocation, + GetUniformLocation(programID, fColorVar->getName().c_str())); +} + +void GrGLLight::setData(const GrGLInterface* gl, const SkLight* light) const { + setUniformPoint3(gl, fColorVarLocation, light->color() * SkScalarInvert(SkIntToScalar(255))); +} + +GrGLLight* SkDistantLight::createGLLight() const { + return new GrGLDistantLight(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLDistantLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fDirectionVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType, + "uLightDirection", stage); +} + +void GrGLDistantLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fDirectionLocation, + GetUniformLocation(programID, fDirectionVar->getName().c_str())); +} + +void GrGLDistantLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kDistant_LightType); + const SkDistantLight* distantLight = static_cast(light); + setUniformPoint3(gl, fDirectionLocation, distantLight->direction()); +} + +void GrGLDistantLight::emitSurfaceToLight(SkString* builder, + const char* z) const { + builder->append(fDirectionVar->getName().c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLPointLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fLocationVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType, + "uLightLocation", stage); + state->addVarying(kFloat_GrSLType, "Height", stage, &fHeightVaryingName); +} + +void GrGLPointLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fLocationLocation, + GetUniformLocation(programID, fLocationVar->getName().c_str())); +} + +void GrGLPointLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kPoint_LightType); + const SkPointLight* pointLight = static_cast(light); + setUniformPoint3(gl, fLocationLocation, pointLight->location()); +} + +void GrGLPointLight::emitVS(SkString* builder) const { + // Compute viewport height from the Y scale of the matrix. + builder->appendf("\t\t%s = -2.0 / uViewM[1][1];\n", fHeightVaryingName); +} + +void GrGLPointLight::emitSurfaceToLight(SkString* builder, + const char* z) const { + builder->appendf( + "normalize(%s - vec3(gl_FragCoord.x, %s - gl_FragCoord.y, %s))", + fLocationVar->getName().c_str(), fHeightVaryingName, z); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLSpotLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fLocationVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uLightLocation", stage); + fExponentVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uExponent", stage); + fCosInnerConeAngleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uCosInnerConeAngle", stage); + fCosOuterConeAngleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uCosOuterConeAngle", stage); + fConeScaleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uConeScale", stage); + fSVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uS", stage); + state->addVarying(kFloat_GrSLType, "Height", stage, &fHeightVaryingName); +} + +void GrGLSpotLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fLocationLocation, + GetUniformLocation(programID, fLocationVar->getName().c_str())); + GR_GL_CALL_RET(gl, fExponentLocation, + GetUniformLocation(programID, fExponentVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosInnerConeAngleLocation, + GetUniformLocation(programID, fCosInnerConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosOuterConeAngleLocation, + GetUniformLocation(programID, fCosOuterConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosOuterConeAngleLocation, + GetUniformLocation(programID, fCosOuterConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fConeScaleLocation, + GetUniformLocation(programID, fConeScaleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fSLocation, + GetUniformLocation(programID, fSVar->getName().c_str())); +} + +void GrGLSpotLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kSpot_LightType); + const SkSpotLight* spotLight = static_cast(light); + setUniformPoint3(gl, fLocationLocation, spotLight->location()); + GR_GL_CALL(gl, Uniform1f(fExponentLocation, spotLight->specularExponent())); + GR_GL_CALL(gl, Uniform1f(fCosInnerConeAngleLocation, spotLight->cosInnerConeAngle())); + GR_GL_CALL(gl, Uniform1f(fCosOuterConeAngleLocation, spotLight->cosOuterConeAngle())); + GR_GL_CALL(gl, Uniform1f(fConeScaleLocation, spotLight->coneScale())); + setUniformPoint3(gl, fSLocation, spotLight->s()); +} + +void GrGLSpotLight::emitVS(SkString* builder) const { + // Compute viewport height from the Y scale of the matrix. + builder->appendf("\t\t%s = -2.0 / uViewM[1][1];\n", fHeightVaryingName); +} + +void GrGLSpotLight::emitFuncs(SkString* builder) const { + builder->appendf("vec3 lightColor(vec3 surfaceToLight) {\n"); + builder->appendf("\tfloat cosAngle = -dot(surfaceToLight, %s);\n", fSVar->getName().c_str()); + builder->appendf("\tif (cosAngle < %s) {\n", fCosOuterConeAngleVar->getName().c_str()); + builder->appendf("\t\treturn vec3(0);\n"); + builder->appendf("\t}\n"); + builder->appendf("\tfloat scale = pow(cosAngle, %s);\n", fExponentVar->getName().c_str()); + builder->appendf("\tif (cosAngle < %s) {\n", fCosInnerConeAngleVar->getName().c_str()); + builder->appendf("\t\treturn %s * scale * (cosAngle - %s) * %s;\n", fColorVar->getName().c_str(), fCosOuterConeAngleVar->getName().c_str(), fConeScaleVar->getName().c_str()); + builder->appendf("\t}\n"); + builder->appendf("\treturn %s;\n", fColorVar->getName().c_str()); + builder->appendf("}\n"); +} + +void GrGLSpotLight::emitSurfaceToLight(SkString* builder, const char* z) const { + builder->appendf("normalize(%s - vec3(gl_FragCoord.x, %s - gl_FragCoord.y, %s))", fLocationVar->getName().c_str(), fHeightVaryingName, z); +} + +void GrGLSpotLight::emitLightColor(SkString* builder, const char *surfaceToLight) const { + builder->appendf("lightColor(%s)", surfaceToLight); +} + SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiffuseLightingImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpecularLightingImageFilter) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index fc73f0d02..e5227a275 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1424,6 +1424,33 @@ void SkGpuDevice::internalDrawBitmap(const SkDraw& draw, fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m); } +namespace { + +void apply_custom_stage(GrContext* context, + GrTexture* srcTexture, + GrTexture* dstTexture, + const GrRect& rect, + GrCustomStage* stage) { + SkASSERT(srcTexture && srcTexture->getContext() == context); + GrAutoMatrix avm(context, GrMatrix::I()); + GrContext::AutoRenderTarget art(context, dstTexture->asRenderTarget()); + GrClip oldClip = context->getClip(); + context->setClip(rect); + + GrMatrix sampleM; + sampleM.setIDiv(srcTexture->width(), srcTexture->height()); + GrPaint paint; + paint.reset(); + paint.textureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter); + paint.textureSampler(0)->reset(sampleM); + paint.textureSampler(0)->setCustomStage(stage); + paint.setTexture(0, srcTexture); + context->drawRect(paint, rect); + context->setClip(oldClip); +} + +}; + static GrTexture* filter_texture(GrContext* context, GrTexture* texture, SkImageFilter* filter, const GrRect& rect) { GrAssert(filter); @@ -1436,8 +1463,14 @@ static GrTexture* filter_texture(GrContext* context, GrTexture* texture, desc.fWidth = SkScalarCeilToInt(rect.width()); desc.fHeight = SkScalarCeilToInt(rect.height()); desc.fConfig = kRGBA_8888_PM_GrPixelConfig; + GrCustomStage* stage; - if (filter->asABlur(&blurSize)) { + if (filter->asNewCustomStage(&stage)) { + GrAutoScratchTexture dst(context, desc); + apply_custom_stage(context, texture, dst.texture(), rect, stage); + texture = dst.detach(); + stage->unref(); + } else if (filter->asABlur(&blurSize)) { GrAutoScratchTexture temp1, temp2; texture = context->gaussianBlur(texture, &temp1, &temp2, rect, blurSize.width(), @@ -1564,7 +1597,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) { SkSize size; SkISize radius; - if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) { + + if (!filter->asNewCustomStage(NULL) && + !filter->asABlur(&size) && + !filter->asADilate(&radius) && + !filter->asAnErode(&radius)) { return false; } return true; diff --git a/src/gpu/gl/GrGpuGL_unittest.cpp b/src/gpu/gl/GrGpuGL_unittest.cpp index 7f71f4820..82804450b 100644 --- a/src/gpu/gl/GrGpuGL_unittest.cpp +++ b/src/gpu/gl/GrGpuGL_unittest.cpp @@ -3,6 +3,7 @@ #include "effects/GrConvolutionEffect.h" #include "effects/GrGradientEffects.h" #include "effects/GrMorphologyEffect.h" +#include "SkLightingImageFilter.h" #include "GrProgramStageFactory.h" #include "GrRandom.h" @@ -23,6 +24,10 @@ bool random_bool(GrRandom* r) { return r->nextF() > .5f; } +SkPoint3 random_point3(GrRandom* r) { + return SkPoint3(r->nextF(), r->nextF(), r->nextF()); +} + typedef GrGLProgram::StageDesc StageDesc; // TODO: Effects should be able to register themselves for inclusion in the // randomly generated shaders. They should be able to configure themselves @@ -35,6 +40,12 @@ GrCustomStage* create_random_effect(StageDesc* stageDesc, kDilate_EffectType, kRadialGradient_EffectType, kRadial2Gradient_EffectType, + kDiffuseDistant_EffectType, + kDiffusePoint_EffectType, + kDiffuseSpot_EffectType, + kSpecularDistant_EffectType, + kSpecularPoint_EffectType, + kSpecularSpot_EffectType, kSweepGradient_EffectType, kEffectCount @@ -107,6 +118,86 @@ GrCustomStage* create_random_effect(StageDesc* stageDesc, case kSweepGradient_EffectType: { return SkNEW(GrSweepGradient); } + case kDiffuseDistant_EffectType: { + SkPoint3 direction = random_point3(random); + direction.normalize(); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar kd = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateDistantLitDiffuse(direction, lightColor, surfaceScale, kd)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kDiffusePoint_EffectType: { + SkPoint3 location = random_point3(random); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar kd = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreatePointLitDiffuse(location, lightColor, surfaceScale, kd)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kDiffuseSpot_EffectType: { + SkPoint3 location = random_point3(random); + SkPoint3 target = random_point3(random); + SkScalar cutoffAngle = SkFloatToScalar(random->nextF()); + SkScalar specularExponent = SkFloatToScalar(random->nextF()); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateSpotLitSpecular( + location, target, specularExponent, cutoffAngle, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularDistant_EffectType: { + SkPoint3 direction = random_point3(random); + direction.normalize(); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateDistantLitSpecular(direction, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularPoint_EffectType: { + SkPoint3 location = random_point3(random); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreatePointLitSpecular(location, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularSpot_EffectType: { + SkPoint3 location = random_point3(random); + SkPoint3 target = random_point3(random); + SkScalar cutoffAngle = SkFloatToScalar(random->nextF()); + SkScalar specularExponent = SkFloatToScalar(random->nextF()); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateSpotLitSpecular( + location, target, specularExponent, cutoffAngle, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } default: GrCrash("Unexpected custom effect type"); }