2 * Copyright 2022 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/core/SkShaderCodeDictionary.h"
10 #include "include/core/SkCombinationBuilder.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "include/private/SkSLString.h"
13 #include "src/core/SkOpts.h"
14 #include "src/sksl/SkSLUtil.h"
16 #ifdef SK_GRAPHITE_ENABLED
17 #include "include/gpu/graphite/Context.h"
20 // We need to ensure that the user-defined snippet ID can't conflict with the SkBlendMode
21 // values (since they are used "raw" in the combination system).
22 static const int kMinUserDefinedSnippetID = std::max(kBuiltInCodeSnippetIDCount, kSkBlendModeCount);
26 std::string get_mangled_local_var_name(const char* baseName, int manglingSuffix) {
27 return std::string(baseName) + "_" + std::to_string(manglingSuffix);
30 void add_indent(std::string* result, int indent) {
31 result->append(4*indent, ' ');
34 #if SK_SUPPORT_GPU && defined(SK_GRAPHITE_ENABLED) && defined(SK_METAL)
35 std::string generate_default_before_children_glue_code(int entryIndex,
36 const SkPaintParamsKey::BlockReader& reader,
37 const std::string& parentPreLocalName,
41 if (reader.entry()->needsLocalCoords()) {
42 // Every snippet that requests local coordinates must have a preLocalMatrix as its first
44 SkASSERT(reader.entry()->fUniforms.size() >= 1);
45 SkASSERT(reader.entry()->fUniforms[0].type() == SkSLType::kFloat4x4);
47 std::string localMatrixUniformName = reader.entry()->getMangledUniformName(0, entryIndex);
49 std::string preLocalMatrixVarName = get_mangled_local_var_name("preLocal", entryIndex);
51 add_indent(&result, indent);
52 SkSL::String::appendf(&result,
53 "float4x4 %s = %s * %s;\n",
54 preLocalMatrixVarName.c_str(),
55 parentPreLocalName.c_str(),
56 localMatrixUniformName.c_str());
63 } // anonymous namespace
66 std::string SkShaderSnippet::getMangledUniformName(int uniformIndex, int mangleId) const {
68 result = fUniforms[uniformIndex].name() + std::string("_") + std::to_string(mangleId);
72 // TODO: SkShaderInfo::toSkSL needs to work outside of both just graphite and metal. To do
73 // so we'll need to switch over to using SkSL's uniform capabilities.
74 #if SK_SUPPORT_GPU && defined(SK_GRAPHITE_ENABLED) && defined(SK_METAL)
76 // TODO: switch this over to using SkSL's uniform system
77 namespace skgpu::graphite {
78 std::string GetMtlUniforms(int bufferID,
80 const std::vector<SkPaintParamsKey::BlockReader>&,
82 std::string GetMtlTexturesAndSamplers(const std::vector<SkPaintParamsKey::BlockReader>&,
84 } // namespace skgpu::graphite
86 // Emit the glue code needed to invoke a single static helper isolated w/in its own scope.
87 // The structure of this will be:
91 // half4 child-outColor%d; // for each child
93 // /* emitted snippet sksl assigns to child-outColor%d */
96 // /* emitted snippet sksl assigns to outColor%d - taking a vector of child var names */
98 // Where the %d is filled in with 'entryIndex'.
99 std::string SkShaderInfo::emitGlueCodeForEntry(int* entryIndex,
100 const std::string& priorStageOutputName,
101 const std::string& parentPreLocalName,
104 const SkPaintParamsKey::BlockReader& reader = fBlockReaders[*entryIndex];
105 int curEntryIndex = *entryIndex;
107 std::string scopeOutputVar = get_mangled_local_var_name("outColor", curEntryIndex);
109 add_indent(result, indent);
110 SkSL::String::appendf(result,
111 "half4 %s; // output of %s\n",
112 scopeOutputVar.c_str(),
113 reader.entry()->fName);
114 add_indent(result, indent);
117 *result += generate_default_before_children_glue_code(curEntryIndex, reader,
118 parentPreLocalName, indent+1);
120 // TODO: this could be returned by generate_default_before_children_glue_code
121 std::string currentPreLocalName;
122 if (reader.entry()->needsLocalCoords()) {
123 currentPreLocalName = get_mangled_local_var_name("preLocal", curEntryIndex);
125 currentPreLocalName = parentPreLocalName;
128 // Although the children appear after the parent in the shader info they are emitted
130 std::vector<std::string> childOutputVarNames;
131 for (int j = 0; j < reader.numChildren(); ++j) {
133 std::string childOutputVar = this->emitGlueCodeForEntry(entryIndex,
134 priorStageOutputName,
137 childOutputVarNames.push_back(childOutputVar);
140 *result += (reader.entry()->fGlueCodeGenerator)(scopeOutputVar, curEntryIndex, reader,
141 priorStageOutputName,
142 childOutputVarNames, indent+1);
143 add_indent(result, indent);
146 return scopeOutputVar;
149 // The current, incomplete, model for shader construction is:
150 // - Static code snippets (which can have an arbitrary signature) live in the Graphite
151 // pre-compiled module, which is located at `src/sksl/sksl_graphite_frag.sksl`.
152 // - Glue code is generated in a `main` method which calls these static code snippets.
153 // The glue code is responsible for:
154 // 1) gathering the correct (mangled) uniforms
155 // 2) passing the uniforms and any other parameters to the helper method
156 // - The result of the final code snippet is then copied into "sk_FragColor".
157 // Note: each entry's 'fStaticFunctionName' field is expected to match the name of a function
158 // in the Graphite pre-compiled module.
159 std::string SkShaderInfo::toSkSL() const {
160 // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
161 std::string result = skgpu::graphite::GetMtlUniforms(2, "FS", fBlockReaders,
162 this->needsLocalCoords());
165 result += skgpu::graphite::GetMtlTexturesAndSamplers(fBlockReaders, &binding);
166 result += "layout(location = 0, index = 0) out half4 sk_FragColor;\n";
167 result += "void main() {\n";
169 if (this->needsLocalCoords()) {
170 result += "const float4x4 initialPreLocal = float4x4(1);\n";
173 std::string parentPreLocal = "initialPreLocal";
174 std::string lastOutputVar = "initialColor";
176 // TODO: what is the correct initial color to feed in?
177 add_indent(&result, 1);
178 SkSL::String::appendf(&result, " half4 %s = half4(0.0);", lastOutputVar.c_str());
180 for (int entryIndex = 0; entryIndex < (int) fBlockReaders.size(); ++entryIndex) {
181 lastOutputVar = this->emitGlueCodeForEntry(&entryIndex, lastOutputVar, parentPreLocal,
185 SkSL::String::appendf(&result, " sk_FragColor = %s;\n", lastOutputVar.c_str());
192 SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::makeEntry(
193 const SkPaintParamsKey& key
194 #ifdef SK_GRAPHITE_ENABLED
195 , const SkPipelineDataGatherer::BlendInfo& blendInfo
198 uint8_t* newKeyData = fArena.makeArray<uint8_t>(key.sizeInBytes());
199 memcpy(newKeyData, key.data(), key.sizeInBytes());
201 SkSpan<const uint8_t> newKeyAsSpan = SkMakeSpan(newKeyData, key.sizeInBytes());
202 #ifdef SK_GRAPHITE_ENABLED
203 return fArena.make([&](void *ptr) { return new(ptr) Entry(newKeyAsSpan, blendInfo); });
205 return fArena.make([&](void *ptr) { return new(ptr) Entry(newKeyAsSpan); });
209 size_t SkShaderCodeDictionary::Hash::operator()(const SkPaintParamsKey* key) const {
210 return SkOpts::hash_fn(key->data(), key->sizeInBytes(), 0);
213 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::findOrCreate(
214 const SkPaintParamsKey& key
215 #ifdef SK_GRAPHITE_ENABLED
216 , const SkPipelineDataGatherer::BlendInfo& blendInfo
219 SkAutoSpinlock lock{fSpinLock};
221 auto iter = fHash.find(&key);
222 if (iter != fHash.end()) {
223 SkASSERT(fEntryVector[iter->second->uniqueID().asUInt()] == iter->second);
227 #ifdef SK_GRAPHITE_ENABLED
228 Entry* newEntry = this->makeEntry(key, blendInfo);
230 Entry* newEntry = this->makeEntry(key);
232 newEntry->setUniqueID(fEntryVector.size());
233 fHash.insert(std::make_pair(&newEntry->paintParamsKey(), newEntry));
234 fEntryVector.push_back(newEntry);
239 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::lookup(
240 SkUniquePaintParamsID codeID) const {
242 if (!codeID.isValid()) {
246 SkAutoSpinlock lock{fSpinLock};
248 SkASSERT(codeID.asUInt() < fEntryVector.size());
250 return fEntryVector[codeID.asUInt()];
253 SkSpan<const SkUniform> SkShaderCodeDictionary::getUniforms(SkBuiltInCodeSnippetID id) const {
254 return fBuiltInCodeSnippets[(int) id].fUniforms;
257 SkSpan<const SkPaintParamsKey::DataPayloadField> SkShaderCodeDictionary::dataPayloadExpectations(
258 int codeSnippetID) const {
259 // All callers of this entry point should already have ensured that 'codeSnippetID' is valid
260 return this->getEntry(codeSnippetID)->fDataPayloadExpectations;
263 const SkShaderSnippet* SkShaderCodeDictionary::getEntry(int codeSnippetID) const {
264 if (codeSnippetID < 0) {
268 if (codeSnippetID < kBuiltInCodeSnippetIDCount) {
269 return &fBuiltInCodeSnippets[codeSnippetID];
272 if (codeSnippetID < kMinUserDefinedSnippetID) {
276 int userDefinedCodeSnippetID = codeSnippetID - kMinUserDefinedSnippetID;
277 if (userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size())) {
278 return fUserDefinedCodeSnippets[userDefinedCodeSnippetID].get();
284 const SkShaderSnippet* SkShaderCodeDictionary::getEntry(SkBlenderID id) const {
285 return this->getEntry(id.asUInt());
288 void SkShaderCodeDictionary::getShaderInfo(SkUniquePaintParamsID uniqueID, SkShaderInfo* info) {
289 auto entry = this->lookup(uniqueID);
291 entry->paintParamsKey().toShaderInfo(this, info);
293 #ifdef SK_GRAPHITE_ENABLED
294 info->setBlendInfo(entry->blendInfo());
298 //--------------------------------------------------------------------------------------------------
301 using DataPayloadField = SkPaintParamsKey::DataPayloadField;
303 // The default glue code just calls a helper function with the signature:
304 // half4 fStaticFunctionName(/* all uniforms as parameters */,
305 // /* all child output variable names as parameters */);
306 // and stores the result in a variable named "resultName".
307 std::string GenerateDefaultGlueCode(const std::string& resultName,
309 const SkPaintParamsKey::BlockReader& reader,
310 const std::string& priorStageOutputName,
311 const std::vector<std::string>& childOutputVarNames,
313 const SkShaderSnippet* entry = reader.entry();
315 SkASSERT((int)childOutputVarNames.size() == entry->numExpectedChildren());
317 if (entry->needsLocalCoords()) {
318 // Every snippet that requests local coordinates must have a localMatrix as its first
320 SkASSERT(reader.entry()->fUniforms.size() >= 1);
321 SkASSERT(reader.entry()->fUniforms[0].type() == SkSLType::kFloat4x4);
326 add_indent(&result, indent);
327 SkSL::String::appendf(&result,
330 entry->fStaticFunctionName);
331 for (size_t i = 0; i < entry->fUniforms.size(); ++i) {
332 if (i == 0 && reader.entry()->needsLocalCoords()) {
333 std::string preLocalMatrixVarName = get_mangled_local_var_name("preLocal",
335 result += preLocalMatrixVarName;
336 result += " * dev2LocalUni";
338 result += entry->getMangledUniformName(i, entryIndex);
340 if (i+1 < entry->fUniforms.size() + childOutputVarNames.size()) {
344 for (size_t i = 0; i < childOutputVarNames.size(); ++i) {
345 result += childOutputVarNames[i].c_str();
346 if (i+1 < childOutputVarNames.size()) {
355 //--------------------------------------------------------------------------------------------------
356 static constexpr int kFourStopGradient = 4;
357 static constexpr int kEightStopGradient = 8;
359 static constexpr int kNumLinearGradientUniforms = 9;
360 static constexpr SkUniform kLinearGradientUniforms4[kNumLinearGradientUniforms] = {
361 { "localMatrix", SkSLType::kFloat4x4 },
362 { "colors", SkSLType::kFloat4, kFourStopGradient },
363 { "offsets", SkSLType::kFloat, kFourStopGradient },
364 { "point0", SkSLType::kFloat2 },
365 { "point1", SkSLType::kFloat2 },
366 { "tilemode", SkSLType::kInt },
367 { "padding1", SkSLType::kFloat }, // TODO: add automatic uniform padding
368 { "padding2", SkSLType::kFloat },
369 { "padding3", SkSLType::kFloat },
371 static constexpr SkUniform kLinearGradientUniforms8[kNumLinearGradientUniforms] = {
372 { "localMatrix", SkSLType::kFloat4x4 },
373 { "colors", SkSLType::kFloat4, kEightStopGradient },
374 { "offsets", SkSLType::kFloat, kEightStopGradient },
375 { "point0", SkSLType::kFloat2 },
376 { "point1", SkSLType::kFloat2 },
377 { "tilemode", SkSLType::kInt },
378 { "padding1", SkSLType::kFloat }, // TODO: add automatic uniform padding
379 { "padding2", SkSLType::kFloat },
380 { "padding3", SkSLType::kFloat },
383 static constexpr int kNumRadialGradientUniforms = 6;
384 static constexpr SkUniform kRadialGradientUniforms4[kNumRadialGradientUniforms] = {
385 { "localMatrix", SkSLType::kFloat4x4 },
386 { "colors", SkSLType::kFloat4, kFourStopGradient },
387 { "offsets", SkSLType::kFloat, kFourStopGradient },
388 { "center", SkSLType::kFloat2 },
389 { "radius", SkSLType::kFloat },
390 { "tilemode", SkSLType::kInt },
392 static constexpr SkUniform kRadialGradientUniforms8[kNumRadialGradientUniforms] = {
393 { "localMatrix", SkSLType::kFloat4x4 },
394 { "colors", SkSLType::kFloat4, kEightStopGradient },
395 { "offsets", SkSLType::kFloat, kEightStopGradient },
396 { "center", SkSLType::kFloat2 },
397 { "radius", SkSLType::kFloat },
398 { "tilemode", SkSLType::kInt },
401 static constexpr int kNumSweepGradientUniforms = 10;
402 static constexpr SkUniform kSweepGradientUniforms4[kNumSweepGradientUniforms] = {
403 { "localMatrix", SkSLType::kFloat4x4 },
404 { "colors", SkSLType::kFloat4, kFourStopGradient },
405 { "offsets", SkSLType::kFloat, kFourStopGradient },
406 { "center", SkSLType::kFloat2 },
407 { "bias", SkSLType::kFloat },
408 { "scale", SkSLType::kFloat },
409 { "tilemode", SkSLType::kInt },
410 { "padding1", SkSLType::kFloat }, // TODO: add automatic uniform padding
411 { "padding2", SkSLType::kFloat },
412 { "padding3", SkSLType::kFloat },
414 static constexpr SkUniform kSweepGradientUniforms8[kNumSweepGradientUniforms] = {
415 { "localMatrix", SkSLType::kFloat4x4 },
416 { "colors", SkSLType::kFloat4, kEightStopGradient },
417 { "offsets", SkSLType::kFloat, kEightStopGradient },
418 { "center", SkSLType::kFloat2 },
419 { "bias", SkSLType::kFloat },
420 { "scale", SkSLType::kFloat },
421 { "tilemode", SkSLType::kInt },
422 { "padding1", SkSLType::kFloat }, // TODO: add automatic uniform padding
423 { "padding2", SkSLType::kFloat },
424 { "padding3", SkSLType::kFloat },
427 static constexpr int kNumConicalGradientUniforms = 9;
428 static constexpr SkUniform kConicalGradientUniforms4[kNumConicalGradientUniforms] = {
429 { "localMatrix", SkSLType::kFloat4x4 },
430 { "colors", SkSLType::kFloat4, kFourStopGradient },
431 { "offsets", SkSLType::kFloat, kFourStopGradient },
432 { "point0", SkSLType::kFloat2 },
433 { "point1", SkSLType::kFloat2 },
434 { "radius0", SkSLType::kFloat },
435 { "radius1", SkSLType::kFloat },
436 { "tilemode", SkSLType::kInt },
437 { "padding", SkSLType::kFloat }, // TODO: add automatic uniform padding
439 static constexpr SkUniform kConicalGradientUniforms8[kNumConicalGradientUniforms] = {
440 { "localMatrix", SkSLType::kFloat4x4 },
441 { "colors", SkSLType::kFloat4, kEightStopGradient },
442 { "offsets", SkSLType::kFloat, kEightStopGradient },
443 { "point0", SkSLType::kFloat2 },
444 { "point1", SkSLType::kFloat2 },
445 { "radius0", SkSLType::kFloat },
446 { "radius1", SkSLType::kFloat },
447 { "tilemode", SkSLType::kInt },
448 { "padding", SkSLType::kFloat }, // TODO: add automatic uniform padding
451 static constexpr char kLinearGradient4Name[] = "sk_linear_grad_4_shader";
452 static constexpr char kLinearGradient8Name[] = "sk_linear_grad_8_shader";
453 static constexpr char kRadialGradient4Name[] = "sk_radial_grad_4_shader";
454 static constexpr char kRadialGradient8Name[] = "sk_radial_grad_8_shader";
455 static constexpr char kSweepGradient4Name[] = "sk_sweep_grad_4_shader";
456 static constexpr char kSweepGradient8Name[] = "sk_sweep_grad_8_shader";
457 static constexpr char kConicalGradient4Name[] = "sk_conical_grad_4_shader";
458 static constexpr char kConicalGradient8Name[] = "sk_conical_grad_8_shader";
460 //--------------------------------------------------------------------------------------------------
461 static constexpr int kNumSolidShaderUniforms = 1;
462 static constexpr SkUniform kSolidShaderUniforms[kNumSolidShaderUniforms] = {
463 { "color", SkSLType::kFloat4 }
466 static constexpr char kSolidShaderName[] = "sk_solid_shader";
468 //--------------------------------------------------------------------------------------------------
469 static constexpr int kNumLocalMatrixShaderUniforms = 1;
470 static constexpr SkUniform kLocalMatrixShaderUniforms[kNumLocalMatrixShaderUniforms] = {
471 { "localMatrix", SkSLType::kFloat4x4 },
474 static constexpr int kNumLocalMatrixShaderChildren = 1;
476 static constexpr char kLocalMatrixShaderName[] = "sk_local_matrix_shader";
478 //--------------------------------------------------------------------------------------------------
479 static constexpr int kNumImageShaderUniforms = 6;
480 static constexpr SkUniform kImageShaderUniforms[kNumImageShaderUniforms] = {
481 { "localMatrix", SkSLType::kFloat4x4 },
482 { "subset", SkSLType::kFloat4 },
483 { "tilemodeX", SkSLType::kInt },
484 { "tilemodeY", SkSLType::kInt },
485 { "imgWidth", SkSLType::kInt },
486 { "imgHeight", SkSLType::kInt },
489 static constexpr int kNumImageShaderTexturesAndSamplers = 1;
490 static constexpr SkTextureAndSampler kISTexturesAndSamplers[kNumImageShaderTexturesAndSamplers] = {
494 static_assert(0 == static_cast<int>(SkTileMode::kClamp), "ImageShader code depends on SkTileMode");
495 static_assert(1 == static_cast<int>(SkTileMode::kRepeat), "ImageShader code depends on SkTileMode");
496 static_assert(2 == static_cast<int>(SkTileMode::kMirror), "ImageShader code depends on SkTileMode");
497 static_assert(3 == static_cast<int>(SkTileMode::kDecal), "ImageShader code depends on SkTileMode");
499 static constexpr char kImageShaderName[] = "sk_compute_coords";
501 // This is _not_ what we want to do.
502 // Ideally the "compute_coords" code snippet could just take texture and
503 // sampler references and do everything. That is going to take more time to figure out though so,
504 // for the sake of expediency, we're generating custom code to do the sampling.
505 std::string GenerateImageShaderGlueCode(const std::string& resultName,
507 const SkPaintParamsKey::BlockReader& reader,
508 const std::string& priorStageOutputName,
509 const std::vector<std::string>& childNames,
511 SkASSERT(childNames.empty());
513 std::string samplerVarName = std::string("sampler_") + std::to_string(entryIndex) + "_0";
514 std::string preLocalMatrixVarName = get_mangled_local_var_name("preLocal", entryIndex);
516 // Uniform slot 0 is being used for the localMatrix but is handled in
517 // generate_default_before_children_glue_code.
518 std::string subsetName = reader.entry()->getMangledUniformName(1, entryIndex);
519 std::string tmXName = reader.entry()->getMangledUniformName(2, entryIndex);
520 std::string tmYName = reader.entry()->getMangledUniformName(3, entryIndex);
521 std::string imgWidthName = reader.entry()->getMangledUniformName(4, entryIndex);
522 std::string imgHeightName = reader.entry()->getMangledUniformName(5, entryIndex);
526 add_indent(&result, indent);
527 SkSL::String::appendf(&result,
528 "float2 coords = %s(%s * dev2LocalUni, %s, %s, %s, %s, %s);",
529 reader.entry()->fStaticFunctionName,
530 preLocalMatrixVarName.c_str(),
534 imgWidthName.c_str(),
535 imgHeightName.c_str());
537 add_indent(&result, indent);
538 SkSL::String::appendf(&result,
539 "%s = sample(%s, coords);\n",
541 samplerVarName.c_str());
546 //--------------------------------------------------------------------------------------------------
547 static constexpr int kNumBlendShaderUniforms = 4;
548 static constexpr SkUniform kBlendShaderUniforms[kNumBlendShaderUniforms] = {
549 { "blendMode", SkSLType::kInt },
550 { "padding1", SkSLType::kInt }, // TODO: add automatic uniform padding
551 { "padding2", SkSLType::kInt },
552 { "padding3", SkSLType::kInt },
555 static constexpr int kNumBlendShaderChildren = 2;
557 static constexpr char kBlendShaderName[] = "sk_blend_shader";
559 //--------------------------------------------------------------------------------------------------
560 static constexpr char kErrorName[] = "sk_error";
562 //--------------------------------------------------------------------------------------------------
563 // This method generates the glue code for the case where the SkBlendMode-based blending is
564 // handled with fixed function blending.
565 std::string GenerateFixedFunctionBlenderGlueCode(const std::string& resultName,
567 const SkPaintParamsKey::BlockReader& reader,
568 const std::string& priorStageOutputName,
569 const std::vector<std::string>& childNames,
571 SkASSERT(childNames.empty());
572 SkASSERT(reader.entry()->fUniforms.empty());
573 SkASSERT(reader.numDataPayloadFields() == 0);
575 // The actual blending is set up via the fixed function pipeline so we don't actually
576 // need to access the blend mode in the glue code.
579 add_indent(&result, indent);
580 result += "// Fixed-function blending\n";
581 add_indent(&result, indent);
582 SkSL::String::appendf(&result, "%s = %s;", resultName.c_str(), priorStageOutputName.c_str());
587 //--------------------------------------------------------------------------------------------------
588 static constexpr int kNumShaderBasedBlenderUniforms = 4;
589 static constexpr SkUniform kShaderBasedBlenderUniforms[kNumShaderBasedBlenderUniforms] = {
590 { "blendMode", SkSLType::kInt },
591 { "padding1", SkSLType::kInt }, // TODO: add automatic uniform padding
592 { "padding2", SkSLType::kInt },
593 { "padding3", SkSLType::kInt },
596 static constexpr char kBlendHelperName[] = "sk_blend";
598 // This method generates the glue code for the case where the SkBlendMode-based blending must occur
599 // in the shader (i.e., fixed function blending isn't possible).
600 // It exists as custom glue code so that we can deal with the dest reads. If that can be
601 // standardized (e.g., via a snippets requirement flag) this could be removed.
602 std::string GenerateShaderBasedBlenderGlueCode(const std::string& resultName,
604 const SkPaintParamsKey::BlockReader& reader,
605 const std::string& priorStageOutputName,
606 const std::vector<std::string>& childNames,
608 SkASSERT(childNames.empty());
609 SkASSERT(reader.entry()->fUniforms.size() == 4); // actual blend uniform + 3 padding int
610 SkASSERT(reader.numDataPayloadFields() == 0);
612 std::string uniformName = reader.entry()->getMangledUniformName(0, entryIndex);
616 add_indent(&result, indent);
617 result += "// Shader-based blending\n";
619 // TODO: emit code to perform dest read here
620 add_indent(&result, indent);
621 result += "half4 dummyDst = half4(1.0, 1.0, 1.0, 1.0);\n";
623 add_indent(&result, indent);
624 SkSL::String::appendf(&result, "%s = %s(%s, %s, dummyDst);",
626 reader.entry()->fStaticFunctionName,
628 priorStageOutputName.c_str());
633 //--------------------------------------------------------------------------------------------------
635 } // anonymous namespace
637 bool SkShaderCodeDictionary::isValidID(int snippetID) const {
642 if (snippetID < kBuiltInCodeSnippetIDCount) {
646 if (snippetID < kMinUserDefinedSnippetID) {
650 int userDefinedCodeSnippetID = snippetID - kMinUserDefinedSnippetID;
651 return userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size());
654 static constexpr int kNoChildren = 0;
656 // TODO: this version needs to be removed
657 int SkShaderCodeDictionary::addUserDefinedSnippet(
659 SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations) {
661 std::unique_ptr<SkShaderSnippet> entry(new SkShaderSnippet("UserDefined",
663 SnippetRequirementFlags::kNone,
666 GenerateDefaultGlueCode,
668 dataPayloadExpectations));
670 // TODO: the memory for user-defined entries could go in the dictionary's arena but that
671 // would have to be a thread safe allocation since the arena also stores entries for
672 // 'fHash' and 'fEntryVector'
673 fUserDefinedCodeSnippets.push_back(std::move(entry));
675 return kMinUserDefinedSnippetID + fUserDefinedCodeSnippets.size() - 1;
678 SkBlenderID SkShaderCodeDictionary::addUserDefinedBlender(sk_sp<SkRuntimeEffect> effect) {
683 // TODO: at this point we need to extract the uniform definitions, children and helper functions
684 // from the runtime effect in order to create a real SkShaderSnippet
685 // Additionally, we need to hash the provided code to deduplicate the runtime effects in case
686 // the client keeps giving us different rtEffects w/ the same backing SkSL.
688 std::unique_ptr<SkShaderSnippet> entry(new SkShaderSnippet("UserDefined",
689 {}, // missing uniforms
690 SnippetRequirementFlags::kNone,
691 {}, // missing samplers
693 GenerateDefaultGlueCode,
695 {})); // missing data payload
697 // TODO: the memory for user-defined entries could go in the dictionary's arena but that
698 // would have to be a thread safe allocation since the arena also stores entries for
699 // 'fHash' and 'fEntryVector'
700 fUserDefinedCodeSnippets.push_back(std::move(entry));
702 return SkBlenderID(kMinUserDefinedSnippetID + fUserDefinedCodeSnippets.size() - 1);
705 SkShaderCodeDictionary::SkShaderCodeDictionary() {
706 // The 0th index is reserved as invalid
707 fEntryVector.push_back(nullptr);
709 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kError] = {
712 SnippetRequirementFlags::kNone,
715 GenerateDefaultGlueCode,
719 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSolidColorShader] = {
721 SkMakeSpan(kSolidShaderUniforms, kNumSolidShaderUniforms),
722 SnippetRequirementFlags::kNone,
725 GenerateDefaultGlueCode,
729 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader4] = {
731 SkMakeSpan(kLinearGradientUniforms4, kNumLinearGradientUniforms),
732 SnippetRequirementFlags::kLocalCoords,
734 kLinearGradient4Name,
735 GenerateDefaultGlueCode,
739 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader8] = {
741 SkMakeSpan(kLinearGradientUniforms8, kNumLinearGradientUniforms),
742 SnippetRequirementFlags::kLocalCoords,
744 kLinearGradient8Name,
745 GenerateDefaultGlueCode,
749 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader4] = {
751 SkMakeSpan(kRadialGradientUniforms4, kNumRadialGradientUniforms),
752 SnippetRequirementFlags::kLocalCoords,
754 kRadialGradient4Name,
755 GenerateDefaultGlueCode,
759 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader8] = {
761 SkMakeSpan(kRadialGradientUniforms8, kNumRadialGradientUniforms),
762 SnippetRequirementFlags::kLocalCoords,
764 kRadialGradient8Name,
765 GenerateDefaultGlueCode,
769 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader4] = {
771 SkMakeSpan(kSweepGradientUniforms4, kNumSweepGradientUniforms),
772 SnippetRequirementFlags::kLocalCoords,
775 GenerateDefaultGlueCode,
779 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader8] = {
781 SkMakeSpan(kSweepGradientUniforms8, kNumSweepGradientUniforms),
782 SnippetRequirementFlags::kLocalCoords,
785 GenerateDefaultGlueCode,
789 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader4] = {
791 SkMakeSpan(kConicalGradientUniforms4, kNumConicalGradientUniforms),
792 SnippetRequirementFlags::kLocalCoords,
794 kConicalGradient4Name,
795 GenerateDefaultGlueCode,
799 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader8] = {
801 SkMakeSpan(kConicalGradientUniforms8, kNumConicalGradientUniforms),
802 SnippetRequirementFlags::kLocalCoords,
804 kConicalGradient8Name,
805 GenerateDefaultGlueCode,
809 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLocalMatrixShader] = {
811 SkMakeSpan(kLocalMatrixShaderUniforms, kNumLocalMatrixShaderUniforms),
812 SnippetRequirementFlags::kLocalCoords,
814 kLocalMatrixShaderName,
815 GenerateDefaultGlueCode,
816 kNumLocalMatrixShaderChildren,
819 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kImageShader] = {
821 SkMakeSpan(kImageShaderUniforms, kNumImageShaderUniforms),
822 SnippetRequirementFlags::kLocalCoords,
823 SkMakeSpan(kISTexturesAndSamplers, kNumImageShaderTexturesAndSamplers),
825 GenerateImageShaderGlueCode,
829 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kBlendShader] = {
831 { kBlendShaderUniforms, kNumBlendShaderUniforms },
832 SnippetRequirementFlags::kNone,
835 GenerateDefaultGlueCode,
836 kNumBlendShaderChildren,
839 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kFixedFunctionBlender] = {
840 "FixedFunctionBlender",
842 SnippetRequirementFlags::kNone,
844 "FF-blending", // fixed function blending doesn't use static SkSL
845 GenerateFixedFunctionBlenderGlueCode,
849 fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kShaderBasedBlender] = {
850 "ShaderBasedBlender",
851 { kShaderBasedBlenderUniforms, kNumShaderBasedBlenderUniforms },
852 SnippetRequirementFlags::kNone,
855 GenerateShaderBasedBlenderGlueCode,