xref: /aosp_15_r20/external/skia/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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/dawn/DawnGraphicsPipeline.h"
9 
10 #include "include/gpu/graphite/TextureInfo.h"
11 #include "include/private/base/SkTemplates.h"
12 #include "src/gpu/SkSLToBackend.h"
13 #include "src/gpu/Swizzle.h"
14 #include "src/gpu/graphite/Attribute.h"
15 #include "src/gpu/graphite/ContextUtils.h"
16 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
17 #include "src/gpu/graphite/Log.h"
18 #include "src/gpu/graphite/RenderPassDesc.h"
19 #include "src/gpu/graphite/RendererProvider.h"
20 #include "src/gpu/graphite/ShaderInfo.h"
21 #include "src/gpu/graphite/UniformManager.h"
22 #include "src/gpu/graphite/dawn/DawnCaps.h"
23 #include "src/gpu/graphite/dawn/DawnErrorChecker.h"
24 #include "src/gpu/graphite/dawn/DawnGraphiteTypesPriv.h"
25 #include "src/gpu/graphite/dawn/DawnGraphiteUtilsPriv.h"
26 #include "src/gpu/graphite/dawn/DawnResourceProvider.h"
27 #include "src/gpu/graphite/dawn/DawnSharedContext.h"
28 #include "src/gpu/graphite/dawn/DawnUtilsPriv.h"
29 #include "src/sksl/SkSLProgramSettings.h"
30 #include "src/sksl/SkSLUtil.h"
31 #include "src/sksl/ir/SkSLProgram.h"
32 
33 #include <vector>
34 
35 namespace skgpu::graphite {
36 
37 namespace {
38 
attribute_type_to_dawn(VertexAttribType type)39 inline wgpu::VertexFormat attribute_type_to_dawn(VertexAttribType type) {
40     switch (type) {
41         case VertexAttribType::kFloat:
42             return wgpu::VertexFormat::Float32;
43         case VertexAttribType::kFloat2:
44             return wgpu::VertexFormat::Float32x2;
45         case VertexAttribType::kFloat3:
46             return wgpu::VertexFormat::Float32x3;
47         case VertexAttribType::kFloat4:
48             return wgpu::VertexFormat::Float32x4;
49         case VertexAttribType::kHalf2:
50             return wgpu::VertexFormat::Float16x2;
51         case VertexAttribType::kHalf4:
52             return wgpu::VertexFormat::Float16x4;
53         case VertexAttribType::kInt2:
54             return wgpu::VertexFormat::Sint32x2;
55         case VertexAttribType::kInt3:
56             return wgpu::VertexFormat::Sint32x3;
57         case VertexAttribType::kInt4:
58             return wgpu::VertexFormat::Sint32x4;
59         case VertexAttribType::kUInt2:
60             return wgpu::VertexFormat::Uint32x2;
61         case VertexAttribType::kByte2:
62             return wgpu::VertexFormat::Sint8x2;
63         case VertexAttribType::kByte4:
64             return wgpu::VertexFormat::Sint8x4;
65         case VertexAttribType::kUByte2:
66             return wgpu::VertexFormat::Uint8x2;
67         case VertexAttribType::kUByte4:
68             return wgpu::VertexFormat::Uint8x4;
69         case VertexAttribType::kUByte4_norm:
70             return wgpu::VertexFormat::Unorm8x4;
71         case VertexAttribType::kShort2:
72             return wgpu::VertexFormat::Sint16x2;
73         case VertexAttribType::kShort4:
74             return wgpu::VertexFormat::Sint16x4;
75         case VertexAttribType::kUShort2:
76             return wgpu::VertexFormat::Uint16x2;
77         case VertexAttribType::kUShort2_norm:
78             return wgpu::VertexFormat::Unorm16x2;
79         case VertexAttribType::kInt:
80             return wgpu::VertexFormat::Sint32;
81         case VertexAttribType::kUInt:
82             return wgpu::VertexFormat::Uint32;
83         case VertexAttribType::kUShort4_norm:
84             return wgpu::VertexFormat::Unorm16x4;
85         case VertexAttribType::kHalf:
86         case VertexAttribType::kByte:
87         case VertexAttribType::kUByte:
88         case VertexAttribType::kUByte_norm:
89         case VertexAttribType::kUShort_norm:
90             // Not supported.
91             break;
92     }
93     SkUNREACHABLE;
94 }
95 
compare_op_to_dawn(CompareOp op)96 wgpu::CompareFunction compare_op_to_dawn(CompareOp op) {
97     switch (op) {
98         case CompareOp::kAlways:
99             return wgpu::CompareFunction::Always;
100         case CompareOp::kNever:
101             return wgpu::CompareFunction::Never;
102         case CompareOp::kGreater:
103             return wgpu::CompareFunction::Greater;
104         case CompareOp::kGEqual:
105             return wgpu::CompareFunction::GreaterEqual;
106         case CompareOp::kLess:
107             return wgpu::CompareFunction::Less;
108         case CompareOp::kLEqual:
109             return wgpu::CompareFunction::LessEqual;
110         case CompareOp::kEqual:
111             return wgpu::CompareFunction::Equal;
112         case CompareOp::kNotEqual:
113             return wgpu::CompareFunction::NotEqual;
114     }
115     SkUNREACHABLE;
116 }
117 
stencil_op_to_dawn(StencilOp op)118 wgpu::StencilOperation stencil_op_to_dawn(StencilOp op) {
119     switch (op) {
120         case StencilOp::kKeep:
121             return wgpu::StencilOperation::Keep;
122         case StencilOp::kZero:
123             return wgpu::StencilOperation::Zero;
124         case StencilOp::kReplace:
125             return wgpu::StencilOperation::Replace;
126         case StencilOp::kInvert:
127             return wgpu::StencilOperation::Invert;
128         case StencilOp::kIncWrap:
129             return wgpu::StencilOperation::IncrementWrap;
130         case StencilOp::kDecWrap:
131             return wgpu::StencilOperation::DecrementWrap;
132         case StencilOp::kIncClamp:
133             return wgpu::StencilOperation::IncrementClamp;
134         case StencilOp::kDecClamp:
135             return wgpu::StencilOperation::DecrementClamp;
136     }
137     SkUNREACHABLE;
138 }
139 
stencil_face_to_dawn(DepthStencilSettings::Face face)140 wgpu::StencilFaceState stencil_face_to_dawn(DepthStencilSettings::Face face) {
141     wgpu::StencilFaceState state;
142     state.compare = compare_op_to_dawn(face.fCompareOp);
143     state.failOp = stencil_op_to_dawn(face.fStencilFailOp);
144     state.depthFailOp = stencil_op_to_dawn(face.fDepthFailOp);
145     state.passOp = stencil_op_to_dawn(face.fDepthStencilPassOp);
146     return state;
147 }
148 
create_vertex_attributes(SkSpan<const Attribute> attrs,int shaderLocationOffset,std::vector<wgpu::VertexAttribute> * out)149 size_t create_vertex_attributes(SkSpan<const Attribute> attrs,
150                                 int shaderLocationOffset,
151                                 std::vector<wgpu::VertexAttribute>* out) {
152     SkASSERT(out && out->empty());
153     out->resize(attrs.size());
154     size_t vertexAttributeOffset = 0;
155     int attributeIndex = 0;
156     for (const auto& attr : attrs) {
157         wgpu::VertexAttribute& vertexAttribute =  (*out)[attributeIndex];
158         vertexAttribute.format = attribute_type_to_dawn(attr.cpuType());
159         vertexAttribute.offset = vertexAttributeOffset;
160         vertexAttribute.shaderLocation = shaderLocationOffset + attributeIndex;
161         vertexAttributeOffset += attr.sizeAlign4();
162         attributeIndex++;
163     }
164     return vertexAttributeOffset;
165 }
166 
167 // TODO: share this w/ Ganesh dawn backend?
blend_coeff_to_dawn_blend(const DawnCaps & caps,skgpu::BlendCoeff coeff)168 static wgpu::BlendFactor blend_coeff_to_dawn_blend(const DawnCaps& caps, skgpu::BlendCoeff coeff) {
169 #if defined(__EMSCRIPTEN__)
170 #define VALUE_IF_DSB_OR_ZERO(VALUE) wgpu::BlendFactor::Zero
171 #else
172 #define VALUE_IF_DSB_OR_ZERO(VALUE) \
173     ((caps.shaderCaps()->fDualSourceBlendingSupport) ? (VALUE) : wgpu::BlendFactor::Zero)
174 #endif
175     switch (coeff) {
176         case skgpu::BlendCoeff::kZero:
177             return wgpu::BlendFactor::Zero;
178         case skgpu::BlendCoeff::kOne:
179             return wgpu::BlendFactor::One;
180         case skgpu::BlendCoeff::kSC:
181             return wgpu::BlendFactor::Src;
182         case skgpu::BlendCoeff::kISC:
183             return wgpu::BlendFactor::OneMinusSrc;
184         case skgpu::BlendCoeff::kDC:
185             return wgpu::BlendFactor::Dst;
186         case skgpu::BlendCoeff::kIDC:
187             return wgpu::BlendFactor::OneMinusDst;
188         case skgpu::BlendCoeff::kSA:
189             return wgpu::BlendFactor::SrcAlpha;
190         case skgpu::BlendCoeff::kISA:
191             return wgpu::BlendFactor::OneMinusSrcAlpha;
192         case skgpu::BlendCoeff::kDA:
193             return wgpu::BlendFactor::DstAlpha;
194         case skgpu::BlendCoeff::kIDA:
195             return wgpu::BlendFactor::OneMinusDstAlpha;
196         case skgpu::BlendCoeff::kConstC:
197             return wgpu::BlendFactor::Constant;
198         case skgpu::BlendCoeff::kIConstC:
199             return wgpu::BlendFactor::OneMinusConstant;
200         case skgpu::BlendCoeff::kS2C:
201             return VALUE_IF_DSB_OR_ZERO(wgpu::BlendFactor::Src1);
202         case skgpu::BlendCoeff::kIS2C:
203             return VALUE_IF_DSB_OR_ZERO(wgpu::BlendFactor::OneMinusSrc1);
204         case skgpu::BlendCoeff::kS2A:
205             return VALUE_IF_DSB_OR_ZERO(wgpu::BlendFactor::Src1Alpha);
206         case skgpu::BlendCoeff::kIS2A:
207             return VALUE_IF_DSB_OR_ZERO(wgpu::BlendFactor::OneMinusSrc1Alpha);
208         case skgpu::BlendCoeff::kIllegal:
209             return wgpu::BlendFactor::Zero;
210     }
211     SkUNREACHABLE;
212 #undef VALUE_IF_DSB_OR_ZERO
213 }
214 
blend_coeff_to_dawn_blend_for_alpha(const DawnCaps & caps,skgpu::BlendCoeff coeff)215 static wgpu::BlendFactor blend_coeff_to_dawn_blend_for_alpha(const DawnCaps& caps,
216                                                              skgpu::BlendCoeff coeff) {
217     switch (coeff) {
218         // Force all srcColor used in alpha slot to alpha version.
219         case skgpu::BlendCoeff::kSC:
220             return wgpu::BlendFactor::SrcAlpha;
221         case skgpu::BlendCoeff::kISC:
222             return wgpu::BlendFactor::OneMinusSrcAlpha;
223         case skgpu::BlendCoeff::kDC:
224             return wgpu::BlendFactor::DstAlpha;
225         case skgpu::BlendCoeff::kIDC:
226             return wgpu::BlendFactor::OneMinusDstAlpha;
227         default:
228             return blend_coeff_to_dawn_blend(caps, coeff);
229     }
230 }
231 
232 // TODO: share this w/ Ganesh Metal backend?
blend_equation_to_dawn_blend_op(skgpu::BlendEquation equation)233 static wgpu::BlendOperation blend_equation_to_dawn_blend_op(skgpu::BlendEquation equation) {
234     static const wgpu::BlendOperation gTable[] = {
235             wgpu::BlendOperation::Add,              // skgpu::BlendEquation::kAdd
236             wgpu::BlendOperation::Subtract,         // skgpu::BlendEquation::kSubtract
237             wgpu::BlendOperation::ReverseSubtract,  // skgpu::BlendEquation::kReverseSubtract
238     };
239     static_assert(std::size(gTable) == (int)skgpu::BlendEquation::kFirstAdvanced);
240     static_assert(0 == (int)skgpu::BlendEquation::kAdd);
241     static_assert(1 == (int)skgpu::BlendEquation::kSubtract);
242     static_assert(2 == (int)skgpu::BlendEquation::kReverseSubtract);
243 
244     SkASSERT((unsigned)equation < skgpu::kBlendEquationCnt);
245     return gTable[(int)equation];
246 }
247 
248 struct AsyncPipelineCreationBase {
249     wgpu::RenderPipeline fRenderPipeline;
250     bool fFinished = false;
251 #if SK_HISTOGRAMS_ENABLED
252     // We need these three for the Graphite.PipelineCreationTimes.* histograms (cf.
253     // log_pipeline_creation)
254     skgpu::StdSteadyClock::time_point fStartTime;
255     bool fFromPrecompile;
256     bool fAsynchronous = false;
257 #endif
258 };
259 
log_pipeline_creation(const AsyncPipelineCreationBase * apcb)260 void log_pipeline_creation(const AsyncPipelineCreationBase* apcb) {
261 #if SK_HISTOGRAMS_ENABLED
262     [[maybe_unused]] static constexpr int kBucketCount = 100;
263     [[maybe_unused]] static constexpr int kOneSecInUS = 1000000;
264 
265     SkASSERT(apcb->fFinished);
266 
267     if (!apcb->fRenderPipeline) {
268         // A null fRenderPipeline means Pipeline creation failed
269         return; // TODO: log failures to their own UMA stat
270     }
271 
272     [[maybe_unused]] auto micros_since = [](skgpu::StdSteadyClock::time_point start) {
273         skgpu::StdSteadyClock::duration elapsed = skgpu::StdSteadyClock::now() - start;
274         return std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
275     };
276 
277     if (apcb->fFromPrecompile) {
278         SkASSERT(!apcb->fAsynchronous);     // precompile is done synchronously on a thread
279         SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
280             "Graphite.PipelineCreationTimes.Precompile",
281             micros_since(apcb->fStartTime),
282             /* minUSec= */ 1,
283             /* maxUSec= */ kOneSecInUS,
284             kBucketCount);
285     } else if (apcb->fAsynchronous) {
286         SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
287             "Graphite.PipelineCreationTimes.Asynchronous",
288             micros_since(apcb->fStartTime),
289             /* minUSec= */ 1,
290             /* maxUSec= */ kOneSecInUS,
291             kBucketCount);
292     } else {
293         SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
294             "Graphite.PipelineCreationTimes.Synchronous",
295             micros_since(apcb->fStartTime),
296             /* minUSec= */ 1,
297             /* maxUSec= */ kOneSecInUS,
298             kBucketCount);
299     }
300 #endif // SK_HISTOGRAMS_ENABLED
301 }
302 
303 } // anonymous namespace
304 
305 #if defined(__EMSCRIPTEN__)
306 // For wasm, we don't use async compilation.
307 struct DawnGraphicsPipeline::AsyncPipelineCreation : public AsyncPipelineCreationBase {};
308 #else
309 struct DawnGraphicsPipeline::AsyncPipelineCreation : public AsyncPipelineCreationBase {
310     wgpu::Future fFuture;
311 };
312 #endif
313 
314 // static
Make(const DawnSharedContext * sharedContext,DawnResourceProvider * resourceProvider,const RuntimeEffectDictionary * runtimeDict,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc,SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags)315 sk_sp<DawnGraphicsPipeline> DawnGraphicsPipeline::Make(
316         const DawnSharedContext* sharedContext,
317         DawnResourceProvider* resourceProvider,
318         const RuntimeEffectDictionary* runtimeDict,
319         const GraphicsPipelineDesc& pipelineDesc,
320         const RenderPassDesc& renderPassDesc,
321         SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
322     const DawnCaps& caps = *static_cast<const DawnCaps*>(sharedContext->caps());
323     const auto& device = sharedContext->device();
324 
325     SkSL::Program::Interface vsInterface, fsInterface;
326 
327     SkSL::ProgramSettings settings;
328     settings.fSharpenTextures = true;
329     settings.fForceNoRTFlip = true;
330 
331     ShaderErrorHandler* errorHandler = caps.shaderErrorHandler();
332 
333     const RenderStep* step = sharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID());
334     const bool useStorageBuffers = caps.storageBufferSupport();
335 
336     std::string vsCode, fsCode;
337     wgpu::ShaderModule fsModule, vsModule;
338 
339     // Some steps just render depth buffer but not color buffer, so the fragment
340     // shader is null.
341     UniquePaintParamsID paintID = pipelineDesc.paintParamsID();
342 
343     skia_private::TArray<SamplerDesc>* samplerDescArrPtr = nullptr;
344 #if !defined(__EMSCRIPTEN__)
345     skia_private::TArray<SamplerDesc> samplerDescArr {};
346     samplerDescArrPtr = &samplerDescArr;
347 #endif
348 
349     std::unique_ptr<ShaderInfo> shaderInfo = ShaderInfo::Make(&caps,
350                                                               sharedContext->shaderCodeDictionary(),
351                                                               runtimeDict,
352                                                               step,
353                                                               paintID,
354                                                               useStorageBuffers,
355                                                               renderPassDesc.fWriteSwizzle,
356                                                               samplerDescArrPtr);
357 
358     const std::string& fsSkSL = shaderInfo->fragmentSkSL();
359     const BlendInfo& blendInfo = shaderInfo->blendInfo();
360     const int numTexturesAndSamplers = shaderInfo->numFragmentTexturesAndSamplers();
361 
362     const bool hasFragmentSkSL = !fsSkSL.empty();
363     if (hasFragmentSkSL) {
364         if (!skgpu::SkSLToWGSL(caps.shaderCaps(),
365                                fsSkSL,
366                                SkSL::ProgramKind::kGraphiteFragment,
367                                settings,
368                                &fsCode,
369                                &fsInterface,
370                                errorHandler)) {
371             return {};
372         }
373         if (!DawnCompileWGSLShaderModule(sharedContext, shaderInfo->fsLabel().c_str(), fsCode,
374                                          &fsModule, errorHandler)) {
375             return {};
376         }
377     }
378 
379     const std::string& vsSkSL = shaderInfo->vertexSkSL();
380     if (!skgpu::SkSLToWGSL(caps.shaderCaps(),
381                            vsSkSL,
382                            SkSL::ProgramKind::kGraphiteVertex,
383                            settings,
384                            &vsCode,
385                            &vsInterface,
386                            errorHandler)) {
387         return {};
388     }
389     if (!DawnCompileWGSLShaderModule(sharedContext, shaderInfo->vsLabel().c_str(), vsCode,
390                                      &vsModule, errorHandler)) {
391         return {};
392     }
393 
394     std::string pipelineLabel =
395             GetPipelineLabel(sharedContext->shaderCodeDictionary(), renderPassDesc, step, paintID);
396     wgpu::RenderPipelineDescriptor descriptor;
397     // Always set the label for pipelines, dawn may need it for tracing.
398     descriptor.label = pipelineLabel.c_str();
399 
400     // Fragment state
401     skgpu::BlendEquation equation = blendInfo.fEquation;
402     skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend;
403     skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend;
404     bool blendOn = !skgpu::BlendShouldDisable(equation, srcCoeff, dstCoeff);
405 
406     wgpu::BlendState blend;
407     if (blendOn) {
408         blend.color.operation = blend_equation_to_dawn_blend_op(equation);
409         blend.color.srcFactor = blend_coeff_to_dawn_blend(caps, srcCoeff);
410         blend.color.dstFactor = blend_coeff_to_dawn_blend(caps, dstCoeff);
411         blend.alpha.operation = blend_equation_to_dawn_blend_op(equation);
412         blend.alpha.srcFactor = blend_coeff_to_dawn_blend_for_alpha(caps, srcCoeff);
413         blend.alpha.dstFactor = blend_coeff_to_dawn_blend_for_alpha(caps, dstCoeff);
414     }
415 
416     wgpu::ColorTargetState colorTarget;
417     colorTarget.format =
418             TextureInfos::GetDawnViewFormat(renderPassDesc.fColorAttachment.fTextureInfo);
419     colorTarget.blend = blendOn ? &blend : nullptr;
420     colorTarget.writeMask = blendInfo.fWritesColor && hasFragmentSkSL ? wgpu::ColorWriteMask::All
421                                                                       : wgpu::ColorWriteMask::None;
422 
423 #if !defined(__EMSCRIPTEN__)
424     const bool loadMsaaFromResolve =
425             renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
426             renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
427     // Special case: a render pass loading resolve texture requires additional settings for the
428     // pipeline to make it compatible.
429     wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAALoadResolveTextureDesc;
430     if (loadMsaaFromResolve && sharedContext->dawnCaps()->resolveTextureLoadOp().has_value()) {
431         SkASSERT(device.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture));
432         colorTarget.nextInChain = &pipelineMSAALoadResolveTextureDesc;
433         pipelineMSAALoadResolveTextureDesc.enabled = true;
434     }
435 #endif
436 
437     wgpu::FragmentState fragment;
438     // Dawn doesn't allow having a color attachment but without fragment shader, so have to use a
439     // noop fragment shader, if fragment shader is null.
440     fragment.module = hasFragmentSkSL ? std::move(fsModule) : sharedContext->noopFragment();
441     fragment.entryPoint = "main";
442     fragment.targetCount = 1;
443     fragment.targets = &colorTarget;
444     descriptor.fragment = &fragment;
445 
446     // Depth stencil state
447     const auto& depthStencilSettings = step->depthStencilSettings();
448     SkASSERT(depthStencilSettings.fDepthTestEnabled ||
449              depthStencilSettings.fDepthCompareOp == CompareOp::kAlways);
450     wgpu::DepthStencilState depthStencil;
451     if (renderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid()) {
452         wgpu::TextureFormat dsFormat = TextureInfos::GetDawnViewFormat(
453                 renderPassDesc.fDepthStencilAttachment.fTextureInfo);
454         depthStencil.format =
455                 DawnFormatIsDepthOrStencil(dsFormat) ? dsFormat : wgpu::TextureFormat::Undefined;
456         if (depthStencilSettings.fDepthTestEnabled) {
457             depthStencil.depthWriteEnabled = depthStencilSettings.fDepthWriteEnabled;
458         }
459         depthStencil.depthCompare = compare_op_to_dawn(depthStencilSettings.fDepthCompareOp);
460 
461         // Dawn validation fails if the stencil state is non-default and the
462         // format doesn't have the stencil aspect.
463         if (DawnFormatIsStencil(dsFormat) && depthStencilSettings.fStencilTestEnabled) {
464             depthStencil.stencilFront = stencil_face_to_dawn(depthStencilSettings.fFrontStencil);
465             depthStencil.stencilBack = stencil_face_to_dawn(depthStencilSettings.fBackStencil);
466             depthStencil.stencilReadMask = depthStencilSettings.fFrontStencil.fReadMask;
467             depthStencil.stencilWriteMask = depthStencilSettings.fFrontStencil.fWriteMask;
468         }
469 
470         descriptor.depthStencil = &depthStencil;
471     }
472 
473     // Determine the BindGroupLayouts that will be used to make up the pipeline layout.
474     BindGroupLayouts groupLayouts;
475     // If immutable samplers are used with this pipeline, they must be included in the pipeline
476     // layout and passed in to the pipline constructor for lifetime management.
477     skia_private::TArray<sk_sp<DawnSampler>> immutableSamplers;
478     {
479         SkASSERT(resourceProvider);
480         groupLayouts[0] = resourceProvider->getOrCreateUniformBuffersBindGroupLayout();
481         if (!groupLayouts[0]) {
482             return {};
483         }
484 
485         bool hasFragmentSamplers = hasFragmentSkSL && numTexturesAndSamplers > 0;
486         if (hasFragmentSamplers) {
487             // Check if we can optimize for the common case of a single texture + 1 dynamic sampler
488             if (numTexturesAndSamplers == 2 &&
489                 !(samplerDescArrPtr && samplerDescArrPtr->at(0).isImmutable())) {
490                 groupLayouts[1] =
491                         resourceProvider->getOrCreateSingleTextureSamplerBindGroupLayout();
492             } else {
493                 std::vector<wgpu::BindGroupLayoutEntry> entries(numTexturesAndSamplers);
494 #if !defined(__EMSCRIPTEN__)
495                 // Static sampler layouts are passed into Dawn by address and therefore must stay
496                 // alive until the BindGroupLayoutDescriptor is created. So, store them outside of
497                 // the loop that iterates over each BindGroupLayoutEntry.
498                 skia_private::TArray<wgpu::StaticSamplerBindingLayout> staticSamplerLayouts;
499 
500                 // Note that the number of samplers is equivalent to numTexturesAndSamplers / 2. So,
501                 // a sampler's index within any container that only pertains to sampler information
502                 // (as in, excludes textures) is equivalent to 1/2 of that sampler's binding index
503                 // within the BindGroupLayout. Assert that we have analyzed the appropriate number
504                 // of samplers by equating samplerDescArr size to sampler quantity.
505                 SkASSERT(samplerDescArrPtr && samplerDescArr.size() == numTexturesAndSamplers / 2);
506 #endif
507 
508                 for (int i = 0; i < numTexturesAndSamplers;) {
509                     entries[i].binding = i;
510                     entries[i].visibility = wgpu::ShaderStage::Fragment;
511 #if !defined(__EMSCRIPTEN__)
512                     // Index of sampler information = 1/2 of cumulative texture and sampler index.
513                     // If we have a non-default-initialized SamplerDesc at that index,
514                     // fetch an immutable sampler that matches that description to include in the
515                     // pipeline layout.
516                     const SamplerDesc& samplerDesc = samplerDescArr.at(i/2);
517                     if (samplerDesc.isImmutable()) {
518                         sk_sp<Sampler> immutableSampler =
519                                 resourceProvider->findOrCreateCompatibleSampler(samplerDesc);
520                         if (!immutableSampler) {
521                             SKGPU_LOG_E("Failed to find/create immutable sampler for pipeline");
522                             return {};
523                         }
524                         sk_sp<DawnSampler> dawnImmutableSampler = sk_ref_sp<DawnSampler>(
525                                 static_cast<DawnSampler*>(immutableSampler.get()));
526                         SkASSERT(dawnImmutableSampler);
527 
528                         wgpu::StaticSamplerBindingLayout& immutableSamplerBinding =
529                                 staticSamplerLayouts.emplace_back();
530                         immutableSamplerBinding.sampler = dawnImmutableSampler->dawnSampler();
531                         // Static samplers sample from the subsequent texture in the BindGroupLayout
532                         immutableSamplerBinding.sampledTextureBinding = i + 1;
533 
534                         immutableSamplers.push_back(std::move(dawnImmutableSampler));
535                         entries[i].nextInChain = &immutableSamplerBinding;
536                     } else {
537 #endif
538                         entries[i].sampler.type = wgpu::SamplerBindingType::Filtering;
539 #if !defined(__EMSCRIPTEN__)
540                     }
541 #endif
542                     ++i;
543 
544                     entries[i].binding = i;
545                     entries[i].visibility = wgpu::ShaderStage::Fragment;
546                     entries[i].texture.sampleType = wgpu::TextureSampleType::Float;
547                     entries[i].texture.viewDimension = wgpu::TextureViewDimension::e2D;
548                     entries[i].texture.multisampled = false;
549                     ++i;
550                 }
551 
552                 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
553                 if (sharedContext->caps()->setBackendLabels()) {
554                     groupLayoutDesc.label = shaderInfo->vsLabel().c_str();
555                 }
556                 groupLayoutDesc.entryCount = entries.size();
557                 groupLayoutDesc.entries = entries.data();
558                 groupLayouts[1] = device.CreateBindGroupLayout(&groupLayoutDesc);
559             }
560             if (!groupLayouts[1]) {
561                 return {};
562             }
563         }
564 
565         wgpu::PipelineLayoutDescriptor layoutDesc;
566         if (sharedContext->caps()->setBackendLabels()) {
567             layoutDesc.label = shaderInfo->fsLabel().c_str();
568         }
569         layoutDesc.bindGroupLayoutCount =
570             hasFragmentSamplers ? groupLayouts.size() : groupLayouts.size() - 1;
571         layoutDesc.bindGroupLayouts = groupLayouts.data();
572         auto layout = device.CreatePipelineLayout(&layoutDesc);
573         if (!layout) {
574             return {};
575         }
576         descriptor.layout = std::move(layout);
577     }
578 
579     // Vertex state
580     std::array<wgpu::VertexBufferLayout, kNumVertexBuffers> vertexBufferLayouts;
581     // Vertex buffer layout
582     std::vector<wgpu::VertexAttribute> vertexAttributes;
583     {
584         auto arrayStride = create_vertex_attributes(step->vertexAttributes(),
585                                                     0,
586                                                     &vertexAttributes);
587         auto& layout = vertexBufferLayouts[kVertexBufferIndex];
588         if (arrayStride) {
589             layout.arrayStride = arrayStride;
590             layout.stepMode = wgpu::VertexStepMode::Vertex;
591             layout.attributeCount = vertexAttributes.size();
592             layout.attributes = vertexAttributes.data();
593         } else {
594             layout.arrayStride = 0;
595             layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
596             layout.attributeCount = 0;
597             layout.attributes = nullptr;
598         }
599     }
600 
601     // Instance buffer layout
602     std::vector<wgpu::VertexAttribute> instanceAttributes;
603     {
604         auto arrayStride = create_vertex_attributes(step->instanceAttributes(),
605                                                     step->vertexAttributes().size(),
606                                                     &instanceAttributes);
607         auto& layout = vertexBufferLayouts[kInstanceBufferIndex];
608         if (arrayStride) {
609             layout.arrayStride = arrayStride;
610             layout.stepMode = wgpu::VertexStepMode::Instance;
611             layout.attributeCount = instanceAttributes.size();
612             layout.attributes = instanceAttributes.data();
613         } else {
614             layout.arrayStride = 0;
615             layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
616             layout.attributeCount = 0;
617             layout.attributes = nullptr;
618         }
619     }
620 
621     auto& vertex = descriptor.vertex;
622     vertex.module = std::move(vsModule);
623     vertex.entryPoint = "main";
624     vertex.constantCount = 0;
625     vertex.constants = nullptr;
626     vertex.bufferCount = vertexBufferLayouts.size();
627     vertex.buffers = vertexBufferLayouts.data();
628 
629     // Other state
630     descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
631     descriptor.primitive.cullMode = wgpu::CullMode::None;
632     switch (step->primitiveType()) {
633         case PrimitiveType::kTriangles:
634             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
635             break;
636         case PrimitiveType::kTriangleStrip:
637             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
638             descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint16;
639             break;
640         case PrimitiveType::kPoints:
641             descriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
642             break;
643     }
644 
645     // Multisampled state
646     descriptor.multisample.count = renderPassDesc.fSampleCount;
647     descriptor.multisample.mask = 0xFFFFFFFF;
648     descriptor.multisample.alphaToCoverageEnabled = false;
649 
650     const bool forPrecompilation =
651             SkToBool(pipelineCreationFlags & PipelineCreationFlags::kForPrecompilation);
652     // For Dawn, we want Precompilation to happen synchronously
653     const bool useAsync = caps.useAsyncPipelineCreation() && !forPrecompilation;
654 
655     auto asyncCreation = std::make_unique<AsyncPipelineCreation>();
656 #if SK_HISTOGRAMS_ENABLED
657     asyncCreation->fStartTime = skgpu::StdSteadyClock::now();
658     asyncCreation->fFromPrecompile = forPrecompilation;
659     asyncCreation->fAsynchronous = useAsync;
660 #endif
661 
662     if (useAsync) {
663 #if defined(__EMSCRIPTEN__)
664         // We shouldn't use CreateRenderPipelineAsync in wasm.
665         SKGPU_LOG_F("CreateRenderPipelineAsync shouldn't be used in WASM");
666 #else
667         asyncCreation->fFuture = device.CreateRenderPipelineAsync(
668                 &descriptor,
669                 wgpu::CallbackMode::WaitAnyOnly,
670                 [asyncCreationPtr = asyncCreation.get()](wgpu::CreatePipelineAsyncStatus status,
671                                                          wgpu::RenderPipeline pipeline,
672                                                          char const* message) {
673                     if (status != wgpu::CreatePipelineAsyncStatus::Success) {
674                         SKGPU_LOG_E("Failed to create render pipeline (%d): %s",
675                                     static_cast<int>(status),
676                                     message);
677                         // invalidate AsyncPipelineCreation pointer to signal that this pipeline has
678                         // failed.
679                         asyncCreationPtr->fRenderPipeline = nullptr;
680                     } else {
681                         asyncCreationPtr->fRenderPipeline = std::move(pipeline);
682                     }
683 
684                     asyncCreationPtr->fFinished = true;
685 
686                     log_pipeline_creation(asyncCreationPtr);
687                 });
688 #endif
689     } else {
690         std::optional<DawnErrorChecker> errorChecker;
691         if (sharedContext->dawnCaps()->allowScopedErrorChecks()) {
692             errorChecker.emplace(sharedContext);
693         }
694         asyncCreation->fRenderPipeline = device.CreateRenderPipeline(&descriptor);
695         asyncCreation->fFinished = true;
696 
697         if (errorChecker.has_value() && errorChecker->popErrorScopes() != DawnErrorType::kNoError) {
698             asyncCreation->fRenderPipeline = nullptr;
699         }
700 
701         log_pipeline_creation(asyncCreation.get());
702     }
703 
704     PipelineInfo pipelineInfo{*shaderInfo, pipelineCreationFlags};
705 #if defined(GPU_TEST_UTILS)
706     pipelineInfo.fNativeVertexShader = std::move(vsCode);
707     pipelineInfo.fNativeFragmentShader = std::move(fsCode);
708 #endif
709 
710     return sk_sp<DawnGraphicsPipeline>(
711             new DawnGraphicsPipeline(sharedContext,
712                                      pipelineInfo,
713                                      std::move(asyncCreation),
714                                      std::move(groupLayouts),
715                                      step->primitiveType(),
716                                      depthStencilSettings.fStencilReferenceValue,
717                                      std::move(immutableSamplers)));
718 }
719 
DawnGraphicsPipeline(const skgpu::graphite::SharedContext * sharedContext,const PipelineInfo & pipelineInfo,std::unique_ptr<AsyncPipelineCreation> asyncCreationInfo,BindGroupLayouts groupLayouts,PrimitiveType primitiveType,uint32_t refValue,skia_private::TArray<sk_sp<DawnSampler>> immutableSamplers)720 DawnGraphicsPipeline::DawnGraphicsPipeline(
721         const skgpu::graphite::SharedContext* sharedContext,
722         const PipelineInfo& pipelineInfo,
723         std::unique_ptr<AsyncPipelineCreation> asyncCreationInfo,
724         BindGroupLayouts groupLayouts,
725         PrimitiveType primitiveType,
726         uint32_t refValue,
727         skia_private::TArray<sk_sp<DawnSampler>> immutableSamplers)
728     : GraphicsPipeline(sharedContext, pipelineInfo)
729     , fAsyncPipelineCreation(std::move(asyncCreationInfo))
730     , fGroupLayouts(std::move(groupLayouts))
731     , fPrimitiveType(primitiveType)
732     , fStencilReferenceValue(refValue)
733     , fImmutableSamplers(std::move(immutableSamplers)) {}
734 
~DawnGraphicsPipeline()735 DawnGraphicsPipeline::~DawnGraphicsPipeline() {
736     this->freeGpuData();
737 }
738 
freeGpuData()739 void DawnGraphicsPipeline::freeGpuData() {
740     // Wait for async creation to finish before we can destroy this object.
741     (void)this->dawnRenderPipeline();
742     fAsyncPipelineCreation = nullptr;
743 }
744 
dawnRenderPipeline() const745 const wgpu::RenderPipeline& DawnGraphicsPipeline::dawnRenderPipeline() const {
746     if (!fAsyncPipelineCreation) {
747         static const wgpu::RenderPipeline kNullPipeline = nullptr;
748         return kNullPipeline;
749     }
750     if (fAsyncPipelineCreation->fFinished) {
751         return fAsyncPipelineCreation->fRenderPipeline;
752     }
753 #if defined(__EMSCRIPTEN__)
754     // We shouldn't use CreateRenderPipelineAsync in wasm.
755     SKGPU_LOG_F("CreateRenderPipelineAsync shouldn't be used in WASM");
756 #else
757     wgpu::FutureWaitInfo waitInfo{};
758     waitInfo.future = fAsyncPipelineCreation->fFuture;
759     const auto& instance = static_cast<const DawnSharedContext*>(sharedContext())
760                                    ->device()
761                                    .GetAdapter()
762                                    .GetInstance();
763 
764     [[maybe_unused]] auto status =
765             instance.WaitAny(1, &waitInfo, /*timeoutNS=*/std::numeric_limits<uint64_t>::max());
766     SkASSERT(status == wgpu::WaitStatus::Success);
767     SkASSERT(waitInfo.completed);
768 #endif
769 
770     return fAsyncPipelineCreation->fRenderPipeline;
771 }
772 
773 } // namespace skgpu::graphite
774