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