• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrGaussianConvolutionFragmentProcessor.h"
9 
10 #include "GrProxyMove.h"
11 #include "GrTexture.h"
12 #include "GrTextureProxy.h"
13 #include "../private/GrGLSL.h"
14 #include "glsl/GrGLSLFragmentProcessor.h"
15 #include "glsl/GrGLSLFragmentShaderBuilder.h"
16 #include "glsl/GrGLSLProgramDataManager.h"
17 #include "glsl/GrGLSLUniformHandler.h"
18 
19 // For brevity
20 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
21 
22 class GrGLConvolutionEffect : public GrGLSLFragmentProcessor {
23 public:
24     void emitCode(EmitArgs&) override;
25 
26     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
27 
28 protected:
29     void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
30 
31 private:
32     UniformHandle fKernelUni;
33     UniformHandle fImageIncrementUni;
34     UniformHandle fBoundsUni;
35 
36     typedef GrGLSLFragmentProcessor INHERITED;
37 };
38 
emitCode(EmitArgs & args)39 void GrGLConvolutionEffect::emitCode(EmitArgs& args) {
40     const GrGaussianConvolutionFragmentProcessor& ce =
41             args.fFp.cast<GrGaussianConvolutionFragmentProcessor>();
42 
43     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
44     fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
45                                                     kDefault_GrSLPrecision, "ImageIncrement");
46     if (ce.useBounds()) {
47         fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
48                                                 kDefault_GrSLPrecision, "Bounds");
49     }
50 
51     int width = Gr1DKernelEffect::WidthFromRadius(ce.radius());
52 
53     int arrayCount = (width + 3) / 4;
54     SkASSERT(4 * arrayCount >= width);
55 
56     fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec4f_GrSLType,
57                                                  kDefault_GrSLPrecision, "Kernel", arrayCount);
58 
59     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
60     SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
61 
62     fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor);
63 
64     const GrShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni);
65     const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
66 
67     fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc);
68     fragBuilder->codeAppend("vec2 coordSampled = vec2(0, 0);");
69 
70     // Manually unroll loop because some drivers don't; yields 20-30% speedup.
71     const char* kVecSuffix[4] = {".x", ".y", ".z", ".w"};
72     for (int i = 0; i < width; i++) {
73         SkString index;
74         SkString kernelIndex;
75         index.appendS32(i / 4);
76         kernel.appendArrayAccess(index.c_str(), &kernelIndex);
77         kernelIndex.append(kVecSuffix[i & 0x3]);
78 
79         fragBuilder->codeAppend("coordSampled = coord;");
80         if (ce.useBounds()) {
81             // We used to compute a bool indicating whether we're in bounds or not, cast it to a
82             // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems
83             // to have a bug that caused corruption.
84             const char* bounds = uniformHandler->getUniformCStr(fBoundsUni);
85             const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
86 
87             switch (ce.mode()) {
88                 case GrTextureDomain::kClamp_Mode: {
89                     fragBuilder->codeAppendf("coordSampled.%s = clamp(coord.%s, %s.x, %s.y);\n",
90                                              component, component, bounds, bounds);
91                     break;
92                 }
93                 case GrTextureDomain::kRepeat_Mode: {
94                     fragBuilder->codeAppendf("coordSampled.%s = "
95                                              "mod(coord.%s - %s.x, %s.y - %s.x) + %s.x;\n",
96                                              component, component, bounds, bounds, bounds, bounds);
97                     break;
98                 }
99                 case GrTextureDomain::kDecal_Mode: {
100                     fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {",
101                                              component, bounds, component, bounds);
102                     break;
103                 }
104                 default: {
105                     SkFAIL("Unsupported operation.");
106                 }
107             }
108         }
109         fragBuilder->codeAppendf("%s += ", args.fOutputColor);
110         fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coordSampled");
111         fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str());
112         if (GrTextureDomain::kDecal_Mode == ce.mode()) {
113             fragBuilder->codeAppend("}");
114         }
115         fragBuilder->codeAppendf("coord += %s;\n", imgInc);
116     }
117     fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
118 }
119 
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & processor)120 void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
121                                       const GrFragmentProcessor& processor) {
122     const GrGaussianConvolutionFragmentProcessor& conv =
123             processor.cast<GrGaussianConvolutionFragmentProcessor>();
124     GrTexture& texture = *conv.textureSampler(0).peekTexture();
125 
126     float imageIncrement[2] = {0};
127     float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
128     switch (conv.direction()) {
129         case Gr1DKernelEffect::kX_Direction:
130             imageIncrement[0] = 1.0f / texture.width();
131             break;
132         case Gr1DKernelEffect::kY_Direction:
133             imageIncrement[1] = ySign / texture.height();
134             break;
135         default:
136             SkFAIL("Unknown filter direction.");
137     }
138     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
139     if (conv.useBounds()) {
140         float bounds[2] = {0};
141         bounds[0] = conv.bounds()[0];
142         bounds[1] = conv.bounds()[1];
143         if (GrTextureDomain::kClamp_Mode == conv.mode()) {
144             bounds[0] += SK_ScalarHalf;
145             bounds[1] -= SK_ScalarHalf;
146         }
147         if (Gr1DKernelEffect::kX_Direction == conv.direction()) {
148             SkScalar inv = SkScalarInvert(SkIntToScalar(texture.width()));
149             pdman.set2f(fBoundsUni, inv * bounds[0], inv * bounds[1]);
150         } else {
151             SkScalar inv = SkScalarInvert(SkIntToScalar(texture.height()));
152             if (texture.origin() != kTopLeft_GrSurfaceOrigin) {
153                 pdman.set2f(fBoundsUni, 1.0f - (inv * bounds[1]), 1.0f - (inv * bounds[0]));
154             } else {
155                 pdman.set2f(fBoundsUni, inv * bounds[1], inv * bounds[0]);
156             }
157         }
158     }
159     int width = Gr1DKernelEffect::WidthFromRadius(conv.radius());
160 
161     int arrayCount = (width + 3) / 4;
162     SkASSERT(4 * arrayCount >= width);
163     pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
164 }
165 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)166 void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
167                                    GrProcessorKeyBuilder* b) {
168     const GrGaussianConvolutionFragmentProcessor& conv =
169             processor.cast<GrGaussianConvolutionFragmentProcessor>();
170     uint32_t key = conv.radius();
171     key <<= 3;
172     key |= GrGaussianConvolutionFragmentProcessor::kY_Direction == conv.direction() ? 0x4 : 0x0;
173     key |= static_cast<uint32_t>(conv.mode());
174     b->add32(key);
175 }
176 
177 ///////////////////////////////////////////////////////////////////////////////
fill_in_1D_guassian_kernel(float * kernel,int width,float gaussianSigma,int radius)178 static void fill_in_1D_guassian_kernel(float* kernel, int width, float gaussianSigma, int radius) {
179     const float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
180 
181     float sum = 0.0f;
182     for (int i = 0; i < width; ++i) {
183         float x = static_cast<float>(i - radius);
184         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
185         // is dropped here, since we renormalize the kernel below.
186         kernel[i] = sk_float_exp(-x * x * denom);
187         sum += kernel[i];
188     }
189     // Normalize the kernel
190     float scale = 1.0f / sum;
191     for (int i = 0; i < width; ++i) {
192         kernel[i] *= scale;
193     }
194 }
195 
GrGaussianConvolutionFragmentProcessor(sk_sp<GrTextureProxy> proxy,Direction direction,int radius,float gaussianSigma,GrTextureDomain::Mode mode,int bounds[2])196 GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
197                                                             sk_sp<GrTextureProxy> proxy,
198                                                             Direction direction,
199                                                             int radius,
200                                                             float gaussianSigma,
201                                                             GrTextureDomain::Mode mode,
202                                                             int bounds[2])
203         : INHERITED{ModulationFlags(proxy->config()),
204                     GR_PROXY_MOVE(proxy),
205                     direction,
206                     radius}
207         , fMode(mode) {
208     this->initClassID<GrGaussianConvolutionFragmentProcessor>();
209     SkASSERT(radius <= kMaxKernelRadius);
210 
211     fill_in_1D_guassian_kernel(fKernel, this->width(), gaussianSigma, this->radius());
212 
213     memcpy(fBounds, bounds, sizeof(fBounds));
214 }
215 
~GrGaussianConvolutionFragmentProcessor()216 GrGaussianConvolutionFragmentProcessor::~GrGaussianConvolutionFragmentProcessor() {}
217 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const218 void GrGaussianConvolutionFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
219                                                                    GrProcessorKeyBuilder* b) const {
220     GrGLConvolutionEffect::GenKey(*this, caps, b);
221 }
222 
onCreateGLSLInstance() const223 GrGLSLFragmentProcessor* GrGaussianConvolutionFragmentProcessor::onCreateGLSLInstance() const {
224     return new GrGLConvolutionEffect;
225 }
226 
onIsEqual(const GrFragmentProcessor & sBase) const227 bool GrGaussianConvolutionFragmentProcessor::onIsEqual(const GrFragmentProcessor& sBase) const {
228     const GrGaussianConvolutionFragmentProcessor& s =
229             sBase.cast<GrGaussianConvolutionFragmentProcessor>();
230     return (this->radius() == s.radius() && this->direction() == s.direction() &&
231             this->mode() == s.mode() &&
232             0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
233             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
234 }
235 
236 ///////////////////////////////////////////////////////////////////////////////
237 
238 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGaussianConvolutionFragmentProcessor);
239 
240 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)241 sk_sp<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::TestCreate(
242         GrProcessorTestData* d) {
243     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
244                                         : GrProcessorUnitTest::kAlphaTextureIdx;
245     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
246 
247     int bounds[2];
248     int modeIdx = d->fRandom->nextRangeU(0, GrTextureDomain::kModeCount-1);
249 
250     Direction dir;
251     if (d->fRandom->nextBool()) {
252         dir = kX_Direction;
253         bounds[0] = d->fRandom->nextRangeU(0, proxy->width()-1);
254         bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->width()-1);
255     } else {
256         dir = kY_Direction;
257         bounds[0] = d->fRandom->nextRangeU(0, proxy->height()-1);
258         bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->height()-1);
259     }
260 
261     int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
262     float sigma = radius / 3.f;
263 
264     return GrGaussianConvolutionFragmentProcessor::Make(
265             d->textureProxy(texIdx),
266             dir, radius, sigma, static_cast<GrTextureDomain::Mode>(modeIdx), bounds);
267 }
268 #endif
269