blob: d63b0238f06f01d9583efc0410ed1be61b76ada6 [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
tomhudson@google.comf1d88062012-05-10 12:43:21 +000061const char* GrGLConvolutionEffect::name() const {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000062 return fData->name();
63}
64
65void GrGLConvolutionEffect::setupVSUnis(VarArray* vsUnis,
tomhudson@google.comf1d88062012-05-10 12:43:21 +000066 int stage) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000067 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,
tomhudson@google.comf1d88062012-05-10 12:43:21 +000079 int stage) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000080 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,
tomhudson@google.comf1d88062012-05-10 12:43:21 +000095 const char* vertexCoords) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000096 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,
tomhudson@google.comf1d88062012-05-10 12:43:21 +0000107 const char* sampleCoords) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000108 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,
tomhudson@google.comf1d88062012-05-10 12:43:21 +0000137 int programID) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000138 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,
tomhudson@google.comf1d88062012-05-10 12:43:21 +0000147 const GrGLTexture* texture) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000148 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
tomhudson@google.comf1d88062012-05-10 12:43:21 +0000198uint16_t GrConvolutionEffectFactory::stageKey(const GrCustomStage* s) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000199 const GrConvolutionEffect* c =
200 static_cast<const GrConvolutionEffect*>(s);
201 GrAssert(c->width() < 256);
202 return (sEffectId << 8) | (c->width() & 0xff);
203}
204
205GrGLProgramStage* GrConvolutionEffectFactory::createGLInstance(
tomhudson@google.comf1d88062012-05-10 12:43:21 +0000206 GrCustomStage* s) {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000207 return new GrGLConvolutionEffect(static_cast<GrConvolutionEffect*>(s));
208}
209
210GrConvolutionEffectFactory* GrConvolutionEffectFactory::getInstance() {
211 static GrConvolutionEffectFactory* instance =
212 new GrConvolutionEffectFactory;
213 return instance;
214}
215
216GrConvolutionEffectFactory::GrConvolutionEffectFactory() {
217
218}
219
220/////////////////////////////////////////////////////////////////////
221
222GrConvolutionEffect::GrConvolutionEffect(
223 GrSamplerState::FilterDirection direction,
224 unsigned int kernelWidth,
225 const float* kernel)
226 : fDirection (direction)
227 , fKernelWidth (kernelWidth) {
228 GrAssert(kernelWidth <= MAX_KERNEL_WIDTH);
229 for (unsigned int i = 0; i < kernelWidth; i++) {
230 fKernel[i] = kernel[i];
231 }
232}
233
234GrConvolutionEffect::~GrConvolutionEffect() {
235
236}
237
238const char* GrConvolutionEffect::name() const {
239 return "Convolution";
240}
241
242GrProgramStageFactory* GrConvolutionEffect::getFactory() const {
243 return GrConvolutionEffectFactory::getInstance();
244}
245
246bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const {
247 const GrConvolutionEffect* s =
248 static_cast<const GrConvolutionEffect*>(sBase);
249
250 return (fKernelWidth == s->fKernelWidth &&
251 fDirection == s->fDirection &&
252 0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float)));
253}
254
255
256