• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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/ContextUtils.h"
9 
10 #include <string>
11 #include "src/core/SkBlenderBase.h"
12 #include "src/gpu/BlendFormula.h"
13 #include "src/gpu/graphite/Caps.h"
14 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
15 #include "src/gpu/graphite/KeyContext.h"
16 #include "src/gpu/graphite/PaintParams.h"
17 #include "src/gpu/graphite/PipelineData.h"
18 #include "src/gpu/graphite/RecorderPriv.h"
19 #include "src/gpu/graphite/RenderPassDesc.h"
20 #include "src/gpu/graphite/Renderer.h"
21 #include "src/gpu/graphite/ResourceProvider.h"
22 #include "src/gpu/graphite/ShaderCodeDictionary.h"
23 #include "src/gpu/graphite/UniformManager.h"
24 #include "src/gpu/graphite/UniquePaintParamsID.h"
25 #include "src/gpu/graphite/compute/ComputeStep.h"
26 #include "src/gpu/graphite/geom/Geometry.h"
27 #include "src/sksl/SkSLString.h"
28 #include "src/sksl/SkSLUtil.h"
29 
30 namespace skgpu::graphite {
31 
ExtractPaintData(Recorder * recorder,PipelineDataGatherer * gatherer,PaintParamsKeyBuilder * builder,const Layout layout,const SkM44 & local2Dev,const PaintParams & p,const Geometry & geometry,sk_sp<TextureProxy> dstTexture,SkIPoint dstOffset,const SkColorInfo & targetColorInfo)32 std::tuple<UniquePaintParamsID, const UniformDataBlock*, const TextureDataBlock*> ExtractPaintData(
33         Recorder* recorder,
34         PipelineDataGatherer* gatherer,
35         PaintParamsKeyBuilder* builder,
36         const Layout layout,
37         const SkM44& local2Dev,
38         const PaintParams& p,
39         const Geometry& geometry,
40         sk_sp<TextureProxy> dstTexture,
41         SkIPoint dstOffset,
42         const SkColorInfo& targetColorInfo) {
43     SkDEBUGCODE(builder->checkReset());
44 
45     gatherer->resetWithNewLayout(layout);
46 
47     KeyContext keyContext(recorder,
48                           local2Dev,
49                           targetColorInfo,
50                           geometry.isShape() || geometry.isEdgeAAQuad()
51                                   ? KeyContext::OptimizeSampling::kYes
52                                   : KeyContext::OptimizeSampling::kNo,
53                           p.color(),
54                           std::move(dstTexture),
55                           dstOffset);
56     p.toKey(keyContext, builder, gatherer);
57 
58     UniquePaintParamsID paintID = recorder->priv().shaderCodeDictionary()->findOrCreate(builder);
59     const UniformDataBlock* uniforms = nullptr;
60     const TextureDataBlock* textures = nullptr;
61     if (paintID.isValid()) {
62         if (gatherer->hasUniforms()) {
63             UniformDataCache* uniformDataCache = recorder->priv().uniformDataCache();
64             uniforms = uniformDataCache->insert(gatherer->finishUniformDataBlock());
65         }
66         if (gatherer->hasTextures()) {
67             TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
68             textures = textureDataCache->insert(gatherer->textureDataBlock());
69         }
70     }
71 
72     return { paintID, uniforms, textures };
73 }
74 
ExtractRenderStepData(UniformDataCache * uniformDataCache,TextureDataCache * textureDataCache,PipelineDataGatherer * gatherer,const Layout layout,const RenderStep * step,const DrawParams & params)75 std::tuple<const UniformDataBlock*, const TextureDataBlock*> ExtractRenderStepData(
76         UniformDataCache* uniformDataCache,
77         TextureDataCache* textureDataCache,
78         PipelineDataGatherer* gatherer,
79         const Layout layout,
80         const RenderStep* step,
81         const DrawParams& params) {
82     gatherer->resetWithNewLayout(layout);
83     step->writeUniformsAndTextures(params, gatherer);
84 
85     const UniformDataBlock* uniforms =
86             gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock())
87                                     : nullptr;
88     const TextureDataBlock* textures =
89             gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock())
90                                     : nullptr;
91 
92     return { uniforms, textures };
93 }
94 
GetDstReadRequirement(const Caps * caps,std::optional<SkBlendMode> blendMode,Coverage coverage)95 DstReadRequirement GetDstReadRequirement(const Caps* caps,
96                                          std::optional<SkBlendMode> blendMode,
97                                          Coverage coverage) {
98     // If the blend mode is absent, this is assumed to be for a runtime blender, for which we always
99     // do a dst read.
100     if (!blendMode || *blendMode > SkBlendMode::kLastCoeffMode) {
101         return caps->getDstReadRequirement();
102     }
103 
104     const bool isLCD = coverage == Coverage::kLCD;
105     const bool hasCoverage = coverage != Coverage::kNone;
106     BlendFormula blendFormula = isLCD ? skgpu::GetLCDBlendFormula(*blendMode)
107                                       : skgpu::GetBlendFormula(false, hasCoverage, *blendMode);
108     if ((blendFormula.hasSecondaryOutput() && !caps->shaderCaps()->fDualSourceBlendingSupport) ||
109         (coverage == Coverage::kLCD && blendMode != SkBlendMode::kSrcOver)) {
110         return caps->getDstReadRequirement();
111     }
112 
113     return DstReadRequirement::kNone;
114 }
115 
116 namespace {
117 
get_uniform_header(int bufferID,const char * name)118 std::string get_uniform_header(int bufferID, const char* name) {
119     std::string result;
120 
121     SkSL::String::appendf(&result, "layout (binding=%d) uniform %sUniforms {\n", bufferID, name);
122 
123     return result;
124 }
125 
get_uniforms(Layout layout,SkSpan<const Uniform> uniforms,int * offset,int manglingSuffix,bool * wrotePaintColor)126 std::string get_uniforms(Layout layout,
127                          SkSpan<const Uniform> uniforms,
128                          int* offset,
129                          int manglingSuffix,
130                          bool* wrotePaintColor) {
131     std::string result;
132     UniformOffsetCalculator offsetter(layout, *offset);
133 
134     std::string uniformName;
135     for (const Uniform& u : uniforms) {
136         uniformName = u.name();
137 
138         if (u.isPaintColor() && wrotePaintColor) {
139             if (*wrotePaintColor) {
140                 SkSL::String::appendf(&result, "    // deduplicated %s\n", u.name());
141                 continue;
142             }
143 
144             *wrotePaintColor = true;
145         } else {
146             if (manglingSuffix >= 0) {
147                 uniformName.append("_");
148                 uniformName.append(std::to_string(manglingSuffix));
149             }
150         }
151 
152         SkSL::String::appendf(&result,
153                               "    layout(offset=%d) %s %s",
154                               offsetter.advanceOffset(u.type(), u.count()),
155                               SkSLTypeString(u.type()),
156                               uniformName.c_str());
157         if (u.count()) {
158             result.append("[");
159             result.append(std::to_string(u.count()));
160             result.append("]");
161         }
162         result.append(";\n");
163     }
164 
165     *offset = offsetter.size();
166     return result;
167 }
168 
get_node_uniforms(Layout layout,const ShaderNode * node,int * offset,int * numUniforms,bool * wrotePaintColor)169 std::string get_node_uniforms(Layout layout,
170                               const ShaderNode* node,
171                               int* offset,
172                               int* numUniforms,
173                               bool* wrotePaintColor) {
174     std::string result;
175     SkSpan<const Uniform> uniforms = node->entry()->fUniforms;
176 
177     if (!uniforms.empty()) {
178         SkSL::String::appendf(&result, "// %d - %s uniforms\n",
179                               node->keyIndex(), node->entry()->fName);
180         result += get_uniforms(layout, uniforms, offset, node->keyIndex(), wrotePaintColor);
181     }
182 
183     *numUniforms += uniforms.size();
184     for (const ShaderNode* child : node->children()) {
185         result += get_node_uniforms(layout, child, offset, numUniforms, wrotePaintColor);
186     }
187     return result;
188 }
189 
get_ssbo_fields(SkSpan<const Uniform> uniforms,int manglingSuffix,bool * wrotePaintColor)190 std::string get_ssbo_fields(SkSpan<const Uniform> uniforms,
191                             int manglingSuffix,
192                             bool* wrotePaintColor) {
193     std::string result;
194 
195     std::string uniformName;
196     for (const Uniform& u : uniforms) {
197         uniformName = u.name();
198 
199         if (u.isPaintColor() && wrotePaintColor) {
200             if (*wrotePaintColor) {
201                 SkSL::String::appendf(&result, "    // deduplicated %s\n", u.name());
202                 continue;
203             }
204 
205             *wrotePaintColor = true;
206         } else {
207             if (manglingSuffix >= 0) {
208                 uniformName.append("_");
209                 uniformName.append(std::to_string(manglingSuffix));
210             }
211         }
212 
213         SkSL::String::appendf(&result, "    %s %s", SkSLTypeString(u.type()), uniformName.c_str());
214         if (u.count()) {
215             SkSL::String::appendf(&result, "[%d]", u.count());
216         }
217         result.append(";\n");
218     }
219 
220     return result;
221 }
222 
get_node_ssbo_fields(const ShaderNode * node,int * numUniforms,bool * wrotePaintColor)223 std::string get_node_ssbo_fields(const ShaderNode* node, int* numUniforms, bool* wrotePaintColor) {
224     std::string result;
225     SkSpan<const Uniform> uniforms = node->entry()->fUniforms;
226 
227     if (!uniforms.empty()) {
228         SkSL::String::appendf(&result, "// %d - %s uniforms\n",
229                               node->keyIndex(), node->entry()->fName);
230 
231         result += get_ssbo_fields(uniforms, node->keyIndex(), wrotePaintColor);
232     }
233 
234     *numUniforms += uniforms.size();
235     for (const ShaderNode* child : node->children()) {
236         result += get_node_ssbo_fields(child, numUniforms, wrotePaintColor);
237     }
238     return result;
239 }
240 
get_node_texture_samplers(const ResourceBindingRequirements & bindingReqs,const ShaderNode * node,int * binding)241 std::string get_node_texture_samplers(const ResourceBindingRequirements& bindingReqs,
242                                       const ShaderNode* node,
243                                       int* binding) {
244     std::string result;
245     SkSpan<const TextureAndSampler> samplers = node->entry()->fTexturesAndSamplers;
246 
247     if (!samplers.empty()) {
248         SkSL::String::appendf(&result, "// %d - %s samplers\n",
249                               node->keyIndex(), node->entry()->fName);
250 
251         for (const TextureAndSampler& t : samplers) {
252             result += EmitSamplerLayout(bindingReqs, binding);
253             SkSL::String::appendf(&result, " sampler2D %s_%d;\n",
254                                   t.name(), node->keyIndex());
255         }
256     }
257 
258     for (const ShaderNode* child : node->children()) {
259         result += get_node_texture_samplers(bindingReqs, child, binding);
260     }
261     return result;
262 }
263 
264 }  // anonymous namespace
265 
EmitPaintParamsUniforms(int bufferID,const Layout layout,SkSpan<const ShaderNode * > nodes,int * numUniforms,int * uniformsTotalBytes,bool * wrotePaintColor)266 std::string EmitPaintParamsUniforms(int bufferID,
267                                     const Layout layout,
268                                     SkSpan<const ShaderNode*> nodes,
269                                     int* numUniforms,
270                                     int* uniformsTotalBytes,
271                                     bool* wrotePaintColor) {
272     int offset = 0;
273 
274     std::string result = get_uniform_header(bufferID, "FS");
275     for (const ShaderNode* n : nodes) {
276         result += get_node_uniforms(layout, n, &offset, numUniforms, wrotePaintColor);
277     }
278     result.append("};\n\n");
279 
280     if (!*numUniforms) {
281         // No uniforms were added
282         return {};
283     }
284 
285     if (uniformsTotalBytes) {
286         *uniformsTotalBytes = offset;
287     }
288 
289     return result;
290 }
291 
EmitRenderStepUniforms(int bufferID,const Layout layout,SkSpan<const Uniform> uniforms,int * renderStepUniformsTotalBytes)292 std::string EmitRenderStepUniforms(int bufferID,
293                                    const Layout layout,
294                                    SkSpan<const Uniform> uniforms,
295                                    int* renderStepUniformsTotalBytes) {
296     int offset = 0;
297 
298     std::string result = get_uniform_header(bufferID, "Step");
299     result += get_uniforms(layout, uniforms, &offset, -1, /* wrotePaintColor= */ nullptr);
300     result.append("};\n\n");
301 
302     if (renderStepUniformsTotalBytes) {
303         *renderStepUniformsTotalBytes = offset;
304     }
305 
306     return result;
307 }
308 
EmitPaintParamsStorageBuffer(int bufferID,SkSpan<const ShaderNode * > nodes,int * numUniforms,bool * wrotePaintColor)309 std::string EmitPaintParamsStorageBuffer(
310         int bufferID,
311         SkSpan<const ShaderNode*> nodes,
312         int* numUniforms,
313         bool* wrotePaintColor) {
314 
315     std::string result;
316     result += "struct FSUniformData {\n";
317     for (const ShaderNode* n : nodes) {
318         result += get_node_ssbo_fields(n, numUniforms, wrotePaintColor);
319     }
320     result += "};\n\n";
321 
322     if (!*numUniforms) {
323         // No uniforms were added
324         return {};
325     }
326 
327     SkSL::String::appendf(&result,
328                           "layout (binding=%d) readonly buffer FSUniforms {\n"
329                           "    FSUniformData fsUniformData[];\n"
330                           "};\n",
331                           bufferID);
332     return result;
333 }
334 
EmitRenderStepStorageBuffer(int bufferID,SkSpan<const Uniform> uniforms)335 std::string EmitRenderStepStorageBuffer(
336         int bufferID,
337         SkSpan<const Uniform> uniforms) {
338 
339     std::string result;
340     result += "struct StepUniformData {\n" +
341               get_ssbo_fields(uniforms, -1, /* wrotePaintColor= */ nullptr) +
342               "};\n\n";
343 
344     SkSL::String::appendf(&result,
345                           "layout (binding=%d) readonly buffer StepUniforms {\n"
346                           "    StepUniformData stepUniformData[];\n"
347                           "};\n",
348                           bufferID);
349     return result;
350 }
351 
EmitUniformsFromStorageBuffer(const char * bufferNamePrefix,const char * ssboIndex,SkSpan<const Uniform> uniforms)352 std::string EmitUniformsFromStorageBuffer(const char* bufferNamePrefix,
353                                           const char* ssboIndex,
354                                           SkSpan<const Uniform> uniforms) {
355     std::string result;
356 
357     for (const Uniform& u : uniforms) {
358         SkSL::String::appendf(&result, "%s %s", SkSLTypeString(u.type()), u.name());
359         if (u.count()) {
360             SkSL::String::appendf(&result, "[%d]", u.count());
361         }
362         SkSL::String::appendf(
363                 &result, " = %sUniformData[%s].%s;\n", bufferNamePrefix, ssboIndex, u.name());
364     }
365 
366     return result;
367 }
368 
EmitStorageBufferAccess(const char * bufferNamePrefix,const char * ssboIndex,const char * uniformName)369 std::string EmitStorageBufferAccess(const char* bufferNamePrefix,
370                                     const char* ssboIndex,
371                                     const char* uniformName) {
372     return SkSL::String::printf("%sUniformData[%s].%s", bufferNamePrefix, ssboIndex, uniformName);
373 }
374 
EmitTexturesAndSamplers(const ResourceBindingRequirements & bindingReqs,SkSpan<const ShaderNode * > nodes,int * binding)375 std::string EmitTexturesAndSamplers(const ResourceBindingRequirements& bindingReqs,
376                                     SkSpan<const ShaderNode*> nodes,
377                                     int* binding) {
378     std::string result;
379     for (const ShaderNode* n : nodes) {
380         result += get_node_texture_samplers(bindingReqs, n, binding);
381     }
382     return result;
383 }
384 
EmitSamplerLayout(const ResourceBindingRequirements & bindingReqs,int * binding)385 std::string EmitSamplerLayout(const ResourceBindingRequirements& bindingReqs, int* binding) {
386     std::string result;
387 
388     // If fDistinctIndexRanges is false, then texture and sampler indices may clash with other
389     // resource indices. Graphite assumes that they will be placed in descriptor set (Vulkan) and
390     // bind group (Dawn) index 1.
391     const char* distinctIndexRange = bindingReqs.fDistinctIndexRanges ? "" : "set=1, ";
392 
393     if (bindingReqs.fSeparateTextureAndSamplerBinding) {
394         int samplerIndex = (*binding)++;
395         int textureIndex = (*binding)++;
396         result = SkSL::String::printf("layout(webgpu, %ssampler=%d, texture=%d)",
397                                       distinctIndexRange,
398                                       samplerIndex,
399                                       textureIndex);
400     } else {
401         int samplerIndex = (*binding)++;
402         result = SkSL::String::printf("layout(%sbinding=%d)",
403                                       distinctIndexRange,
404                                       samplerIndex);
405     }
406     return result;
407 }
408 
409 namespace {
emit_attributes(SkSpan<const Attribute> vertexAttrs,SkSpan<const Attribute> instanceAttrs)410 std::string emit_attributes(SkSpan<const Attribute> vertexAttrs,
411                             SkSpan<const Attribute> instanceAttrs) {
412     std::string result;
413 
414     int attr = 0;
415     auto add_attrs = [&](SkSpan<const Attribute> attrs) {
416         for (auto a : attrs) {
417             SkSL::String::appendf(&result, "    layout(location=%d) in ", attr++);
418             result.append(SkSLTypeString(a.gpuType()));
419             SkSL::String::appendf(&result, " %s;\n", a.name());
420         }
421     };
422 
423     if (!vertexAttrs.empty()) {
424         result.append("// vertex attrs\n");
425         add_attrs(vertexAttrs);
426     }
427     if (!instanceAttrs.empty()) {
428         result.append("// instance attrs\n");
429         add_attrs(instanceAttrs);
430     }
431 
432     return result;
433 }
434 }  // anonymous namespace
435 
EmitVaryings(const RenderStep * step,const char * direction,bool emitSsboIndicesVarying,bool emitLocalCoordsVarying)436 std::string EmitVaryings(const RenderStep* step,
437                          const char* direction,
438                          bool emitSsboIndicesVarying,
439                          bool emitLocalCoordsVarying) {
440     std::string result;
441     int location = 0;
442 
443     if (emitSsboIndicesVarying) {
444         SkSL::String::appendf(&result,
445                               "    layout(location=%d) %s flat ushort2 %s;\n",
446                               location++,
447                               direction,
448                               RenderStep::ssboIndicesVarying());
449     }
450 
451     if (emitLocalCoordsVarying) {
452         SkSL::String::appendf(&result, "    layout(location=%d) %s ", location++, direction);
453         result.append(SkSLTypeString(SkSLType::kFloat2));
454         SkSL::String::appendf(&result, " localCoordsVar;\n");
455     }
456 
457     for (auto v : step->varyings()) {
458         SkSL::String::appendf(&result, "    layout(location=%d) %s ", location++, direction);
459         result.append(SkSLTypeString(v.fType));
460         SkSL::String::appendf(&result, " %s;\n", v.fName);
461     }
462 
463     return result;
464 }
465 
BuildVertexSkSL(const ResourceBindingRequirements & bindingReqs,const RenderStep * step,bool useStorageBuffers,bool defineLocalCoordsVarying)466 VertSkSLInfo BuildVertexSkSL(const ResourceBindingRequirements& bindingReqs,
467                             const RenderStep* step,
468                             bool useStorageBuffers,
469                             bool defineLocalCoordsVarying) {
470     VertSkSLInfo result;
471 
472     const bool hasStepUniforms = step->numUniforms() > 0;
473     const bool useStepStorageBuffer = useStorageBuffers && hasStepUniforms;
474     const bool useShadingStorageBuffer = useStorageBuffers && step->performsShading();
475 
476     // TODO: To more completely support end-to-end rendering, this will need to be updated so that
477     // the RenderStep shader snippet can produce a device coord, a local coord, and depth.
478     // If the paint combination doesn't need the local coord it can be ignored, otherwise we need
479     // a varying for it. The fragment function's output will need to be updated to have a color and
480     // the depth, or when there's no combination, just the depth. Lastly, we also should add the
481     // static/intrinsic uniform binding point so that we can handle normalizing the device position
482     // produced by the RenderStep automatically.
483 
484     // Fixed program header
485     std::string sksl =
486         "layout (binding=0) uniform intrinsicUniforms {\n"
487         "    layout(offset=0) float4 rtAdjust;\n"
488         "};\n"
489         "\n";
490 
491     if (step->numVertexAttributes() > 0 || step->numInstanceAttributes() > 0) {
492         sksl += emit_attributes(step->vertexAttributes(), step->instanceAttributes());
493     }
494 
495     // Uniforms needed by RenderStep
496     // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
497     // TODO: replace hard-coded bufferID with the backend's renderstep uniform-buffer index.
498     if (hasStepUniforms) {
499         if (useStepStorageBuffer) {
500             sksl += EmitRenderStepStorageBuffer(/* bufferID= */ 1, step->uniforms());
501         } else {
502             sksl += EmitRenderStepUniforms(/* bufferID= */ 1,
503                                            bindingReqs.fUniformBufferLayout,
504                                            step->uniforms(),
505                                            &result.fRenderStepUniformsTotalBytes);
506         }
507     }
508 
509     // Varyings needed by RenderStep
510     sksl += EmitVaryings(step, "out", useShadingStorageBuffer, defineLocalCoordsVarying);
511 
512     // Vertex shader function declaration
513     sksl += "void main() {";
514     // Create stepLocalCoords which render steps can write to.
515     sksl += "float2 stepLocalCoords = float2(0);";
516     // Vertex shader body
517     if (useStepStorageBuffer) {
518         // Extract out render step uniforms from SSBO, declaring local variables with the expected
519         // uniform names so that RenderStep SkSL is independent of storage choice.
520         SkSL::String::appendf(
521                 &sksl, "uint stepSsboIndex = %s.x;\n", RenderStep::ssboIndicesAttribute());
522         sksl += EmitUniformsFromStorageBuffer("step", "stepSsboIndex", step->uniforms());
523     }
524 
525     sksl += step->vertexSkSL();
526     sksl += "sk_Position = float4(devPosition.xy * rtAdjust.xy + devPosition.ww * rtAdjust.zw,"
527             "devPosition.zw);";
528 
529     if (useShadingStorageBuffer) {
530         // Assign SSBO index values to the SSBO index varying.
531         SkSL::String::appendf(&sksl,
532                               "%s = %s;",
533                               RenderStep::ssboIndicesVarying(),
534                               RenderStep::ssboIndicesAttribute());
535     }
536 
537     if (defineLocalCoordsVarying) {
538         // Assign Render Step's stepLocalCoords to the localCoordsVar varying.
539         sksl += "localCoordsVar = stepLocalCoords;";
540     }
541     sksl += "}";
542 
543     result.fSkSL = std::move(sksl);
544     result.fLabel = step->name();
545     if (defineLocalCoordsVarying) {
546         result.fLabel += " (w/ local coords)";
547     }
548 
549     return result;
550 }
551 
BuildFragmentSkSL(const Caps * caps,const ShaderCodeDictionary * dict,const RuntimeEffectDictionary * rteDict,const RenderStep * step,UniquePaintParamsID paintID,bool useStorageBuffers,skgpu::Swizzle writeSwizzle)552 FragSkSLInfo BuildFragmentSkSL(const Caps* caps,
553                                const ShaderCodeDictionary* dict,
554                                const RuntimeEffectDictionary* rteDict,
555                                const RenderStep* step,
556                                UniquePaintParamsID paintID,
557                                bool useStorageBuffers,
558                                skgpu::Swizzle writeSwizzle) {
559     FragSkSLInfo result;
560     if (!paintID.isValid()) {
561         // Depth-only draw so no fragment shader to compile
562         return {};
563     }
564 
565     const char* shadingSsboIndex =
566             useStorageBuffers && step->performsShading() ? "shadingSsboIndex" : nullptr;
567     ShaderInfo shaderInfo(paintID, dict, rteDict, shadingSsboIndex);
568 
569     result.fSkSL = shaderInfo.toSkSL(caps,
570                                      step,
571                                      useStorageBuffers,
572                                      &result.fNumTexturesAndSamplers,
573                                      &result.fNumPaintUniforms,
574                                      &result.fRenderStepUniformsTotalBytes,
575                                      &result.fPaintUniformsTotalBytes,
576                                      &result.fHasGradientBuffer,
577                                      writeSwizzle);
578 
579     // Extract blend info after integrating the RenderStep into the final fragment shader in case
580     // that changes the HW blending choice to handle analytic coverage.
581     result.fBlendInfo = shaderInfo.blendInfo();
582     result.fRequiresLocalCoords = shaderInfo.needsLocalCoords();
583 
584     result.fLabel = writeSwizzle.asString().c_str();
585     result.fLabel += " + ";
586     result.fLabel = step->name();
587     result.fLabel += " + ";
588     result.fLabel += dict->idToString(paintID).c_str();
589 
590     return result;
591 }
592 
GetPipelineLabel(const ShaderCodeDictionary * dict,const RenderPassDesc & renderPassDesc,const RenderStep * renderStep,UniquePaintParamsID paintID)593 std::string GetPipelineLabel(const ShaderCodeDictionary* dict,
594                              const RenderPassDesc& renderPassDesc,
595                              const RenderStep* renderStep,
596                              UniquePaintParamsID paintID) {
597     std::string label = renderPassDesc.toPipelineLabel().c_str(); // includes the write swizzle
598     label += " + ";
599     label += renderStep->name();
600     label += " + ";
601     label += dict->idToString(paintID).c_str(); // will be "(empty)" for depth-only draws
602     return label;
603 }
604 
BuildComputeSkSL(const Caps * caps,const ComputeStep * step)605 std::string BuildComputeSkSL(const Caps* caps, const ComputeStep* step) {
606     std::string sksl =
607             SkSL::String::printf("layout(local_size_x=%u, local_size_y=%u, local_size_z=%u) in;\n",
608                                  step->localDispatchSize().fWidth,
609                                  step->localDispatchSize().fHeight,
610                                  step->localDispatchSize().fDepth);
611 
612     const auto& bindingReqs = caps->resourceBindingRequirements();
613     bool distinctRanges = bindingReqs.fDistinctIndexRanges;
614     bool separateSampler = bindingReqs.fSeparateTextureAndSamplerBinding;
615 
616     int index = 0;
617     int texIdx = 0;
618     // NOTE: SkSL Metal codegen always assigns the same binding index to a texture and its sampler.
619     // TODO: This could cause sampler indices to not be tightly packed if the sampler2D declaration
620     // comes after 1 or more storage texture declarations (which don't have samplers). An optional
621     // "layout(msl, sampler=T, texture=T)" syntax to count them separately (like we do for WGSL)
622     // could come in handy here but it's not supported in MSL codegen yet.
623 
624     for (const ComputeStep::ResourceDesc& r : step->resources()) {
625         using Type = ComputeStep::ResourceType;
626         switch (r.fType) {
627             case Type::kUniformBuffer:
628                 SkSL::String::appendf(&sksl, "layout(binding=%d) uniform ", index++);
629                 sksl += r.fSkSL;
630                 break;
631             case Type::kStorageBuffer:
632             case Type::kIndirectBuffer:
633                 SkSL::String::appendf(&sksl, "layout(binding=%d) buffer ", index++);
634                 sksl += r.fSkSL;
635                 break;
636             case Type::kReadOnlyStorageBuffer:
637                 SkSL::String::appendf(&sksl, "layout(binding=%d) readonly buffer ", index++);
638                 sksl += r.fSkSL;
639                 break;
640             case Type::kWriteOnlyStorageTexture:
641                 SkSL::String::appendf(&sksl, "layout(binding=%d, rgba8) writeonly texture2D ",
642                                       distinctRanges ? texIdx++ : index++);
643                 sksl += r.fSkSL;
644                 break;
645             case Type::kReadOnlyTexture:
646                 SkSL::String::appendf(&sksl, "layout(binding=%d, rgba8) readonly texture2D ",
647                                       distinctRanges ? texIdx++ : index++);
648                 sksl += r.fSkSL;
649                 break;
650             case Type::kSampledTexture:
651                 if (distinctRanges) {
652                     SkSL::String::appendf(&sksl, "layout(metal, binding=%d) ", texIdx++);
653                 } else if (separateSampler) {
654                     SkSL::String::appendf(
655                             &sksl, "layout(webgpu, sampler=%d, texture=%d) ", index, index + 1);
656                     index += 2;
657                 } else {
658                     SkSL::String::appendf(&sksl, "layout(binding=%d) ", index++);
659                 }
660                 sksl += "sampler2D ";
661                 sksl += r.fSkSL;
662                 break;
663         }
664         sksl += ";\n";
665     }
666 
667     sksl += step->computeSkSL();
668     return sksl;
669 }
670 
671 } // namespace skgpu::graphite
672