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/DawnCaps.h"
9
10 #include <algorithm>
11 #include <string>
12
13 #include "include/core/SkTextureCompressionType.h"
14 #include "include/gpu/graphite/ContextOptions.h"
15 #include "include/gpu/graphite/TextureInfo.h"
16 #include "include/gpu/graphite/dawn/DawnBackendContext.h"
17 #include "src/gpu/SwizzlePriv.h"
18 #include "src/gpu/graphite/ComputePipelineDesc.h"
19 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
20 #include "src/gpu/graphite/GraphiteResourceKey.h"
21 #include "src/gpu/graphite/RenderPassDesc.h"
22 #include "src/gpu/graphite/RendererProvider.h"
23 #include "src/gpu/graphite/ResourceTypes.h"
24 #include "src/gpu/graphite/UniformManager.h"
25 #include "src/gpu/graphite/dawn/DawnGraphicsPipeline.h"
26 #include "src/gpu/graphite/dawn/DawnGraphiteTypesPriv.h"
27 #include "src/gpu/graphite/dawn/DawnGraphiteUtilsPriv.h"
28 #include "src/gpu/graphite/dawn/DawnUtilsPriv.h"
29 #include "src/sksl/SkSLUtil.h"
30
31 #if defined(__EMSCRIPTEN__)
32 #include <emscripten/version.h>
33 #endif
34
35 namespace {
36
get_pipeline_domain()37 skgpu::UniqueKey::Domain get_pipeline_domain() {
38 static const skgpu::UniqueKey::Domain kDawnGraphicsPipelineDomain =
39 skgpu::UniqueKey::GenerateDomain();
40
41 return kDawnGraphicsPipelineDomain;
42 }
43
44 // These are all the valid wgpu::TextureFormat that we currently support in Skia.
45 // They are roughly ordered from most frequently used to least to improve lookup times in arrays.
46 static constexpr wgpu::TextureFormat kFormats[] = {
47 wgpu::TextureFormat::RGBA8Unorm,
48 wgpu::TextureFormat::R8Unorm,
49 #if !defined(__EMSCRIPTEN__)
50 wgpu::TextureFormat::R16Unorm,
51 #endif
52 wgpu::TextureFormat::BGRA8Unorm,
53 wgpu::TextureFormat::RGBA16Float,
54 wgpu::TextureFormat::R16Float,
55 wgpu::TextureFormat::RG8Unorm,
56 #if !defined(__EMSCRIPTEN__)
57 wgpu::TextureFormat::RG16Unorm,
58 #endif
59 wgpu::TextureFormat::RGB10A2Unorm,
60 wgpu::TextureFormat::RG16Float,
61
62 wgpu::TextureFormat::Stencil8,
63 wgpu::TextureFormat::Depth16Unorm,
64 wgpu::TextureFormat::Depth32Float,
65 wgpu::TextureFormat::Depth24PlusStencil8,
66
67 wgpu::TextureFormat::BC1RGBAUnorm,
68 wgpu::TextureFormat::ETC2RGB8Unorm,
69
70 #if !defined(__EMSCRIPTEN__)
71 wgpu::TextureFormat::External,
72 #endif
73 };
74
75 #if !defined(__EMSCRIPTEN__)
IsMultiplanarFormat(wgpu::TextureFormat format)76 bool IsMultiplanarFormat(wgpu::TextureFormat format) {
77 switch (format) {
78 case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
79 case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
80 case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
81 return true;
82 default:
83 return false;
84 }
85 }
86 #endif
87 } // anonymous namespace
88
89 namespace skgpu::graphite {
90
DawnCaps(const DawnBackendContext & backendContext,const ContextOptions & options)91 DawnCaps::DawnCaps(const DawnBackendContext& backendContext, const ContextOptions& options)
92 : Caps() {
93 this->initCaps(backendContext, options);
94 this->initShaderCaps(backendContext.fDevice);
95 this->initFormatTable(backendContext.fDevice);
96 this->finishInitialization(options);
97 }
98
99 DawnCaps::~DawnCaps() = default;
100
channelMask(const TextureInfo & info) const101 uint32_t DawnCaps::channelMask(const TextureInfo& info) const {
102 return DawnFormatChannels(TextureInfos::GetDawnTextureSpec(info).getViewFormat());
103 }
104
onIsTexturable(const TextureInfo & info) const105 bool DawnCaps::onIsTexturable(const TextureInfo& info) const {
106 if (!info.isValid()) {
107 return false;
108 }
109
110 const DawnTextureSpec spec = TextureInfos::GetDawnTextureSpec(info);
111
112 if (!(spec.fUsage & wgpu::TextureUsage::TextureBinding)) {
113 return false;
114 }
115
116 #if !defined(__EMSCRIPTEN__)
117 switch (spec.fFormat) {
118 case wgpu::TextureFormat::R8BG8Biplanar420Unorm: {
119 if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
120 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
121 return false;
122 }
123 if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
124 spec.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
125 return false;
126 }
127 break;
128 }
129 case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm: {
130 if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
131 spec.getViewFormat() != wgpu::TextureFormat::R16Unorm) {
132 return false;
133 }
134 if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
135 spec.getViewFormat() != wgpu::TextureFormat::RG16Unorm) {
136 return false;
137 }
138 break;
139 }
140 case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm: {
141 if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
142 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
143 return false;
144 }
145 if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
146 spec.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
147 return false;
148 }
149 if (spec.fAspect == wgpu::TextureAspect::Plane2Only &&
150 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
151 return false;
152 }
153 break;
154 }
155 default:
156 break;
157 }
158 #endif
159
160 return this->isTexturable(spec.getViewFormat());
161 }
162
isTexturable(wgpu::TextureFormat format) const163 bool DawnCaps::isTexturable(wgpu::TextureFormat format) const {
164 const FormatInfo& formatInfo = this->getFormatInfo(format);
165 return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags);
166 }
167
isRenderable(const TextureInfo & info) const168 bool DawnCaps::isRenderable(const TextureInfo& info) const {
169 const DawnTextureSpec spec = TextureInfos::GetDawnTextureSpec(info);
170
171 return info.isValid() && (spec.fUsage & wgpu::TextureUsage::RenderAttachment) &&
172 this->isRenderable(spec.getViewFormat(), info.numSamples());
173 }
174
isStorage(const TextureInfo & info) const175 bool DawnCaps::isStorage(const TextureInfo& info) const {
176 if (!info.isValid()) {
177 return false;
178 }
179 const DawnTextureSpec spec = TextureInfos::GetDawnTextureSpec(info);
180 if (!(spec.fUsage & wgpu::TextureUsage::StorageBinding)) {
181 return false;
182 }
183 const FormatInfo& formatInfo = this->getFormatInfo(spec.getViewFormat());
184 return info.numSamples() == 1 && SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags);
185 }
186
maxRenderTargetSampleCount(wgpu::TextureFormat format) const187 uint32_t DawnCaps::maxRenderTargetSampleCount(wgpu::TextureFormat format) const {
188 const FormatInfo& formatInfo = this->getFormatInfo(format);
189 if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) {
190 return 0;
191 }
192 if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) {
193 return 8;
194 } else {
195 return 1;
196 }
197 }
198
isRenderable(wgpu::TextureFormat format,uint32_t sampleCount) const199 bool DawnCaps::isRenderable(wgpu::TextureFormat format, uint32_t sampleCount) const {
200 return sampleCount <= this->maxRenderTargetSampleCount(format);
201 }
202
getDefaultSampledTextureInfo(SkColorType colorType,Mipmapped mipmapped,Protected,Renderable renderable) const203 TextureInfo DawnCaps::getDefaultSampledTextureInfo(SkColorType colorType,
204 Mipmapped mipmapped,
205 Protected,
206 Renderable renderable) const {
207 wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
208 wgpu::TextureUsage::CopyDst |
209 wgpu::TextureUsage::CopySrc;
210 if (renderable == Renderable::kYes) {
211 usage |= wgpu::TextureUsage::RenderAttachment;
212 }
213
214 wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
215 if (format == wgpu::TextureFormat::Undefined) {
216 return {};
217 }
218
219 DawnTextureInfo info;
220 info.fSampleCount = 1;
221 info.fMipmapped = mipmapped;
222 info.fFormat = format;
223 info.fViewFormat = format;
224 info.fUsage = usage;
225
226 return TextureInfos::MakeDawn(info);
227 }
228
getTextureInfoForSampledCopy(const TextureInfo & textureInfo,Mipmapped mipmapped) const229 TextureInfo DawnCaps::getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
230 Mipmapped mipmapped) const {
231 DawnTextureInfo info;
232 if (!TextureInfos::GetDawnTextureInfo(textureInfo, &info)) {
233 return {};
234 }
235
236 info.fSampleCount = 1;
237 info.fMipmapped = mipmapped;
238 info.fUsage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst |
239 wgpu::TextureUsage::CopySrc;
240
241 return TextureInfos::MakeDawn(info);
242 }
243
244 namespace {
format_from_compression(SkTextureCompressionType compression)245 wgpu::TextureFormat format_from_compression(SkTextureCompressionType compression) {
246 switch (compression) {
247 case SkTextureCompressionType::kETC2_RGB8_UNORM:
248 return wgpu::TextureFormat::ETC2RGB8Unorm;
249 case SkTextureCompressionType::kBC1_RGBA8_UNORM:
250 return wgpu::TextureFormat::BC1RGBAUnorm;
251 default:
252 return wgpu::TextureFormat::Undefined;
253 }
254 }
255 }
256
getDefaultCompressedTextureInfo(SkTextureCompressionType compression,Mipmapped mipmapped,Protected) const257 TextureInfo DawnCaps::getDefaultCompressedTextureInfo(SkTextureCompressionType compression,
258 Mipmapped mipmapped,
259 Protected) const {
260 wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
261 wgpu::TextureUsage::CopyDst |
262 wgpu::TextureUsage::CopySrc;
263
264 wgpu::TextureFormat format = format_from_compression(compression);
265 if (format == wgpu::TextureFormat::Undefined) {
266 return {};
267 }
268
269 DawnTextureInfo info;
270 info.fSampleCount = 1;
271 info.fMipmapped = mipmapped;
272 info.fFormat = format;
273 info.fViewFormat = format;
274 info.fUsage = usage;
275
276 return TextureInfos::MakeDawn(info);
277 }
278
getDefaultMSAATextureInfo(const TextureInfo & singleSampledInfo,Discardable discardable) const279 TextureInfo DawnCaps::getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
280 Discardable discardable) const {
281 if (fDefaultMSAASamples <= 1) {
282 return {};
283 }
284 const DawnTextureSpec singleSpec = TextureInfos::GetDawnTextureSpec(singleSampledInfo);
285
286 DawnTextureInfo info;
287 info.fSampleCount = fDefaultMSAASamples;
288 info.fMipmapped = Mipmapped::kNo;
289 info.fFormat = singleSpec.fFormat;
290 info.fViewFormat = singleSpec.fFormat;
291 info.fUsage = wgpu::TextureUsage::RenderAttachment;
292
293 if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None &&
294 discardable == Discardable::kYes) {
295 info.fUsage |= fSupportedTransientAttachmentUsage;
296 }
297
298 return TextureInfos::MakeDawn(info);
299 }
300
getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags> depthStencilType,uint32_t sampleCount,Protected) const301 TextureInfo DawnCaps::getDefaultDepthStencilTextureInfo(
302 SkEnumBitMask<DepthStencilFlags> depthStencilType,
303 uint32_t sampleCount,
304 Protected) const {
305 DawnTextureInfo info;
306 info.fSampleCount = sampleCount;
307 info.fMipmapped = Mipmapped::kNo;
308 info.fFormat = DawnDepthStencilFlagsToFormat(depthStencilType);
309 info.fViewFormat = info.fFormat;
310 info.fUsage = wgpu::TextureUsage::RenderAttachment;
311
312 if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None) {
313 info.fUsage |= fSupportedTransientAttachmentUsage;
314 }
315
316 return TextureInfos::MakeDawn(info);
317 }
318
getDefaultStorageTextureInfo(SkColorType colorType) const319 TextureInfo DawnCaps::getDefaultStorageTextureInfo(SkColorType colorType) const {
320 wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
321 if (format == wgpu::TextureFormat::Undefined) {
322 SkDebugf("colorType=%d is not supported\n", static_cast<int>(colorType));
323 return {};
324 }
325
326 const FormatInfo& formatInfo = this->getFormatInfo(format);
327 if (!SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags)) {
328 return {};
329 }
330
331 wgpu::TextureUsage usage = wgpu::TextureUsage::StorageBinding |
332 wgpu::TextureUsage::TextureBinding |
333 wgpu::TextureUsage::CopySrc;
334 DawnTextureInfo info;
335 info.fSampleCount = 1;
336 info.fMipmapped = Mipmapped::kNo;
337 info.fFormat = format;
338 info.fViewFormat = format;
339 info.fUsage = usage;
340
341 return TextureInfos::MakeDawn(info);
342 }
343
getDepthAttachmentDimensions(const TextureInfo & textureInfo,const SkISize colorAttachmentDimensions) const344 SkISize DawnCaps::getDepthAttachmentDimensions(const TextureInfo& textureInfo,
345 const SkISize colorAttachmentDimensions) const {
346 #if !defined(__EMSCRIPTEN__)
347 // For multiplanar textures, texture->textureInfo() uses the format of planes instead of
348 // textures (R8, R8G8, vs R8BG8Biplanar420Unorm), so we have to query texture format from
349 // wgpu::Texture object, and then use it reconstruct the full dimensions.
350 const auto dawnTextureSpec = TextureInfos::GetDawnTextureSpec(textureInfo);
351 wgpu::TextureFormat format = dawnTextureSpec.fFormat;
352 if (IsMultiplanarFormat(format) && dawnTextureSpec.fAspect == wgpu::TextureAspect::Plane1Only) {
353 // Dawn requires depth attachment to match the size of Y plane (texture size).
354 return SkISize::Make(colorAttachmentDimensions.width() * 2,
355 colorAttachmentDimensions.height() * 2);
356 }
357 #endif
358
359 return colorAttachmentDimensions;
360 }
361
getColorTypeInfo(SkColorType colorType,const TextureInfo & textureInfo) const362 const Caps::ColorTypeInfo* DawnCaps::getColorTypeInfo(SkColorType colorType,
363 const TextureInfo& textureInfo) const {
364 auto dawnFormat = TextureInfos::GetDawnTextureSpec(textureInfo).getViewFormat();
365 if (dawnFormat == wgpu::TextureFormat::Undefined) {
366 SkASSERT(false);
367 return nullptr;
368 }
369
370 const FormatInfo& info = this->getFormatInfo(dawnFormat);
371 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
372 const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
373 if (ctInfo.fColorType == colorType) {
374 return &ctInfo;
375 }
376 }
377
378 return nullptr;
379 }
380
supportsWritePixels(const TextureInfo & textureInfo) const381 bool DawnCaps::supportsWritePixels(const TextureInfo& textureInfo) const {
382 const auto spec = TextureInfos::GetDawnTextureSpec(textureInfo);
383 return spec.fUsage & wgpu::TextureUsage::CopyDst;
384 }
385
supportsReadPixels(const TextureInfo & textureInfo) const386 bool DawnCaps::supportsReadPixels(const TextureInfo& textureInfo) const {
387 const auto spec = TextureInfos::GetDawnTextureSpec(textureInfo);
388 return spec.fUsage & wgpu::TextureUsage::CopySrc;
389 }
390
supportedWritePixelsColorType(SkColorType dstColorType,const TextureInfo & dstTextureInfo,SkColorType srcColorType) const391 std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedWritePixelsColorType(
392 SkColorType dstColorType,
393 const TextureInfo& dstTextureInfo,
394 SkColorType srcColorType) const {
395 return {dstColorType, false};
396 }
397
supportedReadPixelsColorType(SkColorType srcColorType,const TextureInfo & srcTextureInfo,SkColorType dstColorType) const398 std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedReadPixelsColorType(
399 SkColorType srcColorType,
400 const TextureInfo& srcTextureInfo,
401 SkColorType dstColorType) const {
402 auto dawnFormat = getFormatFromColorType(srcColorType);
403 const FormatInfo& info = this->getFormatInfo(dawnFormat);
404 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
405 const auto& ctInfo = info.fColorTypeInfos[i];
406 if (ctInfo.fColorType == srcColorType) {
407 return {srcColorType, false};
408 }
409 }
410 return {kUnknown_SkColorType, false};
411 }
412
initCaps(const DawnBackendContext & backendContext,const ContextOptions & options)413 void DawnCaps::initCaps(const DawnBackendContext& backendContext, const ContextOptions& options) {
414 // GetAdapter() is not available in WASM and there's no way to get AdapterInfo off of
415 // the WGPUDevice directly.
416 #if !defined(__EMSCRIPTEN__)
417 wgpu::AdapterInfo info;
418 backendContext.fDevice.GetAdapter().GetInfo(&info);
419
420 #if defined(GPU_TEST_UTILS)
421 this->setDeviceName(std::string(info.device));
422 #endif
423 #endif // defined(__EMSCRIPTEN__)
424
425 wgpu::SupportedLimits limits;
426 #if defined(__EMSCRIPTEN__)
427 // TODO(crbug.com/42241199): Update Emscripten path with when webgpu.h in Emscripten is updated.
428 [[maybe_unused]] bool limitsSucceeded = backendContext.fDevice.GetLimits(&limits);
429 #if (__EMSCRIPTEN_major__ > 3 || (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ > 1) || \
430 (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ > 50))
431 // In Emscripten this always "fails" until
432 // https://github.com/emscripten-core/emscripten/pull/20808, which was first included in 3.1.51.
433 SkASSERT(limitsSucceeded);
434 #endif
435 #else
436 wgpu::DawnTexelCopyBufferRowAlignmentLimits alignmentLimits{};
437 if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
438 limits.nextInChain = &alignmentLimits;
439 }
440 [[maybe_unused]] wgpu::Status status = backendContext.fDevice.GetLimits(&limits);
441 SkASSERT(status == wgpu::Status::Success);
442 #endif
443
444 fMaxTextureSize = limits.limits.maxTextureDimension2D;
445
446 fRequiredTransferBufferAlignment = 4;
447 fRequiredUniformBufferAlignment = limits.limits.minUniformBufferOffsetAlignment;
448 fRequiredStorageBufferAlignment = limits.limits.minStorageBufferOffsetAlignment;
449
450 // Dawn requires 256 bytes per row alignment for buffer texture copies.
451 fTextureDataRowBytesAlignment = 256;
452 #if !defined(__EMSCRIPTEN__)
453 // If the device supports the DawnTexelCopyBufferRowAlignment feature, the alignment can be
454 // queried from its limits.
455 if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
456 fTextureDataRowBytesAlignment = alignmentLimits.minTexelCopyBufferRowAlignment;
457 }
458 #endif
459
460 fResourceBindingReqs.fUniformBufferLayout = Layout::kStd140;
461 // The WGSL generator assumes tightly packed std430 layout for SSBOs which is also the default
462 // for all types outside the uniform address space in WGSL.
463 fResourceBindingReqs.fStorageBufferLayout = Layout::kStd430;
464 fResourceBindingReqs.fSeparateTextureAndSamplerBinding = true;
465
466 fResourceBindingReqs.fIntrinsicBufferBinding =
467 DawnGraphicsPipeline::kIntrinsicUniformBufferIndex;
468 fResourceBindingReqs.fRenderStepBufferBinding =
469 DawnGraphicsPipeline::kRenderStepUniformBufferIndex;
470 fResourceBindingReqs.fPaintParamsBufferBinding = DawnGraphicsPipeline::kPaintUniformBufferIndex;
471 fResourceBindingReqs.fGradientBufferBinding = DawnGraphicsPipeline::kGradientBufferIndex;
472
473 #if !defined(__EMSCRIPTEN__)
474 // TODO(b/344963958): SSBOs contribute to OOB shader memory access and dawn device loss on
475 // Android. Once the problem is fixed SSBOs can be enabled again.
476 // TODO(dawn:388028942): In compat mode, the number of storage buffers in vertex stage could be
477 // zero on some devices. We currently use SSBO in vertex shaders so disabling it entirely in
478 // this case. There is a bug in D3D11's backend where it sets number of SSBOs in vertex shader
479 // to non-zero in compat mode. Once that bug is fixed, and a new limit is used for readonly
480 // SSBOs, we can enable this again.
481 fStorageBufferSupport = info.backendType != wgpu::BackendType::OpenGL &&
482 info.backendType != wgpu::BackendType::OpenGLES &&
483 info.backendType != wgpu::BackendType::Vulkan &&
484 info.compatibilityMode == false;
485 #else
486 // WASM doesn't provide a way to query the backend, so can't tell if we are on a backend that
487 // needs to have SSBOs disabled. Pessimistically assume we could be. Once the above conditions
488 // go away in Dawn-native, then we can assume SSBOs are always supported in pure WebGPU too.
489 fStorageBufferSupport = false;
490 #endif
491
492 fDrawBufferCanBeMapped = false;
493
494 fComputeSupport = true;
495
496 // TODO: support clamp to border.
497 fClampToBorderSupport = false;
498
499 #if defined(GPU_TEST_UTILS)
500 fDrawBufferCanBeMappedForReadback = false;
501 #endif
502
503 #if defined(__EMSCRIPTEN__)
504 // For wasm, we use async map.
505 fBufferMapsAreAsync = true;
506 #else
507 // For Dawn native, we use direct mapping.
508 fBufferMapsAreAsync = false;
509 fDrawBufferCanBeMapped =
510 backendContext.fDevice.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages);
511
512 fMSAARenderToSingleSampledSupport =
513 backendContext.fDevice.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled);
514
515 if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TransientAttachments)) {
516 fSupportedTransientAttachmentUsage = wgpu::TextureUsage::TransientAttachment;
517 }
518 if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
519 fSupportedResolveTextureLoadOp = wgpu::LoadOp::ExpandResolveTexture;
520 }
521 fSupportsPartialLoadResolve =
522 backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture);
523 #endif
524
525 if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TimestampQuery)) {
526 // Native Dawn has an API for writing timestamps on command buffers. WebGPU only supports
527 // begin and end timestamps on render and compute passes.
528 #if !defined(__EMSCRIPTEN__)
529 // TODO(b/42240559): On Apple silicon, the timer queries don't have the correct dependencies
530 // to measure all the encoders that the start/end commands encapsulate in the commandbuffer.
531 // We would prefer to keep this API as it lets us measure our texture uploads. If either
532 // this is fixed in Dawn, we can unconditionally take this approach for dawn-native; or
533 // the WebGPU API can hopefully be extended to capture blit passes.
534 fSupportsCommandBufferTimestamps = info.backendType != wgpu::BackendType::Metal;
535 #endif
536
537 // The emscripten C/C++ interface before 3.1.48 for timestamp query writes on render and
538 // compute passes is different than on current emsdk. The older API isn't correctly
539 // translated to the current JS WebGPU API in emsdk. So we require 3.1.48+.
540 #if !defined(__EMSCRIPTEN__) \
541 || (__EMSCRIPTEN_major__ > 3) \
542 || (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ > 1) \
543 || (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 48)
544 fSupportedGpuStats |= GpuStatsFlags::kElapsedTime;
545 #endif
546 }
547
548 if (!backendContext.fTick) {
549 fAllowCpuSync = false;
550 // This seems paradoxical. However, if we use the async pipeline creation methods (e.g
551 // Device::CreateRenderPipelineAsync) then we may have to synchronize before a submit that
552 // uses the pipeline. If we use the methods that look synchronous (e.g.
553 // Device::CreateRenderPipeline) they actually operate asynchronously on WebGPU but the
554 // browser becomes responsible for synchronizing when we call submit.
555 fUseAsyncPipelineCreation = false;
556
557 // The implementation busy waits after popping.
558 fAllowScopedErrorChecks = false;
559 }
560
561 fFullCompressedUploadSizeMustAlignToBlockDims = true;
562 }
563
initShaderCaps(const wgpu::Device & device)564 void DawnCaps::initShaderCaps(const wgpu::Device& device) {
565 SkSL::ShaderCaps* shaderCaps = fShaderCaps.get();
566
567 // WGSL does not support infinities regardless of hardware support. There are discussions around
568 // enabling it using an extension in the future.
569 shaderCaps->fInfinitySupport = false;
570
571 // WGSL supports shader derivatives in the fragment shader
572 shaderCaps->fShaderDerivativeSupport = true;
573
574 #if !defined(__EMSCRIPTEN__)
575 if (device.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
576 shaderCaps->fDualSourceBlendingSupport = true;
577 }
578 if (device.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
579 shaderCaps->fFBFetchSupport = true;
580 }
581 #endif
582 }
583
initFormatTable(const wgpu::Device & device)584 void DawnCaps::initFormatTable(const wgpu::Device& device) {
585 FormatInfo* info;
586 // Format: RGBA8Unorm
587 {
588 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA8Unorm)];
589 info->fFlags = FormatInfo::kAllFlags;
590 info->fColorTypeInfoCount = 2;
591 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
592 int ctIdx = 0;
593 // Format: RGBA8Unorm, Surface: kRGBA_8888
594 {
595 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
596 ctInfo.fColorType = kRGBA_8888_SkColorType;
597 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
598 }
599 // Format: RGBA8Unorm, Surface: kRGB_888x
600 {
601 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
602 ctInfo.fColorType = kRGB_888x_SkColorType;
603 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
604 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
605 }
606 }
607
608 // Format: R8Unorm
609 {
610 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R8Unorm)];
611 #if !defined(__EMSCRIPTEN__)
612 info->fFlags = FormatInfo::kAllFlags;
613 if (!device.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
614 info->fFlags &= ~FormatInfo::kStorage_Flag;
615 }
616 #else
617 info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
618 #endif
619 info->fColorTypeInfoCount = 3;
620 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
621 int ctIdx = 0;
622 // Format: R8Unorm, Surface: kR8_unorm
623 {
624 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
625 ctInfo.fColorType = kR8_unorm_SkColorType;
626 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
627 }
628 // Format: R8Unorm, Surface: kAlpha_8
629 {
630 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
631 ctInfo.fColorType = kAlpha_8_SkColorType;
632 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
633 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
634 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
635 }
636 // Format: R8Unorm, Surface: kGray_8
637 {
638 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
639 ctInfo.fColorType = kGray_8_SkColorType;
640 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
641 ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
642 }
643 }
644
645 #if !defined(__EMSCRIPTEN__)
646 const bool supportUnorm16 = device.HasFeature(wgpu::FeatureName::Unorm16TextureFormats);
647 // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
648 // Format: R16Unorm
649 {
650 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Unorm)];
651 if (supportUnorm16) {
652 info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
653 info->fColorTypeInfoCount = 1;
654 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
655 int ctIdx = 0;
656 // Format: R16Unorm, Surface: kA16_unorm
657 {
658 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
659 ctInfo.fColorType = kA16_unorm_SkColorType;
660 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
661 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
662 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
663 }
664 }
665 }
666 #endif
667
668 // Format: BGRA8Unorm
669 {
670 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BGRA8Unorm)];
671 info->fFlags = FormatInfo::kAllFlags;
672 info->fColorTypeInfoCount = 2;
673 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
674 int ctIdx = 0;
675 // Format: BGRA8Unorm, Surface: kBGRA_8888
676 {
677 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
678 ctInfo.fColorType = kBGRA_8888_SkColorType;
679 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
680 }
681 // Format: BGRA8Unorm, Surface: kRGB_888x
682 {
683 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
684 ctInfo.fColorType = kRGB_888x_SkColorType;
685 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
686 }
687 }
688
689 // Format: RGBA16Float
690 {
691 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA16Float)];
692 info->fFlags = FormatInfo::kAllFlags;
693 info->fColorTypeInfoCount = 2;
694 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
695 int ctIdx = 0;
696 // Format: RGBA16Float, Surface: RGBA_F16
697 {
698 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
699 ctInfo.fColorType = kRGBA_F16_SkColorType;
700 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
701 }
702 // Format: RGBA16Float, Surface: RGB_F16F16F16x
703 {
704 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
705 ctInfo.fColorType = kRGBA_F16_SkColorType;
706 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
707 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
708 }
709 }
710
711 // Format: R16Float
712 {
713 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Float)];
714 info->fFlags = FormatInfo::kAllFlags;
715 info->fColorTypeInfoCount = 1;
716 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
717 int ctIdx = 0;
718 // Format: R16Float, Surface: kA16_float
719 {
720 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
721 ctInfo.fColorType = kA16_float_SkColorType;
722 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
723 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
724 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
725 }
726 }
727
728 // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
729 // Format: RG8Unorm
730 {
731 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG8Unorm)];
732 info->fFlags = FormatInfo::kAllFlags;
733 info->fColorTypeInfoCount = 1;
734 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
735 int ctIdx = 0;
736 // Format: RG8Unorm, Surface: kR8G8_unorm
737 {
738 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
739 ctInfo.fColorType = kR8G8_unorm_SkColorType;
740 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
741 }
742 }
743
744 #if !defined(__EMSCRIPTEN__)
745 // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
746 // Format: RG16Unorm
747 {
748 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Unorm)];
749 if (supportUnorm16) {
750 info->fFlags = FormatInfo::kAllFlags;
751 info->fColorTypeInfoCount = 1;
752 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
753 int ctIdx = 0;
754 // Format: RG16Unorm, Surface: kR16G16_unorm
755 {
756 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
757 ctInfo.fColorType = kR16G16_unorm_SkColorType;
758 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
759 }
760 }
761 }
762 #endif
763
764 // Format: RGB10A2Unorm
765 {
766 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGB10A2Unorm)];
767 info->fFlags = FormatInfo::kAllFlags;
768 info->fColorTypeInfoCount = 2;
769 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
770 int ctIdx = 0;
771 // Format: RGB10A2Unorm, Surface: kRGBA_1010102
772 {
773 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
774 ctInfo.fColorType = kRGBA_1010102_SkColorType;
775 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
776 }
777 // Format: RGB10A2Unorm, Surface: kRGB_101010x
778 {
779 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
780 ctInfo.fColorType = kRGB_101010x_SkColorType;
781 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
782 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
783 }
784 }
785
786 // Format: RG16Float
787 {
788 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Float)];
789 info->fFlags = FormatInfo::kAllFlags;
790 info->fColorTypeInfoCount = 1;
791 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
792 int ctIdx = 0;
793 // Format: RG16Float, Surface: kR16G16_float
794 {
795 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
796 ctInfo.fColorType = kR16G16_float_SkColorType;
797 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
798 }
799 }
800
801 // Format: ETC2RGB8Unorm
802 {
803 if (device.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
804 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::ETC2RGB8Unorm)];
805 info->fFlags = FormatInfo::kTexturable_Flag;
806 info->fColorTypeInfoCount = 1;
807 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
808 int ctIdx = 0;
809 // Format: ETC2RGB8Unorm, Surface: kRGB_888x
810 {
811 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
812 ctInfo.fColorType = kRGB_888x_SkColorType;
813 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
814 }
815 }
816 }
817
818 // Format: BC1RGBAUnorm
819 {
820 if (device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
821 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BC1RGBAUnorm)];
822 info->fFlags = FormatInfo::kTexturable_Flag;
823 info->fColorTypeInfoCount = 1;
824 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
825 int ctIdx = 0;
826 // Format: BC1RGBAUnorm, Surface: kRGBA_8888
827 {
828 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
829 ctInfo.fColorType = kRGBA_8888_SkColorType;
830 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
831 }
832 }
833 }
834
835 /*
836 * Non-color formats
837 */
838
839 // Format: Stencil8
840 {
841 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Stencil8)];
842 info->fFlags = FormatInfo::kMSAA_Flag;
843 info->fColorTypeInfoCount = 0;
844 }
845
846 // Format: Depth16UNorm
847 {
848 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth16Unorm)];
849 info->fFlags = FormatInfo::kMSAA_Flag;
850 info->fColorTypeInfoCount = 0;
851 }
852
853 // Format: Depth32Float
854 {
855 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth32Float)];
856 info->fFlags = FormatInfo::kMSAA_Flag;
857 info->fColorTypeInfoCount = 0;
858 }
859
860 // Format: Depth24PlusStencil8
861 {
862 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth24PlusStencil8)];
863 info->fFlags = FormatInfo::kMSAA_Flag;
864 info->fColorTypeInfoCount = 0;
865 }
866
867 #if !defined(__EMSCRIPTEN__)
868 // Format: External
869 {
870 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::External)];
871 info->fFlags = FormatInfo::kTexturable_Flag;
872 info->fColorTypeInfoCount = 1;
873 info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
874 int ctIdx = 0;
875 // Format: External, Surface: kRGBA_8888
876 {
877 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
878 ctInfo.fColorType = kRGBA_8888_SkColorType;
879 }
880 }
881 #endif
882
883 ////////////////////////////////////////////////////////////////////////////
884 // Map SkColorTypes (used for creating SkSurfaces) to wgpu::TextureFormat.
885 // The order in which the formats are passed into the setColorType function
886 // indicates the priority in selecting which format we use for a given
887 // SkColorType.
888
889 std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, wgpu::TextureFormat::Undefined);
890
891 this->setColorType(kAlpha_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
892 this->setColorType(kRGBA_8888_SkColorType, { wgpu::TextureFormat::RGBA8Unorm });
893 this->setColorType(kRGB_888x_SkColorType,
894 {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::BGRA8Unorm});
895 this->setColorType(kBGRA_8888_SkColorType, { wgpu::TextureFormat::BGRA8Unorm });
896 this->setColorType(kGray_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
897 this->setColorType(kR8_unorm_SkColorType, { wgpu::TextureFormat::R8Unorm });
898 this->setColorType(kRGBA_F16_SkColorType, { wgpu::TextureFormat::RGBA16Float });
899 this->setColorType(kRGB_F16F16F16x_SkColorType, { wgpu::TextureFormat::RGBA16Float });
900 this->setColorType(kA16_float_SkColorType, { wgpu::TextureFormat::R16Float });
901 this->setColorType(kR8G8_unorm_SkColorType, { wgpu::TextureFormat::RG8Unorm });
902 this->setColorType(kRGBA_1010102_SkColorType, { wgpu::TextureFormat::RGB10A2Unorm });
903 this->setColorType(kRGB_101010x_SkColorType, { wgpu::TextureFormat::RGB10A2Unorm });
904 this->setColorType(kR16G16_float_SkColorType, { wgpu::TextureFormat::RG16Float });
905
906 #if !defined(__EMSCRIPTEN__)
907 this->setColorType(kA16_unorm_SkColorType, { wgpu::TextureFormat::R16Unorm });
908 this->setColorType(kR16G16_unorm_SkColorType, { wgpu::TextureFormat::RG16Unorm });
909 #endif
910 }
911
912 // static
GetFormatIndex(wgpu::TextureFormat format)913 size_t DawnCaps::GetFormatIndex(wgpu::TextureFormat format) {
914 for (size_t i = 0; i < std::size(kFormats); ++i) {
915 if (format == kFormats[i]) {
916 return i;
917 }
918 }
919 SkDEBUGFAILF("Unsupported wgpu::TextureFormat: 0x%08X\n", static_cast<uint32_t>(format));
920 return 0;
921 }
922
setColorType(SkColorType colorType,std::initializer_list<wgpu::TextureFormat> formats)923 void DawnCaps::setColorType(SkColorType colorType,
924 std::initializer_list<wgpu::TextureFormat> formats) {
925 static_assert(std::size(kFormats) <= kFormatCount,
926 "Size is not compatible for DawnCaps::fFormatTable and kFormats");
927 int idx = static_cast<int>(colorType);
928 for (auto it = formats.begin(); it != formats.end(); ++it) {
929 const auto& info = this->getFormatInfo(*it);
930 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
931 if (info.fColorTypeInfos[i].fColorType == colorType) {
932 fColorTypeToFormatTable[idx] = *it;
933 return;
934 }
935 }
936 }
937 }
938
939 // Make sure the format table indices will fit into the packed bits, with room to spare for
940 // representing an unused attachment.
941 static constexpr int kFormatBits = 11; // x2 attachments (color & depthStencil formats)
942 static constexpr int kSampleBits = 4; // x2 attachments (color & depthStencil numSamples)
943 static constexpr int kResolveBits = 1;
944 static constexpr int kUnusedAttachmentIndex = (1 << kFormatBits) - 1;
945 static_assert(2*(kFormatBits + kSampleBits) + kResolveBits <= 32);
946 static_assert(std::size(kFormats) <= kUnusedAttachmentIndex);
947
948 static constexpr int kDepthStencilNumSamplesOffset = kResolveBits;
949 static constexpr int kDepthStencilFormatOffset = kDepthStencilNumSamplesOffset + kSampleBits;
950 static constexpr int kColorNumSamplesOffset = kDepthStencilFormatOffset + kFormatBits;
951 static constexpr int kColorFormatOffset = kColorNumSamplesOffset + kSampleBits;
952
953 static constexpr uint32_t kFormatMask = (1 << kFormatBits) - 1;
954 static constexpr uint32_t kNumSamplesMask = (1 << kSampleBits) - 1;
955 static constexpr uint32_t kResolveMask = (1 << kResolveBits) - 1;
956
getRenderPassDescKeyForPipeline(const RenderPassDesc & renderPassDesc) const957 uint32_t DawnCaps::getRenderPassDescKeyForPipeline(const RenderPassDesc& renderPassDesc) const {
958 const TextureInfo& colorInfo = renderPassDesc.fColorAttachment.fTextureInfo;
959 const TextureInfo& depthStencilInfo = renderPassDesc.fDepthStencilAttachment.fTextureInfo;
960 // The color attachment should be valid; the depth-stencil attachment may not be if it's not
961 // being used.
962 SkASSERT(colorInfo.isValid());
963
964 // Use format indices instead of WGPUTextureFormat values since they can be larger than 16 bits.
965 uint32_t colorFormatIndex =
966 GetFormatIndex(TextureInfos::GetDawnTextureSpec(colorInfo).getViewFormat());
967 uint32_t depthStencilFormatIndex =
968 depthStencilInfo.isValid()
969 ? GetFormatIndex(
970 TextureInfos::GetDawnTextureSpec(depthStencilInfo).getViewFormat())
971 : kUnusedAttachmentIndex;
972
973 // Note: if Dawn supports ExpandResolveTexture load op and the render pass uses it to load
974 // the resolve texture, a render pipeline will need to be created with
975 // wgpu::ColorTargetStateExpandResolveTextureDawn chained struct in order to be compatible.
976 // Hence a render pipeline created for a render pass using ExpandResolveTexture load op will
977 // be different from the one created for a render pass not using that load op.
978 // So we need to include a bit flag to differentiate the two kinds of pipelines.
979 // Also avoid returning a cached pipeline that is not compatible with the render pass using
980 // ExpandResolveTexture load op and vice versa.
981 const bool shouldIncludeLoadResolveAttachmentBit = this->resolveTextureLoadOp().has_value();
982 uint32_t loadResolveAttachmentKey = 0;
983 if (shouldIncludeLoadResolveAttachmentBit &&
984 renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
985 renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
986 loadResolveAttachmentKey = 1;
987 }
988
989 SkASSERT(colorFormatIndex < (1 << kFormatBits));
990 SkASSERT(colorInfo.numSamples() < (1 << kSampleBits));
991 SkASSERT(depthStencilFormatIndex < (1 << kFormatBits));
992 SkASSERT(depthStencilInfo.numSamples() < (1 << kSampleBits));
993 SkASSERT(loadResolveAttachmentKey < (1 << kResolveBits));
994
995 return (colorFormatIndex << kColorFormatOffset) |
996 (colorInfo.numSamples() << kColorNumSamplesOffset) |
997 (depthStencilFormatIndex << kDepthStencilFormatOffset) |
998 (depthStencilInfo.numSamples() << kDepthStencilNumSamplesOffset) |
999 loadResolveAttachmentKey;
1000 }
1001
1002 static constexpr int kDawnGraphicsPipelineKeyData32Count = 4;
1003
makeGraphicsPipelineKey(const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc) const1004 UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
1005 const RenderPassDesc& renderPassDesc) const {
1006 UniqueKey pipelineKey;
1007 {
1008 // 4 uint32_t's (render step id, paint id, uint32 RenderPassDesc, uint16 write swizzle key)
1009 UniqueKey::Builder builder(&pipelineKey, get_pipeline_domain(),
1010 kDawnGraphicsPipelineKeyData32Count, "DawnGraphicsPipeline");
1011 // Add GraphicsPipelineDesc key.
1012 builder[0] = pipelineDesc.renderStepID();
1013 builder[1] = pipelineDesc.paintParamsID().asUInt();
1014
1015 // Add RenderPassDesc key and write swizzle (which is separate from the RenderPassDescKey
1016 // because it is applied in the program writing to the target, and is not actually part of
1017 // the underlying GPU render pass config).
1018 builder[2] = this->getRenderPassDescKeyForPipeline(renderPassDesc);
1019 builder[3] = renderPassDesc.fWriteSwizzle.asKey();
1020 builder.finish();
1021 }
1022
1023 return pipelineKey;
1024 }
1025
extractGraphicsDescs(const UniqueKey & key,GraphicsPipelineDesc * pipelineDesc,RenderPassDesc * renderPassDesc,const RendererProvider * rendererProvider) const1026 bool DawnCaps::extractGraphicsDescs(const UniqueKey& key,
1027 GraphicsPipelineDesc* pipelineDesc,
1028 RenderPassDesc* renderPassDesc,
1029 const RendererProvider* rendererProvider) const {
1030 SkASSERT(key.domain() == get_pipeline_domain());
1031 SkASSERT(key.dataSize() == 4 * kDawnGraphicsPipelineKeyData32Count);
1032
1033 const uint32_t* rawKeyData = key.data();
1034
1035 const RenderStep* renderStep = rendererProvider->lookup(rawKeyData[0]);
1036 *pipelineDesc = GraphicsPipelineDesc(renderStep, UniquePaintParamsID(rawKeyData[1]));
1037 SkASSERT(renderStep->performsShading() == pipelineDesc->paintParamsID().isValid());
1038
1039 uint32_t renderpassDescBits = rawKeyData[2];
1040 uint32_t colorFormatIndex = (renderpassDescBits >> kColorFormatOffset) & kFormatMask;
1041 SkASSERT(colorFormatIndex < std::size(kFormats));
1042
1043 DawnTextureInfo dawnInfo;
1044 dawnInfo.fFormat = dawnInfo.fViewFormat = kFormats[colorFormatIndex];
1045 dawnInfo.fSampleCount = 1;
1046 dawnInfo.fMipmapped = skgpu::Mipmapped::kNo;
1047 dawnInfo.fUsage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
1048
1049 uint32_t colorSampleCount = (renderpassDescBits >> kColorNumSamplesOffset) & kNumSamplesMask;
1050 bool requiresMSAA = colorSampleCount > 1;
1051
1052 SkEnumBitMask<DepthStencilFlags> dsFlags = DepthStencilFlags::kNone;
1053
1054 uint32_t depthStencilFormatIndex =
1055 (renderpassDescBits >> kDepthStencilFormatOffset) & kFormatMask;
1056 if (depthStencilFormatIndex != kUnusedAttachmentIndex) {
1057 SkASSERT(depthStencilFormatIndex < std::size(kFormats));
1058 wgpu::TextureFormat dsFormat = kFormats[depthStencilFormatIndex];
1059 if (DawnFormatIsDepth(dsFormat)) {
1060 dsFlags |= DepthStencilFlags::kDepth;
1061 }
1062 if (DawnFormatIsStencil(dsFormat)) {
1063 dsFlags |= DepthStencilFlags::kStencil;
1064 }
1065 }
1066 SkDEBUGCODE(uint32_t dsSampleCount =
1067 (renderpassDescBits >> kDepthStencilNumSamplesOffset) & kNumSamplesMask;)
1068 SkASSERT(colorSampleCount == dsSampleCount);
1069
1070 LoadOp loadOp = LoadOp::kClear;
1071 if (renderpassDescBits & kResolveMask) {
1072 // This bit should only be set if Dawn supports ExpandResolveTexture load op
1073 SkASSERT(this->resolveTextureLoadOp().has_value());
1074 loadOp = LoadOp::kLoad;
1075 }
1076
1077 Swizzle writeSwizzle = SwizzleCtorAccessor::Make(rawKeyData[3]);
1078
1079 *renderPassDesc = RenderPassDesc::Make(this,
1080 TextureInfos::MakeDawn(dawnInfo),
1081 loadOp,
1082 StoreOp::kStore,
1083 dsFlags,
1084 /* clearColor= */ { .0f, .0f, .0f, .0f },
1085 requiresMSAA,
1086 writeSwizzle);
1087
1088 return true;
1089 }
1090
makeComputePipelineKey(const ComputePipelineDesc & pipelineDesc) const1091 UniqueKey DawnCaps::makeComputePipelineKey(const ComputePipelineDesc& pipelineDesc) const {
1092 UniqueKey pipelineKey;
1093 {
1094 static const skgpu::UniqueKey::Domain kComputePipelineDomain = UniqueKey::GenerateDomain();
1095 // The key is made up of a single uint32_t corresponding to the compute step ID.
1096 UniqueKey::Builder builder(&pipelineKey, kComputePipelineDomain, 1, "ComputePipeline");
1097 builder[0] = pipelineDesc.computeStep()->uniqueID();
1098
1099 // TODO(b/240615224): The local work group size should factor into the key here since it is
1100 // specified in the shader text on Dawn/SPIR-V. This is not a problem right now since
1101 // ComputeSteps don't vary their workgroup size dynamically.
1102
1103 builder.finish();
1104 }
1105 return pipelineKey;
1106 }
1107
1108 #if !defined(__EMSCRIPTEN__)
1109 namespace {
1110 using namespace ycbcrUtils;
1111
non_format_info_as_uint32(const wgpu::YCbCrVkDescriptor & desc)1112 uint32_t non_format_info_as_uint32(const wgpu::YCbCrVkDescriptor& desc) {
1113 static_assert(kComponentAShift + kComponentBits <= 32);
1114 SkASSERT(desc.vkYCbCrModel < (1u << kYcbcrModelBits ));
1115 SkASSERT(desc.vkYCbCrRange < (1u << kYcbcrRangeBits ));
1116 SkASSERT(desc.vkXChromaOffset < (1u << kXChromaOffsetBits ));
1117 SkASSERT(desc.vkYChromaOffset < (1u << kYChromaOffsetBits ));
1118 SkASSERT(static_cast<uint32_t>(desc.vkChromaFilter) < (1u << kChromaFilterBits ));
1119 SkASSERT(desc.vkComponentSwizzleRed < (1u << kComponentBits ));
1120 SkASSERT(desc.vkComponentSwizzleGreen < (1u << kComponentBits ));
1121 SkASSERT(desc.vkComponentSwizzleBlue < (1u << kComponentBits ));
1122 SkASSERT(desc.vkComponentSwizzleAlpha < (1u << kComponentBits ));
1123 SkASSERT(static_cast<uint32_t>(desc.forceExplicitReconstruction)
1124 < (1u << kForceExplicitReconBits));
1125
1126 return (((uint32_t)(DawnDescriptorUsesExternalFormat(desc)) << kUsesExternalFormatShift) |
1127 ((uint32_t)(desc.vkYCbCrModel ) << kYcbcrModelShift ) |
1128 ((uint32_t)(desc.vkYCbCrRange ) << kYcbcrRangeShift ) |
1129 ((uint32_t)(desc.vkXChromaOffset ) << kXChromaOffsetShift ) |
1130 ((uint32_t)(desc.vkYChromaOffset ) << kYChromaOffsetShift ) |
1131 ((uint32_t)(desc.vkChromaFilter ) << kChromaFilterShift ) |
1132 ((uint32_t)(desc.forceExplicitReconstruction ) << kForceExplicitReconShift) |
1133 ((uint32_t)(desc.vkComponentSwizzleRed ) << kComponentRShift ) |
1134 ((uint32_t)(desc.vkComponentSwizzleGreen ) << kComponentGShift ) |
1135 ((uint32_t)(desc.vkComponentSwizzleBlue ) << kComponentBShift ) |
1136 ((uint32_t)(desc.vkComponentSwizzleAlpha ) << kComponentAShift ));
1137 }
1138 } // anonymous
1139 #endif
1140
getImmutableSamplerInfo(const TextureProxy * proxy) const1141 ImmutableSamplerInfo DawnCaps::getImmutableSamplerInfo(const TextureProxy* proxy) const {
1142 #if !defined(__EMSCRIPTEN__)
1143 if (proxy) {
1144 const wgpu::YCbCrVkDescriptor& ycbcrConversionInfo =
1145 TextureInfos::GetDawnTextureSpec(proxy->textureInfo()).fYcbcrVkDescriptor;
1146
1147 if (ycbcrUtils::DawnDescriptorIsValid(ycbcrConversionInfo)) {
1148 ImmutableSamplerInfo immutableSamplerInfo;
1149 // A vkFormat of 0 indicates we are using an external format rather than a known one.
1150 immutableSamplerInfo.fFormat = (ycbcrConversionInfo.vkFormat == 0)
1151 ? ycbcrConversionInfo.externalFormat
1152 : ycbcrConversionInfo.vkFormat;
1153 immutableSamplerInfo.fNonFormatYcbcrConversionInfo =
1154 non_format_info_as_uint32(ycbcrConversionInfo);
1155 return immutableSamplerInfo;
1156 }
1157 }
1158 #endif
1159
1160 // If the proxy is null or the YCbCr conversion for that proxy is invalid, then return a
1161 // default ImmutableSamplerInfo struct.
1162 return {};
1163 }
1164
buildKeyForTexture(SkISize dimensions,const TextureInfo & info,ResourceType type,Shareable shareable,GraphiteResourceKey * key) const1165 void DawnCaps::buildKeyForTexture(SkISize dimensions,
1166 const TextureInfo& info,
1167 ResourceType type,
1168 Shareable shareable,
1169 GraphiteResourceKey* key) const {
1170 const DawnTextureSpec dawnSpec = TextureInfos::GetDawnTextureSpec(info);
1171
1172 SkASSERT(!dimensions.isEmpty());
1173
1174 SkASSERT(dawnSpec.getViewFormat() != wgpu::TextureFormat::Undefined);
1175 uint32_t formatKey = static_cast<uint32_t>(dawnSpec.getViewFormat());
1176
1177 uint32_t samplesKey = SamplesToKey(info.numSamples());
1178 // We don't have to key the number of mip levels because it is inherit in the combination of
1179 // isMipped and dimensions.
1180 bool isMipped = info.mipmapped() == Mipmapped::kYes;
1181
1182 // Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
1183 // amounts in the asserts must be less than or equal to 32.
1184 SkASSERT(samplesKey < (1u << 3)); // sample key is first 3 bits
1185 SkASSERT(static_cast<uint32_t>(isMipped) < (1u << 1)); // isMapped is 4th bit
1186 SkASSERT(static_cast<uint32_t>(dawnSpec.fUsage) < (1u << 28)); // usage is remaining 28 bits
1187
1188 // We need two uint32_ts for dimensions, 1 for format, and 1 for the rest of the key;
1189 int num32DataCnt = 2 + 1 + 1;
1190 bool hasYcbcrInfo = false;
1191 #if !defined(__EMSCRIPTEN__)
1192 // If we are using ycbcr texture/sampling, more key information is needed.
1193 if ((hasYcbcrInfo = ycbcrUtils::DawnDescriptorIsValid(dawnSpec.fYcbcrVkDescriptor))) {
1194 num32DataCnt += ycbcrUtils::DawnDescriptorUsesExternalFormat(dawnSpec.fYcbcrVkDescriptor)
1195 ? SamplerDesc::kInt32sNeededExternalFormat
1196 : SamplerDesc::kInt32sNeededKnownFormat;
1197 }
1198 #endif
1199 GraphiteResourceKey::Builder builder(key, type, num32DataCnt, shareable);
1200
1201 builder[0] = dimensions.width();
1202 builder[1] = dimensions.height();
1203 builder[2] = formatKey;
1204 builder[3] = (samplesKey << 0) |
1205 (static_cast<uint32_t>(isMipped) << 3) |
1206 (static_cast<uint32_t>(dawnSpec.fUsage) << 4);
1207
1208 #if !defined(__EMSCRIPTEN__)
1209 if (hasYcbcrInfo) {
1210 builder[4] = non_format_info_as_uint32(dawnSpec.fYcbcrVkDescriptor);
1211 // Even though we already have formatKey appended to the texture key, we still need to add
1212 // fYcbcrVkDescriptor's vkFormat or externalFormat. The latter two are distinct from
1213 // dawnSpec's wgpu::TextureFormat.
1214 if (!ycbcrUtils::DawnDescriptorUsesExternalFormat(dawnSpec.fYcbcrVkDescriptor)) {
1215 builder[5] = dawnSpec.fYcbcrVkDescriptor.vkFormat;
1216 } else {
1217 builder[5] = (uint32_t)(dawnSpec.fYcbcrVkDescriptor.externalFormat >> 32);
1218 builder[6] = (uint32_t)dawnSpec.fYcbcrVkDescriptor.externalFormat;
1219 }
1220 }
1221 #endif
1222 }
1223
makeSamplerKey(const SamplerDesc & samplerDesc) const1224 GraphiteResourceKey DawnCaps::makeSamplerKey(const SamplerDesc& samplerDesc) const {
1225 GraphiteResourceKey samplerKey;
1226 const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan();
1227 static const ResourceType kSamplerType = GraphiteResourceKey::GenerateResourceType();
1228 // Non-format ycbcr and sampler information are guaranteed to fit within one uint32, so the size
1229 // of the returned span accurately captures the quantity of uint32s needed whether the sampler
1230 // is immutable or not.
1231 GraphiteResourceKey::Builder builder(&samplerKey, kSamplerType, samplerData.size(),
1232 Shareable::kYes);
1233
1234 for (size_t i = 0; i < samplerData.size(); i++) {
1235 builder[i] = samplerData[i];
1236 }
1237 builder.finish();
1238 return samplerKey;
1239 }
1240
1241 } // namespace skgpu::graphite
1242