• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/dawn/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