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