1 /*
2 * Copyright 2024 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/ShaderInfo.h"
9
10 #include "src/gpu/BlendFormula.h"
11 #include "src/gpu/graphite/ContextUtils.h"
12 #include "src/gpu/graphite/PaintParamsKey.h"
13 #include "src/gpu/graphite/Renderer.h"
14 #include "src/gpu/graphite/ShaderCodeDictionary.h"
15 #include "src/gpu/graphite/UniformManager.h"
16 #include "src/sksl/SkSLString.h"
17 #include "src/sksl/SkSLUtil.h"
18
19 using namespace skia_private;
20
21 namespace skgpu::graphite {
22
23 namespace {
24
get_uniform_header(int bufferID,const char * name)25 std::string get_uniform_header(int bufferID, const char* name) {
26 std::string result;
27
28 SkSL::String::appendf(&result, "layout (binding=%d) uniform %sUniforms {\n", bufferID, name);
29
30 return result;
31 }
32
get_uniforms(UniformOffsetCalculator * offsetter,SkSpan<const Uniform> uniforms,int manglingSuffix,bool * wrotePaintColor)33 std::string get_uniforms(UniformOffsetCalculator* offsetter,
34 SkSpan<const Uniform> uniforms,
35 int manglingSuffix,
36 bool* wrotePaintColor) {
37 std::string result;
38 std::string uniformName;
39 for (const Uniform& u : uniforms) {
40 uniformName = u.name();
41
42 if (u.isPaintColor() && wrotePaintColor) {
43 if (*wrotePaintColor) {
44 SkSL::String::appendf(&result, " // deduplicated %s\n", u.name());
45 continue;
46 }
47
48 *wrotePaintColor = true;
49 } else {
50 if (manglingSuffix >= 0) {
51 uniformName.append("_");
52 uniformName.append(std::to_string(manglingSuffix));
53 }
54 }
55
56 SkSL::String::appendf(&result,
57 " layout(offset=%d) %s %s",
58 offsetter->advanceOffset(u.type(), u.count()),
59 SkSLTypeString(u.type()),
60 uniformName.c_str());
61 if (u.count()) {
62 result.append("[");
63 result.append(std::to_string(u.count()));
64 result.append("]");
65 }
66 result.append(";\n");
67 }
68
69 return result;
70 }
71
get_node_uniforms(UniformOffsetCalculator * offsetter,const ShaderNode * node,bool * wrotePaintColor)72 std::string get_node_uniforms(UniformOffsetCalculator* offsetter,
73 const ShaderNode* node,
74 bool* wrotePaintColor) {
75 std::string result;
76 SkSpan<const Uniform> uniforms = node->entry()->fUniforms;
77
78 if (!uniforms.empty()) {
79 if (node->entry()->fUniformStructName) {
80 auto substruct = UniformOffsetCalculator::ForStruct(offsetter->layout());
81 for (const Uniform& u : uniforms) {
82 substruct.advanceOffset(u.type(), u.count());
83 }
84
85 const int structOffset = offsetter->advanceStruct(substruct);
86 SkSL::String::appendf(&result,
87 "layout(offset=%d) %s node_%d;",
88 structOffset,
89 node->entry()->fUniformStructName,
90 node->keyIndex());
91 } else {
92 SkSL::String::appendf(&result, "// %d - %s uniforms\n",
93 node->keyIndex(), node->entry()->fName);
94 result += get_uniforms(offsetter, uniforms, node->keyIndex(), wrotePaintColor);
95 }
96 }
97
98 for (const ShaderNode* child : node->children()) {
99 result += get_node_uniforms(offsetter, child, wrotePaintColor);
100 }
101 return result;
102 }
103
get_ssbo_fields(SkSpan<const Uniform> uniforms,int manglingSuffix,bool * wrotePaintColor)104 std::string get_ssbo_fields(SkSpan<const Uniform> uniforms,
105 int manglingSuffix,
106 bool* wrotePaintColor) {
107 std::string result;
108
109 std::string uniformName;
110 for (const Uniform& u : uniforms) {
111 uniformName = u.name();
112
113 if (u.isPaintColor() && wrotePaintColor) {
114 if (*wrotePaintColor) {
115 SkSL::String::appendf(&result, " // deduplicated %s\n", u.name());
116 continue;
117 }
118
119 *wrotePaintColor = true;
120 } else {
121 if (manglingSuffix >= 0) {
122 uniformName.append("_");
123 uniformName.append(std::to_string(manglingSuffix));
124 }
125 }
126
127 SkSL::String::appendf(&result, " %s %s", SkSLTypeString(u.type()), uniformName.c_str());
128 if (u.count()) {
129 SkSL::String::appendf(&result, "[%d]", u.count());
130 }
131 result.append(";\n");
132 }
133
134 return result;
135 }
136
get_node_ssbo_fields(const ShaderNode * node,bool * wrotePaintColor)137 std::string get_node_ssbo_fields(const ShaderNode* node, bool* wrotePaintColor) {
138 std::string result;
139 SkSpan<const Uniform> uniforms = node->entry()->fUniforms;
140
141 if (!uniforms.empty()) {
142 if (node->entry()->fUniformStructName) {
143 SkSL::String::appendf(&result, "%s node_%d;",
144 node->entry()->fUniformStructName, node->keyIndex());
145 } else {
146 SkSL::String::appendf(&result, "// %d - %s uniforms\n",
147 node->keyIndex(), node->entry()->fName);
148
149 result += get_ssbo_fields(uniforms, node->keyIndex(), wrotePaintColor);
150 }
151 }
152
153 for (const ShaderNode* child : node->children()) {
154 result += get_node_ssbo_fields(child, wrotePaintColor);
155 }
156 return result;
157 }
158
emit_intrinsic_constants(const ResourceBindingRequirements & bindingReqs)159 std::string emit_intrinsic_constants(const ResourceBindingRequirements& bindingReqs) {
160 std::string result;
161 auto offsetter = UniformOffsetCalculator::ForTopLevel(bindingReqs.fUniformBufferLayout);
162
163 if (bindingReqs.fUseVulkanPushConstantsForIntrinsicConstants) {
164 result = "layout (vulkan, push_constant) uniform IntrinsicUniforms {\n";
165 } else {
166 result = get_uniform_header(bindingReqs.fIntrinsicBufferBinding, "Intrinsic");
167 }
168 result += get_uniforms(&offsetter, kIntrinsicUniforms, -1, /* wrotePaintColor= */ nullptr);
169 result.append("};\n\n");
170 SkASSERTF(bindingReqs.fUseVulkanPushConstantsForIntrinsicConstants ||
171 result.find('[') == std::string::npos,
172 "Arrays are not supported in intrinsic uniforms");
173 return result;
174 }
175
emit_paint_params_uniforms(int bufferID,const Layout layout,SkSpan<const ShaderNode * > nodes,bool * hasUniforms,bool * wrotePaintColor)176 std::string emit_paint_params_uniforms(int bufferID,
177 const Layout layout,
178 SkSpan<const ShaderNode*> nodes,
179 bool* hasUniforms,
180 bool* wrotePaintColor) {
181 auto offsetter = UniformOffsetCalculator::ForTopLevel(layout);
182
183 std::string result = get_uniform_header(bufferID, "FS");
184 for (const ShaderNode* n : nodes) {
185 result += get_node_uniforms(&offsetter, n, wrotePaintColor);
186 }
187 result.append("};\n\n");
188
189 *hasUniforms = offsetter.size() > 0;
190 if (!*hasUniforms) {
191 // No uniforms were added
192 return {};
193 }
194
195 return result;
196 }
197
emit_render_step_uniforms(int bufferID,const Layout layout,SkSpan<const Uniform> uniforms)198 std::string emit_render_step_uniforms(int bufferID,
199 const Layout layout,
200 SkSpan<const Uniform> uniforms) {
201 auto offsetter = UniformOffsetCalculator::ForTopLevel(layout);
202
203 std::string result = get_uniform_header(bufferID, "Step");
204 result += get_uniforms(&offsetter, uniforms, -1, /* wrotePaintColor= */ nullptr);
205 result.append("};\n\n");
206
207 return result;
208 }
209
emit_paint_params_storage_buffer(int bufferID,SkSpan<const ShaderNode * > nodes,bool * hasUniforms,bool * wrotePaintColor)210 std::string emit_paint_params_storage_buffer(int bufferID,
211 SkSpan<const ShaderNode*> nodes,
212 bool* hasUniforms,
213 bool* wrotePaintColor) {
214 *hasUniforms = false;
215
216 std::string fields;
217 for (const ShaderNode* n : nodes) {
218 fields += get_node_ssbo_fields(n, wrotePaintColor);
219 }
220
221 if (fields.empty()) {
222 // No uniforms were added
223 *hasUniforms = false;
224 return {};
225 }
226
227 *hasUniforms = true;
228 return SkSL::String::printf(
229 "struct FSUniformData {\n"
230 "%s\n"
231 "};\n\n"
232 "layout (binding=%d) readonly buffer FSUniforms {\n"
233 "FSUniformData fsUniformData[];\n"
234 "};\n",
235 fields.c_str(),
236 bufferID);
237 }
238
emit_render_step_storage_buffer(int bufferID,SkSpan<const Uniform> uniforms)239 std::string emit_render_step_storage_buffer(int bufferID, SkSpan<const Uniform> uniforms) {
240 SkASSERT(!uniforms.empty());
241 std::string fields = get_ssbo_fields(uniforms, -1, /*wrotePaintColor=*/nullptr);
242 return SkSL::String::printf(
243 "struct StepUniformData {\n"
244 "%s\n"
245 "};\n\n"
246 "layout (binding=%d) readonly buffer StepUniforms {\n"
247 " StepUniformData stepUniformData[];\n"
248 "};\n",
249 fields.c_str(),
250 bufferID);
251 }
252
emit_uniforms_from_storage_buffer(const char * bufferNamePrefix,const char * ssboIndex,SkSpan<const Uniform> uniforms)253 std::string emit_uniforms_from_storage_buffer(const char* bufferNamePrefix,
254 const char* ssboIndex,
255 SkSpan<const Uniform> uniforms) {
256 std::string result;
257
258 for (const Uniform& u : uniforms) {
259 SkSL::String::appendf(&result, "%s %s", SkSLTypeString(u.type()), u.name());
260 if (u.count()) {
261 SkSL::String::appendf(&result, "[%d]", u.count());
262 }
263 SkSL::String::appendf(
264 &result, " = %sUniformData[%s].%s;\n", bufferNamePrefix, ssboIndex, u.name());
265 }
266
267 return result;
268 }
269
append_sampler_descs(const SkSpan<const uint32_t> samplerData,skia_private::TArray<SamplerDesc> & outDescs)270 void append_sampler_descs(const SkSpan<const uint32_t> samplerData,
271 skia_private::TArray<SamplerDesc>& outDescs) {
272 // Sampler data consists of variable-length SamplerDesc representations which can differ based
273 // upon a sampler's immutability and format. For this reason, handle incrementing i in the loop.
274 for (size_t i = 0; i < samplerData.size();) {
275 // Create a default-initialized SamplerDesc (which only takes up one uint32). If we are
276 // using a dynamic sampler, this will be directly inserted into outDescs. Otherwise, it will
277 // be populated with actual immutable sampler data and then inserted.
278 SamplerDesc desc{};
279 size_t samplerDescLength = 1;
280 SkASSERT(desc.asSpan().size() == samplerDescLength);
281
282 // Isolate the ImmutableSamplerInfo portion of the SamplerDesc represented by samplerData.
283 // If immutableSamplerInfo is non-zero, that means we are using an immutable sampler.
284 uint32_t immutableSamplerInfo = samplerData[i] >> SamplerDesc::kImmutableSamplerInfoShift;
285 if (immutableSamplerInfo != 0) {
286 // Consult the first bit of immutableSamplerInfo which tells us whether the sampler uses
287 // a known or external format. With this, update sampler description length.
288 bool usesExternalFormat = immutableSamplerInfo & 0b1;
289 samplerDescLength = usesExternalFormat ? SamplerDesc::kInt32sNeededExternalFormat
290 : SamplerDesc::kInt32sNeededKnownFormat;
291 // Populate a SamplerDesc with samplerDescLength quantity of immutable sampler data
292 memcpy(&desc, samplerData.begin() + i, samplerDescLength * sizeof(uint32_t));
293 }
294 outDescs.push_back(desc);
295 i += samplerDescLength;
296 }
297 }
298
get_node_texture_samplers(const ResourceBindingRequirements & bindingReqs,const ShaderNode * node,int * binding,skia_private::TArray<SamplerDesc> * outDescs)299 std::string get_node_texture_samplers(const ResourceBindingRequirements& bindingReqs,
300 const ShaderNode* node,
301 int* binding,
302 skia_private::TArray<SamplerDesc>* outDescs) {
303 std::string result;
304 SkSpan<const TextureAndSampler> samplers = node->entry()->fTexturesAndSamplers;
305
306 if (!samplers.empty()) {
307 SkSL::String::appendf(&result, "// %d - %s samplers\n",
308 node->keyIndex(), node->entry()->fName);
309
310 // Determine whether we need to analyze & interpret a ShaderNode's data as immutable
311 // SamplerDescs based upon whether:
312 // 1) A backend passes in a non-nullptr outImmutableSamplers param (may be nullptr in
313 // backends or circumstances where we know immutable sampler data is never stored)
314 // 2) Any data is stored on the ShaderNode
315 // 3) Whether the ShaderNode snippet's ID matches that of any snippet ID that could store
316 // immutable sampler data.
317 int32_t snippetId = node->codeSnippetId();
318 if (outDescs) {
319 // TODO(b/369846881): Refactor checking snippet ID to instead having a named
320 // snippet requirement flag that we can check here to decrease fragility.
321 if (!node->data().empty() &&
322 (snippetId == static_cast<int32_t>(BuiltInCodeSnippetID::kImageShader) ||
323 snippetId == static_cast<int32_t>(BuiltInCodeSnippetID::kCubicImageShader) ||
324 snippetId == static_cast<int32_t>(BuiltInCodeSnippetID::kHWImageShader))) {
325 append_sampler_descs(node->data(), *outDescs);
326 } else {
327 // Add default SamplerDescs for any dynamic samplers to outDescs.
328 outDescs->push_back_n(samplers.size());
329 }
330 }
331
332 for (const TextureAndSampler& t : samplers) {
333 result += EmitSamplerLayout(bindingReqs, binding);
334 SkSL::String::appendf(&result, " sampler2D %s_%d;\n", t.name(), node->keyIndex());
335 }
336 }
337
338 for (const ShaderNode* child : node->children()) {
339 result += get_node_texture_samplers(bindingReqs, child, binding, outDescs);
340 }
341 return result;
342 }
343
emit_textures_and_samplers(const ResourceBindingRequirements & bindingReqs,SkSpan<const ShaderNode * > nodes,int * binding,skia_private::TArray<SamplerDesc> * outDescs)344 std::string emit_textures_and_samplers(const ResourceBindingRequirements& bindingReqs,
345 SkSpan<const ShaderNode*> nodes,
346 int* binding,
347 skia_private::TArray<SamplerDesc>* outDescs) {
348 std::string result;
349 for (const ShaderNode* n : nodes) {
350 result += get_node_texture_samplers(bindingReqs, n, binding, outDescs);
351 }
352 return result;
353 }
354
emit_varyings(const RenderStep * step,const char * direction,bool emitSsboIndicesVarying,bool emitLocalCoordsVarying)355 std::string emit_varyings(const RenderStep* step,
356 const char* direction,
357 bool emitSsboIndicesVarying,
358 bool emitLocalCoordsVarying) {
359 std::string result;
360 int location = 0;
361
362 auto appendVarying = [&](const Varying& v) {
363 const char* interpolation;
364 switch (v.interpolation()) {
365 case Interpolation::kPerspective: interpolation = ""; break;
366 case Interpolation::kLinear: interpolation = "noperspective "; break;
367 case Interpolation::kFlat: interpolation = "flat "; break;
368 }
369 SkSL::String::appendf(&result, "layout(location=%d) %s %s%s %s;\n",
370 location++,
371 direction,
372 interpolation,
373 SkSLTypeString(v.gpuType()),
374 v.name());
375 };
376
377 if (emitSsboIndicesVarying) {
378 appendVarying({RenderStep::ssboIndicesVarying(), SkSLType::kUInt2});
379 }
380
381 if (emitLocalCoordsVarying) {
382 appendVarying({"localCoordsVar", SkSLType::kFloat2});
383 }
384
385 for (auto v : step->varyings()) {
386 appendVarying(v);
387 }
388
389 return result;
390 }
391
392 // Walk the node tree and generate all preambles, accumulating into 'preamble'.
emit_preambles(const ShaderInfo & shaderInfo,SkSpan<const ShaderNode * > nodes,std::string treeLabel,std::string * preamble)393 void emit_preambles(const ShaderInfo& shaderInfo,
394 SkSpan<const ShaderNode*> nodes,
395 std::string treeLabel,
396 std::string* preamble) {
397 for (int i = 0; i < SkTo<int>(nodes.size()); ++i) {
398 const ShaderNode* node = nodes[i];
399 std::string nodeLabel = std::to_string(i);
400 std::string nextLabel = treeLabel.empty() ? nodeLabel : (treeLabel + "<-" + nodeLabel);
401
402 if (node->numChildren() > 0) {
403 emit_preambles(shaderInfo, node->children(), nextLabel, preamble);
404 }
405
406 std::string nodePreamble = node->entry()->fPreambleGenerator
407 ? node->entry()->fPreambleGenerator(shaderInfo, node)
408 : node->generateDefaultPreamble(shaderInfo);
409 if (!nodePreamble.empty()) {
410 SkSL::String::appendf(preamble,
411 "// [%d] %s: %s\n"
412 "%s\n",
413 node->keyIndex(),
414 nextLabel.c_str(),
415 node->entry()->fName,
416 nodePreamble.c_str());
417 }
418 }
419 }
420
emit_color_output(BlendFormula::OutputType outputType,const char * outColor,const char * inColor)421 std::string emit_color_output(BlendFormula::OutputType outputType,
422 const char* outColor,
423 const char* inColor) {
424 switch (outputType) {
425 case BlendFormula::kNone_OutputType:
426 return SkSL::String::printf("%s = half4(0.0);", outColor);
427
428 case BlendFormula::kCoverage_OutputType:
429 return SkSL::String::printf("%s = outputCoverage;", outColor);
430
431 case BlendFormula::kModulate_OutputType:
432 return SkSL::String::printf("%s = %s * outputCoverage;", outColor, inColor);
433
434 case BlendFormula::kSAModulate_OutputType:
435 return SkSL::String::printf("%s = %s.a * outputCoverage;", outColor, inColor);
436
437 case BlendFormula::kISAModulate_OutputType:
438 return SkSL::String::printf("%s = (1.0 - %s.a) * outputCoverage;", outColor, inColor);
439
440 case BlendFormula::kISCModulate_OutputType:
441 return SkSL::String::printf(
442 "%s = (half4(1.0) - %s) * outputCoverage;", outColor, inColor);
443
444 default:
445 SkUNREACHABLE;
446 }
447 }
448
make_simple_blendInfo(skgpu::BlendCoeff srcCoeff,skgpu::BlendCoeff dstCoeff)449 constexpr skgpu::BlendInfo make_simple_blendInfo(skgpu::BlendCoeff srcCoeff,
450 skgpu::BlendCoeff dstCoeff) {
451 return { skgpu::BlendEquation::kAdd,
452 srcCoeff,
453 dstCoeff,
454 SK_PMColor4fTRANSPARENT,
455 skgpu::BlendModifiesDst(skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff) };
456 }
457
458 static constexpr int kNumCoeffModes = (int)SkBlendMode::kLastCoeffMode + 1;
459 static constexpr skgpu::BlendInfo gBlendTable[kNumCoeffModes] = {
460 /* clear */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
461 /* src */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
462 /* dst */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
463 /* src-over */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
464 /* dst-over */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
465 /* src-in */ make_simple_blendInfo(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
466 /* dst-in */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA),
467 /* src-out */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
468 /* dst-out */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
469 /* src-atop */ make_simple_blendInfo(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
470 /* dst-atop */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA),
471 /* xor */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
472 /* plus */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
473 /* modulate */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
474 /* screen */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC)
475 };
476
477 } // anonymous namespace
478
Make(const Caps * caps,const ShaderCodeDictionary * dict,const RuntimeEffectDictionary * rteDict,const RenderStep * step,UniquePaintParamsID paintID,bool useStorageBuffers,skgpu::Swizzle writeSwizzle,skia_private::TArray<SamplerDesc> * outDescs)479 std::unique_ptr<ShaderInfo> ShaderInfo::Make(const Caps* caps,
480 const ShaderCodeDictionary* dict,
481 const RuntimeEffectDictionary* rteDict,
482 const RenderStep* step,
483 UniquePaintParamsID paintID,
484 bool useStorageBuffers,
485 skgpu::Swizzle writeSwizzle,
486 skia_private::TArray<SamplerDesc>* outDescs) {
487 const char* shadingSsboIndex =
488 useStorageBuffers && step->performsShading() ? "shadingSsboIndex" : nullptr;
489 std::unique_ptr<ShaderInfo> result =
490 std::unique_ptr<ShaderInfo>(new ShaderInfo(rteDict, shadingSsboIndex));
491
492 // The fragment shader must be generated before the vertex shader, because we determine
493 // properties of the entire program while generating the fragment shader.
494
495 // If paintID is not valid this is a depth-only draw and there's no fragment shader to compile.
496 if (paintID.isValid()) {
497 result->generateFragmentSkSL(caps,
498 dict,
499 step,
500 paintID,
501 useStorageBuffers,
502 writeSwizzle,
503 outDescs);
504 }
505
506 result->generateVertexSkSL(caps,
507 step,
508 useStorageBuffers);
509
510 return result;
511 }
512
ShaderInfo(const RuntimeEffectDictionary * rteDict,const char * ssboIndex)513 ShaderInfo::ShaderInfo(const RuntimeEffectDictionary* rteDict, const char* ssboIndex)
514 : fRuntimeEffectDictionary(rteDict), fSsboIndex(ssboIndex) {}
515
516 // The current, incomplete, model for shader construction is:
517 // - Static code snippets (which can have an arbitrary signature) live in the Graphite
518 // pre-compiled modules, which are located at `src/sksl/sksl_graphite_frag.sksl` and
519 // `src/sksl/sksl_graphite_frag_es2.sksl`.
520 // - Glue code is generated in a `main` method which calls these static code snippets.
521 // The glue code is responsible for:
522 // 1) gathering the correct (mangled) uniforms
523 // 2) passing the uniforms and any other parameters to the helper method
524 // - The result of the final code snippet is then copied into "sk_FragColor".
525 // Note: each entry's 'fStaticFunctionName' field is expected to match the name of a function
526 // in the Graphite pre-compiled module, or be null if the preamble and expression generators are
527 // overridden to not use a static function.
generateFragmentSkSL(const Caps * caps,const ShaderCodeDictionary * dict,const RenderStep * step,UniquePaintParamsID paintID,bool useStorageBuffers,Swizzle writeSwizzle,skia_private::TArray<SamplerDesc> * outDescs)528 void ShaderInfo::generateFragmentSkSL(const Caps* caps,
529 const ShaderCodeDictionary* dict,
530 const RenderStep* step,
531 UniquePaintParamsID paintID,
532 bool useStorageBuffers,
533 Swizzle writeSwizzle,
534 skia_private::TArray<SamplerDesc>* outDescs) {
535 PaintParamsKey key = dict->lookup(paintID);
536 SkASSERT(key.isValid()); // invalid keys should have been caught by invalid paint ID earlier
537
538 std::string label = key.toString(dict, /*includeData=*/false).c_str();
539 fRootNodes = key.getRootNodes(dict, &fShaderNodeAlloc);
540
541 // TODO(b/366220690): aggregateSnippetData() goes away entirely once the VulkanGraphicsPipeline
542 // is updated to use the extracted SamplerDescs directly.
543 for (const ShaderNode* root : fRootNodes) {
544 this->aggregateSnippetData(root);
545 }
546
547 #if defined(SK_DEBUG)
548 // Validate the root node structure of the key.
549 SkASSERT(fRootNodes.size() == 2 || fRootNodes.size() == 3);
550 // First node produces the source color (all snippets return a half4), so we just require that
551 // its signature takes no extra args or just local coords.
552 const ShaderSnippet* srcSnippet = dict->getEntry(fRootNodes[0]->codeSnippetId());
553 // TODO(b/349997190): Once SkEmptyShader doesn't use the passthrough snippet, we can assert
554 // that srcSnippet->needsPriorStageOutput() is false.
555 SkASSERT(!srcSnippet->needsBlenderDstColor());
556 // Second node is the final blender, so it must take both the src color and dst color, and not
557 // any local coordinate.
558 const ShaderSnippet* blendSnippet = dict->getEntry(fRootNodes[1]->codeSnippetId());
559 SkASSERT(blendSnippet->needsPriorStageOutput() && blendSnippet->needsBlenderDstColor());
560 SkASSERT(!blendSnippet->needsLocalCoords());
561
562 const ShaderSnippet* clipSnippet =
563 fRootNodes.size() > 2 ? dict->getEntry(fRootNodes[2]->codeSnippetId()) : nullptr;
564 SkASSERT(!clipSnippet ||
565 (!clipSnippet->needsPriorStageOutput() && !clipSnippet->needsBlenderDstColor()));
566 #endif
567
568 // The RenderStep should be performing shading since otherwise there's no need to generate a
569 // fragment shader program at all.
570 SkASSERT(step->performsShading());
571 // TODO(b/372912880): Release assert debugging for illegal instruction occurring in the wild.
572 SkASSERTF_RELEASE(step->performsShading(),
573 "render step: %s, label: %s",
574 step->name(),
575 label.c_str());
576
577 // Extract the root nodes for clarity
578 // TODO(b/372912880): Release assert debugging for illegal instruction occurring in the wild.
579 SkASSERTF_RELEASE(fRootNodes.size() == 2 || fRootNodes.size() == 3,
580 "root node size = %zu, label = %s",
581 fRootNodes.size(),
582 label.c_str());
583 const ShaderNode* const srcColorRoot = fRootNodes[0];
584 const ShaderNode* const finalBlendRoot = fRootNodes[1];
585 const ShaderNode* const clipRoot = fRootNodes.size() > 2 ? fRootNodes[2] : nullptr;
586
587 // Determine the algorithm for final blending: direct HW blending, coverage-modified HW
588 // blending (w/ or w/o dual-source blending) or via dst-read requirement.
589 Coverage finalCoverage = step->coverage();
590 if (finalCoverage == Coverage::kNone && SkToBool(clipRoot)) {
591 finalCoverage = Coverage::kSingleChannel;
592 }
593 std::optional<SkBlendMode> finalBlendMode;
594 if (finalBlendRoot->codeSnippetId() < kBuiltInCodeSnippetIDCount &&
595 finalBlendRoot->codeSnippetId() >= kFixedBlendIDOffset) {
596 finalBlendMode =
597 static_cast<SkBlendMode>(finalBlendRoot->codeSnippetId() - kFixedBlendIDOffset);
598 if (*finalBlendMode > SkBlendMode::kLastCoeffMode) {
599 // TODO(b/239726010): When we support advanced blend modes in HW, these modes could
600 // still be handled by fBlendInfo instead of SkSL
601 finalBlendMode.reset();
602 }
603 }
604 fDstReadRequirement = GetDstReadRequirement(caps, finalBlendMode, finalCoverage);
605 // TODO(b/372912880): Release assert debugging for illegal instruction occurring in the wild.
606 SkASSERTF_RELEASE(finalBlendMode.has_value() ||
607 fDstReadRequirement != DstReadRequirement::kNone,
608 "blend mode: %d, dst read: %d, coverage: %d, label = %s",
609 finalBlendMode.has_value() ? (int)*finalBlendMode : -1,
610 (int) fDstReadRequirement,
611 (int) finalCoverage,
612 label.c_str());
613
614 const bool hasStepUniforms = step->numUniforms() > 0 && step->coverage() != Coverage::kNone;
615 const bool useStepStorageBuffer = useStorageBuffers && hasStepUniforms;
616 const bool useShadingStorageBuffer = useStorageBuffers && step->performsShading();
617
618 auto allReqFlags = srcColorRoot->requiredFlags() | finalBlendRoot->requiredFlags();
619 if (clipRoot) {
620 allReqFlags |= clipRoot->requiredFlags();
621 }
622 const bool useGradientStorageBuffer = caps->gradientBufferSupport() &&
623 (allReqFlags & SnippetRequirementFlags::kGradientBuffer);
624 const bool useDstSampler = fDstReadRequirement == DstReadRequirement::kTextureCopy ||
625 fDstReadRequirement == DstReadRequirement::kTextureSample;
626
627 const bool defineLocalCoordsVarying = this->needsLocalCoords();
628 std::string preamble = emit_varyings(step,
629 /*direction=*/"in",
630 /*emitSsboIndicesVarying=*/useShadingStorageBuffer,
631 defineLocalCoordsVarying);
632
633 // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
634 const ResourceBindingRequirements& bindingReqs = caps->resourceBindingRequirements();
635 preamble += emit_intrinsic_constants(bindingReqs);
636 if (hasStepUniforms) {
637 if (useStepStorageBuffer) {
638 preamble += emit_render_step_storage_buffer(bindingReqs.fRenderStepBufferBinding,
639 step->uniforms());
640 } else {
641 preamble += emit_render_step_uniforms(bindingReqs.fRenderStepBufferBinding,
642 bindingReqs.fUniformBufferLayout,
643 step->uniforms());
644 }
645 }
646
647 bool wrotePaintColor = false;
648 if (useShadingStorageBuffer) {
649 preamble += emit_paint_params_storage_buffer(bindingReqs.fPaintParamsBufferBinding,
650 fRootNodes,
651 &fHasPaintUniforms,
652 &wrotePaintColor);
653 SkSL::String::appendf(&preamble, "uint %s;\n", this->ssboIndex());
654 } else {
655 preamble += emit_paint_params_uniforms(bindingReqs.fPaintParamsBufferBinding,
656 bindingReqs.fUniformBufferLayout,
657 fRootNodes,
658 &fHasPaintUniforms,
659 &wrotePaintColor);
660 }
661
662 if (useGradientStorageBuffer) {
663 SkSL::String::appendf(&preamble,
664 "layout (binding=%d) readonly buffer FSGradientBuffer {\n"
665 " float %s[];\n"
666 "};\n",
667 bindingReqs.fGradientBufferBinding,
668 ShaderInfo::kGradientBufferName);
669 fHasGradientBuffer = true;
670 }
671
672 {
673 int binding = 0;
674 preamble += emit_textures_and_samplers(bindingReqs, fRootNodes, &binding, outDescs);
675 int paintTextureCount = binding;
676 if (step->hasTextures()) {
677 preamble += step->texturesAndSamplersSkSL(bindingReqs, &binding);
678 if (outDescs) {
679 // Determine how many render step samplers were used by comparing the binding value
680 // against paintTextureCount, taking into account the binding requirements. We
681 // assume and do not anticipate the render steps to use immutable samplers.
682 int renderStepSamplerCount = bindingReqs.fSeparateTextureAndSamplerBinding
683 ? (binding - paintTextureCount) / 2
684 : binding - paintTextureCount;
685 // Add default SamplerDescs for all the dynamic samplers used by the render step so
686 // the size of outDescs will be equivalent to the total number of samplers.
687 outDescs->push_back_n(renderStepSamplerCount);
688 }
689 }
690 if (useDstSampler) {
691 preamble += EmitSamplerLayout(bindingReqs, &binding);
692 preamble += " sampler2D dstSampler;";
693 // Add default SamplerDesc for the intrinsic dstSampler to stay consistent with
694 // `fNumFragmentTexturesAndSamplers`.
695 if (outDescs) {
696 outDescs->push_back({});
697 }
698 }
699
700 // Record how many textures and samplers are used.
701 fNumFragmentTexturesAndSamplers = binding;
702 }
703
704 // Emit preamble declarations and helper functions required for snippets. In the default case
705 // this adds functions that bind a node's specific mangled uniforms to the snippet's
706 // implementation in the SkSL modules.
707 emit_preambles(*this, fRootNodes, /*treeLabel=*/"", &preamble);
708
709 std::string mainBody = "void main() {";
710
711 if (useShadingStorageBuffer) {
712 SkSL::String::appendf(&mainBody,
713 "%s = %s.y;\n",
714 this->ssboIndex(),
715 RenderStep::ssboIndicesVarying());
716 }
717
718 if (step->emitsPrimitiveColor()) {
719 mainBody += "half4 primitiveColor;";
720 mainBody += step->fragmentColorSkSL();
721 } else {
722 SkASSERT(!(fRootNodes[0]->requiredFlags() & SnippetRequirementFlags::kPrimitiveColor));
723 }
724
725 // Using kDefaultArgs as the initial value means it will refer to undefined variables, but the
726 // root nodes should--at most--be depending on the coordinate when "needsLocalCoords" is true.
727 // If the PaintParamsKey violates that structure, this will produce SkSL compile errors.
728 ShaderSnippet::Args args = ShaderSnippet::kDefaultArgs;
729 args.fFragCoord = "localCoordsVar"; // the varying added in emit_varyings()
730 // TODO(b/349997190): The paint root node should not depend on any prior stage's output, but
731 // it can happen with how SkEmptyShader is currently mapped to `sk_passthrough`. In this case
732 // it requires that prior stage color to be transparent black. When SkEmptyShader can instead
733 // cause the draw to be skipped, this can go away.
734 args.fPriorStageOutput = "half4(0)";
735
736 // Calculate the src color and stash its output variable in `args`
737 args.fPriorStageOutput = srcColorRoot->invokeAndAssign(*this, args, &mainBody);
738
739 if (fDstReadRequirement != DstReadRequirement::kNone) {
740 // Get the current dst color into a local variable, it may be used later on for coverage
741 // blending as well as the final blend.
742 mainBody += "half4 dstColor;";
743 if (useDstSampler) {
744 // dstCopyBounds is in frag coords and already includes the replay translation. The
745 // reciprocol of the dstCopy dimensions are in ZW.
746 mainBody += "dstColor = sample(dstSampler,"
747 "dstCopyBounds.zw*(sk_FragCoord.xy - dstCopyBounds.xy));";
748 } else {
749 SkASSERT(fDstReadRequirement == DstReadRequirement::kFramebufferFetch);
750 mainBody += "dstColor = sk_LastFragColor;";
751 }
752
753 args.fBlenderDstColor = "dstColor";
754 args.fPriorStageOutput = finalBlendRoot->invokeAndAssign(*this, args, &mainBody);
755 finalBlendMode = SkBlendMode::kSrc;
756 }
757
758 if (writeSwizzle != Swizzle::RGBA()) {
759 SkSL::String::appendf(&mainBody, "%s = %s.%s;", args.fPriorStageOutput.c_str(),
760 args.fPriorStageOutput.c_str(),
761 writeSwizzle.asString().c_str());
762 }
763
764 if (finalCoverage == Coverage::kNone) {
765 // Either direct HW blending or a dst-read w/o any extra coverage. In both cases we just
766 // need to assign directly to sk_FragCoord and update the HW blend info to finalBlendMode.
767 SkASSERT(finalBlendMode.has_value());
768 // TODO(b/372912880): Release assert debugging for illegal instruction occurring in the wild
769 SkASSERTF_RELEASE(finalBlendMode.has_value(),
770 "blend mode: %d, dst read: %d, label = %s",
771 finalBlendMode.has_value() ? (int)*finalBlendMode : -1,
772 (int) fDstReadRequirement,
773 label.c_str());
774
775 fBlendInfo = gBlendTable[static_cast<int>(*finalBlendMode)];
776 SkSL::String::appendf(&mainBody, "sk_FragColor = %s;", args.fPriorStageOutput.c_str());
777 } else {
778 // Accumulate the output coverage. This will either modify the src color and secondary
779 // outputs for dual-source blending, or be combined directly with the in-shader blended
780 // final color if a dst-readback was required.
781 if (useStepStorageBuffer) {
782 SkSL::String::appendf(&mainBody,
783 "uint stepSsboIndex = %s.x;\n",
784 RenderStep::ssboIndicesVarying());
785 mainBody +=
786 emit_uniforms_from_storage_buffer("step", "stepSsboIndex", step->uniforms());
787 }
788
789 mainBody += "half4 outputCoverage = half4(1);";
790 mainBody += step->fragmentCoverageSkSL();
791
792 if (clipRoot) {
793 // The clip block node is invoked with device coords, not local coords like the main
794 // shading root node. However sk_FragCoord includes any replay translation and we
795 // need to recover the original device coordinate.
796 mainBody += "float2 devCoord = sk_FragCoord.xy - viewport.xy;";
797 args.fFragCoord = "devCoord";
798 std::string clipBlockOutput = clipRoot->invokeAndAssign(*this, args, &mainBody);
799 SkSL::String::appendf(&mainBody, "outputCoverage *= %s.a;", clipBlockOutput.c_str());
800 }
801
802 const char* outColor = args.fPriorStageOutput.c_str();
803 if (fDstReadRequirement != DstReadRequirement::kNone) {
804 // If this draw uses a non-coherent dst read, we want to keep the existing dst color (or
805 // whatever has been previously drawn) when there's no coverage. This helps for batching
806 // text draws that need to read from a dst copy for blends. However, this only helps the
807 // case where the outer bounding boxes of each letter overlap and not two actual parts
808 // of the text.
809 if (useDstSampler) {
810 // We don't think any shaders actually output negative coverage, but just as a
811 // safety check for floating point precision errors, we compare with <= here. We
812 // just check the RGB values of the coverage, since the alpha may not have been set
813 // when using LCD. If we are using single-channel coverage, alpha will be equal to
814 // RGB anyway.
815 mainBody +=
816 "if (all(lessThanEqual(outputCoverage.rgb, half3(0)))) {"
817 "discard;"
818 "}";
819 }
820
821 // Use kSrc HW BlendInfo and do the coverage blend with dst in the shader.
822 fBlendInfo = gBlendTable[static_cast<int>(SkBlendMode::kSrc)];
823 SkSL::String::appendf(
824 &mainBody,
825 "sk_FragColor = %s * outputCoverage + dstColor * (1.0 - outputCoverage);",
826 outColor);
827 if (finalCoverage == Coverage::kLCD) {
828 SkSL::String::appendf(
829 &mainBody,
830 "half3 lerpRGB = mix(dstColor.aaa, %s.aaa, outputCoverage.rgb);"
831 "sk_FragColor.a = max(max(lerpRGB.r, lerpRGB.g), lerpRGB.b);",
832 outColor);
833 }
834 } else {
835 // Adjust the shader output(s) to incorporate the coverage so that HW blending produces
836 // the correct output.
837 // TODO: Determine whether draw is opaque and pass that to GetBlendFormula.
838 // TODO(b/372912880): Release assert debugging for illegal instruction
839 SkASSERTF_RELEASE(finalBlendMode.has_value(),
840 "blend mode: %d, dst read: %d, coverage: %d, label = %s",
841 finalBlendMode.has_value() ? (int)*finalBlendMode : -1,
842 (int) fDstReadRequirement,
843 (int) finalCoverage,
844 label.c_str());
845 BlendFormula coverageBlendFormula =
846 finalCoverage == Coverage::kLCD
847 ? skgpu::GetLCDBlendFormula(*finalBlendMode)
848 : skgpu::GetBlendFormula(
849 /*isOpaque=*/false, /*hasCoverage=*/true, *finalBlendMode);
850 fBlendInfo = {coverageBlendFormula.equation(),
851 coverageBlendFormula.srcCoeff(),
852 coverageBlendFormula.dstCoeff(),
853 SK_PMColor4fTRANSPARENT,
854 coverageBlendFormula.modifiesDst()};
855
856 if (finalCoverage == Coverage::kLCD) {
857 mainBody += "outputCoverage.a = max(max(outputCoverage.r, "
858 "outputCoverage.g), "
859 "outputCoverage.b);";
860 }
861
862 mainBody += emit_color_output(coverageBlendFormula.primaryOutput(),
863 "sk_FragColor",
864 outColor);
865 if (coverageBlendFormula.hasSecondaryOutput()) {
866 SkASSERT(caps->shaderCaps()->fDualSourceBlendingSupport);
867 mainBody += emit_color_output(coverageBlendFormula.secondaryOutput(),
868 "sk_SecondaryFragColor",
869 outColor);
870 }
871 }
872 }
873 mainBody += "}\n";
874
875 fFragmentSkSL = preamble + "\n" + mainBody;
876
877 fFSLabel = writeSwizzle.asString().c_str();
878 fFSLabel += " + ";
879 fFSLabel = step->name();
880 fFSLabel += " + ";
881 fFSLabel += label;
882 }
883
generateVertexSkSL(const Caps * caps,const RenderStep * step,bool useStorageBuffers)884 void ShaderInfo::generateVertexSkSL(const Caps* caps,
885 const RenderStep* step,
886 bool useStorageBuffers) {
887 const bool hasStepUniforms = step->numUniforms() > 0;
888 const bool useStepStorageBuffer = useStorageBuffers && hasStepUniforms;
889 const bool useShadingStorageBuffer = useStorageBuffers && step->performsShading();
890 const bool defineLocalCoordsVarying = this->needsLocalCoords();
891
892 // Fixed program header (intrinsics are always declared as an uniform interface block)
893 const ResourceBindingRequirements& bindingReqs = caps->resourceBindingRequirements();
894 std::string sksl = emit_intrinsic_constants(bindingReqs);
895
896 if (step->numVertexAttributes() > 0 || step->numInstanceAttributes() > 0) {
897 int attr = 0;
898 auto add_attrs = [&sksl, &attr](SkSpan<const Attribute> attrs) {
899 for (auto a : attrs) {
900 SkSL::String::appendf(&sksl, " layout(location=%d) in ", attr++);
901 sksl.append(SkSLTypeString(a.gpuType()));
902 SkSL::String::appendf(&sksl, " %s;\n", a.name());
903 }
904 };
905 if (step->numVertexAttributes() > 0) {
906 sksl.append("// vertex attrs\n");
907 add_attrs(step->vertexAttributes());
908 }
909 if (step->numInstanceAttributes() > 0) {
910 sksl.append("// instance attrs\n");
911 add_attrs(step->instanceAttributes());
912 }
913 }
914
915 // Uniforms needed by RenderStep
916 // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
917 if (hasStepUniforms) {
918 if (useStepStorageBuffer) {
919 sksl += emit_render_step_storage_buffer(bindingReqs.fRenderStepBufferBinding,
920 step->uniforms());
921 } else {
922 sksl += emit_render_step_uniforms(bindingReqs.fRenderStepBufferBinding,
923 bindingReqs.fUniformBufferLayout,
924 step->uniforms());
925 }
926 }
927
928 // Varyings needed by RenderStep
929 sksl += emit_varyings(step, "out", useShadingStorageBuffer, defineLocalCoordsVarying);
930
931 // Vertex shader function declaration
932 sksl += "void main() {";
933 // Create stepLocalCoords which render steps can write to.
934 sksl += "float2 stepLocalCoords = float2(0);";
935 // Vertex shader body
936 if (useStepStorageBuffer) {
937 // Extract out render step uniforms from SSBO, declaring local variables with the expected
938 // uniform names so that RenderStep SkSL is independent of storage choice.
939 SkSL::String::appendf(
940 &sksl, "uint stepSsboIndex = %s.x;\n", RenderStep::ssboIndicesAttribute());
941 sksl += emit_uniforms_from_storage_buffer("step", "stepSsboIndex", step->uniforms());
942 }
943
944 sksl += step->vertexSkSL();
945
946 // We want to map the rectangle of logical device pixels from (0,0) to (viewWidth, viewHeight)
947 // to normalized device coordinates: (-1,-1) to (1,1) (actually -w to w since it's before
948 // homogenous division).
949 //
950 // For efficiency, this assumes viewport.zw holds the reciprocol of twice the viewport width and
951 // height. On some backends the NDC Y axis is flipped relative to the device and
952 // viewport coords (i.e. it points up instead of down). In those cases, it's also assumed that
953 // viewport.w holds a negative value. In that case the sign(viewport.zw) changes from
954 // subtracting w to adding w.
955 sksl += "sk_Position = float4(viewport.zw*devPosition.xy - sign(viewport.zw)*devPosition.ww,"
956 "devPosition.zw);";
957
958 if (useShadingStorageBuffer) {
959 // Assign SSBO index values to the SSBO index varying.
960 SkSL::String::appendf(&sksl,
961 "%s = %s;",
962 RenderStep::ssboIndicesVarying(),
963 RenderStep::ssboIndicesAttribute());
964 }
965
966 if (defineLocalCoordsVarying) {
967 // Assign Render Step's stepLocalCoords to the localCoordsVar varying.
968 sksl += "localCoordsVar = stepLocalCoords;";
969 }
970 sksl += "}";
971
972 fVertexSkSL = std::move(sksl);
973 fVSLabel = step->name();
974 if (defineLocalCoordsVarying) {
975 fVSLabel += " (w/ local coords)";
976 }
977 fHasStepUniforms = hasStepUniforms;
978 }
979
needsLocalCoords() const980 bool ShaderInfo::needsLocalCoords() const {
981 return !fRootNodes.empty() &&
982 SkToBool(fRootNodes[0]->requiredFlags() & SnippetRequirementFlags::kLocalCoords);
983 }
984
aggregateSnippetData(const ShaderNode * node)985 void ShaderInfo::aggregateSnippetData(const ShaderNode* node) {
986 if (!node) {
987 return;
988 }
989
990 // Accumulate data of children first.
991 for (const ShaderNode* child : node->children()) {
992 this->aggregateSnippetData(child);
993 }
994
995 if (node->requiredFlags() & SnippetRequirementFlags::kStoresSamplerDescData &&
996 !node->data().empty()) {
997 fData.push_back_n(node->data().size(), node->data().data());
998 }
999 }
1000
1001 } // namespace skgpu::graphite
1002