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