blob: e4ddd2932bcf3d79fba42d992a304a875b4ad1ca [file] [log] [blame]
tomhudson@google.comd8f856c2012-05-10 12:13:36 +00001/*
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(GrConvolutionEffect* data);
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*, GrCustomStage*,
34 const GrGLTexture*) SK_OVERRIDE;
35
36protected:
37
38 GrConvolutionEffect* fData;
39
40 GrGLShaderVar* fKernelVar;
41 GrGLShaderVar* fImageIncrementVar;
42
43 GrGLint fKernelLocation;
44 GrGLint fImageIncrementLocation;
45
46private:
47
48 typedef GrGLProgramStage INHERITED;
49};
50
51GrGLConvolutionEffect::GrGLConvolutionEffect(GrConvolutionEffect* data)
52 : fData(data)
53 , fKernelVar(NULL)
54 , fImageIncrementVar(NULL)
55 , fKernelLocation(0)
56 , fImageIncrementLocation(0)
57{
58
59}
60
61const char* GrGLConvolutionEffect::name() const SK_OVERRIDE {
62 return fData->name();
63}
64
65void GrGLConvolutionEffect::setupVSUnis(VarArray* vsUnis,
66 int stage) SK_OVERRIDE {
67 fImageIncrementVar = &vsUnis->push_back();
68 fImageIncrementVar->setType(kVec2f_GrSLType);
69 fImageIncrementVar->setTypeModifier(
70 GrGLShaderVar::kUniform_TypeModifier);
71 (*fImageIncrementVar->accessName()) = "uImageIncrement";
72 fImageIncrementVar->accessName()->appendS32(stage);
73 fImageIncrementVar->setEmitPrecision(true);
74
75 fImageIncrementLocation = kUseUniform;
76}
77
78void GrGLConvolutionEffect::setupFSUnis(VarArray* fsUnis,
79 int stage) SK_OVERRIDE {
80 fKernelVar = &fsUnis->push_back();
81 fKernelVar->setType(kFloat_GrSLType);
82 fKernelVar->setTypeModifier(
83 GrGLShaderVar::kUniform_TypeModifier);
84 fKernelVar->setArrayCount(fData->fKernelWidth);
85 (*fKernelVar->accessName()) = "uKernel";
86 fKernelVar->accessName()->appendS32(stage);
87
88 fKernelLocation = kUseUniform;
89
90 // Image increment is used in both vertex & fragment shaders.
91 fsUnis->push_back(*fImageIncrementVar).setEmitPrecision(false);
92}
93
94void GrGLConvolutionEffect::emitVS(GrStringBuilder* code,
95 const char* vertexCoords) SK_OVERRIDE {
96 float scale = (fData->fKernelWidth - 1) * 0.5f;
97 code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n",
98 vertexCoords, scale, scale,
99 fImageIncrementVar->getName().c_str());
100
101}
102
103void GrGLConvolutionEffect::emitFS(GrStringBuilder* code,
104 const char* outputColor,
105 const char* inputColor,
106 const char* samplerName,
107 const char* sampleCoords) SK_OVERRIDE {
108 const char* texFunc = "texture2D";
109 bool complexCoord = false;
110
111 GrStringBuilder modulate;
112 if (NULL != inputColor) {
113 modulate.printf(" * %s", inputColor);
114 }
115
116 // Creates the string "kernel[i]" with workarounds for
117 // possible driver bugs
118 GrStringBuilder kernelIndex;
119 fKernelVar->appendArrayAccess("i", &kernelIndex);
120
121 code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
122 code->appendf("\t\tvec2 coord = %s;\n", sampleCoords);
123 code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n",
124 fData->fKernelWidth);
125
126 code->appendf("\t\t\tsum += ");
127 this->emitTextureLookup(code, samplerName, "coord");
128 code->appendf(" * %s;\n", kernelIndex.c_str());
129
130 code->appendf("\t\t\tcoord += %s;\n",
131 fImageIncrementVar->getName().c_str());
132 code->appendf("\t\t}\n");
133 code->appendf("\t\t%s = sum%s;\n", outputColor, modulate.c_str());
134}
135
136void GrGLConvolutionEffect::initUniforms(const GrGLInterface* gl,
137 int programID) SK_OVERRIDE {
138 GR_GL_CALL_RET(gl, fKernelLocation,
139 GetUniformLocation(programID, fKernelVar->getName().c_str()));
140 GR_GL_CALL_RET(gl, fImageIncrementLocation,
141 GetUniformLocation(programID,
142 fImageIncrementVar->getName().c_str()));
143}
144
145void GrGLConvolutionEffect::setData(const GrGLInterface* gl,
146 GrCustomStage* data,
147 const GrGLTexture* texture) SK_OVERRIDE {
148 fData = static_cast<GrConvolutionEffect*>(data);
149 GR_GL_CALL(gl, Uniform1fv(fKernelLocation,
150 fData->fKernelWidth,
151 fData->fKernel));
152 float imageIncrement[2] = { 0 };
153 switch (fData->fDirection) {
154 case GrSamplerState::kX_FilterDirection:
155 imageIncrement[0] = 1.0f / texture->width();
156 break;
157 case GrSamplerState::kY_FilterDirection:
158 imageIncrement[1] = 1.0f / texture->width();
159 break;
160 default:
161 GrCrash("Unknown filter direction.");
162 }
163 GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement));
164}
165
166/////////////////////////////////////////////////////////////////////
167// TODO: stageKey() and sEffectId are the only non-boilerplate in
168// this class; we ought to be able to templatize?
169
170class GrConvolutionEffectFactory : public GrProgramStageFactory {
171
172public:
173
174 virtual ~GrConvolutionEffectFactory();
175
176 virtual uint16_t stageKey(const GrCustomStage* s) SK_OVERRIDE;
177 virtual GrGLProgramStage* createGLInstance(GrCustomStage* s) SK_OVERRIDE;
178
179 static GrConvolutionEffectFactory* getInstance();
180
181protected:
182
183 GrConvolutionEffectFactory();
184
185 // TODO: find a more reliable installation than hand-coding
186 // id values like '1'.
187 static const int sEffectId = 1;
188
189private:
190
191 typedef GrProgramStageFactory INHERITED;
192};
193
194GrConvolutionEffectFactory::~GrConvolutionEffectFactory() {
195
196}
197
198uint16_t GrConvolutionEffectFactory::stageKey(const GrCustomStage* s)
199 SK_OVERRIDE {
200 const GrConvolutionEffect* c =
201 static_cast<const GrConvolutionEffect*>(s);
202 GrAssert(c->width() < 256);
203 return (sEffectId << 8) | (c->width() & 0xff);
204}
205
206GrGLProgramStage* GrConvolutionEffectFactory::createGLInstance(
207 GrCustomStage* s) SK_OVERRIDE {
208 return new GrGLConvolutionEffect(static_cast<GrConvolutionEffect*>(s));
209}
210
211GrConvolutionEffectFactory* GrConvolutionEffectFactory::getInstance() {
212 static GrConvolutionEffectFactory* instance =
213 new GrConvolutionEffectFactory;
214 return instance;
215}
216
217GrConvolutionEffectFactory::GrConvolutionEffectFactory() {
218
219}
220
221/////////////////////////////////////////////////////////////////////
222
223GrConvolutionEffect::GrConvolutionEffect(
224 GrSamplerState::FilterDirection direction,
225 unsigned int kernelWidth,
226 const float* kernel)
227 : fDirection (direction)
228 , fKernelWidth (kernelWidth) {
229 GrAssert(kernelWidth <= MAX_KERNEL_WIDTH);
230 for (unsigned int i = 0; i < kernelWidth; i++) {
231 fKernel[i] = kernel[i];
232 }
233}
234
235GrConvolutionEffect::~GrConvolutionEffect() {
236
237}
238
239const char* GrConvolutionEffect::name() const {
240 return "Convolution";
241}
242
243GrProgramStageFactory* GrConvolutionEffect::getFactory() const {
244 return GrConvolutionEffectFactory::getInstance();
245}
246
247bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const {
248 const GrConvolutionEffect* s =
249 static_cast<const GrConvolutionEffect*>(sBase);
250
251 return (fKernelWidth == s->fKernelWidth &&
252 fDirection == s->fDirection &&
253 0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float)));
254}
255
256
257