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