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