blob: 23b617849ca366fb8323b6ee4b8421569e9ad0cf [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
Brian Salomonaee504b2017-01-24 12:29:36 -05008#include "GrGaussianConvolutionFragmentProcessor.h"
Robert Phillips296b1cc2017-03-15 10:42:12 -04009
Robert Phillips646e4292017-06-13 12:44:56 -040010#include "GrTexture.h"
Hal Canary6f6961e2017-01-31 13:50:44 -050011#include "GrTextureProxy.h"
Brian Salomonaee504b2017-01-24 12:29:36 -050012#include "../private/GrGLSL.h"
egdaniel64c47282015-11-13 06:54:19 -080013#include "glsl/GrGLSLFragmentProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080014#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070015#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080016#include "glsl/GrGLSLUniformHandler.h"
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000017
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +000018// For brevity
Brian Salomonb133ffe2017-07-27 11:53:21 -040019using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
20using Direction = GrGaussianConvolutionFragmentProcessor::Direction;
bsalomon@google.com032b2212012-07-16 13:36:18 +000021
egdaniel64c47282015-11-13 06:54:19 -080022class GrGLConvolutionEffect : public GrGLSLFragmentProcessor {
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000023public:
robertphillips9cdb9922016-02-03 12:25:40 -080024 void emitCode(EmitArgs&) override;
ericrk7a787b42015-07-21 14:06:16 -070025
Brian Salomon94efbf52016-11-29 13:43:05 -050026 static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000027
wangyixb1daa862015-08-18 11:29:31 -070028protected:
Brian Salomonab015ef2017-04-04 10:15:51 -040029 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -070030
ericrk0f386122015-07-21 13:15:47 -070031private:
Brian Salomonaee504b2017-01-24 12:29:36 -050032 UniformHandle fKernelUni;
33 UniformHandle fImageIncrementUni;
34 UniformHandle fBoundsUni;
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000035
egdaniel64c47282015-11-13 06:54:19 -080036 typedef GrGLSLFragmentProcessor INHERITED;
tomhudson@google.comd8f856c2012-05-10 12:13:36 +000037};
38
wangyix7c157a92015-07-22 15:08:53 -070039void GrGLConvolutionEffect::emitCode(EmitArgs& args) {
Brian Salomonaee504b2017-01-24 12:29:36 -050040 const GrGaussianConvolutionFragmentProcessor& ce =
41 args.fFp.cast<GrGaussianConvolutionFragmentProcessor>();
robertphillipsbf536af2016-02-04 06:11:53 -080042
egdaniel7ea439b2015-12-03 09:20:44 -080043 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Brian Salomon1d816b92017-08-17 11:07:59 -040044 fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
45 kDefault_GrSLPrecision, "ImageIncrement");
robertphillipsbf536af2016-02-04 06:11:53 -080046 if (ce.useBounds()) {
Brian Salomon1d816b92017-08-17 11:07:59 -040047 fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
48 kDefault_GrSLPrecision, "Bounds");
ericrk7a787b42015-07-21 14:06:16 -070049 }
robertphillipsbf536af2016-02-04 06:11:53 -080050
Brian Salomonb133ffe2017-07-27 11:53:21 -040051 int width = ce.width();
robertphillipsbf536af2016-02-04 06:11:53 -080052
jvanverth78d6eb02016-03-02 13:21:16 -080053 int arrayCount = (width + 3) / 4;
54 SkASSERT(4 * arrayCount >= width);
55
Brian Salomon1d816b92017-08-17 11:07:59 -040056 fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec4f_GrSLType,
57 kDefault_GrSLPrecision, "Kernel", arrayCount);
ericrk7a787b42015-07-21 14:06:16 -070058
cdalton85285412016-02-18 12:37:07 -080059 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon1a1aa932016-09-12 09:30:36 -070060 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
ericrk7a787b42015-07-21 14:06:16 -070061
Brian Salomon1d816b92017-08-17 11:07:59 -040062 fragBuilder->codeAppendf("%s = float4(0, 0, 0, 0);", args.fOutputColor);
ericrk7a787b42015-07-21 14:06:16 -070063
Brian Salomon99938a82016-11-21 13:41:08 -050064 const GrShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni);
egdaniel7ea439b2015-12-03 09:20:44 -080065 const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
ericrk7a787b42015-07-21 14:06:16 -070066
Brian Salomon1d816b92017-08-17 11:07:59 -040067 fragBuilder->codeAppendf("float2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc);
68 fragBuilder->codeAppend("float2 coordSampled = float2(0, 0);");
ericrk7a787b42015-07-21 14:06:16 -070069
70 // Manually unroll loop because some drivers don't; yields 20-30% speedup.
Brian Salomonaee504b2017-01-24 12:29:36 -050071 const char* kVecSuffix[4] = {".x", ".y", ".z", ".w"};
ericrk7a787b42015-07-21 14:06:16 -070072 for (int i = 0; i < width; i++) {
73 SkString index;
74 SkString kernelIndex;
Brian Salomonaee504b2017-01-24 12:29:36 -050075 index.appendS32(i / 4);
ericrk7a787b42015-07-21 14:06:16 -070076 kernel.appendArrayAccess(index.c_str(), &kernelIndex);
jvanverth78d6eb02016-03-02 13:21:16 -080077 kernelIndex.append(kVecSuffix[i & 0x3]);
ericrk7a787b42015-07-21 14:06:16 -070078
wutao039a7c72017-06-30 10:44:45 -070079 fragBuilder->codeAppend("coordSampled = coord;");
robertphillipsbf536af2016-02-04 06:11:53 -080080 if (ce.useBounds()) {
ericrk7a787b42015-07-21 14:06:16 -070081 // 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.
egdaniel7ea439b2015-12-03 09:20:44 -080084 const char* bounds = uniformHandler->getUniformCStr(fBoundsUni);
Brian Salomonb133ffe2017-07-27 11:53:21 -040085 const char* component = ce.direction() == Direction::kY ? "y" : "x";
wutao039a7c72017-06-30 10:44:45 -070086
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: {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400105 SK_ABORT("Unsupported operation.");
wutao039a7c72017-06-30 10:44:45 -0700106 }
107 }
ericrk7a787b42015-07-21 14:06:16 -0700108 }
Brian Salomonaee504b2017-01-24 12:29:36 -0500109 fragBuilder->codeAppendf("%s += ", args.fOutputColor);
wutao039a7c72017-06-30 10:44:45 -0700110 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coordSampled");
egdaniel4ca2e602015-11-18 08:01:26 -0800111 fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str());
wutao039a7c72017-06-30 10:44:45 -0700112 if (GrTextureDomain::kDecal_Mode == ce.mode()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800113 fragBuilder->codeAppend("}");
ericrk7a787b42015-07-21 14:06:16 -0700114 }
Brian Salomonaee504b2017-01-24 12:29:36 -0500115 fragBuilder->codeAppendf("coord += %s;\n", imgInc);
ericrk7a787b42015-07-21 14:06:16 -0700116 }
Ethan Nicholas2983f402017-05-08 09:36:08 -0400117 fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
ericrk7a787b42015-07-21 14:06:16 -0700118}
119
egdaniel018fb622015-10-28 07:26:40 -0700120void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
Brian Salomonab015ef2017-04-04 10:15:51 -0400121 const GrFragmentProcessor& processor) {
Brian Salomonaee504b2017-01-24 12:29:36 -0500122 const GrGaussianConvolutionFragmentProcessor& conv =
123 processor.cast<GrGaussianConvolutionFragmentProcessor>();
Robert Phillipsc686ce32017-07-21 14:12:29 -0400124 GrSurfaceProxy* proxy = conv.textureSampler(0).proxy();
125 GrTexture& texture = *proxy->priv().peekTexture();
robertphillipsbf536af2016-02-04 06:11:53 -0800126
Brian Salomonaee504b2017-01-24 12:29:36 -0500127 float imageIncrement[2] = {0};
Robert Phillipsc686ce32017-07-21 14:12:29 -0400128 float ySign = proxy->origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
ericrk7a787b42015-07-21 14:06:16 -0700129 switch (conv.direction()) {
Brian Salomonb133ffe2017-07-27 11:53:21 -0400130 case Direction::kX:
ericrk7a787b42015-07-21 14:06:16 -0700131 imageIncrement[0] = 1.0f / texture.width();
132 break;
Brian Salomonb133ffe2017-07-27 11:53:21 -0400133 case Direction::kY:
ericrk7a787b42015-07-21 14:06:16 -0700134 imageIncrement[1] = ySign / texture.height();
135 break;
136 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400137 SK_ABORT("Unknown filter direction.");
ericrk7a787b42015-07-21 14:06:16 -0700138 }
139 pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
140 if (conv.useBounds()) {
wutao039a7c72017-06-30 10:44:45 -0700141 float bounds[2] = {0};
142 bounds[0] = conv.bounds()[0];
143 bounds[1] = conv.bounds()[1];
144 if (GrTextureDomain::kClamp_Mode == conv.mode()) {
145 bounds[0] += SK_ScalarHalf;
146 bounds[1] -= SK_ScalarHalf;
147 }
Brian Salomonb133ffe2017-07-27 11:53:21 -0400148 if (Direction::kX == conv.direction()) {
Robert Phillips08c5ec72017-01-30 12:26:47 -0500149 SkScalar inv = SkScalarInvert(SkIntToScalar(texture.width()));
150 pdman.set2f(fBoundsUni, inv * bounds[0], inv * bounds[1]);
ericrk7a787b42015-07-21 14:06:16 -0700151 } else {
Robert Phillips08c5ec72017-01-30 12:26:47 -0500152 SkScalar inv = SkScalarInvert(SkIntToScalar(texture.height()));
Robert Phillipsc686ce32017-07-21 14:12:29 -0400153 if (proxy->origin() != kTopLeft_GrSurfaceOrigin) {
Robert Phillips08c5ec72017-01-30 12:26:47 -0500154 pdman.set2f(fBoundsUni, 1.0f - (inv * bounds[1]), 1.0f - (inv * bounds[0]));
155 } else {
156 pdman.set2f(fBoundsUni, inv * bounds[1], inv * bounds[0]);
157 }
ericrk7a787b42015-07-21 14:06:16 -0700158 }
159 }
Brian Salomonb133ffe2017-07-27 11:53:21 -0400160 int width = conv.width();
robertphillipsbf536af2016-02-04 06:11:53 -0800161
jvanverth78d6eb02016-03-02 13:21:16 -0800162 int arrayCount = (width + 3) / 4;
163 SkASSERT(4 * arrayCount >= width);
164 pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
ericrk7a787b42015-07-21 14:06:16 -0700165}
166
Brian Salomon94efbf52016-11-29 13:43:05 -0500167void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700168 GrProcessorKeyBuilder* b) {
Brian Salomonaee504b2017-01-24 12:29:36 -0500169 const GrGaussianConvolutionFragmentProcessor& conv =
170 processor.cast<GrGaussianConvolutionFragmentProcessor>();
bsalomon63e99f72014-07-21 08:03:14 -0700171 uint32_t key = conv.radius();
wutao039a7c72017-06-30 10:44:45 -0700172 key <<= 3;
Brian Salomonb133ffe2017-07-27 11:53:21 -0400173 key |= Direction::kY == conv.direction() ? 0x4 : 0x0;
wutao039a7c72017-06-30 10:44:45 -0700174 key |= static_cast<uint32_t>(conv.mode());
bsalomon63e99f72014-07-21 08:03:14 -0700175 b->add32(key);
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000176}
177
bsalomon@google.comb505a122012-05-31 18:40:36 +0000178///////////////////////////////////////////////////////////////////////////////
Robert Phillips369e8b72017-08-01 16:13:04 -0400179static void fill_in_1D_gaussian_kernel(float* kernel, int width, float gaussianSigma, int radius) {
Robert Phillips40fd7c92017-01-30 08:06:27 -0500180 const float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
181
182 float sum = 0.0f;
183 for (int i = 0; i < width; ++i) {
184 float x = static_cast<float>(i - radius);
185 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
186 // is dropped here, since we renormalize the kernel below.
187 kernel[i] = sk_float_exp(-x * x * denom);
188 sum += kernel[i];
189 }
190 // Normalize the kernel
191 float scale = 1.0f / sum;
192 for (int i = 0; i < width; ++i) {
193 kernel[i] *= scale;
194 }
195}
196
Robert Phillips40fd7c92017-01-30 08:06:27 -0500197GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
Robert Phillips296b1cc2017-03-15 10:42:12 -0400198 sk_sp<GrTextureProxy> proxy,
199 Direction direction,
200 int radius,
201 float gaussianSigma,
wutao039a7c72017-06-30 10:44:45 -0700202 GrTextureDomain::Mode mode,
Robert Phillips296b1cc2017-03-15 10:42:12 -0400203 int bounds[2])
Brian Salomonb133ffe2017-07-27 11:53:21 -0400204 : INHERITED(ModulateByConfigOptimizationFlags(proxy->config()))
205 , fCoordTransform(proxy.get())
206 , fTextureSampler(std::move(proxy))
207 , fRadius(radius)
208 , fDirection(direction)
wutao039a7c72017-06-30 10:44:45 -0700209 , fMode(mode) {
Robert Phillips40fd7c92017-01-30 08:06:27 -0500210 this->initClassID<GrGaussianConvolutionFragmentProcessor>();
Brian Salomonb133ffe2017-07-27 11:53:21 -0400211 this->addCoordTransform(&fCoordTransform);
212 this->addTextureSampler(&fTextureSampler);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500213 SkASSERT(radius <= kMaxKernelRadius);
214
Robert Phillips369e8b72017-08-01 16:13:04 -0400215 fill_in_1D_gaussian_kernel(fKernel, this->width(), gaussianSigma, this->radius());
Robert Phillips40fd7c92017-01-30 08:06:27 -0500216
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +0000217 memcpy(fBounds, bounds, sizeof(fBounds));
tomhudson@google.comfde2c0a2012-07-16 12:23:32 +0000218}
219
Brian Salomon3f6f9652017-07-28 07:34:05 -0400220GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
221 const GrGaussianConvolutionFragmentProcessor& that)
222 : INHERITED(that.optimizationFlags())
223 , fCoordTransform(that.fCoordTransform)
224 , fTextureSampler(that.fTextureSampler)
225 , fRadius(that.fRadius)
226 , fDirection(that.fDirection)
227 , fMode(that.fMode) {
228 this->initClassID<GrGaussianConvolutionFragmentProcessor>();
229 this->addCoordTransform(&fCoordTransform);
230 this->addTextureSampler(&fTextureSampler);
231 memcpy(fKernel, that.fKernel, that.width() * sizeof(float));
232 memcpy(fBounds, that.fBounds, sizeof(fBounds));
233}
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000234
Brian Salomonaee504b2017-01-24 12:29:36 -0500235void GrGaussianConvolutionFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
236 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800237 GrGLConvolutionEffect::GenKey(*this, caps, b);
238}
239
Brian Salomonaee504b2017-01-24 12:29:36 -0500240GrGLSLFragmentProcessor* GrGaussianConvolutionFragmentProcessor::onCreateGLSLInstance() const {
robertphillipsbf536af2016-02-04 06:11:53 -0800241 return new GrGLConvolutionEffect;
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000242}
243
Brian Salomonaee504b2017-01-24 12:29:36 -0500244bool GrGaussianConvolutionFragmentProcessor::onIsEqual(const GrFragmentProcessor& sBase) const {
245 const GrGaussianConvolutionFragmentProcessor& s =
246 sBase.cast<GrGaussianConvolutionFragmentProcessor>();
247 return (this->radius() == s.radius() && this->direction() == s.direction() &&
wutao039a7c72017-06-30 10:44:45 -0700248 this->mode() == s.mode() &&
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +0000249 0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000250 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
tomhudson@google.comd8f856c2012-05-10 12:13:36 +0000251}
bsalomon@google.com0a7672f2012-08-03 18:12:20 +0000252
253///////////////////////////////////////////////////////////////////////////////
254
Brian Salomonaee504b2017-01-24 12:29:36 -0500255GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGaussianConvolutionFragmentProcessor);
bsalomon@google.com0a7672f2012-08-03 18:12:20 +0000256
Hal Canary6f6961e2017-01-31 13:50:44 -0500257#if GR_TEST_UTILS
Brian Salomonaff329b2017-08-11 09:40:37 -0400258std::unique_ptr<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::TestCreate(
Brian Salomonaee504b2017-01-24 12:29:36 -0500259 GrProcessorTestData* d) {
260 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
261 : GrProcessorUnitTest::kAlphaTextureIdx;
Robert Phillips08c5ec72017-01-30 12:26:47 -0500262 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
Brian Salomonaee504b2017-01-24 12:29:36 -0500263
Robert Phillips08c5ec72017-01-30 12:26:47 -0500264 int bounds[2];
wutao039a7c72017-06-30 10:44:45 -0700265 int modeIdx = d->fRandom->nextRangeU(0, GrTextureDomain::kModeCount-1);
Robert Phillips08c5ec72017-01-30 12:26:47 -0500266
267 Direction dir;
268 if (d->fRandom->nextBool()) {
Brian Salomonb133ffe2017-07-27 11:53:21 -0400269 dir = Direction::kX;
Robert Phillips08c5ec72017-01-30 12:26:47 -0500270 bounds[0] = d->fRandom->nextRangeU(0, proxy->width()-1);
271 bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->width()-1);
272 } else {
Brian Salomonb133ffe2017-07-27 11:53:21 -0400273 dir = Direction::kY;
Robert Phillips08c5ec72017-01-30 12:26:47 -0500274 bounds[0] = d->fRandom->nextRangeU(0, proxy->height()-1);
275 bounds[1] = d->fRandom->nextRangeU(bounds[0], proxy->height()-1);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000276 }
bsalomon@google.com0a7672f2012-08-03 18:12:20 +0000277
Robert Phillips08c5ec72017-01-30 12:26:47 -0500278 int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
Brian Salomonaee504b2017-01-24 12:29:36 -0500279 float sigma = radius / 3.f;
Robert Phillips08c5ec72017-01-30 12:26:47 -0500280
wutao039a7c72017-06-30 10:44:45 -0700281 return GrGaussianConvolutionFragmentProcessor::Make(
282 d->textureProxy(texIdx),
283 dir, radius, sigma, static_cast<GrTextureDomain::Mode>(modeIdx), bounds);
bsalomon@google.com0a7672f2012-08-03 18:12:20 +0000284}
Hal Canary6f6961e2017-01-31 13:50:44 -0500285#endif