• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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