Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: src/effects/GrCircleBlurFragmentProcessor.cpp

Issue 2062743003: Bin circular blur profile textures by scale and blur to radius ratio (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: cleanup Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "GrCircleBlurFragmentProcessor.h" 8 #include "GrCircleBlurFragmentProcessor.h"
9 9
10 #if SK_SUPPORT_GPU 10 #if SK_SUPPORT_GPU
11 11
12 #include "GrContext.h" 12 #include "GrContext.h"
13 #include "GrInvariantOutput.h" 13 #include "GrInvariantOutput.h"
14 #include "GrTextureProvider.h" 14 #include "GrTextureProvider.h"
15 15
16 #include "glsl/GrGLSLFragmentProcessor.h" 16 #include "glsl/GrGLSLFragmentProcessor.h"
17 #include "glsl/GrGLSLFragmentShaderBuilder.h" 17 #include "glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "glsl/GrGLSLProgramDataManager.h" 18 #include "glsl/GrGLSLProgramDataManager.h"
19 #include "glsl/GrGLSLUniformHandler.h" 19 #include "glsl/GrGLSLUniformHandler.h"
20 20
21 #include "SkFixed.h" 21 #include "SkFixed.h"
22 22
23 class GrGLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor { 23 class GrCircleBlurFragmentProcessor::GLSLProcessor : public GrGLSLFragmentProces sor {
24 public: 24 public:
25 void emitCode(EmitArgs&) override; 25 void emitCode(EmitArgs&) override;
26 26
27 protected: 27 protected:
28 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override ; 28 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override ;
29 29
30 private: 30 private:
31 GrGLSLProgramDataManager::UniformHandle fDataUniform; 31 GrGLSLProgramDataManager::UniformHandle fDataUniform;
32 32
33 typedef GrGLSLFragmentProcessor INHERITED; 33 typedef GrGLSLFragmentProcessor INHERITED;
34 }; 34 };
35 35
36 void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { 36 void GrCircleBlurFragmentProcessor::GLSLProcessor::emitCode(EmitArgs& args) {
37 37
38 const char *dataName; 38 const char *dataName;
39 39
40 // The data is formatted as: 40 // The data is formatted as:
41 // x,y - the center of the circle 41 // x,y - the center of the circle
42 // z - the distance at which the intensity starts falling off (e.g., the start of the table) 42 // z - the distance at which the intensity starts falling off (e.g., the start of the table)
43 // w - the inverse of the profile texture size 43 // w - the inverse of the distance over which the texture is stretched.
44 fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, 44 fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
45 kVec4f_GrSLType, 45 kVec4f_GrSLType,
46 kDefault_GrSLPrecision, 46 kDefault_GrSLPrecision,
47 "data", 47 "data",
48 &dataName); 48 &dataName);
49 49
50 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 50 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
51 const char *fragmentPos = fragBuilder->fragmentPosition(); 51 const char *fragmentPos = fragBuilder->fragmentPosition();
52 52
53 if (args.fInputColor) { 53 if (args.fInputColor) {
(...skipping 10 matching lines...) Expand all
64 fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;" , 64 fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;" ,
65 dataName, dataName); 65 dataName, dataName);
66 66
67 fragBuilder->codeAppendf("float intensity = "); 67 fragBuilder->codeAppendf("float intensity = ");
68 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); 68 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)");
69 fragBuilder->codeAppend(".a;"); 69 fragBuilder->codeAppend(".a;");
70 70
71 fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); 71 fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor );
72 } 72 }
73 73
74 void GrGLCircleBlurFragmentProcessor::onSetData(const GrGLSLProgramDataManager& pdman, 74 void GrCircleBlurFragmentProcessor::GLSLProcessor::onSetData(const GrGLSLProgram DataManager& pdman,
75 const GrProcessor& proc) { 75 const GrProcessor& proc) {
76 const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentPr ocessor>(); 76 const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentPr ocessor>();
77 const SkRect& circle = cbfp.circle(); 77 const SkRect& circle = cbfp.fCircle;
78 78
79 // The data is formatted as: 79 // The data is formatted as:
80 // x,y - the center of the circle 80 // x,y - the center of the circle
81 // z - the distance at which the intensity starts falling off (e.g., the start of the table) 81 // z - the distance at which the intensity starts falling off (e.g., the start of the table)
82 // w - the inverse of the profile texture size 82 // w - the inverse of the distance over which the profile texture is stre tched.
83 pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.offset(), 83 pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.fSolidRad ius,
84 1.0f / cbfp.profileSize()); 84 1.f / cbfp.fTextureRadius);
85 } 85 }
86 86
87 /////////////////////////////////////////////////////////////////////////////// 87 ///////////////////////////////////////////////////////////////////////////////
88 88
89 GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circl e, 89 GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circl e,
90 float sigma, 90 float sigma,
91 float offset, 91 float solidRadius,
92 float textureRadius ,
92 GrTexture* blurProf ile) 93 GrTexture* blurProf ile)
93 : fCircle(circle) 94 : fCircle(circle)
94 , fSigma(sigma) 95 , fSigma(sigma)
95 , fOffset(offset) 96 , fSolidRadius(solidRadius)
97 , fTextureRadius(textureRadius)
96 , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) { 98 , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) {
97 this->initClassID<GrCircleBlurFragmentProcessor>(); 99 this->initClassID<GrCircleBlurFragmentProcessor>();
98 this->addTextureAccess(&fBlurProfileAccess); 100 this->addTextureAccess(&fBlurProfileAccess);
99 this->setWillReadFragmentPosition(); 101 this->setWillReadFragmentPosition();
100 } 102 }
101 103
102 GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() c onst { 104 GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() c onst {
103 return new GrGLCircleBlurFragmentProcessor; 105 return new GLSLProcessor;
104 } 106 }
105 107
106 void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrGLSLCaps& caps , 108 void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrGLSLCaps& caps ,
107 GrProcessorKeyBuilder* b) const { 109 GrProcessorKeyBuilder* b) const {
108 GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b); 110 return;
jvanverth1 2016/06/14 18:51:15 Won't this return garbage (whatever was there befo
bsalomon 2016/06/14 19:16:51 This just leaves the key empty (since this process
109 } 111 }
110 112
111 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { 113 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
112 inout->mulByUnknownSingleComponent(); 114 inout->mulByUnknownSingleComponent();
113 } 115 }
114 116
115 // Create a Gaussian half-kernel and a summed area table given a sigma and numbe r of discrete 117 // Create a Gaussian half-kernel and a summed area table given a sigma and numbe r of discrete
116 // steps. The half kernel is normalized to sum to 0.5. 118 // steps. The half kernel is normalized to sum to 0.5.
117 static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa lfKernel, 119 static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa lfKernel,
118 int halfKernelSize, float sigma) { 120 int halfKernelSize, float sigma) {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 continue; 188 continue;
187 } 189 }
188 float verticalEval = yKernelEvaluations[i + halfKernelSize]; 190 float verticalEval = yKernelEvaluations[i + halfKernelSize];
189 acc += verticalEval * halfKernel[i]; 191 acc += verticalEval * halfKernel[i];
190 } 192 }
191 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the 193 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the
192 // x axis). 194 // x axis).
193 return SkUnitScalarClampToByte(2.f * acc); 195 return SkUnitScalarClampToByte(2.f * acc);
194 } 196 }
195 197
196 static inline void compute_profile_offset_and_size(float circleR, float sigma,
197 float* offset, int* size) {
198 if (3*sigma <= circleR) {
199 // The circle is bigger than the Gaussian. In this case we know the inte rior of the
200 // blurred circle is solid.
201 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig hts texture.
202 // It should always be 255.
203 *size = SkScalarCeilToInt(6*sigma);
204 } else {
205 // The Gaussian is bigger than the circle.
206 *offset = 0.0f;
207 *size = SkScalarCeilToInt(circleR + 3*sigma);
208 }
209 }
210
211 // This function creates a profile of a blurred circle. It does this by computin g a kernel for 198 // This function creates a profile of a blurred circle. It does this by computin g a kernel for
212 // half the Gaussian and a matching summed area table. The summed area table is used to compute 199 // half the Gaussian and a matching summed area table. The summed area table is used to compute
213 // an array of vertical applications of the half kernel to the circle along the x axis. The table 200 // an array of vertical applications of the half kernel to the circle along the x axis. The table
214 // of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size 201 // of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size
215 // of the profile being computed. Then for each of the n profile entries we walk out k steps in each 202 // of the profile being computed. Then for each of the n profile entries we walk out k steps in each
216 // horizontal direction multiplying the corresponding y evaluation by the half k ernel entry and 203 // horizontal direction multiplying the corresponding y evaluation by the half k ernel entry and
217 // sum these values to compute the profile entry. 204 // sum these values to compute the profile entry.
218 static uint8_t* create_profile(float circleR, float sigma) { 205 static uint8_t* create_profile(float sigma, float circleR, float offset, int pro fileTextureWidth) {
219 float offset; 206 int numSteps = profileTextureWidth;
220 int numSteps; 207 // compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps);
jvanverth1 2016/06/14 18:51:15 Delete this line?
bsalomon 2016/06/14 19:16:51 oops, done.
221 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps);
222 208
223 uint8_t* weights = new uint8_t[numSteps]; 209 uint8_t* weights = new uint8_t[numSteps];
224 210
225 // The full kernel is 6 sigmas wide. 211 // The full kernel is 6 sigmas wide.
226 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); 212 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma);
227 // round up to next multiple of 2 and then divide by 2 213 // round up to next multiple of 2 and then divide by 2
228 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; 214 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
229 215
230 // Number of x steps at which to apply kernel in y to cover all the profile samples in x. 216 // Number of x steps at which to apply kernel in y to cover all the profile samples in x.
231 int numYSteps = numSteps + 2 * halfKernelSize; 217 int numYSteps = numSteps + 2 * halfKernelSize;
232 218
233 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); 219 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
234 float* halfKernel = bulkAlloc.get(); 220 float* halfKernel = bulkAlloc.get();
235 float* summedKernel = bulkAlloc.get() + halfKernelSize; 221 float* summedKernel = bulkAlloc.get() + halfKernelSize;
236 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; 222 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
237 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); 223 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
238 224
239 float firstX = offset - halfKernelSize + 0.5f; 225 float firstX = offset - halfKernelSize + 0.5f;
240 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel); 226 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel);
241 227
242 for (int i = 0; i < numSteps - 1; ++i) { 228 for (int i = 0; i < numSteps - 1; ++i) {
243 float evalX = offset + i + 0.5f; 229 float evalX = offset + i + 0.5f;
244 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); 230 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
245 } 231 }
246 // Ensure the tail of the Gaussian goes to zero. 232 // Ensure the tail of the Gaussian goes to zero.
247 weights[numSteps - 1] = 0; 233 weights[numSteps - 1] = 0;
248 return weights; 234 return weights;
249 } 235 }
250 236
237 static int next_pow2_16bits(int x) {
238 SkASSERT(x > 0);
239 SkASSERT(x <= SK_MaxS16);
240 x--;
241 x |= x >> 1;
242 x |= x >> 2;
243 x |= x >> 4;
244 x |= x >> 8;
245 return x + 1;
246 }
247
251 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( 248 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture(
252 GrTextureProvide r* textureProvider, 249 GrTextureProvide r* textureProvider,
253 const SkRect& ci rcle, 250 const SkRect& ci rcle,
254 float sigma, 251 float sigma,
255 float* offset) { 252 float* solidRadi us,
253 float* textureRa dius) {
256 float circleR = circle.width() / 2.0f; 254 float circleR = circle.width() / 2.0f;
255 // Profile textures are cached by the ratio of sigma to circle radius and by the size of the
256 // profile texture (binned by powers of 2).
257 SkScalar sigmaToCircleRRatio = sigma / circleR;
258 // When sigma is really small this becomes a equivalent to convolving a Gaus sian with a half-
259 // plane. We could do that simpler computation. However, right now we're jus t using a lower
260 // bound off the ratio. Similarly, in the extreme high ratio cases circle be comes a point WRT to
261 // the Guassian and the profile texture is a just a Gaussian evaluation.
262 sigmaToCircleRRatio = SkTPin(sigmaToCircleRRatio, 0.05f, 8.f);
263 // Convert to fixed point for the key.
264 SkFixed sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
265 // We shave off some bits to reduce the number of unique entries. We could p robably shave off
266 // more than we do.
267 sigmaToCircleRRatioFixed &= ~0xff;
268 // From the circle center to solidRadius is all 1s and represented by the le ftmost pixel (with
269 // value 255) in the profile texture. If it is zero then there is no solid c enter to the
270 // blurred circle.
271 if (3*sigma <= circleR) {
272 // The circle is bigger than the Gaussian. In this case we know the inte rior of the
273 // blurred circle is solid.
274 *solidRadius = circleR - 3 * sigma; // This location maps to 0.5f in the weights texture.
275 // It should always be 255.
276 *textureRadius = SkScalarCeilToInt(6*sigma);
277 } else {
278 // The Gaussian is bigger than the circle.
279 *solidRadius = 0.0f;
280 *textureRadius = SkScalarCeilToInt(circleR + 3*sigma);
281 }
282 int profileTextureWidth = SkScalarCeilToInt(*textureRadius);
283 profileTextureWidth = (profileTextureWidth >= 1024) ? 1024 :
284 next_pow2_16bits(profileTextureWidth);
257 285
258 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 286 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
259 GrUniqueKey key; 287 GrUniqueKey key;
260 GrUniqueKey::Builder builder(&key, kDomain, 2); 288 GrUniqueKey::Builder builder(&key, kDomain, 2);
261 // The profile curve varies with both the sigma of the Gaussian and the size of the 289 builder[0] = sigmaToCircleRRatioFixed;
262 // disk. Quantizing to 16.16 should be close enough though. 290 builder[1] = profileTextureWidth;
263 builder[0] = SkScalarToFixed(sigma);
264 builder[1] = SkScalarToFixed(circleR);
265 builder.finish(); 291 builder.finish();
266 292
267 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); 293 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
268 294
269 int profileSize;
270 compute_profile_offset_and_size(circleR, sigma, offset, &profileSize);
271
272 if (!blurProfile) { 295 if (!blurProfile) {
273
274 GrSurfaceDesc texDesc; 296 GrSurfaceDesc texDesc;
275 texDesc.fWidth = profileSize; 297 texDesc.fWidth = profileTextureWidth;
276 texDesc.fHeight = 1; 298 texDesc.fHeight = 1;
277 texDesc.fConfig = kAlpha_8_GrPixelConfig; 299 texDesc.fConfig = kAlpha_8_GrPixelConfig;
278 300
279 SkAutoTDeleteArray<uint8_t> profile(create_profile(circleR, sigma)); 301 // Rescale params to the size of the texture we're creating.
302 SkScalar scale = profileTextureWidth / *textureRadius;
303 SkAutoTDeleteArray<uint8_t> profile(create_profile(sigma * scale, circle R * scale,
304 *solidRadius * scale,
305 profileTextureWidth)) ;
280 306
281 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); 307 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0);
282 if (blurProfile) { 308 if (blurProfile) {
283 textureProvider->assignUniqueKeyToTexture(key, blurProfile); 309 textureProvider->assignUniqueKeyToTexture(key, blurProfile);
284 } 310 }
285 } 311 }
286 312
287 return blurProfile; 313 return blurProfile;
288 } 314 }
289 315
290 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); 316 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
291 317
292 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { 318 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) {
293 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); 319 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f);
294 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); 320 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
295 SkRect circle = SkRect::MakeWH(wh, wh); 321 SkRect circle = SkRect::MakeWH(wh, wh);
296 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma); 322 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma);
297 } 323 }
298 324
299 #endif 325 #endif
OLDNEW
« src/effects/GrCircleBlurFragmentProcessor.h ('K') | « src/effects/GrCircleBlurFragmentProcessor.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698