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(¶ms, node, &args);
178 append_uniforms(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, this, &ShaderSnippet::kDefaultArgs);
240 append_uniforms(¶ms, 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