• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "SkLights.h"
9 #include "SkPoint3.h"
10 #include "SkRadialShadowMapShader.h"
11 
12 ////////////////////////////////////////////////////////////////////////////
13 #ifdef SK_EXPERIMENTAL_SHADOWING
14 
15 
16 /** \class SkRadialShadowMapShaderImpl
17     This subclass of shader applies shadowing radially around a light
18 */
19 class SkRadialShadowMapShaderImpl : public SkShader {
20 public:
21     /** Create a new shadowing shader that shadows radially around a light
22     */
SkRadialShadowMapShaderImpl(sk_sp<SkShader> occluderShader,sk_sp<SkLights> lights,int diffuseWidth,int diffuseHeight)23     SkRadialShadowMapShaderImpl(sk_sp<SkShader> occluderShader,
24                                 sk_sp<SkLights> lights,
25                                 int diffuseWidth, int diffuseHeight)
26         : fOccluderShader(std::move(occluderShader))
27         , fLight(std::move(lights))
28         , fWidth(diffuseWidth)
29         , fHeight(diffuseHeight) { }
30 
31     bool isOpaque() const override;
32 
33 #if SK_SUPPORT_GPU
34     sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
35 #endif
36 
37     class ShadowMapRadialShaderContext : public SkShader::Context {
38     public:
39         // The context takes ownership of the states. It will call their destructors
40         // but will NOT free the memory.
41         ShadowMapRadialShaderContext(const SkRadialShadowMapShaderImpl&, const ContextRec&,
42                                  SkShader::Context* occluderContext,
43                                  void* heapAllocated);
44 
45         ~ShadowMapRadialShaderContext() override;
46 
47         void shadeSpan(int x, int y, SkPMColor[], int count) override;
48 
getFlags() const49         uint32_t getFlags() const override { return fFlags; }
50 
51     private:
52         SkShader::Context*        fOccluderContext;
53         uint32_t                  fFlags;
54 
55         void* fHeapAllocated;
56 
57         typedef SkShader::Context INHERITED;
58     };
59 
60     SK_TO_STRING_OVERRIDE()
61     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialShadowMapShaderImpl)
62 
63 protected:
64     void flatten(SkWriteBuffer&) const override;
65     size_t onContextSize(const ContextRec&) const override;
66     Context* onCreateContext(const ContextRec&, void*) const override;
67 
68 private:
69     sk_sp<SkShader> fOccluderShader;
70     sk_sp<SkLights> fLight;
71 
72     int fWidth;
73     int fHeight;
74 
75     friend class SkRadialShadowMapShader;
76 
77     typedef SkShader INHERITED;
78 };
79 
80 ////////////////////////////////////////////////////////////////////////////
81 
82 #if SK_SUPPORT_GPU
83 
84 #include "GrContext.h"
85 #include "GrCoordTransform.h"
86 #include "GrFragmentProcessor.h"
87 #include "glsl/GrGLSLFragmentProcessor.h"
88 #include "glsl/GrGLSLFragmentShaderBuilder.h"
89 #include "SkGr.h"
90 #include "SkImage_Base.h"
91 #include "GrInvariantOutput.h"
92 #include "SkSpecialImage.h"
93 
94 class RadialShadowMapFP : public GrFragmentProcessor {
95 public:
RadialShadowMapFP(sk_sp<GrFragmentProcessor> occluder,sk_sp<SkLights> light,int diffuseWidth,int diffuseHeight,GrContext * context)96     RadialShadowMapFP(sk_sp<GrFragmentProcessor> occluder,
97                       sk_sp<SkLights> light,
98                       int diffuseWidth, int diffuseHeight,
99                       GrContext* context) {
100         fLightPos = light->light(0).pos();
101 
102         fWidth = diffuseWidth;
103         fHeight = diffuseHeight;
104 
105         this->registerChildProcessor(std::move(occluder));
106         this->initClassID<RadialShadowMapFP>();
107     }
108 
109     class GLSLRadialShadowMapFP : public GrGLSLFragmentProcessor {
110     public:
GLSLRadialShadowMapFP()111         GLSLRadialShadowMapFP() { }
112 
emitCode(EmitArgs & args)113         void emitCode(EmitArgs& args) override {
114 
115             GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
116             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
117 
118             const char* lightPosUniName = nullptr;
119 
120             fLightPosUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
121                                                       kVec3f_GrSLType,
122                                                       kDefault_GrSLPrecision,
123                                                       "lightPos",
124                                                       &lightPosUniName);
125 
126             const char* widthUniName = nullptr;
127             const char* heightUniName = nullptr;
128 
129             fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
130                                                    kInt_GrSLType,
131                                                    kDefault_GrSLPrecision,
132                                                    "width", &widthUniName);
133             fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
134                                                     kInt_GrSLType,
135                                                     kDefault_GrSLPrecision,
136                                                     "height", &heightUniName);
137 
138 
139             SkString occluder("occluder");
140             this->emitChild(0, nullptr, &occluder, args);
141 
142             // Modify the input texture coordinates to index into our 1D output
143             fragBuilder->codeAppend("float distHere;");
144 
145             // we use a max shadow distance of 2 times the max of width/height
146             fragBuilder->codeAppend("float closestDistHere = 2;");
147             fragBuilder->codeAppend("vec2 coords = vMatrixCoord_0_0_Stage0;");
148             fragBuilder->codeAppend("coords.y = 0;");
149             fragBuilder->codeAppend("vec2 destCoords = vec2(0,0);");
150             fragBuilder->codeAppendf("float step = 1.0 / %s;", heightUniName);
151 
152             // assume that we are at 0, 0 light pos
153             // TODO use correct light positions
154 
155             // this goes through each depth value in the final output buffer,
156             // basically raycasting outwards, and finding the first collision.
157             // we also increment coords.y to 2 instead 1 so our shadows stretch the whole screen.
158             fragBuilder->codeAppendf("for (coords.y = 0; coords.y <= 2; coords.y += step) {");
159 
160                 fragBuilder->codeAppend("float theta = (coords.x * 2.0 - 1.0) * 3.1415;");
161                 fragBuilder->codeAppend("float r = coords.y;");
162                 fragBuilder->codeAppend("destCoords = "
163                         "vec2(r * cos(theta), - r * sin(theta)) /2.0 + 0.5;");
164                 fragBuilder->codeAppendf("vec2 lightOffset = (vec2(%s)/vec2(%s,%s) - 0.5)"
165                                                             "* vec2(1.0, 1.0);",
166                                          lightPosUniName, widthUniName, heightUniName);
167 
168                 fragBuilder->codeAppend("distHere = texture(uTextureSampler0_Stage1,"
169                                                            "destCoords + lightOffset).b;");
170                 fragBuilder->codeAppend("if (distHere > 0.0) {"
171                                             "closestDistHere = coords.y;"
172                                         "break;}");
173             fragBuilder->codeAppend("}");
174 
175             fragBuilder->codeAppendf("%s = vec4(vec3(closestDistHere / 2.0),1);", args.fOutputColor);
176         }
177 
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)178         static void GenKey(const GrProcessor& proc, const GrShaderCaps&,
179                            GrProcessorKeyBuilder* b) {
180             b->add32(0); // nothing to add here
181         }
182 
183     protected:
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & proc)184         void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
185             const RadialShadowMapFP &radialShadowMapFP = proc.cast<RadialShadowMapFP>();
186 
187             const SkVector3& lightPos = radialShadowMapFP.lightPos();
188             if (lightPos != fLightPos) {
189                 pdman.set3fv(fLightPosUni, 1, &lightPos.fX);
190                 fLightPos = lightPos;
191             }
192 
193             int width = radialShadowMapFP.width();
194             if (width != fWidth) {
195                 pdman.set1i(fWidthUni, width);
196                 fWidth = width;
197             }
198             int height = radialShadowMapFP.height();
199             if (height != fHeight) {
200                 pdman.set1i(fHeightUni, height);
201                 fHeight = height;
202             }
203         }
204 
205     private:
206         SkVector3 fLightPos;
207         GrGLSLProgramDataManager::UniformHandle fLightPosUni;
208 
209         int fWidth;
210         GrGLSLProgramDataManager::UniformHandle fWidthUni;
211         int fHeight;
212         GrGLSLProgramDataManager::UniformHandle fHeightUni;
213     };
214 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const215     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
216         GLSLRadialShadowMapFP::GenKey(*this, caps, b);
217     }
218 
name() const219     const char* name() const override { return "RadialShadowMapFP"; }
220 
lightPos() const221     const SkVector3& lightPos() const {
222         return fLightPos;
223     }
224 
width() const225     int width() const { return fWidth; }
height() const226     int height() const { return fHeight; }
227 
228 private:
onCreateGLSLInstance() const229     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
230         return new GLSLRadialShadowMapFP;
231     }
232 
onIsEqual(const GrFragmentProcessor & proc) const233     bool onIsEqual(const GrFragmentProcessor& proc) const override {
234         const RadialShadowMapFP& radialShadowMapFP = proc.cast<RadialShadowMapFP>();
235 
236         if (fWidth != radialShadowMapFP.fWidth || fHeight != radialShadowMapFP.fHeight) {
237             return false;
238         }
239 
240         if (fLightPos != radialShadowMapFP.fLightPos) {
241             return false;
242         }
243 
244         return true;
245     }
246 
247     SkVector3        fLightPos;
248 
249     int              fHeight;
250     int              fWidth;
251 };
252 
253 ////////////////////////////////////////////////////////////////////////////
254 
asFragmentProcessor(const AsFPArgs & fpargs) const255 sk_sp<GrFragmentProcessor> SkRadialShadowMapShaderImpl::asFragmentProcessor
256         (const AsFPArgs& fpargs) const {
257 
258     sk_sp<GrFragmentProcessor> occluderFP = fOccluderShader->asFragmentProcessor(fpargs);
259 
260     sk_sp<GrFragmentProcessor> shadowFP = sk_make_sp<RadialShadowMapFP>(std::move(occluderFP),
261                                                                         fLight, fWidth, fHeight,
262                                                                         fpargs.fContext);
263     return shadowFP;
264 }
265 
266 #endif
267 
268 ////////////////////////////////////////////////////////////////////////////
269 
isOpaque() const270 bool SkRadialShadowMapShaderImpl::isOpaque() const {
271     return fOccluderShader->isOpaque();
272 }
273 
ShadowMapRadialShaderContext(const SkRadialShadowMapShaderImpl & shader,const ContextRec & rec,SkShader::Context * occluderContext,void * heapAllocated)274 SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::ShadowMapRadialShaderContext(
275         const SkRadialShadowMapShaderImpl& shader, const ContextRec& rec,
276         SkShader::Context* occluderContext,
277         void* heapAllocated)
278         : INHERITED(shader, rec)
279         , fOccluderContext(occluderContext)
280         , fHeapAllocated(heapAllocated) {
281     bool isOpaque = shader.isOpaque();
282 
283     // update fFlags
284     uint32_t flags = 0;
285     if (isOpaque && (255 == this->getPaintAlpha())) {
286         flags |= kOpaqueAlpha_Flag;
287     }
288 
289     fFlags = flags;
290 }
291 
~ShadowMapRadialShaderContext()292 SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::~ShadowMapRadialShaderContext() {
293     // The dependencies have been created outside of the context on memory that was allocated by
294     // the onCreateContext() method. Call the destructors and free the memory.
295     fOccluderContext->~Context();
296 
297     sk_free(fHeapAllocated);
298 }
299 
convert(SkColor3f color,U8CPU a)300 static inline SkPMColor convert(SkColor3f color, U8CPU a) {
301     if (color.fX <= 0.0f) {
302         color.fX = 0.0f;
303     } else if (color.fX >= 255.0f) {
304         color.fX = 255.0f;
305     }
306 
307     if (color.fY <= 0.0f) {
308         color.fY = 0.0f;
309     } else if (color.fY >= 255.0f) {
310         color.fY = 255.0f;
311     }
312 
313     if (color.fZ <= 0.0f) {
314         color.fZ = 0.0f;
315     } else if (color.fZ >= 255.0f) {
316         color.fZ = 255.0f;
317     }
318 
319     return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
320 }
321 
322 // larger is better (fewer times we have to loop), but we shouldn't
323 // take up too much stack-space (each one here costs 16 bytes)
324 #define BUFFER_MAX 16
shadeSpan(int x,int y,SkPMColor result[],int count)325 void SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::shadeSpan
326         (int x, int y, SkPMColor result[], int count) {
327     do {
328         int n = SkTMin(count, BUFFER_MAX);
329 
330         // just fill with white for now
331         SkPMColor accum = convert(SkColor3f::Make(1.0f, 1.0f, 1.0f), 0xFF);
332 
333         for (int i = 0; i < n; ++i) {
334             result[i] = accum;
335         }
336 
337         result += n;
338         x += n;
339         count -= n;
340     } while (count > 0);
341 }
342 
343 ////////////////////////////////////////////////////////////////////////////
344 
345 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const346 void SkRadialShadowMapShaderImpl::toString(SkString* str) const {
347     str->appendf("RadialShadowMapShader: ()");
348 }
349 #endif
350 
CreateProc(SkReadBuffer & buf)351 sk_sp<SkFlattenable> SkRadialShadowMapShaderImpl::CreateProc(SkReadBuffer& buf) {
352 
353     // Discarding SkShader flattenable params
354     bool hasLocalMatrix = buf.readBool();
355     SkAssertResult(!hasLocalMatrix);
356 
357     sk_sp<SkLights> light = SkLights::MakeFromBuffer(buf);
358 
359     int diffuseWidth = buf.readInt();
360     int diffuseHeight = buf.readInt();
361 
362     sk_sp<SkShader> occluderShader(buf.readFlattenable<SkShader>());
363 
364     return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader),
365                                                    std::move(light),
366                                                    diffuseWidth, diffuseHeight);
367 }
368 
flatten(SkWriteBuffer & buf) const369 void SkRadialShadowMapShaderImpl::flatten(SkWriteBuffer& buf) const {
370     this->INHERITED::flatten(buf);
371 
372     fLight->flatten(buf);
373 
374     buf.writeInt(fWidth);
375     buf.writeInt(fHeight);
376 
377     buf.writeFlattenable(fOccluderShader.get());
378 }
379 
onContextSize(const ContextRec & rec) const380 size_t SkRadialShadowMapShaderImpl::onContextSize(const ContextRec& rec) const {
381     return sizeof(ShadowMapRadialShaderContext);
382 }
383 
onCreateContext(const ContextRec & rec,void * storage) const384 SkShader::Context* SkRadialShadowMapShaderImpl::onCreateContext(const ContextRec& rec,
385                                                                 void* storage) const {
386     size_t heapRequired = fOccluderShader->contextSize(rec);
387 
388     void* heapAllocated = sk_malloc_throw(heapRequired);
389 
390     void* occluderContextStorage = heapAllocated;
391 
392     SkShader::Context* occluderContext =
393             fOccluderShader->createContext(rec, occluderContextStorage);
394 
395     if (!occluderContext) {
396         sk_free(heapAllocated);
397         return nullptr;
398     }
399 
400     return new (storage) ShadowMapRadialShaderContext(*this, rec, occluderContext, heapAllocated);
401 }
402 
403 ///////////////////////////////////////////////////////////////////////////////
404 
Make(sk_sp<SkShader> occluderShader,sk_sp<SkLights> light,int diffuseWidth,int diffuseHeight)405 sk_sp<SkShader> SkRadialShadowMapShader::Make(sk_sp<SkShader> occluderShader,
406                                               sk_sp<SkLights> light,
407                                               int diffuseWidth, int diffuseHeight) {
408     if (!occluderShader) {
409         // TODO: Use paint's color in absence of a diffuseShader
410         // TODO: Use a default implementation of normalSource instead
411         return nullptr;
412     }
413 
414     return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader),
415                                                    std::move(light),
416                                                    diffuseWidth, diffuseHeight);
417 }
418 
419 ///////////////////////////////////////////////////////////////////////////////
420 
421 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRadialShadowMapShader)
422 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialShadowMapShaderImpl)
423 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
424 
425 ///////////////////////////////////////////////////////////////////////////////
426 
427 #endif
428