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