Line data Source code
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 0 : 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&, const GrFragmentProcessor&) override;
29 :
30 : private:
31 : UniformHandle fKernelUni;
32 : UniformHandle fImageIncrementUni;
33 : UniformHandle fBoundsUni;
34 :
35 : typedef GrGLSLFragmentProcessor INHERITED;
36 : };
37 :
38 0 : void GrGLConvolutionEffect::emitCode(EmitArgs& args) {
39 : const GrGaussianConvolutionFragmentProcessor& ce =
40 0 : args.fFp.cast<GrGaussianConvolutionFragmentProcessor>();
41 :
42 0 : GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
43 : fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
44 0 : kDefault_GrSLPrecision, "ImageIncrement");
45 0 : if (ce.useBounds()) {
46 : fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
47 0 : kDefault_GrSLPrecision, "Bounds");
48 : }
49 :
50 0 : int width = Gr1DKernelEffect::WidthFromRadius(ce.radius());
51 :
52 0 : int arrayCount = (width + 3) / 4;
53 0 : SkASSERT(4 * arrayCount >= width);
54 :
55 : fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec4f_GrSLType,
56 0 : kDefault_GrSLPrecision, "Kernel", arrayCount);
57 :
58 0 : GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
59 0 : SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
60 :
61 0 : fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor);
62 :
63 0 : const GrShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni);
64 0 : const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
65 :
66 0 : 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 0 : const char* kVecSuffix[4] = {".x", ".y", ".z", ".w"};
70 0 : for (int i = 0; i < width; i++) {
71 0 : SkString index;
72 0 : SkString kernelIndex;
73 0 : index.appendS32(i / 4);
74 0 : kernel.appendArrayAccess(index.c_str(), &kernelIndex);
75 0 : kernelIndex.append(kVecSuffix[i & 0x3]);
76 :
77 0 : 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 0 : const char* bounds = uniformHandler->getUniformCStr(fBoundsUni);
82 0 : const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
83 0 : fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", component,
84 0 : bounds, component, bounds);
85 : }
86 0 : fragBuilder->codeAppendf("%s += ", args.fOutputColor);
87 0 : fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord");
88 0 : fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str());
89 0 : if (ce.useBounds()) {
90 0 : fragBuilder->codeAppend("}");
91 : }
92 0 : fragBuilder->codeAppendf("coord += %s;\n", imgInc);
93 : }
94 :
95 0 : SkString modulate;
96 0 : GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
97 0 : fragBuilder->codeAppend(modulate.c_str());
98 0 : }
99 :
100 0 : void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
101 : const GrFragmentProcessor& processor) {
102 : const GrGaussianConvolutionFragmentProcessor& conv =
103 0 : processor.cast<GrGaussianConvolutionFragmentProcessor>();
104 0 : GrTexture& texture = *conv.textureSampler(0).texture();
105 :
106 0 : float imageIncrement[2] = {0};
107 0 : float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
108 0 : switch (conv.direction()) {
109 : case Gr1DKernelEffect::kX_Direction:
110 0 : imageIncrement[0] = 1.0f / texture.width();
111 0 : break;
112 : case Gr1DKernelEffect::kY_Direction:
113 0 : imageIncrement[1] = ySign / texture.height();
114 0 : break;
115 : default:
116 0 : SkFAIL("Unknown filter direction.");
117 : }
118 0 : pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
119 0 : if (conv.useBounds()) {
120 0 : const int* bounds = conv.bounds();
121 0 : if (Gr1DKernelEffect::kX_Direction == conv.direction()) {
122 0 : SkScalar inv = SkScalarInvert(SkIntToScalar(texture.width()));
123 0 : pdman.set2f(fBoundsUni, inv * bounds[0], inv * bounds[1]);
124 : } else {
125 0 : SkScalar inv = SkScalarInvert(SkIntToScalar(texture.height()));
126 0 : if (texture.origin() != kTopLeft_GrSurfaceOrigin) {
127 0 : pdman.set2f(fBoundsUni, 1.0f - (inv * bounds[1]), 1.0f - (inv * bounds[0]));
128 : } else {
129 0 : pdman.set2f(fBoundsUni, inv * bounds[1], inv * bounds[0]);
130 : }
131 : }
132 : }
133 0 : int width = Gr1DKernelEffect::WidthFromRadius(conv.radius());
134 :
135 0 : int arrayCount = (width + 3) / 4;
136 0 : SkASSERT(4 * arrayCount >= width);
137 0 : pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
138 0 : }
139 :
140 0 : void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
141 : GrProcessorKeyBuilder* b) {
142 : const GrGaussianConvolutionFragmentProcessor& conv =
143 0 : processor.cast<GrGaussianConvolutionFragmentProcessor>();
144 0 : uint32_t key = conv.radius();
145 0 : key <<= 2;
146 0 : if (conv.useBounds()) {
147 0 : key |= 0x2;
148 0 : key |= GrGaussianConvolutionFragmentProcessor::kY_Direction == conv.direction() ? 0x1 : 0x0;
149 : }
150 0 : b->add32(key);
151 0 : }
152 :
153 : ///////////////////////////////////////////////////////////////////////////////
154 0 : static void fill_in_1D_guassian_kernel(float* kernel, int width, float gaussianSigma, int radius) {
155 0 : const float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
156 :
157 0 : float sum = 0.0f;
158 0 : for (int i = 0; i < width; ++i) {
159 0 : 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 0 : kernel[i] = sk_float_exp(-x * x * denom);
163 0 : sum += kernel[i];
164 : }
165 : // Normalize the kernel
166 0 : float scale = 1.0f / sum;
167 0 : for (int i = 0; i < width; ++i) {
168 0 : kernel[i] *= scale;
169 : }
170 0 : }
171 :
172 0 : GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
173 : GrResourceProvider* resourceProvider,
174 : sk_sp<GrTextureProxy> proxy,
175 : Direction direction,
176 : int radius,
177 : float gaussianSigma,
178 : bool useBounds,
179 0 : int bounds[2])
180 : : INHERITED{resourceProvider,
181 0 : ModulationFlags(proxy->config()),
182 0 : GR_PROXY_MOVE(proxy),
183 : direction,
184 : radius}
185 0 : , fUseBounds(useBounds) {
186 0 : this->initClassID<GrGaussianConvolutionFragmentProcessor>();
187 0 : SkASSERT(radius <= kMaxKernelRadius);
188 :
189 0 : fill_in_1D_guassian_kernel(fKernel, this->width(), gaussianSigma, this->radius());
190 :
191 0 : memcpy(fBounds, bounds, sizeof(fBounds));
192 0 : }
193 :
194 0 : GrGaussianConvolutionFragmentProcessor::~GrGaussianConvolutionFragmentProcessor() {}
195 :
196 0 : void GrGaussianConvolutionFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
197 : GrProcessorKeyBuilder* b) const {
198 0 : GrGLConvolutionEffect::GenKey(*this, caps, b);
199 0 : }
200 :
201 0 : GrGLSLFragmentProcessor* GrGaussianConvolutionFragmentProcessor::onCreateGLSLInstance() const {
202 0 : return new GrGLConvolutionEffect;
203 : }
204 :
205 0 : bool GrGaussianConvolutionFragmentProcessor::onIsEqual(const GrFragmentProcessor& sBase) const {
206 : const GrGaussianConvolutionFragmentProcessor& s =
207 0 : sBase.cast<GrGaussianConvolutionFragmentProcessor>();
208 0 : return (this->radius() == s.radius() && this->direction() == s.direction() &&
209 0 : this->useBounds() == s.useBounds() &&
210 0 : 0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
211 0 : 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
219 0 : sk_sp<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::TestCreate(
220 : GrProcessorTestData* d) {
221 0 : int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
222 0 : : GrProcessorUnitTest::kAlphaTextureIdx;
223 0 : sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
224 :
225 0 : bool useBounds = d->fRandom->nextBool();
226 : int bounds[2];
227 :
228 : Direction dir;
229 0 : if (d->fRandom->nextBool()) {
230 0 : dir = kX_Direction;
231 0 : bounds[0] = d->fRandom->nextRangeU(0, proxy->width()-1);
232 0 : bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->width()-1);
233 : } else {
234 0 : dir = kY_Direction;
235 0 : bounds[0] = d->fRandom->nextRangeU(0, proxy->height()-1);
236 0 : bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->height()-1);
237 : }
238 :
239 0 : int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
240 0 : float sigma = radius / 3.f;
241 :
242 : return GrGaussianConvolutionFragmentProcessor::Make(
243 0 : d->resourceProvider(), d->textureProxy(texIdx),
244 0 : dir, radius, sigma, useBounds, bounds);
245 : }
246 : #endif
|