Move convolution from code in GrGLProgram to new GrConvolutionEffect
class. This is the first test of the new Ganesh shader pipeline.

Also includes some cleanup of the gpu.gyp file: added src/gpu, allowing
us to remove ../ from many #include directives.

http://codereview.appspot.com/6199053/



git-svn-id: http://skia.googlecode.com/svn/trunk@3887 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
new file mode 100644
index 0000000..e4ddd29
--- /dev/null
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrConvolutionEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrProgramStageFactory.h"
+
+/////////////////////////////////////////////////////////////////////
+
+class GrGLConvolutionEffect : public GrGLProgramStage {
+
+public:
+
+    GrGLConvolutionEffect(GrConvolutionEffect* data);
+    virtual const char* name() const SK_OVERRIDE;
+    virtual void setupVSUnis(VarArray* vsUnis, int stage) SK_OVERRIDE;
+    virtual void setupFSUnis(VarArray* fsUnis, int stage) SK_OVERRIDE;
+    virtual void emitVS(GrStringBuilder* code,
+                        const char* vertexCoords) SK_OVERRIDE;
+    virtual void emitFS(GrStringBuilder* code,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const char* samplerName,
+                        const char* sampleCoords) SK_OVERRIDE;
+    virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE;
+
+    virtual void setData(const GrGLInterface*, GrCustomStage*,
+                         const GrGLTexture*) SK_OVERRIDE;
+
+protected:
+
+    GrConvolutionEffect* fData;
+ 
+    GrGLShaderVar* fKernelVar;
+    GrGLShaderVar* fImageIncrementVar;
+ 
+    GrGLint fKernelLocation;
+    GrGLint fImageIncrementLocation;
+
+private:
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLConvolutionEffect::GrGLConvolutionEffect(GrConvolutionEffect* data)
+    : fData(data)
+    , fKernelVar(NULL)
+    , fImageIncrementVar(NULL)
+    , fKernelLocation(0)
+    , fImageIncrementLocation(0)
+{
+
+}
+
+const char* GrGLConvolutionEffect::name() const SK_OVERRIDE {
+    return fData->name();
+}
+
+void GrGLConvolutionEffect::setupVSUnis(VarArray* vsUnis,
+                                        int stage) SK_OVERRIDE {
+    fImageIncrementVar = &vsUnis->push_back();
+    fImageIncrementVar->setType(kVec2f_GrSLType);
+    fImageIncrementVar->setTypeModifier(
+        GrGLShaderVar::kUniform_TypeModifier);
+    (*fImageIncrementVar->accessName()) = "uImageIncrement";
+    fImageIncrementVar->accessName()->appendS32(stage);
+    fImageIncrementVar->setEmitPrecision(true);
+
+    fImageIncrementLocation = kUseUniform;
+}
+
+void GrGLConvolutionEffect::setupFSUnis(VarArray* fsUnis,
+                                        int stage) SK_OVERRIDE {
+    fKernelVar = &fsUnis->push_back();
+    fKernelVar->setType(kFloat_GrSLType);
+    fKernelVar->setTypeModifier(
+        GrGLShaderVar::kUniform_TypeModifier);
+    fKernelVar->setArrayCount(fData->fKernelWidth);
+    (*fKernelVar->accessName()) = "uKernel";
+    fKernelVar->accessName()->appendS32(stage);
+
+    fKernelLocation = kUseUniform;
+
+    // Image increment is used in both vertex & fragment shaders.
+    fsUnis->push_back(*fImageIncrementVar).setEmitPrecision(false);
+}
+
+void GrGLConvolutionEffect::emitVS(GrStringBuilder* code,
+                        const char* vertexCoords) SK_OVERRIDE {
+    float scale = (fData->fKernelWidth - 1) * 0.5f;
+    code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n",
+                  vertexCoords, scale, scale,
+                  fImageIncrementVar->getName().c_str());
+
+}
+
+void GrGLConvolutionEffect::emitFS(GrStringBuilder* code,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const char* samplerName,
+                        const char* sampleCoords) SK_OVERRIDE {
+    const char* texFunc = "texture2D";
+    bool complexCoord = false;
+
+    GrStringBuilder modulate;
+    if (NULL != inputColor) {
+        modulate.printf(" * %s", inputColor);
+    }
+
+    // Creates the string "kernel[i]" with workarounds for
+    // possible driver bugs
+    GrStringBuilder kernelIndex;
+    fKernelVar->appendArrayAccess("i", &kernelIndex);
+
+    code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
+    code->appendf("\t\tvec2 coord = %s;\n", sampleCoords);
+    code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n",
+                  fData->fKernelWidth);
+
+    code->appendf("\t\t\tsum += ");
+    this->emitTextureLookup(code, samplerName, "coord");
+    code->appendf(" * %s;\n", kernelIndex.c_str());
+
+    code->appendf("\t\t\tcoord += %s;\n",
+                  fImageIncrementVar->getName().c_str());
+    code->appendf("\t\t}\n");
+    code->appendf("\t\t%s = sum%s;\n", outputColor, modulate.c_str());
+}
+
+void GrGLConvolutionEffect::initUniforms(const GrGLInterface* gl,
+                                         int programID) SK_OVERRIDE {
+    GR_GL_CALL_RET(gl, fKernelLocation,
+        GetUniformLocation(programID, fKernelVar->getName().c_str()));
+    GR_GL_CALL_RET(gl, fImageIncrementLocation,
+        GetUniformLocation(programID,
+            fImageIncrementVar->getName().c_str()));
+}
+
+void GrGLConvolutionEffect::setData(const GrGLInterface* gl,
+                                    GrCustomStage* data,
+                                    const GrGLTexture* texture) SK_OVERRIDE {
+    fData = static_cast<GrConvolutionEffect*>(data);
+    GR_GL_CALL(gl, Uniform1fv(fKernelLocation,
+                              fData->fKernelWidth,
+                              fData->fKernel));
+    float imageIncrement[2] = { 0 };
+    switch (fData->fDirection) {
+        case GrSamplerState::kX_FilterDirection:
+            imageIncrement[0] = 1.0f / texture->width();
+            break;
+        case GrSamplerState::kY_FilterDirection:
+            imageIncrement[1] = 1.0f / texture->width();
+            break;
+        default:
+            GrCrash("Unknown filter direction.");
+    }
+    GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement));
+}
+
+/////////////////////////////////////////////////////////////////////
+// TODO: stageKey() and sEffectId are the only non-boilerplate in
+// this class; we ought to be able to templatize?
+
+class GrConvolutionEffectFactory : public GrProgramStageFactory {
+
+public:
+
+    virtual ~GrConvolutionEffectFactory();
+
+    virtual uint16_t stageKey(const GrCustomStage* s) SK_OVERRIDE;
+    virtual GrGLProgramStage* createGLInstance(GrCustomStage* s) SK_OVERRIDE;
+
+    static GrConvolutionEffectFactory* getInstance();
+
+protected:
+
+    GrConvolutionEffectFactory();
+
+    // TODO: find a more reliable installation than hand-coding
+    // id values like '1'. 
+    static const int sEffectId = 1;
+
+private:
+
+    typedef GrProgramStageFactory INHERITED;
+};
+
+GrConvolutionEffectFactory::~GrConvolutionEffectFactory() {
+
+}
+
+uint16_t GrConvolutionEffectFactory::stageKey(const GrCustomStage* s)
+    SK_OVERRIDE {
+    const GrConvolutionEffect* c =
+        static_cast<const GrConvolutionEffect*>(s);
+    GrAssert(c->width() < 256);
+    return (sEffectId << 8) | (c->width() & 0xff);
+}
+
+GrGLProgramStage* GrConvolutionEffectFactory::createGLInstance(
+    GrCustomStage* s) SK_OVERRIDE {
+    return new GrGLConvolutionEffect(static_cast<GrConvolutionEffect*>(s));
+}
+
+GrConvolutionEffectFactory* GrConvolutionEffectFactory::getInstance() {
+    static GrConvolutionEffectFactory* instance =
+        new GrConvolutionEffectFactory;
+    return instance;
+}
+
+GrConvolutionEffectFactory::GrConvolutionEffectFactory() {
+
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrConvolutionEffect::GrConvolutionEffect(
+        GrSamplerState::FilterDirection direction,
+        unsigned int kernelWidth,
+        const float* kernel)
+    : fDirection (direction)
+    , fKernelWidth (kernelWidth) {
+    GrAssert(kernelWidth <= MAX_KERNEL_WIDTH);
+    for (unsigned int i = 0; i < kernelWidth; i++) {
+        fKernel[i] = kernel[i];
+    }
+}
+
+GrConvolutionEffect::~GrConvolutionEffect() {
+
+}
+
+const char* GrConvolutionEffect::name() const {
+    return "Convolution";
+}
+
+GrProgramStageFactory* GrConvolutionEffect::getFactory() const {
+    return GrConvolutionEffectFactory::getInstance();
+}
+
+bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const {
+    const GrConvolutionEffect* s =
+        static_cast<const GrConvolutionEffect*>(sBase);
+
+    return (fKernelWidth == s->fKernelWidth &&
+            fDirection == s->fDirection &&
+            0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float)));
+}
+
+
+