• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 "src/gpu/ganesh/effects/GrSkSLFP.h"
9 
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "include/private/SkSLString.h"
12 #include "include/private/gpu/ganesh/GrContext_Base.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkRuntimeEffectPriv.h"
15 #include "src/core/SkSLTypeShared.h"
16 #include "src/core/SkVM.h"
17 #include "src/gpu/KeyBuilder.h"
18 #include "src/gpu/ganesh/GrBaseContextPriv.h"
19 #include "src/gpu/ganesh/GrColorInfo.h"
20 #include "src/gpu/ganesh/GrTexture.h"
21 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
22 #include "src/gpu/ganesh/glsl/GrGLSLProgramBuilder.h"
23 #include "src/sksl/SkSLUtil.h"
24 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
25 #include "src/sksl/ir/SkSLProgram.h"
26 #include "src/sksl/ir/SkSLVarDeclarations.h"
27 
28 class GrSkSLFP::Impl : public ProgramImpl {
29 public:
emitCode(EmitArgs & args)30     void emitCode(EmitArgs& args) override {
31         const GrSkSLFP& fp            = args.fFp.cast<GrSkSLFP>();
32         const SkSL::Program& program  = *fp.fEffect->fBaseProgram;
33 
34         class FPCallbacks : public SkSL::PipelineStage::Callbacks {
35         public:
36             FPCallbacks(Impl* self,
37                         EmitArgs& args,
38                         const char* inputColor,
39                         const SkSL::Context& context,
40                         const uint8_t* uniformData,
41                         const Specialized* specialized)
42                     : fSelf(self)
43                     , fArgs(args)
44                     , fInputColor(inputColor)
45                     , fContext(context)
46                     , fUniformData(uniformData)
47                     , fSpecialized(specialized) {}
48 
49             std::string declareUniform(const SkSL::VarDeclaration* decl) override {
50                 const SkSL::Variable* var = decl->var();
51                 if (var->type().isOpaque()) {
52                     // Nothing to do. The only opaque types we should see are children, and those
53                     // are handled specially, above.
54                     SkASSERT(var->type().isEffectChild());
55                     return std::string(var->name());
56                 }
57 
58                 const SkSL::Type* type = &var->type();
59                 size_t sizeInBytes = type->slotCount() * sizeof(float);
60                 const float* floatData = reinterpret_cast<const float*>(fUniformData);
61                 const int* intData = reinterpret_cast<const int*>(fUniformData);
62                 fUniformData += sizeInBytes;
63 
64                 bool isArray = false;
65                 if (type->isArray()) {
66                     type = &type->componentType();
67                     isArray = true;
68                 }
69 
70                 SkSLType gpuType;
71                 SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType));
72 
73                 if (*fSpecialized++ == Specialized::kYes) {
74                     SkASSERTF(!isArray, "specializing array uniforms is not allowed");
75                     std::string value = SkSLTypeString(gpuType);
76                     value.append("(");
77 
78                     bool isFloat = SkSLTypeIsFloatType(gpuType);
79                     size_t slots = type->slotCount();
80                     for (size_t i = 0; i < slots; ++i) {
81                         value.append(isFloat ? skstd::to_string(floatData[i])
82                                              : std::to_string(intData[i]));
83                         value.append(",");
84                     }
85                     value.back() = ')';
86                     return value;
87                 }
88 
89                 const char* uniformName = nullptr;
90                 auto handle =
91                         fArgs.fUniformHandler->addUniformArray(&fArgs.fFp.cast<GrSkSLFP>(),
92                                                                kFragment_GrShaderFlag,
93                                                                gpuType,
94                                                                SkString(var->name()).c_str(),
95                                                                isArray ? var->type().columns() : 0,
96                                                                &uniformName);
97                 fSelf->fUniformHandles.push_back(handle);
98                 return std::string(uniformName);
99             }
100 
101             std::string getMangledName(const char* name) override {
102                 return std::string(fArgs.fFragBuilder->getMangledFunctionName(name).c_str());
103             }
104 
105             void defineFunction(const char* decl, const char* body, bool isMain) override {
106                 if (isMain) {
107                     fArgs.fFragBuilder->codeAppend(body);
108                 } else {
109                     fArgs.fFragBuilder->emitFunction(decl, body);
110                 }
111             }
112 
113             void declareFunction(const char* decl) override {
114                 fArgs.fFragBuilder->emitFunctionPrototype(decl);
115             }
116 
117             void defineStruct(const char* definition) override {
118                 fArgs.fFragBuilder->definitionAppend(definition);
119             }
120 
121             void declareGlobal(const char* declaration) override {
122                 fArgs.fFragBuilder->definitionAppend(declaration);
123             }
124 
125             std::string sampleShader(int index, std::string coords) override {
126                 // If the child was sampled using the coords passed to main (and they are never
127                 // modified), then we will have marked the child as PassThrough. The code generator
128                 // doesn't know that, and still supplies coords. Inside invokeChild, we assert that
129                 // any coords passed for a PassThrough child match args.fSampleCoords exactly.
130                 //
131                 // Normally, this is valid. Here, we *copied* the sample coords to a local variable
132                 // (so that they're mutable in the runtime effect SkSL). Thus, the coords string we
133                 // get here is the name of the local copy, and fSampleCoords still points to the
134                 // unmodified original (which might be a varying, for example).
135                 // To prevent the assert, we pass the empty string in this case. Note that for
136                 // children sampled like this, invokeChild doesn't even use the coords parameter,
137                 // except for that assert.
138                 const GrFragmentProcessor* child = fArgs.fFp.childProcessor(index);
139                 if (child && child->sampleUsage().isPassThrough()) {
140                     coords.clear();
141                 }
142                 return std::string(fSelf->invokeChild(index, fInputColor, fArgs, coords).c_str());
143             }
144 
145             std::string sampleColorFilter(int index, std::string color) override {
146                 return std::string(fSelf->invokeChild(index,
147                                                  color.empty() ? fInputColor : color.c_str(),
148                                                  fArgs)
149                                       .c_str());
150             }
151 
152             std::string sampleBlender(int index, std::string src, std::string dst) override {
153                 if (!fSelf->childProcessor(index)) {
154                     return SkSL::String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
155                 }
156                 return std::string(
157                         fSelf->invokeChild(index, src.c_str(), dst.c_str(), fArgs).c_str());
158             }
159 
160             // These intrinsics take and return 3-component vectors, but child FPs operate on
161             // 4-component vectors. We use swizzles here to paper over the difference.
162             std::string toLinearSrgb(std::string color) override {
163                 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
164                 if (fp.fToLinearSrgbChildIndex < 0) {
165                     return color;
166                 }
167                 color = SkSL::String::printf("(%s).rgb1", color.c_str());
168                 SkString xformedColor = fSelf->invokeChild(
169                         fp.fToLinearSrgbChildIndex, color.c_str(), fArgs);
170                 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
171             }
172 
173             std::string fromLinearSrgb(std::string color) override {
174                 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
175                 if (fp.fFromLinearSrgbChildIndex < 0) {
176                     return color;
177                 }
178                 color = SkSL::String::printf("(%s).rgb1", color.c_str());
179                 SkString xformedColor = fSelf->invokeChild(
180                         fp.fFromLinearSrgbChildIndex, color.c_str(), fArgs);
181                 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
182             }
183 
184             Impl*                         fSelf;
185             EmitArgs&                     fArgs;
186             const char*                   fInputColor;
187             const SkSL::Context&          fContext;
188             const uint8_t*                fUniformData;
189             const Specialized*            fSpecialized;
190             int                           fUniformIndex = 0;
191         };
192 
193         // If we have an input child, we invoke it now, and make the result of that be the "input
194         // color" for all other purposes later (eg, the default passed via sample calls, etc.)
195         if (fp.fInputChildIndex >= 0) {
196             args.fFragBuilder->codeAppendf("%s = %s;\n",
197                                            args.fInputColor,
198                                            this->invokeChild(fp.fInputChildIndex, args).c_str());
199         }
200 
201         if (fp.fEffect->allowBlender()) {
202             // If we have an dest-color child, we invoke it now, and make the result of that be the
203             // "dest color" for all other purposes later.
204             if (fp.fDestColorChildIndex >= 0) {
205                 args.fFragBuilder->codeAppendf(
206                         "%s = %s;\n",
207                         args.fDestColor,
208                         this->invokeChild(fp.fDestColorChildIndex, args.fDestColor, args).c_str());
209             }
210         } else {
211             // We're not making a blender, so we don't expect a dest-color child FP to exist.
212             SkASSERT(fp.fDestColorChildIndex < 0);
213         }
214 
215         // Snap off a global copy of the input color at the start of main. We need this when
216         // we call child processors (particularly from helper functions, which can't "see" the
217         // parameter to main). Even from within main, if the code mutates the parameter, calls to
218         // sample should still be passing the original color (by default).
219         SkString inputColorName;
220         if (fp.fEffect->samplesOutsideMain()) {
221             GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
222                                        SkSLType::kHalf4);
223             args.fFragBuilder->declareGlobal(inputColorCopy);
224             inputColorName = inputColorCopy.getName();
225             args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorName.c_str(), args.fInputColor);
226         } else {
227             inputColorName = args.fFragBuilder->newTmpVarName("inColor");
228             args.fFragBuilder->codeAppendf(
229                     "half4 %s = %s;\n", inputColorName.c_str(), args.fInputColor);
230         }
231 
232         // Copy the incoming coords to a local variable. Code in main might modify the coords
233         // parameter. fSampleCoord could be a varying, so writes to it would be illegal.
234         const char* coords = "float2(0)";
235         SkString coordsVarName;
236         if (fp.usesSampleCoordsDirectly()) {
237             coordsVarName = args.fFragBuilder->newTmpVarName("coords");
238             coords = coordsVarName.c_str();
239             args.fFragBuilder->codeAppendf("float2 %s = %s;\n", coords, args.fSampleCoord);
240         }
241 
242         FPCallbacks callbacks(this,
243                               args,
244                               inputColorName.c_str(),
245                               *program.fContext,
246                               fp.uniformData(),
247                               fp.specialized());
248         SkSL::PipelineStage::ConvertProgram(
249                 program, coords, args.fInputColor, args.fDestColor, &callbacks);
250     }
251 
252 private:
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & _proc)253     void onSetData(const GrGLSLProgramDataManager& pdman,
254                    const GrFragmentProcessor& _proc) override {
255         const GrSkSLFP& outer = _proc.cast<GrSkSLFP>();
256         pdman.setRuntimeEffectUniforms(outer.fEffect->uniforms(),
257                                        SkSpan(fUniformHandles),
258                                        SkSpan(outer.specialized(), outer.uniformCount()),
259                                        outer.uniformData());
260     }
261 
262     std::vector<UniformHandle> fUniformHandles;
263 };
264 
MakeWithData(sk_sp<SkRuntimeEffect> effect,const char * name,sk_sp<SkColorSpace> dstColorSpace,std::unique_ptr<GrFragmentProcessor> inputFP,std::unique_ptr<GrFragmentProcessor> destColorFP,sk_sp<const SkData> uniforms,SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs)265 std::unique_ptr<GrSkSLFP> GrSkSLFP::MakeWithData(
266         sk_sp<SkRuntimeEffect> effect,
267         const char* name,
268         sk_sp<SkColorSpace> dstColorSpace,
269         std::unique_ptr<GrFragmentProcessor> inputFP,
270         std::unique_ptr<GrFragmentProcessor> destColorFP,
271         sk_sp<const SkData> uniforms,
272         SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs) {
273     if (uniforms->size() != effect->uniformSize()) {
274         return nullptr;
275     }
276     size_t uniformSize = uniforms->size();
277     size_t specializedSize = effect->uniforms().size() * sizeof(Specialized);
278     std::unique_ptr<GrSkSLFP> fp(new (uniformSize + specializedSize)
279                                          GrSkSLFP(std::move(effect), name, OptFlags::kNone));
280     sk_careful_memcpy(fp->uniformData(), uniforms->data(), uniformSize);
281     for (auto& childFP : childFPs) {
282         fp->addChild(std::move(childFP), /*mergeOptFlags=*/true);
283     }
284     if (inputFP) {
285         fp->setInput(std::move(inputFP));
286     }
287     if (destColorFP) {
288         fp->setDestColorFP(std::move(destColorFP));
289     }
290     if (fp->fEffect->usesColorTransform() && dstColorSpace) {
291         fp->addColorTransformChildren(std::move(dstColorSpace));
292     }
293     return fp;
294 }
295 
GrSkSLFP(sk_sp<SkRuntimeEffect> effect,const char * name,OptFlags optFlags)296 GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name, OptFlags optFlags)
297         : INHERITED(kGrSkSLFP_ClassID,
298                     static_cast<OptimizationFlags>(optFlags) |
299                             (effect->getFilterColorProgram()
300                                      ? kConstantOutputForConstantInput_OptimizationFlag
301                                      : kNone_OptimizationFlags))
302         , fEffect(std::move(effect))
303         , fName(name)
304         , fUniformSize(SkToU32(fEffect->uniformSize())) {
305     std::fill_n(this->specialized(), this->uniformCount(), Specialized::kNo);
306     if (fEffect->usesSampleCoords()) {
307         this->setUsesSampleCoordsDirectly();
308     }
309     if (fEffect->allowBlender()) {
310         this->setIsBlendFunction();
311     }
312 }
313 
GrSkSLFP(const GrSkSLFP & other)314 GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
315         : INHERITED(other)
316         , fEffect(other.fEffect)
317         , fName(other.fName)
318         , fUniformSize(other.fUniformSize)
319         , fInputChildIndex(other.fInputChildIndex)
320         , fDestColorChildIndex(other.fDestColorChildIndex)
321         , fToLinearSrgbChildIndex(other.fToLinearSrgbChildIndex)
322         , fFromLinearSrgbChildIndex(other.fFromLinearSrgbChildIndex) {
323     std::copy_n(other.specialized(), this->uniformCount(), this->specialized());
324     sk_careful_memcpy(this->uniformData(), other.uniformData(), fUniformSize);
325 }
326 
addChild(std::unique_ptr<GrFragmentProcessor> child,bool mergeOptFlags)327 void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child, bool mergeOptFlags) {
328     SkASSERTF(fInputChildIndex == -1, "all addChild calls must happen before setInput");
329     SkASSERTF(fDestColorChildIndex == -1, "all addChild calls must happen before setDestColorFP");
330     int childIndex = this->numChildProcessors();
331     SkASSERT((size_t)childIndex < fEffect->fSampleUsages.size());
332     if (mergeOptFlags) {
333         this->mergeOptimizationFlags(ProcessorOptimizationFlags(child.get()));
334     }
335     this->registerChild(std::move(child), fEffect->fSampleUsages[childIndex]);
336 }
337 
setInput(std::unique_ptr<GrFragmentProcessor> input)338 void GrSkSLFP::setInput(std::unique_ptr<GrFragmentProcessor> input) {
339     SkASSERTF(fInputChildIndex == -1, "setInput should not be called more than once");
340     fInputChildIndex = this->numChildProcessors();
341     SkASSERT((size_t)fInputChildIndex >= fEffect->fSampleUsages.size());
342     this->mergeOptimizationFlags(ProcessorOptimizationFlags(input.get()));
343     this->registerChild(std::move(input), SkSL::SampleUsage::PassThrough());
344 }
345 
setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP)346 void GrSkSLFP::setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP) {
347     SkASSERTF(fEffect->allowBlender(), "dest colors are only used by blend effects");
348     SkASSERTF(fDestColorChildIndex == -1, "setDestColorFP should not be called more than once");
349     fDestColorChildIndex = this->numChildProcessors();
350     SkASSERT((size_t)fDestColorChildIndex >= fEffect->fSampleUsages.size());
351     this->mergeOptimizationFlags(ProcessorOptimizationFlags(destColorFP.get()));
352     this->registerChild(std::move(destColorFP), SkSL::SampleUsage::PassThrough());
353 }
354 
addColorTransformChildren(sk_sp<SkColorSpace> dstColorSpace)355 void GrSkSLFP::addColorTransformChildren(sk_sp<SkColorSpace> dstColorSpace) {
356     SkASSERTF(fToLinearSrgbChildIndex == -1 && fFromLinearSrgbChildIndex == -1,
357               "addColorTransformChildren should not be called more than once");
358 
359     // We use child FPs for the color transforms. They're really just code snippets that get
360     // invoked, but each one injects a collection of uniforms and helper functions. Doing it
361     // this way leverages per-FP name mangling to avoid conflicts.
362     auto workingToLinear = GrColorSpaceXformEffect::Make(nullptr,
363                                                          dstColorSpace.get(),
364                                                          kUnpremul_SkAlphaType,
365                                                          sk_srgb_linear_singleton(),
366                                                          kUnpremul_SkAlphaType);
367     auto linearToWorking = GrColorSpaceXformEffect::Make(nullptr,
368                                                          sk_srgb_linear_singleton(),
369                                                          kUnpremul_SkAlphaType,
370                                                          dstColorSpace.get(),
371                                                          kUnpremul_SkAlphaType);
372 
373     fToLinearSrgbChildIndex = this->numChildProcessors();
374     SkASSERT((size_t)fToLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
375     this->registerChild(std::move(workingToLinear), SkSL::SampleUsage::PassThrough());
376 
377     fFromLinearSrgbChildIndex = this->numChildProcessors();
378     SkASSERT((size_t)fFromLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
379     this->registerChild(std::move(linearToWorking), SkSL::SampleUsage::PassThrough());
380 }
381 
onMakeProgramImpl() const382 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrSkSLFP::onMakeProgramImpl() const {
383     return std::make_unique<Impl>();
384 }
385 
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const386 void GrSkSLFP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
387     // In the unlikely event of a hash collision, we also include the uniform size in the key.
388     // That ensures that we will (at worst) use the wrong program, but one that expects the same
389     // amount of uniform data.
390     b->add32(fEffect->hash());
391     b->add32(fUniformSize);
392 
393     const Specialized* specialized = this->specialized();
394     const uint8_t* uniformData = this->uniformData();
395     size_t uniformCount = this->uniformCount();
396     auto iter = fEffect->uniforms().begin();
397 
398     for (size_t i = 0; i < uniformCount; ++i, ++iter) {
399         bool specialize = specialized[i] == Specialized::kYes;
400         b->addBool(specialize, "specialize");
401         if (specialize) {
402             b->addBytes(iter->sizeInBytes(), uniformData + iter->offset, iter->name);
403         }
404     }
405 }
406 
onIsEqual(const GrFragmentProcessor & other) const407 bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
408     const GrSkSLFP& sk = other.cast<GrSkSLFP>();
409     const size_t specializedSize = this->uniformCount() * sizeof(Specialized);
410     return fEffect->hash() == sk.fEffect->hash() &&
411            this->uniformCount() == sk.uniformCount() &&
412            fUniformSize == sk.fUniformSize &&
413            !sk_careful_memcmp(this->uniformData(),
414                               sk.uniformData(),
415                               fUniformSize + specializedSize);
416 }
417 
clone() const418 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
419     return std::unique_ptr<GrFragmentProcessor>(new (UniformPayloadSize(fEffect.get()))
420                                                         GrSkSLFP(*this));
421 }
422 
constantOutputForConstantInput(const SkPMColor4f & inputColor) const423 SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputColor) const {
424     const SkFilterColorProgram* program = fEffect->getFilterColorProgram();
425     SkASSERT(program);
426 
427     auto evalChild = [&](int index, SkPMColor4f color) {
428         return ConstantOutputForConstantInput(this->childProcessor(index), color);
429     };
430 
431     SkPMColor4f color = (fInputChildIndex >= 0)
432                                 ? ConstantOutputForConstantInput(
433                                           this->childProcessor(fInputChildIndex), inputColor)
434                                 : inputColor;
435     return program->eval(color, this->uniformData(), evalChild);
436 }
437 
438 /**************************************************************************************************/
439 
440 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP)
441 
442 #if GR_TEST_UTILS
443 
444 #include "include/effects/SkOverdrawColorFilter.h"
445 #include "src/core/SkColorFilterBase.h"
446 
447 extern const char* SKSL_OVERDRAW_SRC;
448 
TestCreate(GrProcessorTestData * d)449 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
450     SkColor colors[SkOverdrawColorFilter::kNumColors];
451     for (SkColor& c : colors) {
452         c = d->fRandom->nextU();
453     }
454     auto filter = SkOverdrawColorFilter::MakeWithSkColors(colors);
455     SkSurfaceProps props; // default props for testing
456     auto [success, fp] = as_CFB(filter)->asFragmentProcessor(/*inputFP=*/nullptr, d->context(),
457                                                              GrColorInfo{}, props);
458     SkASSERT(success);
459     return std::move(fp);
460 }
461 
462 #endif
463