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