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

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: Fix compiler warnings 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
« no previous file with comments | « src/effects/GrCircleBlurFragmentProcessor.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // The code for this processor is always the same so there is nothing to add to the key.
111 return;
109 } 112 }
110 113
111 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { 114 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
112 inout->mulByUnknownSingleComponent(); 115 inout->mulByUnknownSingleComponent();
113 } 116 }
114 117
115 // Create a Gaussian half-kernel and a summed area table given a sigma and numbe r of discrete 118 // 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. 119 // 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, 120 static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa lfKernel,
118 int halfKernelSize, float sigma) { 121 int halfKernelSize, float sigma) {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 continue; 189 continue;
187 } 190 }
188 float verticalEval = yKernelEvaluations[i + halfKernelSize]; 191 float verticalEval = yKernelEvaluations[i + halfKernelSize];
189 acc += verticalEval * halfKernel[i]; 192 acc += verticalEval * halfKernel[i];
190 } 193 }
191 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the 194 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the
192 // x axis). 195 // x axis).
193 return SkUnitScalarClampToByte(2.f * acc); 196 return SkUnitScalarClampToByte(2.f * acc);
194 } 197 }
195 198
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 199 // 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 200 // 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 201 // 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 202 // 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 203 // 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 204 // horizontal direction multiplying the corresponding y evaluation by the half k ernel entry and
217 // sum these values to compute the profile entry. 205 // sum these values to compute the profile entry.
218 static uint8_t* create_profile(float circleR, float sigma) { 206 static uint8_t* create_profile(float sigma, float circleR, float offset, int pro fileTextureWidth) {
219 float offset; 207 const int numSteps = profileTextureWidth;
220 int numSteps;
221 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps);
222
223 uint8_t* weights = new uint8_t[numSteps]; 208 uint8_t* weights = new uint8_t[numSteps];
224 209
225 // The full kernel is 6 sigmas wide. 210 // The full kernel is 6 sigmas wide.
226 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); 211 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma);
227 // round up to next multiple of 2 and then divide by 2 212 // round up to next multiple of 2 and then divide by 2
228 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; 213 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
229 214
230 // Number of x steps at which to apply kernel in y to cover all the profile samples in x. 215 // 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; 216 int numYSteps = numSteps + 2 * halfKernelSize;
232 217
233 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); 218 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
234 float* halfKernel = bulkAlloc.get(); 219 float* halfKernel = bulkAlloc.get();
235 float* summedKernel = bulkAlloc.get() + halfKernelSize; 220 float* summedKernel = bulkAlloc.get() + halfKernelSize;
236 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; 221 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
237 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); 222 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
238 223
239 float firstX = offset - halfKernelSize + 0.5f; 224 float firstX = offset - halfKernelSize + 0.5f;
240 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel); 225 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel);
241 226
242 for (int i = 0; i < numSteps - 1; ++i) { 227 for (int i = 0; i < numSteps - 1; ++i) {
243 float evalX = offset + i + 0.5f; 228 float evalX = offset + i + 0.5f;
244 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); 229 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
245 } 230 }
246 // Ensure the tail of the Gaussian goes to zero. 231 // Ensure the tail of the Gaussian goes to zero.
247 weights[numSteps - 1] = 0; 232 weights[numSteps - 1] = 0;
248 return weights; 233 return weights;
249 } 234 }
250 235
236 static int next_pow2_16bits(int x) {
237 SkASSERT(x > 0);
238 SkASSERT(x <= SK_MaxS16);
239 x--;
240 x |= x >> 1;
241 x |= x >> 2;
242 x |= x >> 4;
243 x |= x >> 8;
244 return x + 1;
245 }
246
251 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( 247 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture(
252 GrTextureProvide r* textureProvider, 248 GrTextureProvide r* textureProvider,
253 const SkRect& ci rcle, 249 const SkRect& ci rcle,
254 float sigma, 250 float sigma,
255 float* offset) { 251 float* solidRadi us,
252 float* textureRa dius) {
256 float circleR = circle.width() / 2.0f; 253 float circleR = circle.width() / 2.0f;
254 // Profile textures are cached by the ratio of sigma to circle radius and by the size of the
255 // profile texture (binned by powers of 2).
256 SkScalar sigmaToCircleRRatio = sigma / circleR;
257 // When sigma is really small this becomes a equivalent to convolving a Gaus sian with a half-
258 // plane. We could do that simpler computation. However, right now we're jus t using a lower
259 // bound off the ratio. Similarly, in the extreme high ratio cases circle be comes a point WRT to
260 // the Guassian and the profile texture is a just a Gaussian evaluation.
261 sigmaToCircleRRatio = SkTPin(sigmaToCircleRRatio, 0.05f, 8.f);
262 // Convert to fixed point for the key.
263 SkFixed sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
264 // We shave off some bits to reduce the number of unique entries. We could p robably shave off
265 // more than we do.
266 sigmaToCircleRRatioFixed &= ~0xff;
267 // From the circle center to solidRadius is all 1s and represented by the le ftmost pixel (with
268 // value 255) in the profile texture. If it is zero then there is no solid c enter to the
269 // blurred circle.
270 if (3*sigma <= circleR) {
271 // The circle is bigger than the Gaussian. In this case we know the inte rior of the
272 // blurred circle is solid.
273 *solidRadius = circleR - 3 * sigma; // This location maps to 0.5f in the weights texture.
274 // It should always be 255.
275 *textureRadius = SkScalarCeilToScalar(6 * sigma);
276 } else {
277 // The Gaussian is bigger than the circle.
278 *solidRadius = 0.0f;
279 *textureRadius = SkScalarCeilToScalar(circleR + 3 * sigma);
280 }
281 int profileTextureWidth = SkScalarCeilToInt(*textureRadius);
282 profileTextureWidth = (profileTextureWidth >= 1024) ? 1024 :
283 next_pow2_16bits(profileTextureWidth);
257 284
258 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 285 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
259 GrUniqueKey key; 286 GrUniqueKey key;
260 GrUniqueKey::Builder builder(&key, kDomain, 2); 287 GrUniqueKey::Builder builder(&key, kDomain, 2);
261 // The profile curve varies with both the sigma of the Gaussian and the size of the 288 builder[0] = sigmaToCircleRRatioFixed;
262 // disk. Quantizing to 16.16 should be close enough though. 289 builder[1] = profileTextureWidth;
263 builder[0] = SkScalarToFixed(sigma);
264 builder[1] = SkScalarToFixed(circleR);
265 builder.finish(); 290 builder.finish();
266 291
267 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); 292 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
268 293
269 int profileSize;
270 compute_profile_offset_and_size(circleR, sigma, offset, &profileSize);
271
272 if (!blurProfile) { 294 if (!blurProfile) {
273
274 GrSurfaceDesc texDesc; 295 GrSurfaceDesc texDesc;
275 texDesc.fWidth = profileSize; 296 texDesc.fWidth = profileTextureWidth;
276 texDesc.fHeight = 1; 297 texDesc.fHeight = 1;
277 texDesc.fConfig = kAlpha_8_GrPixelConfig; 298 texDesc.fConfig = kAlpha_8_GrPixelConfig;
278 299
279 SkAutoTDeleteArray<uint8_t> profile(create_profile(circleR, sigma)); 300 // Rescale params to the size of the texture we're creating.
301 SkScalar scale = profileTextureWidth / *textureRadius;
302 SkAutoTDeleteArray<uint8_t> profile(create_profile(sigma * scale, circle R * scale,
303 *solidRadius * scale,
304 profileTextureWidth)) ;
280 305
281 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); 306 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0);
282 if (blurProfile) { 307 if (blurProfile) {
283 textureProvider->assignUniqueKeyToTexture(key, blurProfile); 308 textureProvider->assignUniqueKeyToTexture(key, blurProfile);
284 } 309 }
285 } 310 }
286 311
287 return blurProfile; 312 return blurProfile;
288 } 313 }
289 314
290 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); 315 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
291 316
292 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { 317 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) {
293 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); 318 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f);
294 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); 319 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
295 SkRect circle = SkRect::MakeWH(wh, wh); 320 SkRect circle = SkRect::MakeWH(wh, wh);
296 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma); 321 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma);
297 } 322 }
298 323
299 #endif 324 #endif
OLDNEW
« no previous file with comments | « src/effects/GrCircleBlurFragmentProcessor.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698