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