Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / core / SkShaderCodeDictionary.cpp
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/core/SkShaderCodeDictionary.h"
9
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"
15
16 #ifdef SK_GRAPHITE_ENABLED
17 #include "include/gpu/graphite/Context.h"
18 #endif
19
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);
23
24 namespace {
25
26 std::string get_mangled_local_var_name(const char* baseName, int manglingSuffix) {
27     return std::string(baseName) + "_" + std::to_string(manglingSuffix);
28 }
29
30 void add_indent(std::string* result, int indent) {
31     result->append(4*indent, ' ');
32 }
33
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,
38                                                        int indent) {
39     std::string result;
40
41     if (reader.entry()->needsLocalCoords()) {
42         // Every snippet that requests local coordinates must have a preLocalMatrix as its first
43         // uniform
44         SkASSERT(reader.entry()->fUniforms.size() >= 1);
45         SkASSERT(reader.entry()->fUniforms[0].type() == SkSLType::kFloat4x4);
46
47         std::string localMatrixUniformName = reader.entry()->getMangledUniformName(0, entryIndex);
48
49         std::string preLocalMatrixVarName = get_mangled_local_var_name("preLocal", entryIndex);
50
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());
57     }
58
59     return result;
60 }
61 #endif
62
63 } // anonymous namespace
64
65
66 std::string SkShaderSnippet::getMangledUniformName(int uniformIndex, int mangleId) const {
67     std::string result;
68     result = fUniforms[uniformIndex].name() + std::string("_") + std::to_string(mangleId);
69     return result;
70 }
71
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)
75
76 // TODO: switch this over to using SkSL's uniform system
77 namespace skgpu::graphite {
78 std::string GetMtlUniforms(int bufferID,
79                            const char* name,
80                            const std::vector<SkPaintParamsKey::BlockReader>&,
81                            bool needsDev2Local);
82 std::string GetMtlTexturesAndSamplers(const std::vector<SkPaintParamsKey::BlockReader>&,
83                                       int* binding);
84 } // namespace skgpu::graphite
85
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:
88 //
89 //     half4 outColor%d;
90 //     {
91 //         half4 child-outColor%d;  // for each child
92 //         {
93 //             /* emitted snippet sksl assigns to child-outColor%d */
94 //         }
95 //
96 //         /* emitted snippet sksl assigns to outColor%d - taking a vector of child var names */
97 //     }
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,
102                                                std::string* result,
103                                                int indent) const {
104     const SkPaintParamsKey::BlockReader& reader = fBlockReaders[*entryIndex];
105     int curEntryIndex = *entryIndex;
106
107     std::string scopeOutputVar = get_mangled_local_var_name("outColor", curEntryIndex);
108
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);
115     *result += "{\n";
116
117     *result += generate_default_before_children_glue_code(curEntryIndex, reader,
118                                                           parentPreLocalName, indent+1);
119
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);
124     } else {
125         currentPreLocalName = parentPreLocalName;
126     }
127
128     // Although the children appear after the parent in the shader info they are emitted
129     // before the parent
130     std::vector<std::string> childOutputVarNames;
131     for (int j = 0; j < reader.numChildren(); ++j) {
132         *entryIndex += 1;
133         std::string childOutputVar = this->emitGlueCodeForEntry(entryIndex,
134                                                                 priorStageOutputName,
135                                                                 currentPreLocalName,
136                                                                 result, indent+1);
137         childOutputVarNames.push_back(childOutputVar);
138     }
139
140     *result += (reader.entry()->fGlueCodeGenerator)(scopeOutputVar, curEntryIndex, reader,
141                                                     priorStageOutputName,
142                                                     childOutputVarNames, indent+1);
143     add_indent(result, indent);
144     *result += "}\n";
145
146     return scopeOutputVar;
147 }
148
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());
163
164     int binding = 0;
165     result += skgpu::graphite::GetMtlTexturesAndSamplers(fBlockReaders, &binding);
166     result += "layout(location = 0, index = 0) out half4 sk_FragColor;\n";
167     result += "void main() {\n";
168
169     if (this->needsLocalCoords()) {
170         result += "const float4x4 initialPreLocal = float4x4(1);\n";
171     }
172
173     std::string parentPreLocal = "initialPreLocal";
174     std::string lastOutputVar = "initialColor";
175
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());
179
180     for (int entryIndex = 0; entryIndex < (int) fBlockReaders.size(); ++entryIndex) {
181         lastOutputVar = this->emitGlueCodeForEntry(&entryIndex, lastOutputVar, parentPreLocal,
182                                                    &result, 1);
183     }
184
185     SkSL::String::appendf(&result, "    sk_FragColor = %s;\n", lastOutputVar.c_str());
186     result += "}\n";
187
188     return result;
189 }
190 #endif
191
192 SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::makeEntry(
193         const SkPaintParamsKey& key
194 #ifdef SK_GRAPHITE_ENABLED
195         , const SkPipelineDataGatherer::BlendInfo& blendInfo
196 #endif
197         ) {
198     uint8_t* newKeyData = fArena.makeArray<uint8_t>(key.sizeInBytes());
199     memcpy(newKeyData, key.data(), key.sizeInBytes());
200
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); });
204 #else
205     return fArena.make([&](void *ptr) { return new(ptr) Entry(newKeyAsSpan); });
206 #endif
207 }
208
209 size_t SkShaderCodeDictionary::Hash::operator()(const SkPaintParamsKey* key) const {
210     return SkOpts::hash_fn(key->data(), key->sizeInBytes(), 0);
211 }
212
213 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::findOrCreate(
214         const SkPaintParamsKey& key
215 #ifdef SK_GRAPHITE_ENABLED
216         , const SkPipelineDataGatherer::BlendInfo& blendInfo
217 #endif
218         ) {
219     SkAutoSpinlock lock{fSpinLock};
220
221     auto iter = fHash.find(&key);
222     if (iter != fHash.end()) {
223         SkASSERT(fEntryVector[iter->second->uniqueID().asUInt()] == iter->second);
224         return iter->second;
225     }
226
227 #ifdef SK_GRAPHITE_ENABLED
228     Entry* newEntry = this->makeEntry(key, blendInfo);
229 #else
230     Entry* newEntry = this->makeEntry(key);
231 #endif
232     newEntry->setUniqueID(fEntryVector.size());
233     fHash.insert(std::make_pair(&newEntry->paintParamsKey(), newEntry));
234     fEntryVector.push_back(newEntry);
235
236     return newEntry;
237 }
238
239 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::lookup(
240         SkUniquePaintParamsID codeID) const {
241
242     if (!codeID.isValid()) {
243         return nullptr;
244     }
245
246     SkAutoSpinlock lock{fSpinLock};
247
248     SkASSERT(codeID.asUInt() < fEntryVector.size());
249
250     return fEntryVector[codeID.asUInt()];
251 }
252
253 SkSpan<const SkUniform> SkShaderCodeDictionary::getUniforms(SkBuiltInCodeSnippetID id) const {
254     return fBuiltInCodeSnippets[(int) id].fUniforms;
255 }
256
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;
261 }
262
263 const SkShaderSnippet* SkShaderCodeDictionary::getEntry(int codeSnippetID) const {
264     if (codeSnippetID < 0) {
265         return nullptr;
266     }
267
268     if (codeSnippetID < kBuiltInCodeSnippetIDCount) {
269         return &fBuiltInCodeSnippets[codeSnippetID];
270     }
271
272     if (codeSnippetID < kMinUserDefinedSnippetID) {
273         return nullptr;
274     }
275
276     int userDefinedCodeSnippetID = codeSnippetID - kMinUserDefinedSnippetID;
277     if (userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size())) {
278         return fUserDefinedCodeSnippets[userDefinedCodeSnippetID].get();
279     }
280
281     return nullptr;
282 }
283
284 const SkShaderSnippet* SkShaderCodeDictionary::getEntry(SkBlenderID id) const {
285     return this->getEntry(id.asUInt());
286 }
287
288 void SkShaderCodeDictionary::getShaderInfo(SkUniquePaintParamsID uniqueID, SkShaderInfo* info) {
289     auto entry = this->lookup(uniqueID);
290
291     entry->paintParamsKey().toShaderInfo(this, info);
292
293 #ifdef SK_GRAPHITE_ENABLED
294     info->setBlendInfo(entry->blendInfo());
295 #endif
296 }
297
298 //--------------------------------------------------------------------------------------------------
299 namespace {
300
301 using DataPayloadField = SkPaintParamsKey::DataPayloadField;
302
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,
308                                     int entryIndex,
309                                     const SkPaintParamsKey::BlockReader& reader,
310                                     const std::string& priorStageOutputName,
311                                     const std::vector<std::string>& childOutputVarNames,
312                                     int indent) {
313     const SkShaderSnippet* entry = reader.entry();
314
315     SkASSERT((int)childOutputVarNames.size() == entry->numExpectedChildren());
316
317     if (entry->needsLocalCoords()) {
318         // Every snippet that requests local coordinates must have a localMatrix as its first
319         // uniform
320         SkASSERT(reader.entry()->fUniforms.size() >= 1);
321         SkASSERT(reader.entry()->fUniforms[0].type() == SkSLType::kFloat4x4);
322     }
323
324     std::string result;
325
326     add_indent(&result, indent);
327     SkSL::String::appendf(&result,
328                           "%s = %s(",
329                           resultName.c_str(),
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",
334                                                                            entryIndex);
335             result += preLocalMatrixVarName;
336             result += " * dev2LocalUni";
337         } else {
338             result += entry->getMangledUniformName(i, entryIndex);
339         }
340         if (i+1 < entry->fUniforms.size() + childOutputVarNames.size()) {
341             result += ", ";
342         }
343     }
344     for (size_t i = 0; i < childOutputVarNames.size(); ++i) {
345         result += childOutputVarNames[i].c_str();
346         if (i+1 < childOutputVarNames.size()) {
347             result += ", ";
348         }
349     }
350     result += ");\n";
351
352     return result;
353 }
354
355 //--------------------------------------------------------------------------------------------------
356 static constexpr int kFourStopGradient = 4;
357 static constexpr int kEightStopGradient = 8;
358
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 },
370 };
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 },
381 };
382
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 },
391 };
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 },
399 };
400
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 },
413 };
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 },
425 };
426
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
438 };
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
449 };
450
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";
459
460 //--------------------------------------------------------------------------------------------------
461 static constexpr int kNumSolidShaderUniforms = 1;
462 static constexpr SkUniform kSolidShaderUniforms[kNumSolidShaderUniforms] = {
463         { "color", SkSLType::kFloat4 }
464 };
465
466 static constexpr char kSolidShaderName[] = "sk_solid_shader";
467
468 //--------------------------------------------------------------------------------------------------
469 static constexpr int kNumLocalMatrixShaderUniforms = 1;
470 static constexpr SkUniform kLocalMatrixShaderUniforms[kNumLocalMatrixShaderUniforms] = {
471         { "localMatrix", SkSLType::kFloat4x4 },
472 };
473
474 static constexpr int kNumLocalMatrixShaderChildren = 1;
475
476 static constexpr char kLocalMatrixShaderName[] = "sk_local_matrix_shader";
477
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 },
487 };
488
489 static constexpr int kNumImageShaderTexturesAndSamplers = 1;
490 static constexpr SkTextureAndSampler kISTexturesAndSamplers[kNumImageShaderTexturesAndSamplers] = {
491         {"sampler"},
492 };
493
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");
498
499 static constexpr char kImageShaderName[] = "sk_compute_coords";
500
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,
506                                         int entryIndex,
507                                         const SkPaintParamsKey::BlockReader& reader,
508                                         const std::string& priorStageOutputName,
509                                         const std::vector<std::string>& childNames,
510                                         int indent) {
511     SkASSERT(childNames.empty());
512
513     std::string samplerVarName = std::string("sampler_") + std::to_string(entryIndex) + "_0";
514     std::string preLocalMatrixVarName = get_mangled_local_var_name("preLocal", entryIndex);
515
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);
523
524     std::string result;
525
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(),
531                           subsetName.c_str(),
532                           tmXName.c_str(),
533                           tmYName.c_str(),
534                           imgWidthName.c_str(),
535                           imgHeightName.c_str());
536
537     add_indent(&result, indent);
538     SkSL::String::appendf(&result,
539                           "%s = sample(%s, coords);\n",
540                           resultName.c_str(),
541                           samplerVarName.c_str());
542
543     return result;
544 }
545
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 },
553 };
554
555 static constexpr int kNumBlendShaderChildren = 2;
556
557 static constexpr char kBlendShaderName[] = "sk_blend_shader";
558
559 //--------------------------------------------------------------------------------------------------
560 static constexpr char kErrorName[] = "sk_error";
561
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,
566                                                  int entryIndex,
567                                                  const SkPaintParamsKey::BlockReader& reader,
568                                                  const std::string& priorStageOutputName,
569                                                  const std::vector<std::string>& childNames,
570                                                  int indent) {
571     SkASSERT(childNames.empty());
572     SkASSERT(reader.entry()->fUniforms.empty());
573     SkASSERT(reader.numDataPayloadFields() == 0);
574
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.
577
578     std::string result;
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());
583
584     return result;
585 }
586
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 },
594 };
595
596 static constexpr char kBlendHelperName[] = "sk_blend";
597
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,
603                                                int entryIndex,
604                                                const SkPaintParamsKey::BlockReader& reader,
605                                                const std::string& priorStageOutputName,
606                                                const std::vector<std::string>& childNames,
607                                                int indent) {
608     SkASSERT(childNames.empty());
609     SkASSERT(reader.entry()->fUniforms.size() == 4); // actual blend uniform + 3 padding int
610     SkASSERT(reader.numDataPayloadFields() == 0);
611
612     std::string uniformName = reader.entry()->getMangledUniformName(0, entryIndex);
613
614     std::string result;
615
616     add_indent(&result, indent);
617     result += "// Shader-based blending\n";
618
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";
622
623     add_indent(&result, indent);
624     SkSL::String::appendf(&result, "%s = %s(%s, %s, dummyDst);",
625                           resultName.c_str(),
626                           reader.entry()->fStaticFunctionName,
627                           uniformName.c_str(),
628                           priorStageOutputName.c_str());
629
630     return result;
631 }
632
633 //--------------------------------------------------------------------------------------------------
634
635 } // anonymous namespace
636
637 bool SkShaderCodeDictionary::isValidID(int snippetID) const {
638     if (snippetID < 0) {
639         return false;
640     }
641
642     if (snippetID < kBuiltInCodeSnippetIDCount) {
643         return true;
644     }
645
646     if (snippetID < kMinUserDefinedSnippetID) {
647         return false;
648     }
649
650     int userDefinedCodeSnippetID = snippetID - kMinUserDefinedSnippetID;
651     return userDefinedCodeSnippetID < SkTo<int>(fUserDefinedCodeSnippets.size());
652 }
653
654 static constexpr int kNoChildren = 0;
655
656 // TODO: this version needs to be removed
657 int SkShaderCodeDictionary::addUserDefinedSnippet(
658         const char* name,
659         SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations) {
660
661     std::unique_ptr<SkShaderSnippet> entry(new SkShaderSnippet("UserDefined",
662                                                                {}, // no uniforms
663                                                                SnippetRequirementFlags::kNone,
664                                                                {}, // no samplers
665                                                                name,
666                                                                GenerateDefaultGlueCode,
667                                                                kNoChildren,
668                                                                dataPayloadExpectations));
669
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));
674
675     return kMinUserDefinedSnippetID + fUserDefinedCodeSnippets.size() - 1;
676 }
677
678 SkBlenderID SkShaderCodeDictionary::addUserDefinedBlender(sk_sp<SkRuntimeEffect> effect) {
679     if (!effect) {
680         return {};
681     }
682
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.
687
688     std::unique_ptr<SkShaderSnippet> entry(new SkShaderSnippet("UserDefined",
689                                                                {}, // missing uniforms
690                                                                SnippetRequirementFlags::kNone,
691                                                                {}, // missing samplers
692                                                                "foo",
693                                                                GenerateDefaultGlueCode,
694                                                                kNoChildren,
695                                                                {}));  // missing data payload
696
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));
701
702     return SkBlenderID(kMinUserDefinedSnippetID + fUserDefinedCodeSnippets.size() - 1);
703 }
704
705 SkShaderCodeDictionary::SkShaderCodeDictionary() {
706     // The 0th index is reserved as invalid
707     fEntryVector.push_back(nullptr);
708
709     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kError] = {
710             "Error",
711             { },     // no uniforms
712             SnippetRequirementFlags::kNone,
713             { },     // no samplers
714             kErrorName,
715             GenerateDefaultGlueCode,
716             kNoChildren,
717             { }
718     };
719     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSolidColorShader] = {
720             "SolidColor",
721             SkMakeSpan(kSolidShaderUniforms, kNumSolidShaderUniforms),
722             SnippetRequirementFlags::kNone,
723             { },     // no samplers
724             kSolidShaderName,
725             GenerateDefaultGlueCode,
726             kNoChildren,
727             { }
728     };
729     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader4] = {
730             "LinearGradient4",
731             SkMakeSpan(kLinearGradientUniforms4, kNumLinearGradientUniforms),
732             SnippetRequirementFlags::kLocalCoords,
733             { },     // no samplers
734             kLinearGradient4Name,
735             GenerateDefaultGlueCode,
736             kNoChildren,
737             { }
738     };
739     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader8] = {
740             "LinearGradient8",
741             SkMakeSpan(kLinearGradientUniforms8, kNumLinearGradientUniforms),
742             SnippetRequirementFlags::kLocalCoords,
743             { },     // no samplers
744             kLinearGradient8Name,
745             GenerateDefaultGlueCode,
746             kNoChildren,
747             { }
748     };
749     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader4] = {
750             "RadialGradient4",
751             SkMakeSpan(kRadialGradientUniforms4, kNumRadialGradientUniforms),
752             SnippetRequirementFlags::kLocalCoords,
753             { },     // no samplers
754             kRadialGradient4Name,
755             GenerateDefaultGlueCode,
756             kNoChildren,
757             { }
758     };
759     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader8] = {
760             "RadialGradient8",
761             SkMakeSpan(kRadialGradientUniforms8, kNumRadialGradientUniforms),
762             SnippetRequirementFlags::kLocalCoords,
763             { },     // no samplers
764             kRadialGradient8Name,
765             GenerateDefaultGlueCode,
766             kNoChildren,
767             { }
768     };
769     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader4] = {
770             "SweepGradient4",
771             SkMakeSpan(kSweepGradientUniforms4, kNumSweepGradientUniforms),
772             SnippetRequirementFlags::kLocalCoords,
773             { },     // no samplers
774             kSweepGradient4Name,
775             GenerateDefaultGlueCode,
776             kNoChildren,
777             { }
778     };
779     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader8] = {
780             "SweepGradient8",
781             SkMakeSpan(kSweepGradientUniforms8, kNumSweepGradientUniforms),
782             SnippetRequirementFlags::kLocalCoords,
783             { },     // no samplers
784             kSweepGradient8Name,
785             GenerateDefaultGlueCode,
786             kNoChildren,
787             { }
788     };
789     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader4] = {
790             "ConicalGradient4",
791             SkMakeSpan(kConicalGradientUniforms4, kNumConicalGradientUniforms),
792             SnippetRequirementFlags::kLocalCoords,
793             { },     // no samplers
794             kConicalGradient4Name,
795             GenerateDefaultGlueCode,
796             kNoChildren,
797             { }
798     };
799     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader8] = {
800             "ConicalGradient8",
801             SkMakeSpan(kConicalGradientUniforms8, kNumConicalGradientUniforms),
802             SnippetRequirementFlags::kLocalCoords,
803             { },     // no samplers
804             kConicalGradient8Name,
805             GenerateDefaultGlueCode,
806             kNoChildren,
807             { }
808     };
809     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLocalMatrixShader] = {
810             "LocalMatrixShader",
811             SkMakeSpan(kLocalMatrixShaderUniforms, kNumLocalMatrixShaderUniforms),
812             SnippetRequirementFlags::kLocalCoords,
813             { },     // no samplers
814             kLocalMatrixShaderName,
815             GenerateDefaultGlueCode,
816             kNumLocalMatrixShaderChildren,
817             { }
818     };
819     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kImageShader] = {
820             "ImageShader",
821             SkMakeSpan(kImageShaderUniforms, kNumImageShaderUniforms),
822             SnippetRequirementFlags::kLocalCoords,
823             SkMakeSpan(kISTexturesAndSamplers, kNumImageShaderTexturesAndSamplers),
824             kImageShaderName,
825             GenerateImageShaderGlueCode,
826             kNoChildren,
827             { }
828     };
829     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kBlendShader] = {
830             "BlendShader",
831             { kBlendShaderUniforms, kNumBlendShaderUniforms },
832             SnippetRequirementFlags::kNone,
833             { },     // no samplers
834             kBlendShaderName,
835             GenerateDefaultGlueCode,
836             kNumBlendShaderChildren,
837             { }
838     };
839     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kFixedFunctionBlender] = {
840             "FixedFunctionBlender",
841             { },     // no uniforms
842             SnippetRequirementFlags::kNone,
843             { },     // no samplers
844             "FF-blending",  // fixed function blending doesn't use static SkSL
845             GenerateFixedFunctionBlenderGlueCode,
846             kNoChildren,
847             { }
848     };
849     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kShaderBasedBlender] = {
850             "ShaderBasedBlender",
851             { kShaderBasedBlenderUniforms, kNumShaderBasedBlenderUniforms },
852             SnippetRequirementFlags::kNone,
853             { },     // no samplers
854             kBlendHelperName,
855             GenerateShaderBasedBlenderGlueCode,
856             kNoChildren,
857             { }
858     };
859 }