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