xref: /aosp_15_r20/external/skia/src/gpu/graphite/ShaderCodeDictionary.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 Google LLC
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/graphite/ShaderCodeDictionary.h"
9 
10 #include "include/core/SkTileMode.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "src/core/SkColorSpaceXformSteps.h"
13 #include "src/core/SkRuntimeEffectPriv.h"
14 #include "src/gpu/BlendFormula.h"
15 #include "src/gpu/graphite/Caps.h"
16 #include "src/gpu/graphite/ContextUtils.h"
17 #include "src/gpu/graphite/ReadSwizzle.h"
18 #include "src/gpu/graphite/Renderer.h"
19 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
20 #include "src/gpu/graphite/ShaderInfo.h"
21 #include "src/sksl/SkSLString.h"
22 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
23 #include "src/sksl/ir/SkSLVarDeclarations.h"
24 
25 using namespace skia_private;
26 using namespace SkKnownRuntimeEffects;
27 
28 namespace skgpu::graphite {
29 
30 static_assert(static_cast<int>(BuiltInCodeSnippetID::kLast) < kSkiaBuiltInReservedCnt);
31 
32 namespace {
33 
get_known_rte_name(StableKey key)34 const char* get_known_rte_name(StableKey key) {
35     switch (key) {
36 #define M(type) case StableKey::k##type : return "KnownRuntimeEffect_" #type;
37 #define M1(type)
38 #define M2(type, initializer) case StableKey::k##type : return "KnownRuntimeEffect_" #type;
39         SK_ALL_STABLEKEYS(M, M1, M2)
40 #undef M2
41 #undef M1
42 #undef M
43     }
44 
45     SkUNREACHABLE;
46 }
47 
get_storage_buffer_access(const char * bufferNamePrefix,const char * ssboIndex,const char * uniformName)48 std::string get_storage_buffer_access(const char* bufferNamePrefix,
49                                       const char* ssboIndex,
50                                       const char* uniformName) {
51     return SkSL::String::printf("%sUniformData[%s].%s", bufferNamePrefix, ssboIndex, uniformName);
52 }
53 
get_mangled_name(const std::string & baseName,int manglingSuffix)54 std::string get_mangled_name(const std::string& baseName, int manglingSuffix) {
55     return baseName + "_" + std::to_string(manglingSuffix);
56 }
57 
get_mangled_uniform_name(const ShaderInfo & shaderInfo,const Uniform & uniform,int manglingSuffix)58 std::string get_mangled_uniform_name(const ShaderInfo& shaderInfo,
59                                      const Uniform& uniform,
60                                      int manglingSuffix) {
61     std::string result;
62 
63     if (uniform.isPaintColor()) {
64         // Due to deduplication there will only ever be one of these
65         result = uniform.name();
66     } else {
67         result = uniform.name() + std::string("_") + std::to_string(manglingSuffix);
68     }
69     if (shaderInfo.ssboIndex()) {
70         result = get_storage_buffer_access("fs", shaderInfo.ssboIndex(), result.c_str());
71     }
72     return result;
73 }
74 
get_mangled_sampler_name(const TextureAndSampler & tex,int manglingSuffix)75 std::string get_mangled_sampler_name(const TextureAndSampler& tex, int manglingSuffix) {
76     return tex.name() + std::string("_") + std::to_string(manglingSuffix);
77 }
78 
get_mangled_struct_reference(const ShaderInfo & shaderInfo,const ShaderNode * node)79 std::string get_mangled_struct_reference(const ShaderInfo& shaderInfo,
80                                          const ShaderNode* node) {
81     SkASSERT(node->entry()->fUniformStructName);
82     std::string result = "node_" + std::to_string(node->keyIndex()); // Field holding the struct
83     if (shaderInfo.ssboIndex()) {
84         result = get_storage_buffer_access("fs", shaderInfo.ssboIndex(), result.c_str());
85     }
86     return result;
87 }
88 
stitch_csv(SkSpan<const std::string> args)89 std::string stitch_csv(SkSpan<const std::string> args) {
90     std::string code = "";
91     const char* separator = "";
92     for (const std::string& arg : args) {
93         code += separator;
94         code += arg;
95         separator = ", ";
96     }
97 
98     return code;
99 }
100 
101 // If 'args' is null, the generated list is assumed to be for parameter declarations. If it's non
102 // null, it is assumed to be the expressions to invoke the default signature.
append_defaults(TArray<std::string> * list,const ShaderNode * node,const ShaderSnippet::Args * args)103 void append_defaults(TArray<std::string>* list,
104                      const ShaderNode* node,
105                      const ShaderSnippet::Args* args) {
106     // Use the node's aggregate required flags so that the provided dynamic variables propagate
107     // to the child nodes that require them.
108     if (node->requiredFlags() & SnippetRequirementFlags::kPriorStageOutput) {
109         list->push_back(args ? args->fPriorStageOutput.c_str() : "half4 inColor");
110     }
111     if (node->requiredFlags() & SnippetRequirementFlags::kBlenderDstColor) {
112         list->push_back(args ? args->fBlenderDstColor.c_str() : "half4 destColor");
113     }
114     if (node->requiredFlags() & SnippetRequirementFlags::kLocalCoords) {
115         list->push_back(args ? args->fFragCoord.c_str() : "float2 pos");
116     }
117 
118     // Special variables and/or "global" scope variables that have to propagate
119     // through the node tree.
120     if (node->requiredFlags() & SnippetRequirementFlags::kPrimitiveColor) {
121         list->push_back(args ? "primitiveColor" : "half4 primitiveColor");
122     }
123 }
124 
append_uniforms(TArray<std::string> * list,const ShaderInfo & shaderInfo,const ShaderNode * node,SkSpan<const std::string> childOutputs)125 void append_uniforms(TArray<std::string>* list,
126                      const ShaderInfo& shaderInfo,
127                      const ShaderNode* node,
128                      SkSpan<const std::string> childOutputs) {
129     const ShaderSnippet* entry = node->entry();
130 
131     if (entry->fUniformStructName) {
132         // The node's uniforms are aggregated in a sub-struct within the global uniforms so we just
133         // need to append a reference to the node's instance
134         list->push_back(get_mangled_struct_reference(shaderInfo, node));
135     } else {
136         // The uniforms are in the global scope, so just pass in the ones bound to 'node'
137         for (int i = 0; i < entry->fUniforms.size(); ++i) {
138             list->push_back(get_mangled_uniform_name(shaderInfo,
139                                                      entry->fUniforms[i],
140                                                      node->keyIndex()));
141         }
142     }
143 
144     // Append samplers
145     for (int i = 0; i < entry->fTexturesAndSamplers.size(); ++i) {
146         list->push_back(get_mangled_sampler_name(entry->fTexturesAndSamplers[i], node->keyIndex()));
147     }
148 
149     // Append gradient buffer.
150     if (node->requiredFlags() & SnippetRequirementFlags::kGradientBuffer) {
151         list->push_back(ShaderInfo::kGradientBufferName);
152     }
153 
154     // Append child output names.
155     if (!childOutputs.empty()) {
156         list->push_back_n(childOutputs.size(), childOutputs.data());
157     }
158 }
159 
160 // If we have no children, the default expression just calls a built-in snippet with the signature:
161 //     half4 BuiltinFunctionName(/* required variable inputs (e.g. float2 pos) */,
162 //                               /* all uniforms as parameters (bound to node's values) */) { ... }
163 // If we do have children, we will have created a glue function in the preamble and that is called
164 // instead. Its signature looks like this:
165 //     half4 SnippetName_N(/* required variable inputs (e.g. float2 pos) */) { ... }
invoke_node(const ShaderInfo & shaderInfo,const ShaderNode * node,const ShaderSnippet::Args & args)166 std::string invoke_node(const ShaderInfo& shaderInfo,
167                         const ShaderNode* node,
168                         const ShaderSnippet::Args& args) {
169     std::string fnName;
170     STArray<3, std::string> params; // 1-2 inputs and a uniform struct or texture
171 
172     if (node->numChildren() == 0 && node->entry()->fStaticFunctionName) {
173         // We didn't generate a helper function in the preamble, so add uniforms to the parameter
174         // list and call the static function directly.
175         fnName = node->entry()->fStaticFunctionName;
176         append_defaults(&params, node, &args);
177         append_uniforms(&params, shaderInfo, node, /*childOutputs=*/{});
178     } else {
179         // Invoke the generated helper function added to the preamble, which will handle invoking
180         // any children and appending their values to the rest of the static fn's arguments.
181         fnName = get_mangled_name(node->entry()->fName, node->keyIndex());
182         append_defaults(&params, node, &args);
183     }
184 
185     return SkSL::String::printf("%s(%s)", fnName.c_str(), stitch_csv(params).c_str());
186 }
187 
188 // Emit a declaration for a helper function that represents the ShaderNode (named using the node's
189 // mangled name). The dynamic parameters are declared to match kDefaultArgs. The returned string
190 // can either be followed by a "{ body }" to fully define it or a ";" for a forward declaration.
emit_helper_declaration(const ShaderNode * node)191 std::string emit_helper_declaration(const ShaderNode* node) {
192     const ShaderSnippet* entry = node->entry();
193     std::string helperFnName = get_mangled_name(entry->fName, node->keyIndex());
194 
195     STArray<3, std::string> params;
196     append_defaults(&params, node, /*args=*/nullptr); // null args emits declarations
197 
198     return SkSL::String::printf("half4 %s(%s)", helperFnName.c_str(), stitch_csv(params).c_str());
199 }
200 
201 } // anonymous namespace
202 
203 //--------------------------------------------------------------------------------------------------
204 // ShaderSnippet
205 
206 const ShaderSnippet::Args ShaderSnippet::kDefaultArgs = {"inColor", "destColor", "pos"};
207 
208 //--------------------------------------------------------------------------------------------------
209 // ShaderNode
210 
211 // If we have no children, we don't need to add anything into the preamble.
212 // If we have child entries, we create a function in the preamble with a signature of:
213 //     half4 SnippetName_N(/* required variable inputs (e.g. float2 pos) */) { ... }
214 // This function invokes each child in sequence, and then calls the built-in function, passing all
215 // uniforms and child outputs along:
216 //     half4 BuiltinFunctionName(/* required variable inputs (e.g. float2 pos) */,
217 //                               /* all uniforms as parameters */,
218 //                               /* all child output variable names as parameters */);
generateDefaultPreamble(const ShaderInfo & shaderInfo) const219 std::string ShaderNode::generateDefaultPreamble(const ShaderInfo& shaderInfo) const {
220     if (this->numChildren() == 0) {
221         // We don't need a helper function to wrap the snippet's static function
222         return "";
223     }
224 
225     std::string code = emit_helper_declaration(this) + " {";
226 
227     // Invoke each child with unmodified input values and collect in a list of local variables
228     STArray<2, std::string> childOutputVarNames;
229     for (const ShaderNode* child : this->children()) {
230         // Emit glue code into our helper function body (i.e. lifting the child execution up front
231         // so their outputs can be passed to the static module function for the node's snippet).
232         childOutputVarNames.push_back(
233                 child->invokeAndAssign(shaderInfo, ShaderSnippet::kDefaultArgs, &code));
234     }
235 
236     // Finally, invoke the snippet from the helper function, passing uniforms and child outputs.
237     STArray<3, std::string> params;
238     append_defaults(&params, this, &ShaderSnippet::kDefaultArgs);
239     append_uniforms(&params, shaderInfo, this, childOutputVarNames);
240 
241     SkSL::String::appendf(&code,
242                               "return %s(%s);"
243                           "}",
244                           this->entry()->fStaticFunctionName,
245                           stitch_csv(params).c_str());
246     return code;
247 }
248 
249 // Emit the glue code needed to invoke a single static helper isolated within its own scope.
250 // Glue code will assign the resulting color into a variable `half4 outColor%d`, where the %d is
251 // filled in with 'node->keyIndex()'.
invokeAndAssign(const ShaderInfo & shaderInfo,const ShaderSnippet::Args & args,std::string * funcBody) const252 std::string ShaderNode::invokeAndAssign(const ShaderInfo& shaderInfo,
253                                         const ShaderSnippet::Args& args,
254                                         std::string* funcBody) const {
255     std::string expr = invoke_node(shaderInfo, this, args);
256     std::string outputVar = get_mangled_name("outColor", this->keyIndex());
257     SkSL::String::appendf(funcBody,
258                           "// [%d] %s\n"
259                           "half4 %s = %s;",
260                           this->keyIndex(),
261                           this->entry()->fName,
262                           outputVar.c_str(),
263                           expr.c_str());
264     return outputVar;
265 }
266 
267 //--------------------------------------------------------------------------------------------------
268 // ShaderCodeDictionary
269 
findOrCreate(PaintParamsKeyBuilder * builder)270 UniquePaintParamsID ShaderCodeDictionary::findOrCreate(PaintParamsKeyBuilder* builder) {
271     AutoLockBuilderAsKey keyView{builder};
272     if (!keyView->isValid()) {
273         return UniquePaintParamsID::InvalidID();
274     }
275 
276     SkAutoSpinlock lock{fSpinLock};
277 
278     UniquePaintParamsID* existingEntry = fPaintKeyToID.find(*keyView);
279     if (existingEntry) {
280         SkASSERT(fIDToPaintKey[(*existingEntry).asUInt()] == *keyView);
281         return *existingEntry;
282     }
283 
284     // Detach from the builder and copy into the arena
285     PaintParamsKey key = keyView->clone(&fArena);
286     UniquePaintParamsID newID{SkTo<uint32_t>(fIDToPaintKey.size())};
287 
288     fPaintKeyToID.set(key, newID);
289     fIDToPaintKey.push_back(key);
290     return newID;
291 }
292 
lookup(UniquePaintParamsID codeID) const293 PaintParamsKey ShaderCodeDictionary::lookup(UniquePaintParamsID codeID) const {
294     if (!codeID.isValid()) {
295         return PaintParamsKey::Invalid();
296     }
297 
298     SkAutoSpinlock lock{fSpinLock};
299     SkASSERT(codeID.asUInt() < SkTo<uint32_t>(fIDToPaintKey.size()));
300     return fIDToPaintKey[codeID.asUInt()];
301 }
302 
getEntry(int codeSnippetID) const303 const ShaderSnippet* ShaderCodeDictionary::getEntry(int codeSnippetID) const {
304     if (codeSnippetID < 0) {
305         return nullptr;
306     }
307 
308     if (codeSnippetID < kBuiltInCodeSnippetIDCount) {
309         return &fBuiltInCodeSnippets[codeSnippetID];
310     }
311 
312     SkAutoSpinlock lock{fSpinLock};
313 
314     if (codeSnippetID >= kSkiaKnownRuntimeEffectsStart &&
315         codeSnippetID < kSkiaKnownRuntimeEffectsStart + kStableKeyCnt) {
316         int knownRTECodeSnippetID = codeSnippetID - kSkiaKnownRuntimeEffectsStart;
317 
318         // TODO(b/238759147): if the snippet hasn't been initialized, get the SkRuntimeEffect and
319         // initialize it here
320         SkASSERT(fKnownRuntimeEffectCodeSnippets[knownRTECodeSnippetID].fPreambleGenerator);
321         return &fKnownRuntimeEffectCodeSnippets[knownRTECodeSnippetID];
322     }
323 
324     // TODO(b/238759147): handle Android and chrome known runtime effects
325 
326     if (codeSnippetID >= kUnknownRuntimeEffectIDStart) {
327         int userDefinedCodeSnippetID = codeSnippetID - kUnknownRuntimeEffectIDStart;
328         if (userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size())) {
329             return &fUserDefinedCodeSnippets[userDefinedCodeSnippetID];
330         }
331     }
332 
333     return nullptr;
334 }
335 
336 //--------------------------------------------------------------------------------------------------
337 namespace {
338 
339 static constexpr int kNumCoordinateManipulateChildren = 1;
340 
341 // Create a helper function that manipulates the coordinates passed into a child. The specific
342 // manipulation is pre-determined by the code id (local matrix or clamp). This helper function meets
343 // the requirements for use with GenerateDefaultExpression, so there's no need to have a separate
344 // special GenerateLocalMatrixExpression.
345 // TODO: This is effectively GenerateComposePreamble except that 'node' is counting as the inner.
GenerateCoordManipulationPreamble(const ShaderInfo & shaderInfo,const ShaderNode * node)346 std::string GenerateCoordManipulationPreamble(const ShaderInfo& shaderInfo,
347                                               const ShaderNode* node) {
348     SkASSERT(node->numChildren() == kNumCoordinateManipulateChildren);
349 
350     std::string perspectiveStatement;
351 
352     const ShaderSnippet::Args& defaultArgs = ShaderSnippet::kDefaultArgs;
353     ShaderSnippet::Args localArgs = ShaderSnippet::kDefaultArgs;
354     if (node->child(0)->requiredFlags() & SnippetRequirementFlags::kLocalCoords) {
355         std::string controlUni =
356                 get_mangled_uniform_name(shaderInfo, node->entry()->fUniforms[0], node->keyIndex());
357 
358         if (node->codeSnippetId() == (int) BuiltInCodeSnippetID::kLocalMatrixShader) {
359             localArgs.fFragCoord = SkSL::String::printf("(%s * %s.xy01).xy",
360                                                         controlUni.c_str(),
361                                                         defaultArgs.fFragCoord.c_str());
362         } else if (node->codeSnippetId() == (int) BuiltInCodeSnippetID::kLocalMatrixShaderPersp) {
363             perspectiveStatement = SkSL::String::printf("float4 perspCoord = %s * %s.xy01;",
364                                                         controlUni.c_str(),
365                                                         defaultArgs.fFragCoord.c_str());
366             localArgs.fFragCoord = "perspCoord.xy / perspCoord.w";
367         } else {
368             SkASSERT(node->codeSnippetId() == (int) BuiltInCodeSnippetID::kCoordClampShader);
369             localArgs.fFragCoord = SkSL::String::printf("clamp(%s, %s.LT, %s.RB)",
370                                                         defaultArgs.fFragCoord.c_str(),
371                                                         controlUni.c_str(), controlUni.c_str());
372         }
373     } // else this is a no-op
374 
375     std::string decl = emit_helper_declaration(node);
376     std::string invokeChild = invoke_node(shaderInfo, node->child(0), localArgs);
377     return SkSL::String::printf("%s { %s return %s; }",
378                                 decl.c_str(),
379                                 perspectiveStatement.c_str(),
380                                 invokeChild.c_str());
381 }
382 
383 //--------------------------------------------------------------------------------------------------
384 
385 // Compose N-1 children into the Nth child, must have at least two children. The ith child provides
386 // the value for the ith enabled ShaderSnippet::Arg.
GenerateComposePreamble(const ShaderInfo & shaderInfo,const ShaderNode * node)387 std::string GenerateComposePreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) {
388     SkASSERT(node->numChildren() >= 2);
389 
390     const ShaderNode* outer = node->child(node->numChildren() - 1);
391 
392 #if defined(SK_DEBUG)
393     const int numOuterParameters =
394             SkToBool((outer->requiredFlags() & SnippetRequirementFlags::kPriorStageOutput)) +
395             SkToBool((outer->requiredFlags() & SnippetRequirementFlags::kBlenderDstColor)) +
396             SkToBool((outer->requiredFlags() & SnippetRequirementFlags::kLocalCoords));
397     SkASSERT(node->numChildren() == numOuterParameters + 1);
398 #endif
399 
400     const ShaderSnippet::Args& defaultArgs = ShaderSnippet::kDefaultArgs;
401     ShaderSnippet::Args outerArgs = ShaderSnippet::kDefaultArgs;
402     int child = 0;
403     if (outer->requiredFlags() & SnippetRequirementFlags::kLocalCoords) {
404         outerArgs.fFragCoord = invoke_node(shaderInfo, node->child(child++), defaultArgs);
405     }
406     if (outer->requiredFlags() & SnippetRequirementFlags::kPriorStageOutput) {
407         outerArgs.fPriorStageOutput = invoke_node(shaderInfo, node->child(child++), defaultArgs);
408     }
409     if (outer->requiredFlags() & SnippetRequirementFlags::kBlenderDstColor) {
410         outerArgs.fBlenderDstColor = invoke_node(shaderInfo, node->child(child++), defaultArgs);
411     }
412 
413     std::string decl = emit_helper_declaration(node);
414     std::string invokeOuter = invoke_node(shaderInfo, outer, outerArgs);
415     return SkSL::String::printf("%s { return %s; }", decl.c_str(), invokeOuter.c_str());
416 }
417 
418 //--------------------------------------------------------------------------------------------------
419 class GraphitePipelineCallbacks : public SkSL::PipelineStage::Callbacks {
420 public:
GraphitePipelineCallbacks(const ShaderInfo & shaderInfo,const ShaderNode * node,std::string * preamble,const SkRuntimeEffect * effect)421     GraphitePipelineCallbacks(const ShaderInfo& shaderInfo,
422                               const ShaderNode* node,
423                               std::string* preamble,
424                               [[maybe_unused]] const SkRuntimeEffect* effect)
425             : fShaderInfo(shaderInfo)
426             , fNode(node)
427             , fPreamble(preamble) {
428         SkDEBUGCODE(fEffect = effect;)
429     }
430 
declareUniform(const SkSL::VarDeclaration * decl)431     std::string declareUniform(const SkSL::VarDeclaration* decl) override {
432         std::string result = get_mangled_name(std::string(decl->var()->name()), fNode->keyIndex());
433         if (fShaderInfo.ssboIndex()) {
434             result = get_storage_buffer_access("fs", fShaderInfo.ssboIndex(), result.c_str());
435         }
436         return result;
437     }
438 
defineFunction(const char * decl,const char * body,bool isMain)439     void defineFunction(const char* decl, const char* body, bool isMain) override {
440         if (isMain) {
441             SkSL::String::appendf(
442                     fPreamble,
443                     "%s { %s }",
444                     emit_helper_declaration(fNode).c_str(),
445                     body);
446         } else {
447             SkSL::String::appendf(fPreamble, "%s {%s}\n", decl, body);
448         }
449     }
450 
declareFunction(const char * decl)451     void declareFunction(const char* decl) override {
452         *fPreamble += std::string(decl);
453     }
454 
defineStruct(const char * definition)455     void defineStruct(const char* definition) override {
456         *fPreamble += std::string(definition);
457     }
458 
declareGlobal(const char * declaration)459     void declareGlobal(const char* declaration) override {
460         *fPreamble += std::string(declaration);
461     }
462 
sampleShader(int index,std::string coords)463     std::string sampleShader(int index, std::string coords) override {
464         ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
465         args.fFragCoord = coords;
466         return invoke_node(fShaderInfo, fNode->child(index), args);
467     }
468 
sampleColorFilter(int index,std::string color)469     std::string sampleColorFilter(int index, std::string color) override {
470         ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
471         args.fPriorStageOutput = color;
472         return invoke_node(fShaderInfo, fNode->child(index), args);
473     }
474 
sampleBlender(int index,std::string src,std::string dst)475     std::string sampleBlender(int index, std::string src, std::string dst) override {
476         ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
477         args.fPriorStageOutput = src;
478         args.fBlenderDstColor = dst;
479         return invoke_node(fShaderInfo, fNode->child(index), args);
480     }
481 
toLinearSrgb(std::string color)482     std::string toLinearSrgb(std::string color) override {
483         SkASSERT(SkRuntimeEffectPriv::UsesColorTransform(fEffect));
484         // If we use color transforms (e.g. reference [to|from]LinearSrgb(), we dynamically add two
485         // children to the runtime effect's node after all explicitly declared children. The
486         // conversion *to* linear srgb is the second-to-last child node, and the conversion *from*
487         // linear srgb is the last child node.)
488         const ShaderNode* toLinearSrgbNode = fNode->child(fNode->numChildren() - 2);
489         SkASSERT(toLinearSrgbNode->codeSnippetId() ==
490                         (int) BuiltInCodeSnippetID::kColorSpaceXformColorFilter);
491 
492         ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
493         args.fPriorStageOutput = SkSL::String::printf("(%s).rgb1", color.c_str());
494         std::string xformedColor = invoke_node(fShaderInfo, toLinearSrgbNode, args);
495         return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
496     }
497 
498 
fromLinearSrgb(std::string color)499     std::string fromLinearSrgb(std::string color) override {
500         SkASSERT(SkRuntimeEffectPriv::UsesColorTransform(fEffect));
501         // If we use color transforms (e.g. reference [to|from]LinearSrgb()), we dynamically add two
502         // children to the runtime effect's node after all explicitly declared children. The
503         // conversion *to* linear srgb is the second-to-last child node, and the conversion *from*
504         // linear srgb is the last child node.
505         const ShaderNode* fromLinearSrgbNode = fNode->child(fNode->numChildren() - 1);
506         SkASSERT(fromLinearSrgbNode->codeSnippetId() ==
507                         (int) BuiltInCodeSnippetID::kColorSpaceXformColorFilter);
508 
509         ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
510         args.fPriorStageOutput = SkSL::String::printf("(%s).rgb1", color.c_str());
511         std::string xformedColor = invoke_node(fShaderInfo, fromLinearSrgbNode, args);
512         return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
513     }
514 
getMangledName(const char * name)515     std::string getMangledName(const char* name) override {
516         return get_mangled_name(name, fNode->keyIndex());
517     }
518 
519 private:
520     const ShaderInfo& fShaderInfo;
521     const ShaderNode* fNode;
522     std::string* fPreamble;
523     SkDEBUGCODE(const SkRuntimeEffect* fEffect;)
524 };
525 
GenerateRuntimeShaderPreamble(const ShaderInfo & shaderInfo,const ShaderNode * node)526 std::string GenerateRuntimeShaderPreamble(const ShaderInfo& shaderInfo,
527                                           const ShaderNode* node) {
528     // Find this runtime effect in the runtime-effect dictionary.
529     SkASSERT(node->codeSnippetId() >= kBuiltInCodeSnippetIDCount);
530     const SkRuntimeEffect* effect;
531     if (node->codeSnippetId() < kSkiaKnownRuntimeEffectsStart + kStableKeyCnt) {
532         effect = GetKnownRuntimeEffect(static_cast<StableKey>(node->codeSnippetId()));
533     } else {
534         SkASSERT(node->codeSnippetId() >= kUnknownRuntimeEffectIDStart);
535         effect = shaderInfo.runtimeEffectDictionary()->find(node->codeSnippetId());
536     }
537     SkASSERT(effect);
538 
539     const SkSL::Program& program = SkRuntimeEffectPriv::Program(*effect);
540     const ShaderSnippet::Args& args = ShaderSnippet::kDefaultArgs;
541     std::string preamble;
542     GraphitePipelineCallbacks callbacks{shaderInfo, node, &preamble, effect};
543     SkSL::PipelineStage::ConvertProgram(program,
544                                         args.fFragCoord.c_str(),
545                                         args.fPriorStageOutput.c_str(),
546                                         args.fBlenderDstColor.c_str(),
547                                         &callbacks);
548     return preamble;
549 }
550 
551 } // anonymous namespace
552 
553 #if defined(SK_DEBUG)
isValidID(int snippetID) const554 bool ShaderCodeDictionary::isValidID(int snippetID) const {
555     if (snippetID < 0) {
556         return false;
557     }
558 
559     if (snippetID < kBuiltInCodeSnippetIDCount) {
560         return true;
561     }
562     if (snippetID >= kSkiaKnownRuntimeEffectsStart && snippetID < kSkiaKnownRuntimeEffectsEnd) {
563         return snippetID < kSkiaKnownRuntimeEffectsStart + kStableKeyCnt;
564     }
565 
566     SkAutoSpinlock lock{fSpinLock};
567 
568     if (snippetID >= kUnknownRuntimeEffectIDStart) {
569         int userDefinedCodeSnippetID = snippetID - kUnknownRuntimeEffectIDStart;
570         return userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size());
571     }
572 
573     return false;
574 }
575 
dump(UniquePaintParamsID id) const576 void ShaderCodeDictionary::dump(UniquePaintParamsID id) const {
577     this->lookup(id).dump(this, id);
578 }
579 #endif
580 
uniform_type_to_sksl_type(const SkRuntimeEffect::Uniform & u)581 static SkSLType uniform_type_to_sksl_type(const SkRuntimeEffect::Uniform& u) {
582     using Type = SkRuntimeEffect::Uniform::Type;
583     if (u.flags & SkRuntimeEffect::Uniform::kHalfPrecision_Flag) {
584         switch (u.type) {
585             case Type::kFloat:    return SkSLType::kHalf;
586             case Type::kFloat2:   return SkSLType::kHalf2;
587             case Type::kFloat3:   return SkSLType::kHalf3;
588             case Type::kFloat4:   return SkSLType::kHalf4;
589             case Type::kFloat2x2: return SkSLType::kHalf2x2;
590             case Type::kFloat3x3: return SkSLType::kHalf3x3;
591             case Type::kFloat4x4: return SkSLType::kHalf4x4;
592             // NOTE: shorts cannot be uniforms, so we shouldn't ever get here.
593             // Defensively return the full precision integer type.
594             case Type::kInt:      SkDEBUGFAIL("unsupported uniform type"); return SkSLType::kInt;
595             case Type::kInt2:     SkDEBUGFAIL("unsupported uniform type"); return SkSLType::kInt2;
596             case Type::kInt3:     SkDEBUGFAIL("unsupported uniform type"); return SkSLType::kInt3;
597             case Type::kInt4:     SkDEBUGFAIL("unsupported uniform type"); return SkSLType::kInt4;
598         }
599     } else {
600         switch (u.type) {
601             case Type::kFloat:    return SkSLType::kFloat;
602             case Type::kFloat2:   return SkSLType::kFloat2;
603             case Type::kFloat3:   return SkSLType::kFloat3;
604             case Type::kFloat4:   return SkSLType::kFloat4;
605             case Type::kFloat2x2: return SkSLType::kFloat2x2;
606             case Type::kFloat3x3: return SkSLType::kFloat3x3;
607             case Type::kFloat4x4: return SkSLType::kFloat4x4;
608             case Type::kInt:      return SkSLType::kInt;
609             case Type::kInt2:     return SkSLType::kInt2;
610             case Type::kInt3:     return SkSLType::kInt3;
611             case Type::kInt4:     return SkSLType::kInt4;
612         }
613     }
614     SkUNREACHABLE;
615 }
616 
addTextToArena(std::string_view text)617 const char* ShaderCodeDictionary::addTextToArena(std::string_view text) {
618     char* textInArena = fArena.makeArrayDefault<char>(text.size() + 1);
619     memcpy(textInArena, text.data(), text.size());
620     textInArena[text.size()] = '\0';
621     return textInArena;
622 }
623 
convertUniforms(const SkRuntimeEffect * effect)624 SkSpan<const Uniform> ShaderCodeDictionary::convertUniforms(const SkRuntimeEffect* effect) {
625     using rteUniform = SkRuntimeEffect::Uniform;
626     SkSpan<const rteUniform> uniforms = effect->uniforms();
627 
628     const int numUniforms = uniforms.size();
629 
630     // Convert the SkRuntimeEffect::Uniform array into its Uniform equivalent.
631     Uniform* uniformArray = fArena.makeInitializedArray<Uniform>(numUniforms, [&](int index) {
632         const rteUniform* u;
633         u = &uniforms[index];
634 
635         // The existing uniform names live in the passed-in SkRuntimeEffect and may eventually
636         // disappear. Copy them into fArena. (It's safe to do this within makeInitializedArray; the
637         // entire array is allocated in one big slab before any initialization calls are done.)
638         const char* name = this->addTextToArena(u->name);
639 
640         // Add one Uniform to our array.
641         SkSLType type = uniform_type_to_sksl_type(*u);
642         return (u->flags & rteUniform::kArray_Flag) ? Uniform(name, type, u->count)
643                                                     : Uniform(name, type);
644     });
645 
646     return SkSpan<const Uniform>(uniformArray, numUniforms);
647 }
648 
convertRuntimeEffect(const SkRuntimeEffect * effect,const char * name)649 ShaderSnippet ShaderCodeDictionary::convertRuntimeEffect(const SkRuntimeEffect* effect,
650                                                          const char* name) {
651     SkEnumBitMask<SnippetRequirementFlags> snippetFlags = SnippetRequirementFlags::kNone;
652     if (effect->allowShader()) {
653         // SkRuntimeEffect::usesSampleCoords() can't be used to restrict this because it returns
654         // false when the only use is to pass the coord unmodified to a child. When children can
655         // refer to interpolated varyings directly in this case, we can refine the flags.
656         snippetFlags |= SnippetRequirementFlags::kLocalCoords;
657     } else if (effect->allowColorFilter()) {
658         snippetFlags |= SnippetRequirementFlags::kPriorStageOutput;
659     } else if (effect->allowBlender()) {
660         snippetFlags |= SnippetRequirementFlags::kPriorStageOutput; // src
661         snippetFlags |= SnippetRequirementFlags::kBlenderDstColor;  // dst
662     }
663 
664     // If the runtime effect references toLinearSrgb() or fromLinearSrgb(), we append two
665     // color space transform children that are invoked when converting those "built-in" expressions.
666     int numChildrenIncColorTransforms = SkTo<int>(effect->children().size()) +
667                                         (SkRuntimeEffectPriv::UsesColorTransform(effect) ? 2 : 0);
668 
669     // TODO: We can have the custom runtime effect preamble generator define structs for its
670     // uniforms if it has a lot of uniforms, and then calculate the required alignment here.
671     return ShaderSnippet(name,
672                          /*staticFn=*/nullptr,
673                          snippetFlags,
674                          this->convertUniforms(effect),
675                          /*texturesAndSamplers=*/{},
676                          GenerateRuntimeShaderPreamble,
677                          numChildrenIncColorTransforms);
678 }
679 
findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect * effect)680 int ShaderCodeDictionary::findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect) {
681      SkAutoSpinlock lock{fSpinLock};
682 
683     if (int stableKey = SkRuntimeEffectPriv::StableKey(*effect)) {
684         SkASSERT(stableKey >= kSkiaKnownRuntimeEffectsStart &&
685                  stableKey < kSkiaKnownRuntimeEffectsStart + kStableKeyCnt);
686 
687         int index = stableKey - kSkiaKnownRuntimeEffectsStart;
688 
689         if (!fKnownRuntimeEffectCodeSnippets[index].fPreambleGenerator) {
690             const char* name = get_known_rte_name(static_cast<StableKey>(stableKey));
691             fKnownRuntimeEffectCodeSnippets[index] = this->convertRuntimeEffect(effect, name);
692         }
693 
694         return stableKey;
695     }
696 
697     // Use the combination of {SkSL program hash, uniform size} as our key.
698     // In the unfortunate event of a hash collision, at least we'll have the right amount of
699     // uniform data available.
700     RuntimeEffectKey key;
701     key.fHash = SkRuntimeEffectPriv::Hash(*effect);
702     key.fUniformSize = effect->uniformSize();
703 
704     int32_t* existingCodeSnippetID = fRuntimeEffectMap.find(key);
705     if (existingCodeSnippetID) {
706         return *existingCodeSnippetID;
707     }
708 
709     // TODO: the memory for user-defined entries could go in the dictionary's arena but that
710     // would have to be a thread safe allocation since the arena also stores entries for
711     // 'fHash' and 'fEntryVector'
712     fUserDefinedCodeSnippets.push_back(this->convertRuntimeEffect(effect, "RuntimeEffect"));
713     int newCodeSnippetID = kUnknownRuntimeEffectIDStart + fUserDefinedCodeSnippets.size() - 1;
714 
715     fRuntimeEffectMap.set(key, newCodeSnippetID);
716     return newCodeSnippetID;
717 }
718 
ShaderCodeDictionary(Layout layout)719 ShaderCodeDictionary::ShaderCodeDictionary(Layout layout)
720         : fLayout(layout) {
721     // The 0th index is reserved as invalid
722     fIDToPaintKey.push_back(PaintParamsKey::Invalid());
723 
724     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kError] = {
725             /*name=*/"Error",
726             /*staticFn=*/"sk_error",
727             SnippetRequirementFlags::kNone,
728             /*uniforms=*/{}
729     };
730     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kPriorOutput] = {
731             /*name=*/"PassthroughShader",
732             /*staticFn=*/"sk_passthrough",
733             SnippetRequirementFlags::kPriorStageOutput,
734             /*uniforms=*/{}
735     };
736     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kSolidColorShader] = {
737             /*name=*/"SolidColor",
738             /*staticFn=*/"sk_solid_shader",
739             SnippetRequirementFlags::kNone,
740             /*uniforms=*/{ { "color", SkSLType::kFloat4 } }
741     };
742     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kRGBPaintColor] = {
743             /*name=*/"RGBPaintColor",
744             /*staticFn=*/"sk_rgb_opaque",
745             SnippetRequirementFlags::kNone,
746             /*uniforms=*/{ Uniform::PaintColor() }
747     };
748     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kAlphaOnlyPaintColor] = {
749             /*name=*/"AlphaOnlyPaintColor",
750             /*staticFn=*/"sk_alpha_only",
751             SnippetRequirementFlags::kNone,
752             /*uniforms=*/{ Uniform::PaintColor() }
753     };
754     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLinearGradientShader4] = {
755             /*name=*/"LinearGradient4",
756             /*staticFn=*/"sk_linear_grad_4_shader",
757             SnippetRequirementFlags::kLocalCoords,
758             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 4 },
759                            { "offsets",     SkSLType::kFloat4 },
760                            { "tilemode",    SkSLType::kInt },
761                            { "colorSpace",  SkSLType::kInt },
762                            { "doUnPremul",  SkSLType::kInt } },
763     };
764     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLinearGradientShader8] = {
765             /*name=*/"LinearGradient8",
766             /*staticFn=*/"sk_linear_grad_8_shader",
767             SnippetRequirementFlags::kLocalCoords,
768             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 8 },
769                            { "offsets",     SkSLType::kFloat4, 2 },
770                            { "tilemode",    SkSLType::kInt },
771                            { "colorSpace",  SkSLType::kInt },
772                            { "doUnPremul",  SkSLType::kInt } }
773     };
774     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLinearGradientShaderTexture] = {
775             /*name=*/"LinearGradientTexture",
776             /*staticFn=*/"sk_linear_grad_tex_shader",
777             SnippetRequirementFlags::kLocalCoords,
778             /*uniforms=*/{ { "numStops",    SkSLType::kInt },
779                            { "tilemode",    SkSLType::kInt },
780                            { "colorSpace",  SkSLType::kInt },
781                            { "doUnPremul",  SkSLType::kInt } },
782             /*texturesAndSamplers=*/{"colorAndOffsetSampler"}
783     };
784     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLinearGradientShaderBuffer] = {
785             /*name=*/"LinearGradientBuffer",
786             /*staticFn=*/"sk_linear_grad_buf_shader",
787             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kGradientBuffer,
788             /*uniforms=*/{ { "numStops",     SkSLType::kInt },
789                            { "bufferOffset", SkSLType::kInt },
790                            { "tilemode",     SkSLType::kInt },
791                            { "colorSpace",   SkSLType::kInt },
792                            { "doUnPremul",   SkSLType::kInt } }
793     };
794     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kRadialGradientShader4] = {
795             /*name=*/"RadialGradient4",
796             /*staticFn=*/ "sk_radial_grad_4_shader",
797             SnippetRequirementFlags::kLocalCoords,
798             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 4 },
799                            { "offsets",     SkSLType::kFloat4 },
800                            { "tilemode",    SkSLType::kInt },
801                            { "colorSpace",  SkSLType::kInt },
802                            { "doUnPremul",  SkSLType::kInt } }
803     };
804     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kRadialGradientShader8] = {
805             /*name=*/"RadialGradient8",
806             /*staticFn=*/"sk_radial_grad_8_shader",
807             SnippetRequirementFlags::kLocalCoords,
808             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 8 },
809                            { "offsets",     SkSLType::kFloat4, 2 },
810                            { "tilemode",    SkSLType::kInt },
811                            { "colorSpace",  SkSLType::kInt },
812                            { "doUnPremul",  SkSLType::kInt } }
813     };
814     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kRadialGradientShaderTexture] = {
815             /*name=*/"RadialGradientTexture",
816             /*staticFn=*/"sk_radial_grad_tex_shader",
817             SnippetRequirementFlags::kLocalCoords,
818             /*uniforms=*/{ { "numStops",    SkSLType::kInt },
819                            { "tilemode",    SkSLType::kInt },
820                            { "colorSpace",  SkSLType::kInt },
821                            { "doUnPremul",  SkSLType::kInt } },
822             /*texturesAndSamplers=*/{"colorAndOffsetSampler"}
823     };
824     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kRadialGradientShaderBuffer] = {
825             /*name=*/"RadialGradientBuffer",
826             /*staticFn=*/"sk_radial_grad_buf_shader",
827             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kGradientBuffer,
828             /*uniforms=*/{ { "numStops",     SkSLType::kInt },
829                            { "bufferOffset", SkSLType::kInt },
830                            { "tilemode",     SkSLType::kInt },
831                            { "colorSpace",   SkSLType::kInt },
832                            { "doUnPremul",   SkSLType::kInt } }
833     };
834     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kSweepGradientShader4] = {
835             /*name=*/"SweepGradient4",
836             /*staticFn=*/"sk_sweep_grad_4_shader",
837             SnippetRequirementFlags::kLocalCoords,
838             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 4 },
839                            { "offsets",     SkSLType::kFloat4 },
840                            { "bias",        SkSLType::kFloat },
841                            { "scale",       SkSLType::kFloat },
842                            { "tilemode",    SkSLType::kInt },
843                            { "colorSpace",  SkSLType::kInt },
844                            { "doUnPremul",  SkSLType::kInt } }
845     };
846     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kSweepGradientShader8] = {
847             /*name=*/"SweepGradient8",
848             /*staticFn=*/"sk_sweep_grad_8_shader",
849             SnippetRequirementFlags::kLocalCoords,
850             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 8 },
851                            { "offsets",     SkSLType::kFloat4, 2 },
852                            { "bias",        SkSLType::kFloat },
853                            { "scale",       SkSLType::kFloat },
854                            { "tilemode",    SkSLType::kInt },
855                            { "colorSpace",  SkSLType::kInt },
856                            { "doUnPremul",  SkSLType::kInt } }
857     };
858     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kSweepGradientShaderTexture] = {
859             /*name=*/"SweepGradientTexture",
860             /*staticFn=*/"sk_sweep_grad_tex_shader",
861             SnippetRequirementFlags::kLocalCoords,
862             /*uniforms=*/{ { "bias",        SkSLType::kFloat },
863                             { "scale",      SkSLType::kFloat },
864                             { "numStops",   SkSLType::kInt },
865                             { "tilemode",   SkSLType::kInt },
866                             { "colorSpace", SkSLType::kInt },
867                             { "doUnPremul", SkSLType::kInt } },
868             /*texturesAndSamplers=*/{"colorAndOffsetSampler"}
869     };
870     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kSweepGradientShaderBuffer] = {
871             /*name=*/"SweepGradientBuffer",
872             /*staticFn=*/"sk_sweep_grad_buf_shader",
873             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kGradientBuffer,
874             /*uniforms=*/{ { "bias",         SkSLType::kFloat },
875                            { "scale",        SkSLType::kFloat },
876                            { "numStops",     SkSLType::kInt },
877                            { "bufferOffset", SkSLType::kInt },
878                            { "tilemode",     SkSLType::kInt },
879                            { "colorSpace",   SkSLType::kInt },
880                            { "doUnPremul",   SkSLType::kInt } }
881     };
882     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kConicalGradientShader4] = {
883             /*name=*/"ConicalGradient4",
884             /*staticFn=*/"sk_conical_grad_4_shader",
885             SnippetRequirementFlags::kLocalCoords,
886             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 4 },
887                            { "offsets",     SkSLType::kFloat4 },
888                            { "radius0",     SkSLType::kFloat },
889                            { "dRadius",     SkSLType::kFloat },
890                            { "a",           SkSLType::kFloat },
891                            { "invA",        SkSLType::kFloat },
892                            { "tilemode",    SkSLType::kInt },
893                            { "colorSpace",  SkSLType::kInt },
894                            { "doUnPremul",  SkSLType::kInt } }
895     };
896     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kConicalGradientShader8] = {
897             /*name=*/"ConicalGradient8",
898             /*staticFn=*/"sk_conical_grad_8_shader",
899             SnippetRequirementFlags::kLocalCoords,
900             /*uniforms=*/{ { "colors",      SkSLType::kFloat4, 8 },
901                            { "offsets",     SkSLType::kFloat4, 2 },
902                            { "radius0",     SkSLType::kFloat },
903                            { "dRadius",     SkSLType::kFloat },
904                            { "a",           SkSLType::kFloat },
905                            { "invA",        SkSLType::kFloat },
906                            { "tilemode",    SkSLType::kInt },
907                            { "colorSpace",  SkSLType::kInt },
908                            { "doUnPremul",  SkSLType::kInt } }
909     };
910     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kConicalGradientShaderTexture] = {
911             /*name=*/"ConicalGradientTexture",
912             /*staticFn=*/"sk_conical_grad_tex_shader",
913             SnippetRequirementFlags::kLocalCoords,
914             /*uniforms=*/{ { "radius0",     SkSLType::kFloat },
915                            { "dRadius",     SkSLType::kFloat },
916                            { "a",           SkSLType::kFloat },
917                            { "invA",        SkSLType::kFloat },
918                            { "numStops",    SkSLType::kInt },
919                            { "tilemode",    SkSLType::kInt },
920                            { "colorSpace",  SkSLType::kInt },
921                            { "doUnPremul",  SkSLType::kInt } },
922             /*texturesAndSamplers=*/{"colorAndOffsetSampler"}
923     };
924     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kConicalGradientShaderBuffer] = {
925             /*name=*/"ConicalGradientBuffer",
926             /*staticFn=*/"sk_conical_grad_buf_shader",
927             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kGradientBuffer,
928             /*uniforms=*/{ { "radius0",      SkSLType::kFloat },
929                            { "dRadius",      SkSLType::kFloat },
930                            { "a",            SkSLType::kFloat },
931                            { "invA",         SkSLType::kFloat },
932                            { "numStops",     SkSLType::kInt },
933                            { "bufferOffset", SkSLType::kInt },
934                            { "tilemode",     SkSLType::kInt },
935                            { "colorSpace",   SkSLType::kInt },
936                            { "doUnPremul",   SkSLType::kInt } }
937     };
938 
939     // This snippet operates on local coords if the child requires local coords (hence why it does
940     // not mask off the child's local coord requirement), but does nothing if the child does not
941     // actually use coordinates.
942     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLocalMatrixShader] = {
943             /*name=*/"LocalMatrixShader",
944             /*staticFn=*/nullptr,
945             SnippetRequirementFlags::kNone,
946             /*uniforms=*/{ { "localMatrix", SkSLType::kFloat4x4 } },
947             /*texturesAndSamplers=*/{},
948             GenerateCoordManipulationPreamble,
949             /*numChildren=*/kNumCoordinateManipulateChildren
950     };
951     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kLocalMatrixShaderPersp] = {
952             /*name=*/"LocalMatrixShaderPersp",
953             /*staticFn=*/nullptr,
954             SnippetRequirementFlags::kNone,
955             /*uniforms=*/{ { "localMatrix", SkSLType::kFloat4x4 } },
956             /*texturesAndSamplers=*/{},
957             GenerateCoordManipulationPreamble,
958             /*numChildren=*/kNumCoordinateManipulateChildren
959     };
960 
961     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kImageShader] = {
962             /*name=*/"ImageShader",
963             /*staticFn=*/"sk_image_shader",
964             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kStoresData,
965             /*uniforms=*/{ { "invImgSize",            SkSLType::kFloat2 },
966                            { "subset",                SkSLType::kFloat4 },
967                            { "tilemodeX",             SkSLType::kInt },
968                            { "tilemodeY",             SkSLType::kInt },
969                            { "filterMode",            SkSLType::kInt } },
970             /*texturesAndSamplers=*/{"image"}
971     };
972     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCubicImageShader] = {
973             /*name=*/"CubicImageShader",
974             /*staticFn=*/"sk_cubic_image_shader",
975             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kStoresData,
976             /*uniforms=*/{ { "invImgSize",            SkSLType::kFloat2 },
977                            { "subset",                SkSLType::kFloat4 },
978                            { "tilemodeX",             SkSLType::kInt },
979                            { "tilemodeY",             SkSLType::kInt },
980                            { "cubicCoeffs",           SkSLType::kHalf4x4 } },
981             /*texturesAndSamplers=*/{"image"}
982     };
983     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kHWImageShader] = {
984             /*name=*/"HardwareImageShader",
985             /*staticFn=*/"sk_hw_image_shader",
986             SnippetRequirementFlags::kLocalCoords | SnippetRequirementFlags::kStoresData,
987             /*uniforms=*/{ { "invImgSize",            SkSLType::kFloat2 } },
988             /*texturesAndSamplers=*/{"image"}
989     };
990 
991     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kYUVImageShader] = {
992             /*name=*/"YUVImageShader",
993             /*staticFn=*/"sk_yuv_image_shader",
994             SnippetRequirementFlags::kLocalCoords,
995             /*uniforms=*/{ { "invImgSizeY",         SkSLType::kFloat2 },
996                            { "invImgSizeUV",        SkSLType::kFloat2 },  // Relative to Y's texels
997                            { "subset",              SkSLType::kFloat4 },
998                            { "linearFilterUVInset", SkSLType::kFloat2 },
999                            { "tilemodeX",           SkSLType::kInt },
1000                            { "tilemodeY",           SkSLType::kInt },
1001                            { "filterModeY",         SkSLType::kInt },
1002                            { "filterModeUV",        SkSLType::kInt },
1003                            { "channelSelectY",      SkSLType::kHalf4 },
1004                            { "channelSelectU",      SkSLType::kHalf4 },
1005                            { "channelSelectV",      SkSLType::kHalf4 },
1006                            { "channelSelectA",      SkSLType::kHalf4 },
1007                            { "yuvToRGBMatrix",      SkSLType::kHalf3x3 },
1008                            { "yuvToRGBTranslate",   SkSLType::kHalf3 } },
1009             /*texturesAndSamplers=*/ {{ "samplerY" },
1010                                       { "samplerU" },
1011                                       { "samplerV" },
1012                                       { "samplerA" }}
1013     };
1014     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCubicYUVImageShader] = {
1015             /*name=*/"CubicYUVImageShader",
1016             /*staticFn=*/"sk_cubic_yuv_image_shader",
1017             SnippetRequirementFlags::kLocalCoords,
1018             /*uniforms=*/{ { "invImgSizeY",       SkSLType::kFloat2 },
1019                            { "invImgSizeUV",      SkSLType::kFloat2 },  // Relative to Y's texels
1020                            { "subset",            SkSLType::kFloat4 },
1021                            { "tilemodeX",         SkSLType::kInt },
1022                            { "tilemodeY",         SkSLType::kInt },
1023                            { "cubicCoeffs",       SkSLType::kHalf4x4 },
1024                            { "channelSelectY",    SkSLType::kHalf4 },
1025                            { "channelSelectU",    SkSLType::kHalf4 },
1026                            { "channelSelectV",    SkSLType::kHalf4 },
1027                            { "channelSelectA",    SkSLType::kHalf4 },
1028                            { "yuvToRGBMatrix",    SkSLType::kHalf3x3 },
1029                            { "yuvToRGBTranslate", SkSLType::kHalf3 } },
1030             /*texturesAndSamplers=*/ {{ "samplerY" },
1031                                       { "samplerU" },
1032                                       { "samplerV" },
1033                                       { "samplerA" }}
1034     };
1035     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kHWYUVImageShader] = {
1036             /*name=*/"HWYUVImageShader",
1037             /*staticFn=*/"sk_hw_yuv_image_shader",
1038             SnippetRequirementFlags::kLocalCoords,
1039             /*uniforms=*/{ { "invImgSizeY",           SkSLType::kFloat2 },
1040                            { "invImgSizeUV",          SkSLType::kFloat2 }, // Relative to Y's texels
1041                            { "channelSelectY",        SkSLType::kHalf4 },
1042                            { "channelSelectU",        SkSLType::kHalf4 },
1043                            { "channelSelectV",        SkSLType::kHalf4 },
1044                            { "channelSelectA",        SkSLType::kHalf4 },
1045                            { "yuvToRGBMatrix",        SkSLType::kHalf3x3 },
1046                            { "yuvToRGBTranslate",     SkSLType::kHalf3 } },
1047             /*texturesAndSamplers=*/ {{ "samplerY" },
1048                                       { "samplerU" },
1049                                       { "samplerV" },
1050                                       { "samplerA" }}
1051     };
1052 
1053     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kHWYUVNoSwizzleImageShader] = {
1054             /*name=*/"HWYUVImageShader",
1055             /*staticFn=*/"sk_hw_yuv_no_swizzle_image_shader",
1056             SnippetRequirementFlags::kLocalCoords,
1057             /*uniforms=*/{ { "invImgSizeY",              SkSLType::kFloat2 },
1058                            { "invImgSizeUV",             SkSLType::kFloat2 }, // Relative to Y space
1059                            { "yuvToRGBMatrix",           SkSLType::kHalf3x3 },
1060                            { "yuvToRGBXlateAlphaParams", SkSLType::kHalf4 } },
1061             /*texturesAndSamplers=*/ {{ "samplerY" },
1062                                       { "samplerU" },
1063                                       { "samplerV" },
1064                                       { "samplerA" }}
1065     };
1066 
1067     // Like the local matrix shader, this is a no-op if the child doesn't need coords
1068     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCoordClampShader] = {
1069             /*name=*/"CoordClampShader",
1070             /*staticFn=*/nullptr,
1071             SnippetRequirementFlags::kNone,
1072             /*uniforms=*/{ { "subset", SkSLType::kFloat4 } },
1073             /*texturesAndSamplers=*/{},
1074             GenerateCoordManipulationPreamble,
1075             /*numChildren=*/kNumCoordinateManipulateChildren
1076     };
1077 
1078     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kDitherShader] = {
1079             /*name=*/"Dither",
1080             /*staticFn=*/"sk_dither",
1081             SnippetRequirementFlags::kPriorStageOutput,
1082             /*uniforms=*/{ { "range", SkSLType::kHalf } },
1083             /*texturesAndSamplers=*/{ { "ditherLUT" } }
1084     };
1085 
1086     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kPerlinNoiseShader] = {
1087             /*name=*/"PerlinNoiseShader",
1088             /*staticFn=*/"sk_perlin_noise_shader",
1089             SnippetRequirementFlags::kLocalCoords,
1090             /*uniforms=*/{ { "baseFrequency", SkSLType::kFloat2 },
1091                            { "stitchData",    SkSLType::kFloat2 },
1092                            { "noiseType",     SkSLType::kInt },
1093                            { "numOctaves",    SkSLType::kInt },
1094                            { "stitching",     SkSLType::kInt } },
1095             /*texturesAndSamplers=*/{ { "permutationsSampler" },
1096                                       { "noiseSampler" } }
1097     };
1098 
1099     // SkColorFilter snippets
1100     // TODO(b/349572157): investigate the implications of having separate hlsa and rgba matrix
1101     // colorfilters. It may be that having them separate will not contribute to an explosion.
1102     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kMatrixColorFilter] = {
1103             /*name=*/"MatrixColorFilter",
1104             /*staticFn=*/"sk_matrix_colorfilter",
1105             SnippetRequirementFlags::kPriorStageOutput,
1106             /*uniforms=*/{ { "matrix",    SkSLType::kFloat4x4 },
1107                            { "translate", SkSLType::kFloat4 },
1108                            { "inHSL",     SkSLType::kInt },
1109                            { "clampRGB",  SkSLType::kInt } }
1110     };
1111     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kTableColorFilter] = {
1112             /*name=*/"TableColorFilter",
1113             /*staticFn=*/"sk_table_colorfilter",
1114             SnippetRequirementFlags::kPriorStageOutput,
1115             /*uniforms=*/{},
1116             /*texturesAndSamplers=*/{ {"table"} }};
1117     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kGaussianColorFilter] = {
1118             /*name=*/"GaussianColorFilter",
1119             /*staticFn=*/"sk_gaussian_colorfilter",
1120             SnippetRequirementFlags::kPriorStageOutput,
1121             /*uniforms=*/{}
1122     };
1123     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kColorSpaceXformColorFilter] = {
1124             /*name=*/"ColorSpaceTransform",
1125             /*staticFn=*/"sk_color_space_transform",
1126             SnippetRequirementFlags::kPriorStageOutput,
1127             /*uniforms=*/{ { "flags",          SkSLType::kInt },
1128                            { "srcKind",        SkSLType::kInt },
1129                            { "gamutTransform", SkSLType::kHalf3x3 },
1130                            { "dstKind",        SkSLType::kInt },
1131                            { "csXformCoeffs",  SkSLType::kHalf4x4 } }
1132     };
1133     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kPremulAlphaColorFilter] = {
1134             /*name=*/"PremulAlpha",
1135             /*staticFn=*/"sk_premul_alpha",
1136             SnippetRequirementFlags::kPriorStageOutput,
1137             /*uniforms=*/{}
1138     };
1139 
1140     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kPrimitiveColor] = {
1141             /*name=*/"PrimitiveColor",
1142             /*staticFn=*/"sk_color_space_transform",
1143             SnippetRequirementFlags::kPrimitiveColor,
1144             /*uniforms=*/{ { "csXformFlags",          SkSLType::kInt },
1145                            { "csXformSrcKind",        SkSLType::kInt },
1146                            { "csXformGamutTransform", SkSLType::kHalf3x3 },
1147                            { "csXformDstKind",        SkSLType::kInt },
1148                            { "csXformCoeffs",         SkSLType::kHalf4x4 } },
1149             /*texturesAndSamplers=*/{}
1150     };
1151 
1152     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCircularRRectClip] = {
1153             /*name=*/"CircularRRectClip",
1154             /*staticFn=*/"sk_circular_rrect_clip",
1155             SnippetRequirementFlags::kNone,
1156             /*uniforms=*/{ { "rect",           SkSLType::kFloat4 },
1157                            { "radiusPlusHalf", SkSLType::kFloat2 },
1158                            { "edgeSelect",     SkSLType::kHalf4 } }
1159     };
1160 
1161     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCompose] = {
1162             /*name=*/"Compose",
1163             /*staticFn=*/nullptr,
1164             SnippetRequirementFlags::kNone,
1165             /*uniforms=*/{},
1166             /*texturesAndSamplers=*/{},
1167             GenerateComposePreamble,
1168             /*numChildren=*/2
1169     };
1170     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kBlendCompose] = {
1171             /*name=*/"BlendCompose",
1172             /*staticFn=*/nullptr,
1173             SnippetRequirementFlags::kNone,
1174             /*uniforms=*/{},
1175             /*texturesAndSamplers=*/{},
1176             GenerateComposePreamble,
1177             /*numChildren=*/3
1178     };
1179 
1180     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kPorterDuffBlender] = {
1181             /*name=*/"PorterDuffBlender",
1182             /*staticFn=*/"sk_porter_duff_blend",
1183             SnippetRequirementFlags::kPriorStageOutput | SnippetRequirementFlags::kBlenderDstColor,
1184             /*uniforms=*/{ { "coeffs", SkSLType::kHalf4 } }
1185     };
1186     fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kHSLCBlender] = {
1187             /*name=*/"HSLCBlender",
1188             /*staticFn=*/"sk_hslc_blend",
1189             SnippetRequirementFlags::kPriorStageOutput | SnippetRequirementFlags::kBlenderDstColor,
1190             /*uniforms=*/{ { "flipSat", SkSLType::kHalf2 } }
1191     };
1192 
1193     // Fixed-function blend mode snippets are all the same, their functionality is entirely defined
1194     // by their unique code snippet IDs.
1195     for (int i = 0; i <= (int) SkBlendMode::kLastMode; ++i) {
1196         int ffBlendModeID = kFixedBlendIDOffset + i;
1197         fBuiltInCodeSnippets[ffBlendModeID] = {
1198                 /*name=*/SkBlendMode_Name(static_cast<SkBlendMode>(i)),
1199                 /*staticFn=*/skgpu::BlendFuncName(static_cast<SkBlendMode>(i)),
1200                 SnippetRequirementFlags::kPriorStageOutput |
1201                 SnippetRequirementFlags::kBlenderDstColor,
1202                 /*uniforms=*/{}
1203         };
1204     }
1205 
1206     // Complete layout calculations for builtin snippets
1207     for (int i = 0; i < kBuiltInCodeSnippetIDCount; ++i) {
1208         ShaderSnippet& snippet = fBuiltInCodeSnippets[i];
1209         SkASSERT(snippet.fName); // Should not have missed a built-in
1210 
1211         if (snippet.fUniformStructName) {
1212             auto offsetCalculator = UniformOffsetCalculator::ForStruct(fLayout);
1213             for (int j = 0; j < snippet.fUniforms.size(); ++j) {
1214                 SkASSERT(!snippet.fUniforms[j].isPaintColor()); // paint color shouldn't be embedded
1215                 offsetCalculator.advanceOffset(snippet.fUniforms[j].type(),
1216                                                snippet.fUniforms[j].count());
1217             }
1218             snippet.fRequiredAlignment = offsetCalculator.requiredAlignment();
1219         }
1220     }
1221 }
1222 
1223 // clang-format off
1224 
1225 // Verify that the built-in code IDs for fixed function blending are consistent with SkBlendMode.
1226 static_assert((int)SkBlendMode::kClear      == (int)BuiltInCodeSnippetID::kFixedBlend_Clear      - kFixedBlendIDOffset);
1227 static_assert((int)SkBlendMode::kSrc        == (int)BuiltInCodeSnippetID::kFixedBlend_Src        - kFixedBlendIDOffset);
1228 static_assert((int)SkBlendMode::kDst        == (int)BuiltInCodeSnippetID::kFixedBlend_Dst        - kFixedBlendIDOffset);
1229 static_assert((int)SkBlendMode::kSrcOver    == (int)BuiltInCodeSnippetID::kFixedBlend_SrcOver    - kFixedBlendIDOffset);
1230 static_assert((int)SkBlendMode::kDstOver    == (int)BuiltInCodeSnippetID::kFixedBlend_DstOver    - kFixedBlendIDOffset);
1231 static_assert((int)SkBlendMode::kSrcIn      == (int)BuiltInCodeSnippetID::kFixedBlend_SrcIn      - kFixedBlendIDOffset);
1232 static_assert((int)SkBlendMode::kDstIn      == (int)BuiltInCodeSnippetID::kFixedBlend_DstIn      - kFixedBlendIDOffset);
1233 static_assert((int)SkBlendMode::kSrcOut     == (int)BuiltInCodeSnippetID::kFixedBlend_SrcOut     - kFixedBlendIDOffset);
1234 static_assert((int)SkBlendMode::kDstOut     == (int)BuiltInCodeSnippetID::kFixedBlend_DstOut     - kFixedBlendIDOffset);
1235 static_assert((int)SkBlendMode::kSrcATop    == (int)BuiltInCodeSnippetID::kFixedBlend_SrcATop    - kFixedBlendIDOffset);
1236 static_assert((int)SkBlendMode::kDstATop    == (int)BuiltInCodeSnippetID::kFixedBlend_DstATop    - kFixedBlendIDOffset);
1237 static_assert((int)SkBlendMode::kXor        == (int)BuiltInCodeSnippetID::kFixedBlend_Xor        - kFixedBlendIDOffset);
1238 static_assert((int)SkBlendMode::kPlus       == (int)BuiltInCodeSnippetID::kFixedBlend_Plus       - kFixedBlendIDOffset);
1239 static_assert((int)SkBlendMode::kModulate   == (int)BuiltInCodeSnippetID::kFixedBlend_Modulate   - kFixedBlendIDOffset);
1240 static_assert((int)SkBlendMode::kScreen     == (int)BuiltInCodeSnippetID::kFixedBlend_Screen     - kFixedBlendIDOffset);
1241 static_assert((int)SkBlendMode::kOverlay    == (int)BuiltInCodeSnippetID::kFixedBlend_Overlay    - kFixedBlendIDOffset);
1242 static_assert((int)SkBlendMode::kDarken     == (int)BuiltInCodeSnippetID::kFixedBlend_Darken     - kFixedBlendIDOffset);
1243 static_assert((int)SkBlendMode::kColorDodge == (int)BuiltInCodeSnippetID::kFixedBlend_ColorDodge - kFixedBlendIDOffset);
1244 static_assert((int)SkBlendMode::kColorBurn  == (int)BuiltInCodeSnippetID::kFixedBlend_ColorBurn  - kFixedBlendIDOffset);
1245 static_assert((int)SkBlendMode::kHardLight  == (int)BuiltInCodeSnippetID::kFixedBlend_HardLight  - kFixedBlendIDOffset);
1246 static_assert((int)SkBlendMode::kSoftLight  == (int)BuiltInCodeSnippetID::kFixedBlend_SoftLight  - kFixedBlendIDOffset);
1247 static_assert((int)SkBlendMode::kDifference == (int)BuiltInCodeSnippetID::kFixedBlend_Difference - kFixedBlendIDOffset);
1248 static_assert((int)SkBlendMode::kExclusion  == (int)BuiltInCodeSnippetID::kFixedBlend_Exclusion  - kFixedBlendIDOffset);
1249 static_assert((int)SkBlendMode::kMultiply   == (int)BuiltInCodeSnippetID::kFixedBlend_Multiply   - kFixedBlendIDOffset);
1250 static_assert((int)SkBlendMode::kHue        == (int)BuiltInCodeSnippetID::kFixedBlend_Hue        - kFixedBlendIDOffset);
1251 static_assert((int)SkBlendMode::kSaturation == (int)BuiltInCodeSnippetID::kFixedBlend_Saturation - kFixedBlendIDOffset);
1252 static_assert((int)SkBlendMode::kColor      == (int)BuiltInCodeSnippetID::kFixedBlend_Color      - kFixedBlendIDOffset);
1253 static_assert((int)SkBlendMode::kLuminosity == (int)BuiltInCodeSnippetID::kFixedBlend_Luminosity - kFixedBlendIDOffset);
1254 
1255 // Verify enum constants match values expected by static module SkSL functions
1256 static_assert(0 == static_cast<int>(skcms_TFType_Invalid),   "ColorSpaceTransform code depends on skcms_TFType");
1257 static_assert(1 == static_cast<int>(skcms_TFType_sRGBish),   "ColorSpaceTransform code depends on skcms_TFType");
1258 static_assert(2 == static_cast<int>(skcms_TFType_PQish),     "ColorSpaceTransform code depends on skcms_TFType");
1259 static_assert(3 == static_cast<int>(skcms_TFType_HLGish),    "ColorSpaceTransform code depends on skcms_TFType");
1260 static_assert(4 == static_cast<int>(skcms_TFType_HLGinvish), "ColorSpaceTransform code depends on skcms_TFType");
1261 
1262 // TODO: We can meaningfully check these when we can use C++20 features.
1263 // static_assert(0x1  == SkColorSpaceXformSteps::Flags{.unpremul = true}.mask(),        "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags");
1264 // static_assert(0x2  == SkColorSpaceXformSteps::Flags{.linearize = true}.mask(),       "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags");
1265 // static_assert(0x4  == SkColorSpaceXformSteps::Flags{.gamut_transform = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags");
1266 // static_assert(0x8  == SkColorSpaceXformSteps::Flags{.encode = true}.mask(),          "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags");
1267 // static_assert(0x10 == SkColorSpaceXformSteps::Flags{.premul = true}.mask(),          "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags");
1268 
1269 static_assert(0 == static_cast<int>(SkTileMode::kClamp),  "ImageShader code depends on SkTileMode");
1270 static_assert(1 == static_cast<int>(SkTileMode::kRepeat), "ImageShader code depends on SkTileMode");
1271 static_assert(2 == static_cast<int>(SkTileMode::kMirror), "ImageShader code depends on SkTileMode");
1272 static_assert(3 == static_cast<int>(SkTileMode::kDecal),  "ImageShader code depends on SkTileMode");
1273 
1274 static_assert(0 == static_cast<int>(SkFilterMode::kNearest), "ImageShader code depends on SkFilterMode");
1275 static_assert(1 == static_cast<int>(SkFilterMode::kLinear),  "ImageShader code depends on SkFilterMode");
1276 
1277 // clang-format on
1278 
1279 } // namespace skgpu::graphite
1280