GrConvolutionEffect.cpp revision ae4f96a9e06df44f70c3d5f7324f5a7fabcd1026
1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrConvolutionEffect.h"
9#include "gl/GrGLProgramStage.h"
10#include "gl/GrGLSL.h"
11#include "gl/GrGLTexture.h"
12#include "GrProgramStageFactory.h"
13
14/////////////////////////////////////////////////////////////////////
15
16class GrGLConvolutionEffect : public GrGLProgramStage {
17
18public:
19
20    GrGLConvolutionEffect(const GrCustomStage* stage);
21    virtual const char* name() const SK_OVERRIDE;
22    virtual void setupVSUnis(VarArray* vsUnis, int stage) SK_OVERRIDE;
23    virtual void setupFSUnis(VarArray* fsUnis, int stage) SK_OVERRIDE;
24    virtual void emitVS(GrStringBuilder* code,
25                        const char* vertexCoords) SK_OVERRIDE;
26    virtual void emitFS(GrStringBuilder* code,
27                        const char* outputColor,
28                        const char* inputColor,
29                        const char* samplerName,
30                        const char* sampleCoords) SK_OVERRIDE;
31    virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE;
32
33    virtual void setData(const GrGLInterface*, const GrCustomStage*,
34                         const GrGLTexture*) SK_OVERRIDE;
35
36    static inline StageKey GenKey(const GrCustomStage* s);
37
38protected:
39
40    int            fKernelWidth;
41    GrGLShaderVar* fKernelVar;
42    GrGLShaderVar* fImageIncrementVar;
43
44    GrGLint fKernelLocation;
45    GrGLint fImageIncrementLocation;
46
47private:
48
49    typedef GrGLProgramStage INHERITED;
50};
51
52GrGLConvolutionEffect::GrGLConvolutionEffect(const GrCustomStage* data)
53    : fKernelVar(NULL)
54    , fImageIncrementVar(NULL)
55    , fKernelLocation(0)
56    , fImageIncrementLocation(0) {
57    fKernelWidth = static_cast<const GrConvolutionEffect*>(data)->width();
58}
59
60const char* GrGLConvolutionEffect::name() const {
61    return GrConvolutionEffect::Name();
62}
63
64void GrGLConvolutionEffect::setupVSUnis(VarArray* vsUnis,
65                                        int stage) {
66    fImageIncrementVar = &vsUnis->push_back();
67    fImageIncrementVar->setType(kVec2f_GrSLType);
68    fImageIncrementVar->setTypeModifier(
69        GrGLShaderVar::kUniform_TypeModifier);
70    (*fImageIncrementVar->accessName()) = "uImageIncrement";
71    fImageIncrementVar->accessName()->appendS32(stage);
72    fImageIncrementVar->setEmitPrecision(true);
73
74    fImageIncrementLocation = kUseUniform;
75}
76
77void GrGLConvolutionEffect::setupFSUnis(VarArray* fsUnis,
78                                        int stage) {
79    fKernelVar = &fsUnis->push_back();
80    fKernelVar->setType(kFloat_GrSLType);
81    fKernelVar->setTypeModifier(
82        GrGLShaderVar::kUniform_TypeModifier);
83    fKernelVar->setArrayCount(fKernelWidth);
84    (*fKernelVar->accessName()) = "uKernel";
85    fKernelVar->accessName()->appendS32(stage);
86
87    fKernelLocation = kUseUniform;
88
89    // Image increment is used in both vertex & fragment shaders.
90    fsUnis->push_back(*fImageIncrementVar).setEmitPrecision(false);
91}
92
93void GrGLConvolutionEffect::emitVS(GrStringBuilder* code,
94                        const char* vertexCoords) {
95    float scale = (fKernelWidth - 1) * 0.5f;
96    code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n",
97                  vertexCoords, scale, scale,
98                  fImageIncrementVar->getName().c_str());
99
100}
101
102void GrGLConvolutionEffect::emitFS(GrStringBuilder* code,
103                        const char* outputColor,
104                        const char* inputColor,
105                        const char* samplerName,
106                        const char* sampleCoords) {
107    const char* texFunc = "texture2D";
108    bool complexCoord = false;
109
110    GrStringBuilder modulate;
111    if (NULL != inputColor) {
112        modulate.printf(" * %s", inputColor);
113    }
114
115    // Creates the string "kernel[i]" with workarounds for
116    // possible driver bugs
117    GrStringBuilder kernelIndex;
118    fKernelVar->appendArrayAccess("i", &kernelIndex);
119
120    code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
121    code->appendf("\t\tvec2 coord = %s;\n", sampleCoords);
122    code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n",
123                  fKernelWidth);
124
125    code->appendf("\t\t\tsum += ");
126    this->emitTextureLookup(code, samplerName, "coord");
127    code->appendf(" * %s;\n", kernelIndex.c_str());
128
129    code->appendf("\t\t\tcoord += %s;\n",
130                  fImageIncrementVar->getName().c_str());
131    code->appendf("\t\t}\n");
132    code->appendf("\t\t%s = sum%s;\n", outputColor, modulate.c_str());
133}
134
135void GrGLConvolutionEffect::initUniforms(const GrGLInterface* gl,
136                                         int programID) {
137    GR_GL_CALL_RET(gl, fKernelLocation,
138        GetUniformLocation(programID, fKernelVar->getName().c_str()));
139    GR_GL_CALL_RET(gl, fImageIncrementLocation,
140        GetUniformLocation(programID,
141            fImageIncrementVar->getName().c_str()));
142}
143
144void GrGLConvolutionEffect::setData(const GrGLInterface* gl,
145                                    const GrCustomStage* data,
146                                    const GrGLTexture* texture) {
147    const GrConvolutionEffect* conv =
148        static_cast<const GrConvolutionEffect*>(data);
149    // the code we generated was for a specific kernel width
150    GrAssert(conv->width() == fKernelWidth);
151    GR_GL_CALL(gl, Uniform1fv(fKernelLocation,
152                              fKernelWidth,
153                              conv->kernel()));
154    float imageIncrement[2] = { 0 };
155    switch (conv->direction()) {
156        case GrSamplerState::kX_FilterDirection:
157            imageIncrement[0] = 1.0f / texture->width();
158            break;
159        case GrSamplerState::kY_FilterDirection:
160            imageIncrement[1] = 1.0f / texture->width();
161            break;
162        default:
163            GrCrash("Unknown filter direction.");
164    }
165    GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement));
166}
167
168GrGLProgramStage::StageKey GrGLConvolutionEffect::GenKey(
169                                                    const GrCustomStage* s) {
170    return static_cast<const GrConvolutionEffect*>(s)->width();
171}
172
173/////////////////////////////////////////////////////////////////////
174
175GrConvolutionEffect::GrConvolutionEffect(
176        GrSamplerState::FilterDirection direction,
177        unsigned int kernelWidth,
178        const float* kernel)
179    : fDirection (direction)
180    , fKernelWidth (kernelWidth) {
181    GrAssert(kernelWidth <= MAX_KERNEL_WIDTH);
182    for (unsigned int i = 0; i < kernelWidth; i++) {
183        fKernel[i] = kernel[i];
184    }
185}
186
187GrConvolutionEffect::~GrConvolutionEffect() {
188
189}
190
191const char* GrConvolutionEffect::name() const {
192    return Name();
193}
194
195
196const GrProgramStageFactory& GrConvolutionEffect::getFactory() const {
197    return GrTProgramStageFactory<GrConvolutionEffect>::getInstance();
198}
199
200bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const {
201    const GrConvolutionEffect* s =
202        static_cast<const GrConvolutionEffect*>(sBase);
203
204    return (fKernelWidth == s->fKernelWidth &&
205            fDirection == s->fDirection &&
206            0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float)));
207}
208
209
210
211