• 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/gpu/graphite/dawn/DawnGraphiteTypes.h"
12 #include "include/private/base/SkTemplates.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/gpu/SkSLToBackend.h"
15 #include "src/gpu/Swizzle.h"
16 #include "src/gpu/graphite/Attribute.h"
17 #include "src/gpu/graphite/ContextUtils.h"
18 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
19 #include "src/gpu/graphite/Log.h"
20 #include "src/gpu/graphite/RenderPassDesc.h"
21 #include "src/gpu/graphite/RendererProvider.h"
22 #include "src/gpu/graphite/ShaderInfo.h"
23 #include "src/gpu/graphite/TextureInfoPriv.h"
24 #include "src/gpu/graphite/UniformManager.h"
25 #include "src/gpu/graphite/dawn/DawnCaps.h"
26 #include "src/gpu/graphite/dawn/DawnErrorChecker.h"
27 #include "src/gpu/graphite/dawn/DawnGraphiteUtils.h"
28 #include "src/gpu/graphite/dawn/DawnResourceProvider.h"
29 #include "src/gpu/graphite/dawn/DawnSharedContext.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::__anone2a4bdee0111::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 =
360             ShaderInfo::Make(&caps,
361                              sharedContext->shaderCodeDictionary(),
362                              runtimeDict,
363                              step,
364                              paintID,
365                              useStorageBuffers,
366                              renderPassDesc.fWriteSwizzle,
367                              renderPassDesc.fDstReadStrategyIfRequired,
368                              samplerDescArrPtr);
369 
370     const std::string& fsSkSL = shaderInfo->fragmentSkSL();
371     const BlendInfo& blendInfo = shaderInfo->blendInfo();
372     const int numTexturesAndSamplers = shaderInfo->numFragmentTexturesAndSamplers();
373 
374     const bool hasFragmentSkSL = !fsSkSL.empty();
375     if (hasFragmentSkSL) {
376         if (!skgpu::SkSLToWGSL(caps.shaderCaps(),
377                                fsSkSL,
378                                SkSL::ProgramKind::kGraphiteFragment,
379                                settings,
380                                &fsCode,
381                                &fsInterface,
382                                errorHandler)) {
383             return {};
384         }
385         if (!DawnCompileWGSLShaderModule(sharedContext, shaderInfo->fsLabel().c_str(), fsCode,
386                                          &fsModule, errorHandler)) {
387             return {};
388         }
389     }
390 
391     const std::string& vsSkSL = shaderInfo->vertexSkSL();
392     if (!skgpu::SkSLToWGSL(caps.shaderCaps(),
393                            vsSkSL,
394                            SkSL::ProgramKind::kGraphiteVertex,
395                            settings,
396                            &vsCode,
397                            &vsInterface,
398                            errorHandler)) {
399         return {};
400     }
401     if (!DawnCompileWGSLShaderModule(sharedContext, shaderInfo->vsLabel().c_str(), vsCode,
402                                      &vsModule, errorHandler)) {
403         return {};
404     }
405 
406     std::string pipelineLabel =
407             GetPipelineLabel(sharedContext->shaderCodeDictionary(), renderPassDesc, step, paintID);
408     wgpu::RenderPipelineDescriptor descriptor;
409     // Always set the label for pipelines, dawn may need it for tracing.
410     descriptor.label = pipelineLabel.c_str();
411 
412     // Fragment state
413     skgpu::BlendEquation equation = blendInfo.fEquation;
414     skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend;
415     skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend;
416     bool blendOn = !skgpu::BlendShouldDisable(equation, srcCoeff, dstCoeff);
417 
418     wgpu::BlendState blend;
419     if (blendOn) {
420         blend.color.operation = blend_equation_to_dawn_blend_op(equation);
421         blend.color.srcFactor = blend_coeff_to_dawn_blend(caps, srcCoeff);
422         blend.color.dstFactor = blend_coeff_to_dawn_blend(caps, dstCoeff);
423         blend.alpha.operation = blend_equation_to_dawn_blend_op(equation);
424         blend.alpha.srcFactor = blend_coeff_to_dawn_blend_for_alpha(caps, srcCoeff);
425         blend.alpha.dstFactor = blend_coeff_to_dawn_blend_for_alpha(caps, dstCoeff);
426     }
427 
428     wgpu::ColorTargetState colorTarget;
429     colorTarget.format = TextureInfoPriv::Get<DawnTextureInfo>(
430             renderPassDesc.fColorAttachment.fTextureInfo).getViewFormat();
431     colorTarget.blend = blendOn ? &blend : nullptr;
432     colorTarget.writeMask = blendInfo.fWritesColor && hasFragmentSkSL ? wgpu::ColorWriteMask::All
433                                                                       : wgpu::ColorWriteMask::None;
434 
435 #if !defined(__EMSCRIPTEN__)
436     const bool loadMsaaFromResolve =
437             renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
438             renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
439     // Special case: a render pass loading resolve texture requires additional settings for the
440     // pipeline to make it compatible.
441     wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAALoadResolveTextureDesc;
442     if (loadMsaaFromResolve && sharedContext->dawnCaps()->loadOpAffectsMSAAPipelines()) {
443         SkASSERT(device.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture));
444         colorTarget.nextInChain = &pipelineMSAALoadResolveTextureDesc;
445         pipelineMSAALoadResolveTextureDesc.enabled = true;
446     }
447 #endif
448 
449     wgpu::FragmentState fragment;
450     // Dawn doesn't allow having a color attachment but without fragment shader, so have to use a
451     // noop fragment shader, if fragment shader is null.
452     fragment.module = hasFragmentSkSL ? std::move(fsModule) : sharedContext->noopFragment();
453     fragment.entryPoint = "main";
454     fragment.constantCount = 0;
455     fragment.constants = nullptr;
456     fragment.targetCount = 1;
457     fragment.targets = &colorTarget;
458     descriptor.fragment = &fragment;
459 
460     // Depth stencil state
461     const auto& depthStencilSettings = step->depthStencilSettings();
462     SkASSERT(depthStencilSettings.fDepthTestEnabled ||
463              depthStencilSettings.fDepthCompareOp == CompareOp::kAlways);
464     wgpu::DepthStencilState depthStencil;
465     if (renderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid()) {
466         wgpu::TextureFormat dsFormat = TextureInfoPriv::Get<DawnTextureInfo>(
467                 renderPassDesc.fDepthStencilAttachment.fTextureInfo).getViewFormat();
468         depthStencil.format =
469                 DawnFormatIsDepthOrStencil(dsFormat) ? dsFormat : wgpu::TextureFormat::Undefined;
470         if (depthStencilSettings.fDepthTestEnabled) {
471             depthStencil.depthWriteEnabled = depthStencilSettings.fDepthWriteEnabled;
472         }
473         depthStencil.depthCompare = compare_op_to_dawn(depthStencilSettings.fDepthCompareOp);
474 
475         // Dawn validation fails if the stencil state is non-default and the
476         // format doesn't have the stencil aspect.
477         if (DawnFormatIsStencil(dsFormat) && depthStencilSettings.fStencilTestEnabled) {
478             depthStencil.stencilFront = stencil_face_to_dawn(depthStencilSettings.fFrontStencil);
479             depthStencil.stencilBack = stencil_face_to_dawn(depthStencilSettings.fBackStencil);
480             depthStencil.stencilReadMask = depthStencilSettings.fFrontStencil.fReadMask;
481             depthStencil.stencilWriteMask = depthStencilSettings.fFrontStencil.fWriteMask;
482         }
483 
484         descriptor.depthStencil = &depthStencil;
485     }
486 
487     // Determine the BindGroupLayouts that will be used to make up the pipeline layout.
488     BindGroupLayouts groupLayouts;
489     // If immutable samplers are used with this pipeline, they must be included in the pipeline
490     // layout and passed in to the pipline constructor for lifetime management.
491     skia_private::TArray<sk_sp<DawnSampler>> immutableSamplers;
492     {
493         SkASSERT(resourceProvider);
494         groupLayouts[0] = resourceProvider->getOrCreateUniformBuffersBindGroupLayout();
495         if (!groupLayouts[0]) {
496             return {};
497         }
498 
499         bool hasFragmentSamplers = hasFragmentSkSL && numTexturesAndSamplers > 0;
500         if (hasFragmentSamplers) {
501             // Check if we can optimize for the common case of a single texture + 1 dynamic sampler
502             if (numTexturesAndSamplers == 2 &&
503                 !(samplerDescArrPtr && samplerDescArrPtr->at(0).isImmutable())) {
504                 groupLayouts[1] =
505                         resourceProvider->getOrCreateSingleTextureSamplerBindGroupLayout();
506             } else {
507                 std::vector<wgpu::BindGroupLayoutEntry> entries(numTexturesAndSamplers);
508 #if !defined(__EMSCRIPTEN__)
509                 // Static sampler layouts are passed into Dawn by address and therefore must stay
510                 // alive until the BindGroupLayoutDescriptor is created. So, store them outside of
511                 // the loop that iterates over each BindGroupLayoutEntry.
512                 skia_private::TArray<wgpu::StaticSamplerBindingLayout> staticSamplerLayouts;
513 
514                 // Note that the number of samplers is equivalent to numTexturesAndSamplers / 2. So,
515                 // a sampler's index within any container that only pertains to sampler information
516                 // (as in, excludes textures) is equivalent to 1/2 of that sampler's binding index
517                 // within the BindGroupLayout. Assert that we have analyzed the appropriate number
518                 // of samplers by equating samplerDescArr size to sampler quantity.
519                 SkASSERT(samplerDescArrPtr && samplerDescArr.size() == numTexturesAndSamplers / 2);
520 #endif
521 
522                 for (int i = 0; i < numTexturesAndSamplers;) {
523                     entries[i].binding = i;
524                     entries[i].visibility = wgpu::ShaderStage::Fragment;
525 #if !defined(__EMSCRIPTEN__)
526                     // Index of sampler information = 1/2 of cumulative texture and sampler index.
527                     // If we have a non-default-initialized SamplerDesc at that index,
528                     // fetch an immutable sampler that matches that description to include in the
529                     // pipeline layout.
530                     const SamplerDesc& samplerDesc = samplerDescArr.at(i/2);
531                     if (samplerDesc.isImmutable()) {
532                         sk_sp<Sampler> immutableSampler =
533                                 resourceProvider->findOrCreateCompatibleSampler(samplerDesc);
534                         if (!immutableSampler) {
535                             SKGPU_LOG_E("Failed to find/create immutable sampler for pipeline");
536                             return {};
537                         }
538                         sk_sp<DawnSampler> dawnImmutableSampler = sk_ref_sp<DawnSampler>(
539                                 static_cast<DawnSampler*>(immutableSampler.get()));
540                         SkASSERT(dawnImmutableSampler);
541 
542                         wgpu::StaticSamplerBindingLayout& immutableSamplerBinding =
543                                 staticSamplerLayouts.emplace_back();
544                         immutableSamplerBinding.sampler = dawnImmutableSampler->dawnSampler();
545                         // Static samplers sample from the subsequent texture in the BindGroupLayout
546                         immutableSamplerBinding.sampledTextureBinding = i + 1;
547 
548                         immutableSamplers.push_back(std::move(dawnImmutableSampler));
549                         entries[i].nextInChain = &immutableSamplerBinding;
550                     } else {
551 #endif
552                         entries[i].sampler.type = wgpu::SamplerBindingType::Filtering;
553 #if !defined(__EMSCRIPTEN__)
554                     }
555 #endif
556                     ++i;
557 
558                     entries[i].binding = i;
559                     entries[i].visibility = wgpu::ShaderStage::Fragment;
560                     entries[i].texture.sampleType = wgpu::TextureSampleType::Float;
561                     entries[i].texture.viewDimension = wgpu::TextureViewDimension::e2D;
562                     entries[i].texture.multisampled = false;
563                     ++i;
564                 }
565 
566                 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
567                 if (sharedContext->caps()->setBackendLabels()) {
568                     groupLayoutDesc.label = shaderInfo->vsLabel().c_str();
569                 }
570                 groupLayoutDesc.entryCount = entries.size();
571                 groupLayoutDesc.entries = entries.data();
572                 groupLayouts[1] = device.CreateBindGroupLayout(&groupLayoutDesc);
573             }
574             if (!groupLayouts[1]) {
575                 return {};
576             }
577         }
578 
579         wgpu::PipelineLayoutDescriptor layoutDesc;
580         if (sharedContext->caps()->setBackendLabels()) {
581             layoutDesc.label = shaderInfo->fsLabel().c_str();
582         }
583         layoutDesc.bindGroupLayoutCount =
584             hasFragmentSamplers ? groupLayouts.size() : groupLayouts.size() - 1;
585         layoutDesc.bindGroupLayouts = groupLayouts.data();
586         auto layout = device.CreatePipelineLayout(&layoutDesc);
587         if (!layout) {
588             return {};
589         }
590         descriptor.layout = std::move(layout);
591     }
592 
593     // Vertex state
594     std::array<wgpu::VertexBufferLayout, kNumVertexBuffers> vertexBufferLayouts;
595     // Vertex buffer layout
596     std::vector<wgpu::VertexAttribute> vertexAttributes;
597     {
598         auto arrayStride = create_vertex_attributes(step->vertexAttributes(),
599                                                     0,
600                                                     &vertexAttributes);
601         auto& layout = vertexBufferLayouts[kVertexBufferIndex];
602         if (arrayStride) {
603             layout.arrayStride = arrayStride;
604             layout.stepMode = wgpu::VertexStepMode::Vertex;
605             layout.attributeCount = vertexAttributes.size();
606             layout.attributes = vertexAttributes.data();
607         } else {
608             layout.arrayStride = 0;
609 #if defined(__EMSCRIPTEN__)
610             layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
611 #else
612             layout.stepMode = wgpu::VertexStepMode::Undefined;
613 #endif
614             layout.attributeCount = 0;
615             layout.attributes = nullptr;
616         }
617     }
618 
619     // Instance buffer layout
620     std::vector<wgpu::VertexAttribute> instanceAttributes;
621     {
622         auto arrayStride = create_vertex_attributes(step->instanceAttributes(),
623                                                     step->vertexAttributes().size(),
624                                                     &instanceAttributes);
625         auto& layout = vertexBufferLayouts[kInstanceBufferIndex];
626         if (arrayStride) {
627             layout.arrayStride = arrayStride;
628             layout.stepMode = wgpu::VertexStepMode::Instance;
629             layout.attributeCount = instanceAttributes.size();
630             layout.attributes = instanceAttributes.data();
631         } else {
632             layout.arrayStride = 0;
633 #if defined(__EMSCRIPTEN__)
634             layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
635 #else
636             layout.stepMode = wgpu::VertexStepMode::Undefined;
637 #endif
638             layout.attributeCount = 0;
639             layout.attributes = nullptr;
640         }
641     }
642 
643     auto& vertex = descriptor.vertex;
644     vertex.module = std::move(vsModule);
645     vertex.entryPoint = "main";
646     vertex.constantCount = 0;
647     vertex.constants = nullptr;
648     vertex.bufferCount = vertexBufferLayouts.size();
649     vertex.buffers = vertexBufferLayouts.data();
650 
651     // Other state
652     descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
653     descriptor.primitive.cullMode = wgpu::CullMode::None;
654     switch (step->primitiveType()) {
655         case PrimitiveType::kTriangles:
656             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
657             break;
658         case PrimitiveType::kTriangleStrip:
659             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
660             descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint16;
661             break;
662         case PrimitiveType::kPoints:
663             descriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
664             break;
665     }
666 
667     // Multisampled state
668     descriptor.multisample.count = renderPassDesc.fSampleCount;
669     descriptor.multisample.mask = 0xFFFFFFFF;
670     descriptor.multisample.alphaToCoverageEnabled = false;
671 
672     const bool forPrecompilation =
673             SkToBool(pipelineCreationFlags & PipelineCreationFlags::kForPrecompilation);
674     // For Dawn, we want Precompilation to happen synchronously
675     const bool useAsync = caps.useAsyncPipelineCreation() && !forPrecompilation;
676 
677     auto asyncCreation = std::make_unique<AsyncPipelineCreation>(pipelineKey);
678 #if SK_HISTOGRAMS_ENABLED
679     asyncCreation->fStartTime = skgpu::StdSteadyClock::now();
680     asyncCreation->fFromPrecompile = forPrecompilation;
681     asyncCreation->fAsynchronous = useAsync;
682 #endif
683 
684     if (useAsync) {
685 #if defined(__EMSCRIPTEN__)
686         // We shouldn't use CreateRenderPipelineAsync in wasm.
687         SKGPU_LOG_F("CreateRenderPipelineAsync shouldn't be used in WASM");
688 #else
689         asyncCreation->fFuture = device.CreateRenderPipelineAsync(
690                 &descriptor,
691                 wgpu::CallbackMode::WaitAnyOnly,
692                 [asyncCreationPtr = asyncCreation.get()](wgpu::CreatePipelineAsyncStatus status,
693                                                          wgpu::RenderPipeline pipeline,
694                                                          wgpu::StringView message) {
695                     if (status != wgpu::CreatePipelineAsyncStatus::Success) {
696                         SKGPU_LOG_E("Failed to create render pipeline (%d): %.*s",
697                                     static_cast<int>(status),
698                                     static_cast<int>(message.length),
699                                     message.data);
700                         // invalidate AsyncPipelineCreation pointer to signal that this pipeline has
701                         // failed.
702                         asyncCreationPtr->fRenderPipeline = nullptr;
703                     } else {
704                         asyncCreationPtr->fRenderPipeline = std::move(pipeline);
705                     }
706 
707                     asyncCreationPtr->fFinished = true;
708 
709                     log_pipeline_creation(asyncCreationPtr);
710                 });
711 #endif
712     } else {
713         std::optional<DawnErrorChecker> errorChecker;
714         if (sharedContext->dawnCaps()->allowScopedErrorChecks()) {
715             errorChecker.emplace(sharedContext);
716         }
717         asyncCreation->fRenderPipeline = device.CreateRenderPipeline(&descriptor);
718         asyncCreation->fFinished = true;
719 
720         if (errorChecker.has_value() && errorChecker->popErrorScopes() != DawnErrorType::kNoError) {
721             asyncCreation->fRenderPipeline = nullptr;
722         }
723 
724         log_pipeline_creation(asyncCreation.get());
725     }
726 
727     PipelineInfo pipelineInfo{ *shaderInfo, pipelineCreationFlags,
728                                pipelineKey.hash(), compilationID };
729 #if defined(GPU_TEST_UTILS)
730     pipelineInfo.fNativeVertexShader = std::move(vsCode);
731     pipelineInfo.fNativeFragmentShader = std::move(fsCode);
732 #endif
733 
734     return sk_sp<DawnGraphicsPipeline>(
735             new DawnGraphicsPipeline(sharedContext,
736                                      pipelineInfo,
737                                      std::move(asyncCreation),
738                                      std::move(groupLayouts),
739                                      step->primitiveType(),
740                                      depthStencilSettings.fStencilReferenceValue,
741                                      std::move(immutableSamplers)));
742 }
743 
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)744 DawnGraphicsPipeline::DawnGraphicsPipeline(
745         const skgpu::graphite::SharedContext* sharedContext,
746         const PipelineInfo& pipelineInfo,
747         std::unique_ptr<AsyncPipelineCreation> asyncCreationInfo,
748         BindGroupLayouts groupLayouts,
749         PrimitiveType primitiveType,
750         uint32_t refValue,
751         skia_private::TArray<sk_sp<DawnSampler>> immutableSamplers)
752     : GraphicsPipeline(sharedContext, pipelineInfo)
753     , fAsyncPipelineCreation(std::move(asyncCreationInfo))
754     , fGroupLayouts(std::move(groupLayouts))
755     , fPrimitiveType(primitiveType)
756     , fStencilReferenceValue(refValue)
757     , fImmutableSamplers(std::move(immutableSamplers)) {}
758 
~DawnGraphicsPipeline()759 DawnGraphicsPipeline::~DawnGraphicsPipeline() {
760     this->freeGpuData();
761 }
762 
freeGpuData()763 void DawnGraphicsPipeline::freeGpuData() {
764     // Wait for async creation to finish before we can destroy this object.
765     (void)this->dawnRenderPipeline();
766     fAsyncPipelineCreation = nullptr;
767 }
768 
dawnRenderPipeline() const769 const wgpu::RenderPipeline& DawnGraphicsPipeline::dawnRenderPipeline() const {
770     if (!fAsyncPipelineCreation) {
771         static const wgpu::RenderPipeline kNullPipeline = nullptr;
772         return kNullPipeline;
773     }
774     if (fAsyncPipelineCreation->fFinished) {
775         return fAsyncPipelineCreation->fRenderPipeline;
776     }
777 #if defined(__EMSCRIPTEN__)
778     // We shouldn't use CreateRenderPipelineAsync in wasm.
779     SKGPU_LOG_F("CreateRenderPipelineAsync shouldn't be used in WASM");
780 #else
781 
782 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
783     TRACE_EVENT_INSTANT2("skia.gpu",
784                          TRACE_STR_STATIC("WaitBeginN"),
785                          TRACE_EVENT_SCOPE_THREAD,
786                          "key", fAsyncPipelineCreation->fKey.hash(),
787                          "compilationID", this->getPipelineInfo().fCompilationID);
788 #endif
789 
790     wgpu::FutureWaitInfo waitInfo{};
791     waitInfo.future = fAsyncPipelineCreation->fFuture;
792     const auto& instance = static_cast<const DawnSharedContext*>(sharedContext())
793                                    ->device()
794                                    .GetAdapter()
795                                    .GetInstance();
796 
797     [[maybe_unused]] auto status =
798             instance.WaitAny(1, &waitInfo, /*timeoutNS=*/std::numeric_limits<uint64_t>::max());
799     SkASSERT(status == wgpu::WaitStatus::Success);
800     SkASSERT(waitInfo.completed);
801 
802 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
803     TRACE_EVENT_INSTANT2("skia.gpu",
804                          TRACE_STR_STATIC("WaitEndN"),
805                          TRACE_EVENT_SCOPE_THREAD,
806                          "key", fAsyncPipelineCreation->fKey.hash(),
807                          "compilationID", this->getPipelineInfo().fCompilationID);
808 #endif
809 
810 #endif
811 
812     return fAsyncPipelineCreation->fRenderPipeline;
813 }
814 
815 } // namespace skgpu::graphite
816