• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/core/SkShaderCodeDictionary.h"
9 
10 #include "include/private/SkSLString.h"
11 #include "src/core/SkOpts.h"
12 
13 namespace {
14 
add_indent(std::string * result,int indent)15 void add_indent(std::string* result, int indent) {
16     result->append(4*indent, ' ');
17 }
18 
19 } // anonymous namespace
20 
21 // TODO: SkShaderInfo::toSkSL needs to work outside of both just graphite and metal. To do
22 // so we'll need to switch over to using SkSL's uniform capabilities.
23 #if SK_SUPPORT_GPU && defined(SK_GRAPHITE_ENABLED) && defined(SK_METAL)
24 
25 #include <set>
26 
27 // TODO: switch this over to using SkSL's uniform system
28 namespace skgpu::mtl {
29 std::string GetMtlUniforms(int bufferID,
30                            const char* name,
31                            const std::vector<SkShaderInfo::SnippetEntry>&);
32 } // namespace skgpu::mtl
33 
34 // Emit the glue code needed to invoke a single static helper isolated w/in its own scope.
35 // The structure of this will be:
36 //
37 //     half4 outColor%d;
38 //     {
39 //         half4 child-outColor%d;  // for each child
40 //         {
41 //             /* emitted snippet sksl assigns to child-outColor%d */
42 //         }
43 //
44 //         /* emitted snippet sksl assigns to outColor%d - taking a vector of child var names */
45 //     }
46 // Where the %d is filled in with 'entryIndex'.
emitGlueCodeForEntry(int * entryIndex,std::string * result,int indent) const47 std::string SkShaderInfo::emitGlueCodeForEntry(int* entryIndex,
48                                                std::string* result,
49                                                int indent) const {
50     const SkShaderInfo::SnippetEntry& entry = fEntries[*entryIndex];
51     int curEntryIndex = *entryIndex;
52 
53     std::string scopeOutputVar(std::string("outColor") + std::to_string(curEntryIndex));
54 
55     add_indent(result, indent);
56     SkSL::String::appendf(result, "half4 %s;\n", scopeOutputVar.c_str());
57     add_indent(result, indent);
58     *result += "{\n";
59 
60     // Although the children appear after the parent in the shader info they are emitted
61     // before the parent
62     std::vector<std::string> childNames;
63     for (int j = 0; j < entry.fNumChildren; ++j) {
64         *entryIndex += 1;
65         std::string childOutputVar = this->emitGlueCodeForEntry(entryIndex, result, indent+1);
66         childNames.push_back(childOutputVar);
67     }
68 
69     *result += (entry.fGlueCodeGenerator)(scopeOutputVar, curEntryIndex,
70                                           entry, childNames, indent+1);
71     add_indent(result, indent);
72     *result += "}\n";
73 
74     return scopeOutputVar;
75 }
76 
77 // The current, incomplete, model for shader construction is:
78 //   each static code snippet (which can have an arbitrary signature) gets emitted once as a
79 //            preamble
80 //   glue code is then generated w/in the "main" method. The glue code is responsible for:
81 //            1) gathering the correct (mangled) uniforms
82 //            2) passing the uniforms and any other parameters to the helper method
83 //   The result of the last code snippet is then copied into "sk_FragColor".
84 // Note: that each entry's 'fStaticFunctionName' field must match the name of the method defined
85 // in the 'fStaticSkSL' field.
toSkSL() const86 std::string SkShaderInfo::toSkSL() const {
87     // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
88     std::string result = skgpu::mtl::GetMtlUniforms(2, "FS", fEntries);
89 
90     std::set<const char*> emittedStaticSnippets;
91     for (auto c : fEntries) {
92         if (emittedStaticSnippets.find(c.fStaticFunctionName) == emittedStaticSnippets.end()) {
93             result += c.fStaticSkSL;
94             emittedStaticSnippets.insert(c.fStaticFunctionName);
95         }
96     }
97 
98     result += "layout(location = 0, index = 0) out half4 sk_FragColor;\n";
99     result += "void main() {\n";
100 
101     // TODO: for some effects (e.g., SW blending) we will need to feed the output variable
102     // name from the prior step into the current step's glue code (and deal with the
103     // initial color issue).
104     std::string lastOutputVar;
105     for (int entryIndex = 0; entryIndex < (int) fEntries.size(); ++entryIndex) {
106         lastOutputVar = this->emitGlueCodeForEntry(&entryIndex, &result, 1);
107     }
108 
109     SkSL::String::appendf(&result, "    sk_FragColor = %s;\n", lastOutputVar.c_str());
110     result += "}\n";
111 
112     return result;
113 }
114 #endif
115 
makeEntry(std::unique_ptr<SkPaintParamsKey> key)116 SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::makeEntry(
117         std::unique_ptr<SkPaintParamsKey> key) {
118     return fArena.make([&](void *ptr) { return new(ptr) Entry(std::move(key)); });
119 }
120 
operator ()(const SkPaintParamsKey * key) const121 size_t SkShaderCodeDictionary::Hash::operator()(const SkPaintParamsKey* key) const {
122     return SkOpts::hash_fn(key->data(), key->sizeInBytes(), 0);
123 }
124 
findOrCreate(std::unique_ptr<SkPaintParamsKey> key)125 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::findOrCreate(
126         std::unique_ptr<SkPaintParamsKey> key) {
127     SkAutoSpinlock lock{fSpinLock};
128 
129     auto iter = fHash.find(key.get());
130     if (iter != fHash.end()) {
131         SkASSERT(fEntryVector[iter->second->uniqueID().asUInt()] == iter->second);
132         return iter->second;
133     }
134 
135     Entry* newEntry = this->makeEntry(std::move(key));
136     newEntry->setUniqueID(fEntryVector.size());
137     fHash.insert(std::make_pair(newEntry->paintParamsKey(), newEntry));
138     fEntryVector.push_back(newEntry);
139 
140     return newEntry;
141 }
142 
lookup(SkUniquePaintParamsID codeID) const143 const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::lookup(
144         SkUniquePaintParamsID codeID) const {
145 
146     if (!codeID.isValid()) {
147         return nullptr;
148     }
149 
150     SkAutoSpinlock lock{fSpinLock};
151 
152     SkASSERT(codeID.asUInt() < fEntryVector.size());
153 
154     return fEntryVector[codeID.asUInt()];
155 }
156 
getUniforms(SkBuiltInCodeSnippetID id) const157 SkSpan<const SkUniform> SkShaderCodeDictionary::getUniforms(SkBuiltInCodeSnippetID id) const {
158     return fBuiltInCodeSnippets[(int) id].fUniforms;
159 }
160 
getEntry(SkBuiltInCodeSnippetID id) const161 const SkShaderInfo::SnippetEntry* SkShaderCodeDictionary::getEntry(SkBuiltInCodeSnippetID id) const {
162     return &fBuiltInCodeSnippets[(int) id];
163 }
164 
getShaderInfo(SkUniquePaintParamsID uniqueID,SkShaderInfo * info)165 void SkShaderCodeDictionary::getShaderInfo(SkUniquePaintParamsID uniqueID, SkShaderInfo* info) {
166     auto entry = this->lookup(uniqueID);
167 
168     entry->paintParamsKey()->toShaderInfo(this, info);
169 }
170 
addUserDefinedSnippet()171 int SkShaderCodeDictionary::addUserDefinedSnippet() {
172     fUserDefinedCodeSnippets.push_back({});
173     return kBuiltInCodeSnippetIDCount + fUserDefinedCodeSnippets.size() - 1;
174 }
175 
176 //--------------------------------------------------------------------------------------------------
177 namespace {
178 
179 // The default glue code just calls a helper function with the signature:
180 //    half4 fStaticFunctionName(/* all uniforms as parameters */);
181 // and stores the result in a variable named "resultName".
GenerateDefaultGlueCode(const std::string & resultName,int entryIndex,const SkShaderInfo::SnippetEntry & entry,const std::vector<std::string> & childNames,int indent)182 std::string GenerateDefaultGlueCode(const std::string& resultName,
183                                     int entryIndex,
184                                     const SkShaderInfo::SnippetEntry& entry,
185                                     const std::vector<std::string>& childNames,
186                                     int indent) {
187     SkASSERT(childNames.empty());
188 
189     std::string result;
190 
191     add_indent(&result, indent);
192     SkSL::String::appendf(&result, "%s = %s(", resultName.c_str(), entry.fStaticFunctionName);
193     for (size_t i = 0; i < entry.fUniforms.size(); ++i) {
194         // The uniform names are mangled w/ the entry's index as a suffix
195         result += entry.fUniforms[i].name() + std::string("_") + std::to_string(entryIndex);
196 
197         if (i+1 < entry.fUniforms.size()) {
198             result += ", ";
199         }
200     }
201     result += ");\n";
202 
203     return result;
204 }
205 
206 //--------------------------------------------------------------------------------------------------
207 static constexpr int kFourStopGradient = 4;
208 
209 // TODO: For the sprint we unify all the gradient uniforms into a standard set of 6:
210 //   kMaxStops colors
211 //   kMaxStops offsets
212 //   2 points
213 //   2 radii
214 static constexpr int kNumGradientUniforms = 7;
215 static constexpr SkUniform kGradientUniforms[kNumGradientUniforms] = {
216         { "colors",  SkSLType::kFloat4, kFourStopGradient },
217         { "offsets", SkSLType::kFloat, kFourStopGradient },
218         { "point0",  SkSLType::kFloat2 },
219         { "point1",  SkSLType::kFloat2 },
220         { "radius0", SkSLType::kFloat },
221         { "radius1", SkSLType::kFloat },
222         { "padding", SkSLType::kFloat2 } // TODO: add automatic uniform padding
223 };
224 
225 static const char *kLinearGradient4Name = "linear_grad_4_shader";
226 static const char *kLinearGradient4SkSL =
227         // TODO: This should use local coords
228         "half4 linear_grad_4_shader(float4 colorsParam[4],\n"
229         "                           float offsetsParam[4],\n"
230         "                           float2 point0Param,\n"
231         "                           float2 point1Param,\n"
232         "                           float radius0Param,\n"
233         "                           float radius1Param,\n"
234         "                           float2 padding) {\n"
235         "    float2 pos = sk_FragCoord.xy;\n"
236         "    float2 delta = point1Param - point0Param;\n"
237         "    float2 pt = pos - point0Param;\n"
238         "    float t = dot(pt, delta) / dot(delta, delta);\n"
239         "    float4 result = colorsParam[0];\n"
240         "    result = mix(result, colorsParam[1],\n"
241         "                 clamp((t-offsetsParam[0])/(offsetsParam[1]-offsetsParam[0]),\n"
242         "                       0, 1));\n"
243         "    result = mix(result, colorsParam[2],\n"
244         "                 clamp((t-offsetsParam[1])/(offsetsParam[2]-offsetsParam[1]),\n"
245         "                       0, 1));\n"
246         "    result = mix(result, colorsParam[3],\n"
247         "                 clamp((t-offsetsParam[2])/(offsetsParam[3]-offsetsParam[2]),\n"
248         "                 0, 1));\n"
249         "    return half4(result);\n"
250         "}\n";
251 
252 //--------------------------------------------------------------------------------------------------
253 static constexpr int kNumSolidShaderUniforms = 1;
254 static constexpr SkUniform kSolidShaderUniforms[kNumSolidShaderUniforms] = {
255         { "color", SkSLType::kFloat4 }
256 };
257 
258 static const char* kSolidShaderName = "solid_shader";
259 static const char* kSolidShaderSkSL =
260         "half4 solid_shader(float4 colorParam) {\n"
261         "    return half4(colorParam);\n"
262         "}\n";
263 
264 //--------------------------------------------------------------------------------------------------
265 static constexpr int kNumImageShaderUniforms = 0;
266 
267 static const char* kImageShaderName = "image_shader";
268 static const char* kImageShaderSkSL =
269         "half4 image_shader() {\n"
270         "    float c = fract(abs(sk_FragCoord.x/10.0));\n"
271         "    return half4(c, c, c, 1.0);\n"
272         "}\n";
273 
274 //--------------------------------------------------------------------------------------------------
275 static constexpr int kNumBlendShaderUniforms = 0;
276 static constexpr int kNumBlendShaderChildren = 2;
277 
278 // Note: we're counting on the compiler to inline this code and trim it down to just the used
279 // branch(es).
280 static const char* kBlendShaderName = "blend_shader";
281 static const char* kBlendShaderSkSL =
282         "const int kClear      = 0;\n"
283         "const int kSrc        = 1;\n"
284         "const int kDst        = 2;\n"
285         "const int kSrcOver    = 3;\n"
286         "const int kDstOver    = 4;\n"
287         "const int kSrcIn      = 5;\n"
288         "const int kDstIn      = 6;\n"
289         "const int kSrcOut     = 7;\n"
290         "const int kDstOut     = 8;\n"
291         "const int kSrcATop    = 9;\n"
292         "const int kDstATop    = 10;\n"
293         "const int kXor        = 11;\n"
294         "const int kPlus       = 12;\n"
295         "const int kModulate   = 13;\n"
296         "const int kScreen     = 14;\n"
297         "const int kOverlay    = 15;\n"
298         "const int kDarken     = 16;\n"
299         "const int kLighten    = 17;\n"
300         "const int kColorDodge = 18;\n"
301         "const int kColorBurn  = 19;\n"
302         "const int kHardLight  = 20;\n"
303         "const int kSoftLight  = 21;\n"
304         "const int kDifference = 22;\n"
305         "const int kExclusion  = 23;\n"
306         "const int kMultiply   = 24;\n"
307         "const int kHue        = 25;\n"
308         "const int kSaturation = 26;\n"
309         "const int kColor      = 27;\n"
310         "const int kLuminosity = 28;\n"
311         "\n"
312         "half4 blend(int mode, half4 src, half4 dst) {\n"
313         "    switch (mode) {\n"
314         "        case kClear:      { return blend_clear(src, dst); }\n"
315         "        case kSrc:        { return blend_src(src, dst); }\n"
316         "        case kDst:        { return blend_dst(src, dst); }\n"
317         "        case kSrcOver:    { return blend_src_over(src, dst); }\n"
318         "        case kDstOver:    { return blend_dst_over(src, dst); }\n"
319         "        case kSrcIn:      { return blend_src_in(src, dst); }\n"
320         "        case kDstIn:      { return blend_dst_in(src, dst); }\n"
321         "        case kSrcOut:     { return blend_src_out(src, dst); }\n"
322         "        case kDstOut:     { return blend_dst_out(src, dst); }\n"
323         "        case kSrcATop:    { return blend_src_atop(src, dst); }\n"
324         "        case kDstATop:    { return blend_dst_atop(src, dst); }\n"
325         "        case kXor:        { return blend_xor(src, dst); }\n"
326         "        case kPlus:       { return blend_plus(src, dst); }\n"
327         "        case kModulate:   { return blend_modulate(src, dst); }\n"
328         "        case kScreen:     { return blend_screen(src, dst); }\n"
329         "        case kOverlay:    { return blend_overlay(src, dst); }\n"
330         "        case kDarken:     { return blend_darken(src, dst); }\n"
331         "        case kLighten:    { return blend_lighten(src, dst); }\n"
332         "        case kColorDodge: { return blend_color_dodge(src, dst); }\n"
333         "        case kColorBurn:  { return blend_color_burn(src, dst); }\n"
334         "        case kHardLight:  { return blend_hard_light(src, dst); }\n"
335         "        case kSoftLight:  { return blend_soft_light(src, dst); }\n"
336         "        case kDifference: { return blend_difference(src, dst); }\n"
337         "        case kExclusion:  { return blend_exclusion(src, dst); }\n"
338         "        case kMultiply:   { return blend_multiply(src, dst); }\n"
339         "        case kHue:        { return blend_hue(src, dst); }\n"
340         "        case kSaturation: { return blend_saturation(src, dst); }\n"
341         "        case kColor:      { return blend_color(src, dst); }\n"
342         "        case kLuminosity: { return blend_luminosity(src, dst); }\n"
343         "        default: return half4(0);  // Avoids 'blend can exit without returning a value' error\n"
344         "    }\n"
345         "}\n";
346 
GenerateBlendShaderGlueCode(const std::string & resultName,int entryIndex,const SkShaderInfo::SnippetEntry & entry,const std::vector<std::string> & childNames,int indent)347 std::string GenerateBlendShaderGlueCode(const std::string& resultName,
348                                         int entryIndex,
349                                         const SkShaderInfo::SnippetEntry& entry,
350                                         const std::vector<std::string>& childNames,
351                                         int indent) {
352     SkASSERT(childNames.size() == kNumBlendShaderChildren);
353 
354     std::string result;
355 
356     add_indent(&result, indent);
357     // TODO: actually feed in the blend mode either through a uniform or, somehow, from the
358     // SkPaintParamsKey
359     SkSL::String::appendf(&result, "%s = blend(kModulate, %s, %s);\n",
360                           resultName.c_str(),
361                           childNames[1].c_str(),
362                           childNames[0].c_str());
363 
364     return result;
365 }
366 
367 //--------------------------------------------------------------------------------------------------
368 static constexpr int kNumErrorUniforms = 0;
369 static const char* kErrorName = "error";
370 static const char* kErrorSkSL =
371         "half4 error() {\n"
372         "    return half4(1.0, 0.0, 1.0, 1.0);\n"
373         "}\n";
374 
375 } // anonymous namespace
376 
377 static constexpr int kNoChildren = 0;
378 
SkShaderCodeDictionary()379 SkShaderCodeDictionary::SkShaderCodeDictionary() {
380     // The 0th index is reserved as invalid
381     fEntryVector.push_back(nullptr);
382 
383     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw] = {
384             { nullptr, kNumErrorUniforms },
385             kErrorName, kErrorSkSL,
386             GenerateDefaultGlueCode,
387             kNoChildren
388     };
389     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kError] = {
390             { nullptr, kNumErrorUniforms },
391             kErrorName, kErrorSkSL,
392             GenerateDefaultGlueCode,
393             kNoChildren
394     };
395     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSolidColorShader] = {
396             SkMakeSpan(kSolidShaderUniforms, kNumSolidShaderUniforms),
397             kSolidShaderName, kSolidShaderSkSL,
398             GenerateDefaultGlueCode,
399             kNoChildren
400     };
401     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader] = {
402             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
403             kLinearGradient4Name, kLinearGradient4SkSL,
404             GenerateDefaultGlueCode,
405             kNoChildren
406     };
407     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader] = {
408             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
409             kLinearGradient4Name, kLinearGradient4SkSL,
410             GenerateDefaultGlueCode,
411             kNoChildren
412     };
413     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader] = {
414             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
415             kLinearGradient4Name, kLinearGradient4SkSL,
416             GenerateDefaultGlueCode,
417             kNoChildren
418     };
419     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader] = {
420             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
421             kLinearGradient4Name, kLinearGradient4SkSL,
422             GenerateDefaultGlueCode,
423             kNoChildren
424     };
425 
426     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kImageShader] = {
427             { nullptr, kNumImageShaderUniforms },
428             kImageShaderName, kImageShaderSkSL,
429             GenerateDefaultGlueCode,
430             kNoChildren
431     };
432     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kBlendShader] = {
433             { nullptr, kNumBlendShaderUniforms },
434             kBlendShaderName, kBlendShaderSkSL,
435             GenerateBlendShaderGlueCode,
436             kNumBlendShaderChildren
437     };
438     fBuiltInCodeSnippets[(int) SkBuiltInCodeSnippetID::kSimpleBlendMode] = {
439             { nullptr, kNumErrorUniforms },
440             kErrorName, kErrorSkSL,
441             GenerateDefaultGlueCode,
442             kNoChildren
443     };
444 }
445