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