OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2016 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 | |
9 #include "SkLights.h" | |
10 #include "SkReadBuffer.h" | |
11 #include "SkShadowMapShader.h" | |
12 #include "SkPoint3.h" | |
13 | |
14 //////////////////////////////////////////////////////////////////////////// | |
15 #ifdef SK_EXPERIMENTAL_SHADOWING | |
16 | |
17 | |
18 /** \class SkShadowMapShaderImpl | |
19 This subclass of shader applies shadowing | |
20 */ | |
21 class SkShadowMapShaderImpl : public SkShader { | |
22 public: | |
23 /** Create a new shadowing shader that shadows | |
24 @param to do to do | |
25 */ | |
26 SkShadowMapShaderImpl(sk_sp<SkShader> povDepthShader, | |
27 sk_sp<SkLights> lights, | |
28 int diffuseWidth, int diffuseHeight) | |
29 : fPovDepthShader(std::move(povDepthShader)) | |
30 , fLights(std::move(lights)) | |
31 , fDiffuseWidth(diffuseWidth) | |
32 , fDiffuseHeight(diffuseHeight) { } | |
33 | |
34 bool isOpaque() const override; | |
35 | |
36 #if SK_SUPPORT_GPU | |
37 sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const overri de; | |
38 #endif | |
39 | |
40 class ShadowMapShaderContext : public SkShader::Context { | |
41 public: | |
42 // The context takes ownership of the states. It will call their destruc tors | |
43 // but will NOT free the memory. | |
44 ShadowMapShaderContext(const SkShadowMapShaderImpl&, const ContextRec&, | |
45 SkShader::Context* povDepthContext, | |
46 void* heapAllocated); | |
47 | |
48 ~ShadowMapShaderContext() override; | |
49 | |
50 void shadeSpan(int x, int y, SkPMColor[], int count) override; | |
51 | |
52 uint32_t getFlags() const override { return fFlags; } | |
53 | |
54 private: | |
55 SkShader::Context* fPovDepthContext; | |
56 uint32_t fFlags; | |
57 | |
58 void* fHeapAllocated; | |
59 | |
60 typedef SkShader::Context INHERITED; | |
61 }; | |
62 | |
63 SK_TO_STRING_OVERRIDE() | |
64 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkShadowMapShaderImpl) | |
65 | |
66 protected: | |
67 void flatten(SkWriteBuffer&) const override; | |
68 size_t onContextSize(const ContextRec&) const override; | |
69 Context* onCreateContext(const ContextRec&, void*) const override; | |
70 | |
71 private: | |
72 sk_sp<SkShader> fPovDepthShader; | |
73 sk_sp<SkLights> fLights; | |
74 | |
75 int fDiffuseWidth; | |
76 int fDiffuseHeight; | |
77 | |
78 friend class SkShadowMapShader; | |
79 | |
80 typedef SkShader INHERITED; | |
81 }; | |
82 | |
83 //////////////////////////////////////////////////////////////////////////// | |
84 | |
85 #if SK_SUPPORT_GPU | |
86 | |
87 #include "GrCoordTransform.h" | |
88 #include "GrFragmentProcessor.h" | |
89 #include "GrInvariantOutput.h" | |
90 #include "glsl/GrGLSLFragmentProcessor.h" | |
91 #include "glsl/GrGLSLFragmentShaderBuilder.h" | |
92 #include "SkGr.h" | |
93 #include "SkGrPriv.h" | |
94 #include "SkSpecialImage.h" | |
95 #include "SkImage_Base.h" | |
96 #include "GrContext.h" | |
97 | |
98 class ShadowMapFP : public GrFragmentProcessor { | |
99 public: | |
100 ShadowMapFP(sk_sp<GrFragmentProcessor> povDepth, | |
101 sk_sp<SkLights> lights, | |
102 int diffuseWidth, int diffuseHeight, | |
103 GrContext* context) { | |
104 | |
105 // fuse all ambient lights into a single one | |
106 | |
107 fLightDir = lights->light(0).dir(); | |
jvanverth1
2016/08/12 17:39:08
Code indented too far.
| |
108 SkImage_Base* shadowMap = ((SkImage_Base*)lights->light(0).getSh adowMap()); | |
109 | |
110 // gets deleted when the ShadowMapFP is destroyed, and frees the GrTexture* | |
111 fTexture = sk_sp<GrTexture>(shadowMap->asTextureRef(context, | |
112 GrTextureParams::ClampNoFilter(), | |
113 SkSourceGammaTreatment::kIgnore)); | |
114 fDepthMapAccess.reset(fTexture.get()); | |
115 this->addTextureAccess(&fDepthMapAccess); | |
116 | |
117 fDepthMapHeight = shadowMap->height(); | |
118 fDepthMapWidth = shadowMap->width(); | |
119 | |
120 fWidth = diffuseWidth; | |
121 fHeight = diffuseHeight; | |
122 | |
123 this->registerChildProcessor(std::move(povDepth)); | |
124 this->initClassID<ShadowMapFP>(); | |
125 } | |
126 | |
127 class GLSLShadowMapFP : public GrGLSLFragmentProcessor { | |
128 public: | |
129 GLSLShadowMapFP() { } | |
130 | |
131 void emitCode(EmitArgs& args) override { | |
132 | |
133 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; | |
134 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; | |
135 | |
136 // add uniforms | |
137 const char* lightDirUniName = nullptr; | |
138 const char* depthMapWidthUniName = nullptr; | |
139 const char* depthMapHeightUniName = nullptr; | |
140 | |
141 | |
142 fLightDirUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
143 kVec3f_GrSLType, | |
144 kDefault_GrSLPrecision, | |
145 "lightDir", | |
146 &lightDirUniName); | |
147 | |
148 fDepthMapWidthUni = uniformHandler->addUniform(kFragment_GrShaderFl ag, | |
149 kInt_GrSLType, | |
150 kDefault_GrSLPrec ision, | |
151 "dmapWidth", | |
152 &depthMapWidthUni Name); | |
153 fDepthMapHeightUni = uniformHandler->addUniform(kFragment_GrShaderFl ag, | |
154 kInt_GrSLType, | |
155 kDefault_GrSLPrec ision, | |
156 "dmapHeight", | |
157 &depthMapHeightUn iName); | |
158 | |
159 | |
160 const char* widthUniName = nullptr; | |
161 const char* heightUniName = nullptr; | |
162 | |
163 fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
164 kInt_GrSLType, | |
165 kDefault_GrSLPrecision, | |
166 "width", &widthUniName); | |
167 fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
168 kInt_GrSLType, | |
169 kDefault_GrSLPrecision, | |
170 "height", &heightUniName); | |
171 | |
172 | |
173 SkString povDepth("povDepth"); | |
174 this->emitChild(0, nullptr, &povDepth, args); | |
175 | |
176 SkString depthMap; | |
177 | |
178 SkString povCoord("povCoord"); | |
179 | |
180 // vMatrixCoord_0_1_Stage0 is the texture sampler coordinates. | |
181 // povDepth.b * 255 scales it to 0 - 255, bringing it to world s pace, | |
182 // and the / 400 brings it back to a sampler coordinate, 0 - 1 | |
183 // The 400 comes from the shadowmaps GM. | |
184 // TODO use real shadowmaps size | |
185 SkString offset("offset"); | |
186 SkString scaleVec("scaleVec"); | |
187 SkString scaleOffsetVec("scaleOffsetVec"); | |
188 | |
189 fragBuilder->codeAppendf("vec2 %s = vec2(%s) * povDepth.b * 255 / 400;\n", | |
190 offset.c_str(), lightDirUniName); | |
191 | |
192 fragBuilder->codeAppendf("vec2 %s = (vec2(%s, %s) / vec2(%s, %s) );\n", | |
193 scaleVec.c_str(), | |
194 widthUniName, heightUniName, | |
195 depthMapWidthUniName, depthMapHeightUni Name); | |
196 | |
197 fragBuilder->codeAppendf("vec2 %s = (vMatrixCoord_0_0_Stage0 + " | |
198 "vec2(%s.x, 0 - %s.y)) " | |
199 " * %s + vec2(0,1) * (1 - %s);\ n", | |
200 povCoord.c_str(), offset.c_str(), offse t.c_str(), | |
201 scaleVec.c_str(), scaleVec.c_str()); | |
202 // | |
203 // fragBuilder->codeAppendf(" %s *= %s;\n", | |
jvanverth1
2016/08/12 17:39:08
Is this needed?
| |
204 // povCoord.c_str(), scaleVec.c_str()); | |
205 | |
206 // | |
207 fragBuilder->appendTextureLookup(&depthMap, args.fTexSamplers[0] , | |
208 povCoord.c_str(), | |
209 kVec2f_GrSLType); | |
210 // | |
211 | |
212 // fragBuilder->codeAppendf("if (%s.b == %s.b) {" | |
jvanverth1
2016/08/12 17:39:08
Is this needed?
| |
213 // "%s = vec4(0,0,0,1);" | |
214 // "} else {", povDepth.c_str(), dep thMap.c_str() | |
215 // , args.fOutputColor); | |
216 // fragBuilder->codeAppendf("%s = %s;" | |
217 // "}", args.fOutputColor, depthMap. c_str()); | |
218 | |
219 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, depthMap.c_s tr()); | |
220 | |
221 // | |
222 | |
223 | |
224 } | |
225 | |
226 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, | |
227 GrProcessorKeyBuilder* b) { | |
228 const ShadowMapFP& shadowMapFP = proc.cast<ShadowMapFP>(); | |
229 b->add32(shadowMapFP.fNumDirLights); | |
230 } | |
231 | |
232 protected: | |
233 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { | |
234 const ShadowMapFP &shadowMapFP = proc.cast<ShadowMapFP>(); | |
235 | |
236 const SkVector3& lightDir = shadowMapFP.lightDir(); | |
jvanverth1
2016/08/12 17:39:08
Code indented too far.
| |
237 if (lightDir != fLightDir) { | |
238 pdman.set3fv(fLightDirUni, 1, &lightDir.fX); | |
239 fLightDir = lightDir; | |
240 } | |
241 | |
242 int depthMapWidth = shadowMapFP.depthMapWidth(); | |
243 if (depthMapWidth != fDepthMapWidth) { | |
244 pdman.set1i(fDepthMapWidthUni, depthMapWidth); | |
245 fDepthMapWidth = depthMapWidth; | |
246 } | |
247 int depthMapHeight = shadowMapFP.depthMapHeight(); | |
248 if (depthMapHeight != fDepthMapHeight) { | |
249 pdman.set1i(fDepthMapHeightUni, depthMapHeight); | |
250 fDepthMapHeight = depthMapHeight; | |
251 } | |
252 | |
253 int width = shadowMapFP.width(); | |
254 if (width != fWidth) { | |
255 pdman.set1i(fWidthUni, width); | |
256 fWidth = width; | |
257 } | |
258 int height = shadowMapFP.height(); | |
259 if (height != fHeight) { | |
260 pdman.set1i(fHeightUni, height); | |
261 fHeight = height; | |
262 } | |
263 | |
264 } | |
265 | |
266 private: | |
267 SkVector3 fLightDir; | |
268 GrGLSLProgramDataManager::UniformHandle | |
269 fLightDirUni; | |
jvanverth1
2016/08/12 17:39:08
Put on same line as type.
| |
270 | |
271 | |
272 int fDepthMapWidth; | |
273 GrGLSLProgramDataManager::UniformHandle | |
274 fDepthMapWidthUni; | |
jvanverth1
2016/08/12 17:39:08
Same here.
| |
275 | |
276 int fDepthMapHeight; | |
277 GrGLSLProgramDataManager::UniformHandle | |
278 fDepthMapHeightUni; | |
jvanverth1
2016/08/12 17:39:08
same here.
| |
279 | |
280 int fWidth; | |
281 GrGLSLProgramDataManager::UniformHandle fWidthUni; | |
282 int fHeight; | |
283 GrGLSLProgramDataManager::UniformHandle fHeightUni; | |
284 }; | |
285 | |
286 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { | |
287 GLSLShadowMapFP::GenKey(*this, caps, b); | |
288 } | |
289 | |
290 const char* name() const override { return "shadowMapFP"; } | |
291 | |
292 void onComputeInvariantOutput(GrInvariantOutput* inout) const override { | |
293 inout->mulByUnknownFourComponents(); | |
294 } | |
295 int32_t numLights() const { return fNumDirLights; } | |
296 const SkColor3f& ambientColor() const { return fAmbientColor; } | |
297 const SkVector3& lightDir() const { | |
298 return fLightDir; | |
299 } | |
300 int depthMapWidth() const { | |
301 return fDepthMapWidth; | |
302 } | |
303 int depthMapHeight() const { | |
304 return fDepthMapHeight; | |
305 } | |
306 int width() const {return fWidth; } | |
307 int height() const {return fHeight; } | |
308 | |
309 private: | |
310 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLShadowMapFP; } | |
311 | |
312 bool onIsEqual(const GrFragmentProcessor& proc) const override { | |
313 const ShadowMapFP& shadowMapFP = proc.cast<ShadowMapFP>(); | |
314 if (fAmbientColor != shadowMapFP.fAmbientColor || fNumDirLights != shado wMapFP.fNumDirLights) { | |
jvanverth1
2016/08/12 17:39:08
Line too long?
| |
315 return false; | |
316 } | |
317 | |
318 if (fWidth != shadowMapFP.fWidth || fHeight != shadowMapFP.fHeight) { | |
319 return false; | |
320 } | |
321 | |
322 for (int i = 0; i < fNumDirLights; i++) { | |
323 if (fLightDir != shadowMapFP.fLightDir) { | |
324 return false; | |
325 } | |
326 | |
327 if (fDepthMapWidth != shadowMapFP.fDepthMapWidth || | |
328 fDepthMapHeight != shadowMapFP.fDepthMapHeight) { | |
329 return false; | |
330 } | |
331 } | |
332 | |
333 return true; | |
334 } | |
335 | |
336 int fNumDirLights; | |
337 | |
338 SkVector3 fLightDir; | |
339 GrTextureAccess fDepthMapAccess; | |
340 sk_sp<GrTexture> fTexture; | |
341 | |
342 int fDepthMapWidth; | |
343 int fDepthMapHeight; | |
344 | |
345 int fHeight; | |
346 int fWidth; | |
347 | |
348 SkColor3f fAmbientColor; | |
349 }; | |
350 | |
351 //////////////////////////////////////////////////////////////////////////// | |
352 | |
353 sk_sp<GrFragmentProcessor> SkShadowMapShaderImpl::asFragmentProcessor(const AsFP Args& fpargs) const { | |
jvanverth1
2016/08/12 17:39:08
Line too long?
| |
354 | |
355 sk_sp<GrFragmentProcessor> povDepthFP = fPovDepthShader->asFragmentProcessor (fpargs); | |
356 | |
357 sk_sp<GrFragmentProcessor> shadowfp = sk_make_sp<ShadowMapFP>(std::move(povD epthFP), | |
358 std::move(fLights ), | |
359 fDiffuseWidth, fD iffuseHeight, | |
360 fpargs.fContext); | |
361 return shadowfp; | |
362 } | |
363 | |
364 | |
365 #endif | |
366 | |
367 //////////////////////////////////////////////////////////////////////////// | |
368 | |
369 bool SkShadowMapShaderImpl::isOpaque() const { | |
370 return true; | |
371 } | |
372 | |
373 SkShadowMapShaderImpl::ShadowMapShaderContext::ShadowMapShaderContext( | |
374 const SkShadowMapShaderImpl& shader, const ContextRec& rec, | |
375 SkShader::Context* povDepthContext, | |
376 void* heapAllocated) | |
377 : INHERITED(shader, rec) | |
378 , fPovDepthContext(povDepthContext) | |
379 , fHeapAllocated(heapAllocated) { | |
380 bool isOpaque = shader.isOpaque(); | |
381 | |
382 // update fFlags | |
383 uint32_t flags = 0; | |
384 if (isOpaque && (255 == this->getPaintAlpha())) { | |
385 flags |= kOpaqueAlpha_Flag; | |
386 } | |
387 | |
388 fFlags = flags; | |
389 } | |
390 | |
391 SkShadowMapShaderImpl::ShadowMapShaderContext::~ShadowMapShaderContext() { | |
392 // The dependencies have been created outside of the context on memory that was allocated by | |
393 // the onCreateContext() method. Call the destructors and free the memory. | |
394 fPovDepthContext->~Context(); | |
395 | |
396 sk_free(fHeapAllocated); | |
397 } | |
398 | |
399 static inline SkPMColor convert(SkColor3f color, U8CPU a) { | |
400 if (color.fX <= 0.0f) { | |
401 color.fX = 0.0f; | |
402 } else if (color.fX >= 255.0f) { | |
403 color.fX = 255.0f; | |
404 } | |
405 | |
406 if (color.fY <= 0.0f) { | |
407 color.fY = 0.0f; | |
408 } else if (color.fY >= 255.0f) { | |
409 color.fY = 255.0f; | |
410 } | |
411 | |
412 if (color.fZ <= 0.0f) { | |
413 color.fZ = 0.0f; | |
414 } else if (color.fZ >= 255.0f) { | |
415 color.fZ = 255.0f; | |
416 } | |
417 | |
418 return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ) ; | |
419 } | |
420 | |
421 // larger is better (fewer times we have to loop), but we shouldn't | |
422 // take up too much stack-space (each one here costs 16 bytes) | |
423 #define BUFFER_MAX 16 | |
424 void SkShadowMapShaderImpl::ShadowMapShaderContext::shadeSpan(int x, int y, | |
425 SkPMColor result[], int count) { | |
426 const SkShadowMapShaderImpl& lightShader = static_cast<const SkShadowMapShad erImpl&>(fShader); | |
427 | |
428 SkPMColor diffuse[BUFFER_MAX]; | |
429 | |
430 do { | |
431 int n = SkTMin(count, BUFFER_MAX); | |
432 | |
433 fPovDepthContext->shadeSpan(x, y, diffuse, n); | |
434 | |
435 for (int i = 0; i < n; ++i) { | |
436 | |
437 SkColor diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); | |
438 | |
439 SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); | |
440 // This is all done in linear unpremul color space (each component 0 ..255.0f though) | |
441 const SkLights::Light& light = lightShader.fLights->light(0); | |
442 | |
443 accum.fX += light.color().fX * SkColorGetR(diffColor); | |
444 accum.fY += light.color().fY * SkColorGetG(diffColor); | |
445 accum.fZ += light.color().fZ * SkColorGetB(diffColor); | |
446 | |
447 result[i] = convert(accum, SkColorGetA(diffColor)); | |
448 } | |
449 | |
450 result += n; | |
451 x += n; | |
452 count -= n; | |
453 } while (count > 0); | |
454 } | |
455 | |
456 //////////////////////////////////////////////////////////////////////////// | |
457 | |
458 #ifndef SK_IGNORE_TO_STRING | |
459 void SkShadowMapShaderImpl::toString(SkString* str) const { | |
460 str->appendf("ShadowMapShader: ()"); | |
461 } | |
462 #endif | |
463 | |
464 sk_sp<SkFlattenable> SkShadowMapShaderImpl::CreateProc(SkReadBuffer& buf) { | |
465 | |
466 // Discarding SkShader flattenable params | |
467 bool hasLocalMatrix = buf.readBool(); | |
468 SkAssertResult(!hasLocalMatrix); | |
469 | |
470 SkLights::Builder builder; | |
471 | |
472 bool isAmbient = buf.readBool(); | |
jvanverth1
2016/08/12 17:39:08
Indented too far.
| |
473 | |
474 SkColor3f color; | |
475 if (!buf.readScalarArray(&color.fX, 3)) { | |
476 return nullptr; | |
477 } | |
478 | |
479 if (isAmbient) { | |
480 builder.add(SkLights::Light(color)); | |
481 } else { | |
482 SkVector3 dir; | |
483 if (!buf.readScalarArray(&dir.fX, 3)) { | |
484 return nullptr; | |
485 } | |
486 | |
487 sk_sp<SkImage> depthMap; | |
488 if (!(depthMap = sk_ref_sp<SkImage>(buf.readImage()))) { | |
489 return nullptr; | |
490 } | |
491 | |
492 SkLights::Light light = SkLights::Light(color, dir); | |
493 light.setShadowMap(depthMap); | |
494 | |
495 builder.add(light); | |
496 } | |
497 | |
498 sk_sp<SkLights> lights(builder.finish()); | |
499 | |
500 int diffuseWidth = buf.readInt(); | |
501 int diffuseHeight = buf.readInt(); | |
502 | |
503 sk_sp<SkShader> povDepthShader(buf.readFlattenable<SkShader>()); | |
504 | |
505 return sk_make_sp<SkShadowMapShaderImpl>(std::move(povDepthShader), | |
506 std::move(lights), | |
507 diffuseWidth, diffuseHeight); | |
508 } | |
509 | |
510 void SkShadowMapShaderImpl::flatten(SkWriteBuffer& buf) const { | |
511 this->INHERITED::flatten(buf); | |
512 | |
513 buf.writeInt(fLights->numLights()); | |
514 | |
515 const SkLights::Light& light = fLights->light(0); | |
jvanverth1
2016/08/12 17:39:08
Indented too far.
| |
516 | |
517 bool isAmbient = SkLights::Light::kAmbient_LightType == light.type(); | |
518 | |
519 buf.writeBool(isAmbient); | |
520 buf.writeScalarArray(&light.color().fX, 3); | |
521 if (!isAmbient) { | |
522 buf.writeScalarArray(&light.dir().fX, 3); | |
523 } | |
524 | |
525 buf.writeImage(light.getShadowMap()); | |
526 | |
527 buf.writeInt(fDiffuseWidth); | |
528 buf.writeInt(fDiffuseHeight); | |
529 | |
530 buf.writeFlattenable(fPovDepthShader.get()); | |
531 } | |
532 | |
533 size_t SkShadowMapShaderImpl::onContextSize(const ContextRec& rec) const { | |
534 return sizeof(ShadowMapShaderContext); | |
535 } | |
536 | |
537 SkShader::Context* SkShadowMapShaderImpl::onCreateContext(const ContextRec& rec, | |
538 void* storage) const { | |
539 size_t heapRequired = fPovDepthShader->contextSize(rec); | |
540 | |
541 void* heapAllocated = sk_malloc_throw(heapRequired); | |
542 | |
543 void* povDepthContextStorage = heapAllocated; | |
544 | |
545 SkShader::Context* povDepthContext = | |
546 fPovDepthShader->createContext(rec, povDepthContextStorage); | |
547 | |
548 if (!povDepthContext) { | |
549 sk_free(heapAllocated); | |
550 return nullptr; | |
551 } | |
552 | |
553 return new (storage) ShadowMapShaderContext(*this, rec, povDepthContext, | |
554 heapAllocated); | |
555 } | |
556 | |
557 /////////////////////////////////////////////////////////////////////////////// | |
558 | |
559 sk_sp<SkShader> SkShadowMapShader::Make(sk_sp<SkShader> povDepthShader, | |
560 sk_sp<SkLights> lights, | |
561 int diffuseWidth, int diffuseHeight) { | |
562 if (!povDepthShader) { | |
563 // TODO: Use paint's color in absence of a diffuseShader | |
564 // TODO: Use a default implementation of normalSource instead | |
565 return nullptr; | |
566 } | |
567 | |
568 return sk_make_sp<SkShadowMapShaderImpl>(std::move(povDepthShader), | |
569 std::move(lights), | |
570 diffuseWidth, diffuseHeight); | |
571 } | |
572 | |
573 /////////////////////////////////////////////////////////////////////////////// | |
574 | |
575 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShadowMapShader) | |
576 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkShadowMapShaderImpl) | |
jvanverth1
2016/08/12 17:39:08
Remove indent.
| |
577 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END | |
578 | |
579 /////////////////////////////////////////////////////////////////////////////// | |
580 | |
581 #endif | |
OLD | NEW |