• 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 
12 #include "include/core/SkTextureCompressionType.h"
13 #include "include/gpu/graphite/ContextOptions.h"
14 #include "include/gpu/graphite/TextureInfo.h"
15 #include "include/gpu/graphite/dawn/DawnBackendContext.h"
16 #include "src/gpu/graphite/ComputePipelineDesc.h"
17 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
18 #include "src/gpu/graphite/GraphiteResourceKey.h"
19 #include "src/gpu/graphite/RenderPassDesc.h"
20 #include "src/gpu/graphite/ResourceTypes.h"
21 #include "src/gpu/graphite/UniformManager.h"
22 #include "src/gpu/graphite/dawn/DawnGraphiteUtilsPriv.h"
23 #include "src/gpu/graphite/dawn/DawnUtilsPriv.h"
24 #include "src/sksl/SkSLUtil.h"
25 
26 namespace {
27 
28 // These are all the valid wgpu::TextureFormat that we currently support in Skia.
29 // They are roughly ordered from most frequently used to least to improve lookup times in arrays.
30 static constexpr wgpu::TextureFormat kFormats[skgpu::graphite::DawnCaps::kFormatCnt] = {
31         wgpu::TextureFormat::RGBA8Unorm,
32         wgpu::TextureFormat::R8Unorm,
33 #if !defined(__EMSCRIPTEN__)
34         wgpu::TextureFormat::R16Unorm,
35 #endif
36         wgpu::TextureFormat::BGRA8Unorm,
37         wgpu::TextureFormat::RGBA16Float,
38         wgpu::TextureFormat::R16Float,
39         wgpu::TextureFormat::RG8Unorm,
40 #if !defined(__EMSCRIPTEN__)
41         wgpu::TextureFormat::RG16Unorm,
42 #endif
43         wgpu::TextureFormat::RGB10A2Unorm,
44         wgpu::TextureFormat::RG16Float,
45 
46         wgpu::TextureFormat::Stencil8,
47         wgpu::TextureFormat::Depth16Unorm,
48         wgpu::TextureFormat::Depth32Float,
49         wgpu::TextureFormat::Depth24PlusStencil8,
50 
51         wgpu::TextureFormat::BC1RGBAUnorm,
52         wgpu::TextureFormat::ETC2RGB8Unorm,
53 
54 #if !defined(__EMSCRIPTEN__)
55         wgpu::TextureFormat::External,
56 #endif
57 
58         wgpu::TextureFormat::Undefined,
59 };
60 
61 #if !defined(__EMSCRIPTEN__)
IsMultiplanarFormat(wgpu::TextureFormat format)62 bool IsMultiplanarFormat(wgpu::TextureFormat format) {
63     switch (format) {
64         case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
65         case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
66         case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
67             return true;
68         default:
69             return false;
70     }
71 }
72 #endif
73 }  // anonymous namespace
74 
75 namespace skgpu::graphite {
76 
DawnCaps(const DawnBackendContext & backendContext,const ContextOptions & options)77 DawnCaps::DawnCaps(const DawnBackendContext& backendContext, const ContextOptions& options)
78     : Caps() {
79     this->initCaps(backendContext, options);
80     this->initShaderCaps(backendContext.fDevice);
81     this->initFormatTable(backendContext.fDevice);
82     this->finishInitialization(options);
83 }
84 
85 DawnCaps::~DawnCaps() = default;
86 
channelMask(const TextureInfo & info) const87 uint32_t DawnCaps::channelMask(const TextureInfo& info) const {
88     return DawnFormatChannels(info.dawnTextureSpec().getViewFormat());
89 }
90 
onIsTexturable(const TextureInfo & info) const91 bool DawnCaps::onIsTexturable(const TextureInfo& info) const {
92     if (!info.isValid()) {
93         return false;
94     }
95 
96     const auto& spec = info.dawnTextureSpec();
97 
98     if (!(spec.fUsage & wgpu::TextureUsage::TextureBinding)) {
99         return false;
100     }
101 
102 #if !defined(__EMSCRIPTEN__)
103     switch (spec.fFormat) {
104         case wgpu::TextureFormat::R8BG8Biplanar420Unorm: {
105             if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
106                 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
107                 return false;
108             }
109             if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
110                 spec.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
111                 return false;
112             }
113             break;
114         }
115         case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm: {
116             if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
117                 spec.getViewFormat() != wgpu::TextureFormat::R16Unorm) {
118                 return false;
119             }
120             if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
121                 spec.getViewFormat() != wgpu::TextureFormat::RG16Unorm) {
122                 return false;
123             }
124             break;
125         }
126         case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm: {
127             if (spec.fAspect == wgpu::TextureAspect::Plane0Only &&
128                 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
129                 return false;
130             }
131             if (spec.fAspect == wgpu::TextureAspect::Plane1Only &&
132                 spec.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
133                 return false;
134             }
135             if (spec.fAspect == wgpu::TextureAspect::Plane2Only &&
136                 spec.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
137                 return false;
138             }
139             break;
140         }
141         default:
142             break;
143     }
144 #endif
145 
146     return this->isTexturable(info.dawnTextureSpec().getViewFormat());
147 }
148 
isTexturable(wgpu::TextureFormat format) const149 bool DawnCaps::isTexturable(wgpu::TextureFormat format) const {
150     const FormatInfo& formatInfo = this->getFormatInfo(format);
151     return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags);
152 }
153 
isRenderable(const TextureInfo & info) const154 bool DawnCaps::isRenderable(const TextureInfo& info) const {
155     return info.isValid() &&
156            (info.dawnTextureSpec().fUsage & wgpu::TextureUsage::RenderAttachment) &&
157            this->isRenderable(info.dawnTextureSpec().getViewFormat(), info.numSamples());
158 }
159 
isStorage(const TextureInfo & info) const160 bool DawnCaps::isStorage(const TextureInfo& info) const {
161     if (!info.isValid()) {
162         return false;
163     }
164     if (!(info.dawnTextureSpec().fUsage & wgpu::TextureUsage::StorageBinding)) {
165         return false;
166     }
167     const FormatInfo& formatInfo = this->getFormatInfo(info.dawnTextureSpec().getViewFormat());
168     return info.numSamples() == 1 && SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags);
169 }
170 
maxRenderTargetSampleCount(wgpu::TextureFormat format) const171 uint32_t DawnCaps::maxRenderTargetSampleCount(wgpu::TextureFormat format) const {
172     const FormatInfo& formatInfo = this->getFormatInfo(format);
173     if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) {
174         return 0;
175     }
176     if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) {
177         return 8;
178     } else {
179         return 1;
180     }
181 }
182 
isRenderable(wgpu::TextureFormat format,uint32_t sampleCount) const183 bool DawnCaps::isRenderable(wgpu::TextureFormat format, uint32_t sampleCount) const {
184     return sampleCount <= this->maxRenderTargetSampleCount(format);
185 }
186 
getDefaultSampledTextureInfo(SkColorType colorType,Mipmapped mipmapped,Protected,Renderable renderable) const187 TextureInfo DawnCaps::getDefaultSampledTextureInfo(SkColorType colorType,
188                                                    Mipmapped mipmapped,
189                                                    Protected,
190                                                    Renderable renderable) const {
191     wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
192                                wgpu::TextureUsage::CopyDst |
193                                wgpu::TextureUsage::CopySrc;
194     if (renderable == Renderable::kYes) {
195         usage |= wgpu::TextureUsage::RenderAttachment;
196     }
197 
198     wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
199     if (format == wgpu::TextureFormat::Undefined) {
200         return {};
201     }
202 
203     DawnTextureInfo info;
204     info.fSampleCount = 1;
205     info.fMipmapped = mipmapped;
206     info.fFormat = format;
207     info.fViewFormat = format;
208     info.fUsage = usage;
209 
210     return info;
211 }
212 
getTextureInfoForSampledCopy(const TextureInfo & textureInfo,Mipmapped mipmapped) const213 TextureInfo DawnCaps::getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
214                                                    Mipmapped mipmapped) const {
215     DawnTextureInfo info;
216     if (!textureInfo.getDawnTextureInfo(&info)) {
217         return {};
218     }
219 
220     info.fSampleCount = 1;
221     info.fMipmapped = mipmapped;
222     info.fUsage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst |
223                   wgpu::TextureUsage::CopySrc;
224 
225     return info;
226 }
227 
228 namespace {
format_from_compression(SkTextureCompressionType compression)229 wgpu::TextureFormat format_from_compression(SkTextureCompressionType compression) {
230     switch (compression) {
231         case SkTextureCompressionType::kETC2_RGB8_UNORM:
232             return wgpu::TextureFormat::ETC2RGB8Unorm;
233         case SkTextureCompressionType::kBC1_RGBA8_UNORM:
234             return wgpu::TextureFormat::BC1RGBAUnorm;
235         default:
236             return wgpu::TextureFormat::Undefined;
237     }
238 }
239 }
240 
getDefaultCompressedTextureInfo(SkTextureCompressionType compression,Mipmapped mipmapped,Protected) const241 TextureInfo DawnCaps::getDefaultCompressedTextureInfo(SkTextureCompressionType compression,
242                                                       Mipmapped mipmapped,
243                                                       Protected) const {
244     wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
245                                wgpu::TextureUsage::CopyDst |
246                                wgpu::TextureUsage::CopySrc;
247 
248     wgpu::TextureFormat format = format_from_compression(compression);
249     if (format == wgpu::TextureFormat::Undefined) {
250         return {};
251     }
252 
253     DawnTextureInfo info;
254     info.fSampleCount = 1;
255     info.fMipmapped = mipmapped;
256     info.fFormat = format;
257     info.fViewFormat = format;
258     info.fUsage = usage;
259 
260     return info;
261 }
262 
getDefaultMSAATextureInfo(const TextureInfo & singleSampledInfo,Discardable discardable) const263 TextureInfo DawnCaps::getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
264                                                 Discardable discardable) const {
265     if (fDefaultMSAASamples <= 1) {
266         return {};
267     }
268     const DawnTextureSpec& singleSpec = singleSampledInfo.dawnTextureSpec();
269 
270     DawnTextureInfo info;
271     info.fSampleCount = fDefaultMSAASamples;
272     info.fMipmapped   = Mipmapped::kNo;
273     info.fFormat      = singleSpec.fFormat;
274     info.fViewFormat = singleSpec.fFormat;
275     info.fUsage       = wgpu::TextureUsage::RenderAttachment;
276 
277     if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None &&
278         discardable == Discardable::kYes) {
279         info.fUsage |= fSupportedTransientAttachmentUsage;
280     }
281 
282     return info;
283 }
284 
getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags> depthStencilType,uint32_t sampleCount,Protected) const285 TextureInfo DawnCaps::getDefaultDepthStencilTextureInfo(
286     SkEnumBitMask<DepthStencilFlags> depthStencilType,
287     uint32_t sampleCount,
288     Protected) const {
289     DawnTextureInfo info;
290     info.fSampleCount = sampleCount;
291     info.fMipmapped   = Mipmapped::kNo;
292     info.fFormat      = DawnDepthStencilFlagsToFormat(depthStencilType);
293     info.fViewFormat = info.fFormat;
294     info.fUsage       = wgpu::TextureUsage::RenderAttachment;
295 
296     if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None) {
297         info.fUsage |= fSupportedTransientAttachmentUsage;
298     }
299 
300     return info;
301 }
302 
getDefaultStorageTextureInfo(SkColorType colorType) const303 TextureInfo DawnCaps::getDefaultStorageTextureInfo(SkColorType colorType) const {
304     wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
305     if (format == wgpu::TextureFormat::Undefined) {
306         SkDebugf("colorType=%d is not supported\n", static_cast<int>(colorType));
307         return {};
308     }
309 
310     const FormatInfo& formatInfo = this->getFormatInfo(format);
311     if (!SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags)) {
312         return {};
313     }
314 
315     wgpu::TextureUsage usage = wgpu::TextureUsage::StorageBinding |
316                                wgpu::TextureUsage::TextureBinding |
317                                wgpu::TextureUsage::CopySrc;
318     DawnTextureInfo info;
319     info.fSampleCount = 1;
320     info.fMipmapped = Mipmapped::kNo;
321     info.fFormat = format;
322     info.fViewFormat = format;
323     info.fUsage = usage;
324 
325     return info;
326 }
327 
getDepthAttachmentDimensions(const TextureInfo & textureInfo,const SkISize colorAttachmentDimensions) const328 SkISize DawnCaps::getDepthAttachmentDimensions(const TextureInfo& textureInfo,
329                                                const SkISize colorAttachmentDimensions) const {
330 #if !defined(__EMSCRIPTEN__)
331     // For multiplanar textures, texture->textureInfo() uses the format of planes instead of
332     // textures (R8, R8G8, vs R8BG8Biplanar420Unorm), so we have to query texture format from
333     // wgpu::Texture object, and then use it reconstruct the full dimensions.
334     const auto& dawnTextureSpec = textureInfo.dawnTextureSpec();
335     wgpu::TextureFormat format = dawnTextureSpec.fFormat;
336     if (IsMultiplanarFormat(format) && dawnTextureSpec.fAspect == wgpu::TextureAspect::Plane1Only) {
337         // Dawn requires depth attachment to match the size of Y plane (texture size).
338         return SkISize::Make(colorAttachmentDimensions.width() * 2,
339                              colorAttachmentDimensions.height() * 2);
340     }
341 #endif
342 
343     return colorAttachmentDimensions;
344 }
345 
getColorTypeInfo(SkColorType colorType,const TextureInfo & textureInfo) const346 const Caps::ColorTypeInfo* DawnCaps::getColorTypeInfo(SkColorType colorType,
347                                                       const TextureInfo& textureInfo) const {
348     auto dawnFormat = textureInfo.dawnTextureSpec().getViewFormat();
349     if (dawnFormat == wgpu::TextureFormat::Undefined) {
350         SkASSERT(false);
351         return nullptr;
352     }
353 
354     const FormatInfo& info = this->getFormatInfo(dawnFormat);
355     for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
356         const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
357         if (ctInfo.fColorType == colorType) {
358             return &ctInfo;
359         }
360     }
361 
362     return nullptr;
363 }
364 
supportsWritePixels(const TextureInfo & textureInfo) const365 bool DawnCaps::supportsWritePixels(const TextureInfo& textureInfo) const {
366     const auto& spec = textureInfo.dawnTextureSpec();
367     return spec.fUsage & wgpu::TextureUsage::CopyDst;
368 }
369 
supportsReadPixels(const TextureInfo & textureInfo) const370 bool DawnCaps::supportsReadPixels(const TextureInfo& textureInfo) const {
371     const auto& spec = textureInfo.dawnTextureSpec();
372     return spec.fUsage & wgpu::TextureUsage::CopySrc;
373 }
374 
supportedWritePixelsColorType(SkColorType dstColorType,const TextureInfo & dstTextureInfo,SkColorType srcColorType) const375 std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedWritePixelsColorType(
376         SkColorType dstColorType,
377         const TextureInfo& dstTextureInfo,
378         SkColorType srcColorType) const {
379     return {dstColorType, false};
380 }
381 
supportedReadPixelsColorType(SkColorType srcColorType,const TextureInfo & srcTextureInfo,SkColorType dstColorType) const382 std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedReadPixelsColorType(
383         SkColorType srcColorType,
384         const TextureInfo& srcTextureInfo,
385         SkColorType dstColorType) const {
386     auto dawnFormat = getFormatFromColorType(srcColorType);
387     const FormatInfo& info = this->getFormatInfo(dawnFormat);
388     for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
389         const auto& ctInfo = info.fColorTypeInfos[i];
390         if (ctInfo.fColorType == srcColorType) {
391             return {srcColorType, false};
392         }
393     }
394     return {kUnknown_SkColorType, false};
395 }
396 
initCaps(const DawnBackendContext & backendContext,const ContextOptions & options)397 void DawnCaps::initCaps(const DawnBackendContext& backendContext, const ContextOptions& options) {
398     // GetAdapter() is not available in WASM and there's no way to get AdapterProperties off of
399     // the WGPUDevice directly.
400 #if !defined(__EMSCRIPTEN__)
401     wgpu::AdapterProperties props;
402     backendContext.fDevice.GetAdapter().GetProperties(&props);
403 
404 #if defined(GRAPHITE_TEST_UTILS)
405     this->setDeviceName(props.name);
406 #endif
407 #endif // defined(__EMSCRIPTEN__)
408 
409     wgpu::SupportedLimits limits;
410 
411     [[maybe_unused]] bool limitsSucceeded = backendContext.fDevice.GetLimits(&limits);
412     // In Emscripten this always "fails" until
413     // https://github.com/emscripten-core/emscripten/pull/20808, which was first included in 3.1.51.
414 #if !defined(__EMSCRIPTEN__)                                     || \
415         (__EMSCRIPTEN_major__ >  3                               || \
416         (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ >  1) || \
417         (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ > 50))
418     SkASSERT(limitsSucceeded);
419 #endif
420 
421     fMaxTextureSize = limits.limits.maxTextureDimension2D;
422 
423     fRequiredTransferBufferAlignment = 4;
424     fRequiredUniformBufferAlignment = 256;
425     fRequiredStorageBufferAlignment = fRequiredUniformBufferAlignment;
426 
427     // Dawn requires 256 bytes per row alignment for buffer texture copies.
428     fTextureDataRowBytesAlignment = 256;
429 
430     fResourceBindingReqs.fUniformBufferLayout = Layout::kStd140;
431     // The WGSL generator assumes tightly packed std430 layout for SSBOs which is also the default
432     // for all types outside the uniform address space in WGSL.
433     fResourceBindingReqs.fStorageBufferLayout = Layout::kStd430;
434     fResourceBindingReqs.fSeparateTextureAndSamplerBinding = true;
435 
436 #if !defined(__EMSCRIPTEN__)
437     // TODO(b/318817249): SSBOs trigger FXC compiler failures when attempting to unroll loops
438     fStorageBufferSupport = props.backendType != wgpu::BackendType::D3D11;
439     fStorageBufferPreferred = props.backendType != wgpu::BackendType::D3D11;
440 #else
441     // WASM doesn't provide a way to query the backend, so can't tell if we are on d3d11 or not.
442     // Pessimistically assume we could be. Once b/318817249 is fixed, this can go away and SSBOs
443     // can always be enabled.
444     fStorageBufferSupport = false;
445     fStorageBufferPreferred = false;
446 #endif
447 
448     fDrawBufferCanBeMapped = false;
449 
450     fComputeSupport = true;
451 
452     // TODO: support clamp to border.
453     fClampToBorderSupport = false;
454 
455 #if defined(GRAPHITE_TEST_UTILS)
456     fDrawBufferCanBeMappedForReadback = false;
457 #endif
458 
459 #if defined(__EMSCRIPTEN__)
460     // For wasm, we use async map.
461     fBufferMapsAreAsync = true;
462 #else
463     // For Dawn native, we use direct mapping.
464     fBufferMapsAreAsync = false;
465     fDrawBufferCanBeMapped =
466             backendContext.fDevice.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages);
467 
468     fMSAARenderToSingleSampledSupport =
469             backendContext.fDevice.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled);
470 
471     if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TransientAttachments)) {
472         fSupportedTransientAttachmentUsage = wgpu::TextureUsage::TransientAttachment;
473     }
474     if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
475         fSupportedResolveTextureLoadOp = wgpu::LoadOp::ExpandResolveTexture;
476     }
477 #endif
478 
479     if (!backendContext.fTick) {
480         fAllowCpuSync = false;
481         // This seems paradoxical. However, if we use the async pipeline creation methods (e.g
482         // Device::CreateRenderPipelineAsync) then we may have to synchronize before a submit that
483         // uses the pipeline. If we use the methods that look synchronous (e.g.
484         // Device::CreateRenderPipeline) they actually operate asynchronously on WebGPU but the
485         // browser becomes responsible for synchronizing when we call submit.
486         fUseAsyncPipelineCreation = false;
487 
488         // The implementation busy waits after popping.
489         fAllowScopedErrorChecks = false;
490     }
491 
492     fFullCompressedUploadSizeMustAlignToBlockDims = true;
493 }
494 
initShaderCaps(const wgpu::Device & device)495 void DawnCaps::initShaderCaps(const wgpu::Device& device) {
496     SkSL::ShaderCaps* shaderCaps = fShaderCaps.get();
497 
498     // WGSL does not support infinities regardless of hardware support. There are discussions around
499     // enabling it using an extension in the future.
500     shaderCaps->fInfinitySupport = false;
501 
502     // WGSL supports shader derivatives in the fragment shader
503     shaderCaps->fShaderDerivativeSupport = true;
504 
505 #if !defined(__EMSCRIPTEN__)
506     if (device.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
507         shaderCaps->fDualSourceBlendingSupport = true;
508     }
509     if (device.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
510         shaderCaps->fFBFetchSupport = true;
511     }
512 #endif
513 }
514 
initFormatTable(const wgpu::Device & device)515 void DawnCaps::initFormatTable(const wgpu::Device& device) {
516     FormatInfo* info;
517     // Format: RGBA8Unorm
518     {
519         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA8Unorm)];
520         info->fFlags = FormatInfo::kAllFlags;
521         info->fColorTypeInfoCount = 2;
522         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
523         int ctIdx = 0;
524         // Format: RGBA8Unorm, Surface: kRGBA_8888
525         {
526             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
527             ctInfo.fColorType = kRGBA_8888_SkColorType;
528             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
529         }
530         // Format: RGBA8Unorm, Surface: kRGB_888x
531         {
532             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
533             ctInfo.fColorType = kRGB_888x_SkColorType;
534             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
535             ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
536         }
537     }
538 
539     // Format: R8Unorm
540     {
541         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R8Unorm)];
542 #if !defined(__EMSCRIPTEN__)
543         info->fFlags = FormatInfo::kAllFlags;
544         if (!device.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
545             info->fFlags &= ~FormatInfo::kStorage_Flag;
546         }
547 #else
548         info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
549 #endif
550         info->fColorTypeInfoCount = 3;
551         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
552         int ctIdx = 0;
553         // Format: R8Unorm, Surface: kR8_unorm
554         {
555             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
556             ctInfo.fColorType = kR8_unorm_SkColorType;
557             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
558         }
559         // Format: R8Unorm, Surface: kAlpha_8
560         {
561             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
562             ctInfo.fColorType = kAlpha_8_SkColorType;
563             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
564             ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
565             ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
566         }
567         // Format: R8Unorm, Surface: kGray_8
568         {
569             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
570             ctInfo.fColorType = kGray_8_SkColorType;
571             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
572             ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
573         }
574     }
575 
576 #if !defined(__EMSCRIPTEN__)
577     const bool supportUnorm16 = device.HasFeature(wgpu::FeatureName::Unorm16TextureFormats);
578     // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
579     // Format: R16Unorm
580     {
581         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Unorm)];
582         if (supportUnorm16) {
583             info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
584             info->fColorTypeInfoCount = 1;
585             info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
586             int ctIdx = 0;
587             // Format: R16Unorm, Surface: kA16_unorm
588             {
589                 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
590                 ctInfo.fColorType = kA16_unorm_SkColorType;
591                 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
592                 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
593                 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
594             }
595         }
596     }
597 #endif
598 
599     // Format: BGRA8Unorm
600     {
601         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BGRA8Unorm)];
602         info->fFlags = FormatInfo::kAllFlags;
603         info->fColorTypeInfoCount = 2;
604         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
605         int ctIdx = 0;
606         // Format: BGRA8Unorm, Surface: kBGRA_8888
607         {
608             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
609             ctInfo.fColorType = kBGRA_8888_SkColorType;
610             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
611         }
612         // Format: BGRA8Unorm, Surface: kRGB_888x
613         {
614             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
615             ctInfo.fColorType = kRGB_888x_SkColorType;
616             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
617         }
618     }
619 
620     // Format: RGBA16Float
621     {
622         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA16Float)];
623         info->fFlags = FormatInfo::kAllFlags;
624         info->fColorTypeInfoCount = 1;
625         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
626         int ctIdx = 0;
627         // Format: RGBA16Float, Surface: RGBA_F16
628         {
629             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
630             ctInfo.fColorType = kRGBA_F16_SkColorType;
631             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
632         }
633     }
634 
635     // Format: R16Float
636     {
637         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Float)];
638         info->fFlags = FormatInfo::kAllFlags;
639         info->fColorTypeInfoCount = 1;
640         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
641         int ctIdx = 0;
642         // Format: R16Float, Surface: kA16_float
643         {
644             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
645             ctInfo.fColorType = kA16_float_SkColorType;
646             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
647             ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
648             ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
649         }
650     }
651 
652     // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
653     // Format: RG8Unorm
654     {
655         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG8Unorm)];
656         info->fFlags = FormatInfo::kAllFlags;
657         info->fColorTypeInfoCount = 1;
658         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
659         int ctIdx = 0;
660         // Format: RG8Unorm, Surface: kR8G8_unorm
661         {
662             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
663             ctInfo.fColorType = kR8G8_unorm_SkColorType;
664             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
665         }
666     }
667 
668 #if !defined(__EMSCRIPTEN__)
669     // TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
670     // Format: RG16Unorm
671     {
672         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Unorm)];
673         if (supportUnorm16) {
674             info->fFlags = FormatInfo::kAllFlags;
675             info->fColorTypeInfoCount = 1;
676             info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
677             int ctIdx = 0;
678             // Format: RG16Unorm, Surface: kR16G16_unorm
679             {
680                 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
681                 ctInfo.fColorType = kR16G16_unorm_SkColorType;
682                 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
683             }
684         }
685     }
686 #endif
687 
688     // Format: RGB10A2Unorm
689     {
690         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGB10A2Unorm)];
691         info->fFlags = FormatInfo::kAllFlags;
692         info->fColorTypeInfoCount = 1;
693         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
694         int ctIdx = 0;
695         // Format: RGB10A2Unorm, Surface: kRGBA_1010102
696         {
697             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
698             ctInfo.fColorType = kRGBA_1010102_SkColorType;
699             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
700         }
701     }
702 
703     // Format: RG16Float
704     {
705         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Float)];
706         info->fFlags = FormatInfo::kAllFlags;
707         info->fColorTypeInfoCount = 1;
708         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
709         int ctIdx = 0;
710         // Format: RG16Float, Surface: kR16G16_float
711         {
712             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
713             ctInfo.fColorType = kR16G16_float_SkColorType;
714             ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
715         }
716     }
717 
718     // Format: ETC2RGB8Unorm
719     {
720         if (device.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
721             info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::ETC2RGB8Unorm)];
722             info->fFlags = FormatInfo::kTexturable_Flag;
723             info->fColorTypeInfoCount = 1;
724             info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
725             int ctIdx = 0;
726             // Format: ETC2RGB8Unorm, Surface: kRGB_888x
727             {
728                 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
729                 ctInfo.fColorType = kRGB_888x_SkColorType;
730                 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
731             }
732         }
733     }
734 
735     // Format: BC1RGBAUnorm
736     {
737         if (device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
738             info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BC1RGBAUnorm)];
739             info->fFlags = FormatInfo::kTexturable_Flag;
740             info->fColorTypeInfoCount = 1;
741             info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
742             int ctIdx = 0;
743             // Format: BC1RGBAUnorm, Surface: kRGBA_8888
744             {
745                 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
746                 ctInfo.fColorType = kRGBA_8888_SkColorType;
747                 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
748             }
749         }
750     }
751 
752     /*
753      * Non-color formats
754      */
755 
756     // Format: Stencil8
757     {
758         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Stencil8)];
759         info->fFlags = FormatInfo::kMSAA_Flag;
760         info->fColorTypeInfoCount = 0;
761     }
762 
763     // Format: Depth16UNorm
764     {
765         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth16Unorm)];
766         info->fFlags = FormatInfo::kMSAA_Flag;
767         info->fColorTypeInfoCount = 0;
768     }
769 
770     // Format: Depth32Float
771     {
772         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth32Float)];
773         info->fFlags = FormatInfo::kMSAA_Flag;
774         info->fColorTypeInfoCount = 0;
775     }
776 
777     // Format: Depth24PlusStencil8
778     {
779         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth24PlusStencil8)];
780         info->fFlags = FormatInfo::kMSAA_Flag;
781         info->fColorTypeInfoCount = 0;
782     }
783 
784 #if !defined(__EMSCRIPTEN__)
785     // Format: External
786     {
787         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::External)];
788         info->fFlags = FormatInfo::kTexturable_Flag;
789         info->fColorTypeInfoCount = 1;
790         info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
791         int ctIdx = 0;
792         // Format: External, Surface: kRGBA_8888
793         {
794             auto& ctInfo = info->fColorTypeInfos[ctIdx++];
795             ctInfo.fColorType = kRGBA_8888_SkColorType;
796         }
797     }
798 #endif
799 
800     // Format: Undefined
801     {
802         info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Undefined)];
803         info->fFlags = 0;
804         info->fColorTypeInfoCount = 0;
805     }
806 
807     ////////////////////////////////////////////////////////////////////////////
808     // Map SkColorTypes (used for creating SkSurfaces) to wgpu::TextureFormat.
809     // The order in which the formats are passed into the setColorType function
810     // indicates the priority in selecting which format we use for a given
811     // SkColorType.
812 
813     std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, wgpu::TextureFormat::Undefined);
814 
815     this->setColorType(kAlpha_8_SkColorType,          { wgpu::TextureFormat::R8Unorm });
816     this->setColorType(kRGBA_8888_SkColorType,        { wgpu::TextureFormat::RGBA8Unorm });
817     this->setColorType(kRGB_888x_SkColorType,
818                        {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::BGRA8Unorm});
819     this->setColorType(kBGRA_8888_SkColorType,        { wgpu::TextureFormat::BGRA8Unorm });
820     this->setColorType(kGray_8_SkColorType,           { wgpu::TextureFormat::R8Unorm });
821     this->setColorType(kR8_unorm_SkColorType,         { wgpu::TextureFormat::R8Unorm });
822     this->setColorType(kRGBA_F16_SkColorType,         { wgpu::TextureFormat::RGBA16Float });
823     this->setColorType(kA16_float_SkColorType,        { wgpu::TextureFormat::R16Float });
824     this->setColorType(kR8G8_unorm_SkColorType,       { wgpu::TextureFormat::RG8Unorm });
825     this->setColorType(kRGBA_1010102_SkColorType,     { wgpu::TextureFormat::RGB10A2Unorm });
826     this->setColorType(kR16G16_float_SkColorType,     { wgpu::TextureFormat::RG16Float });
827 
828 #if !defined(__EMSCRIPTEN__)
829     this->setColorType(kA16_unorm_SkColorType,        { wgpu::TextureFormat::R16Unorm });
830     this->setColorType(kR16G16_unorm_SkColorType,     { wgpu::TextureFormat::RG16Unorm });
831 #endif
832 }
833 
834 // static
GetFormatIndex(wgpu::TextureFormat format)835 size_t DawnCaps::GetFormatIndex(wgpu::TextureFormat format) {
836     for (size_t i = 0; i < std::size(kFormats); ++i) {
837         if (format == kFormats[i]) {
838             return i;
839         }
840         if (kFormats[i] == wgpu::TextureFormat::Undefined) {
841             SkDEBUGFAILF("Unsupported wgpu::TextureFormat: %d\n", static_cast<int>(format));
842             return i;
843         }
844     }
845     SkUNREACHABLE;
846     return 0;
847 }
848 
setColorType(SkColorType colorType,std::initializer_list<wgpu::TextureFormat> formats)849 void DawnCaps::setColorType(SkColorType colorType,
850                             std::initializer_list<wgpu::TextureFormat> formats) {
851     static_assert(std::size(kFormats) == kFormatCnt,
852                   "Size is not same for DawnCaps::fFormatTable and kFormats");
853     int idx = static_cast<int>(colorType);
854     for (auto it = formats.begin(); it != formats.end(); ++it) {
855         const auto& info = this->getFormatInfo(*it);
856         for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
857             if (info.fColorTypeInfos[i].fColorType == colorType) {
858                 fColorTypeToFormatTable[idx] = *it;
859                 return;
860             }
861         }
862     }
863 }
864 
getRenderPassDescKeyForPipeline(const RenderPassDesc & renderPassDesc) const865 uint64_t DawnCaps::getRenderPassDescKeyForPipeline(const RenderPassDesc& renderPassDesc) const {
866     DawnTextureInfo colorInfo, depthStencilInfo;
867     renderPassDesc.fColorAttachment.fTextureInfo.getDawnTextureInfo(&colorInfo);
868     renderPassDesc.fDepthStencilAttachment.fTextureInfo.getDawnTextureInfo(&depthStencilInfo);
869     SkASSERT(static_cast<uint32_t>(colorInfo.getViewFormat()) <= 0xffff &&
870              static_cast<uint32_t>(depthStencilInfo.getViewFormat()) <= 0xffff &&
871              colorInfo.fSampleCount < 0x7fff);
872 
873     // Note: if Dawn supports ExpandResolveTexture load op and the render pass uses it to load
874     // the resolve texture, a render pipeline will need to be created with
875     // wgpu::ColorTargetStateExpandResolveTextureDawn chained struct in order to be compatible.
876     // Hence a render pipeline created for a render pass using ExpandResolveTexture load op will
877     // be different from the one created for a render pass not using that load op.
878     // So we need to include a bit flag to differentiate the two kinds of pipelines.
879     // Also avoid returning a cached pipeline that is not compatible with the render pass using
880     // ExpandResolveTexture load op and vice versa.
881     const bool shouldIncludeLoadResolveAttachmentBit = this->resolveTextureLoadOp().has_value();
882     uint32_t loadResolveAttachmentKey = 0;
883     if (shouldIncludeLoadResolveAttachmentBit &&
884         renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
885         renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
886         loadResolveAttachmentKey = 1;
887     }
888 
889     uint32_t colorAttachmentKey = static_cast<uint32_t>(colorInfo.getViewFormat()) << 16 |
890                                   colorInfo.fSampleCount << 1 | loadResolveAttachmentKey;
891 
892     uint32_t dsAttachmentKey = static_cast<uint32_t>(depthStencilInfo.getViewFormat()) << 16 |
893                                depthStencilInfo.fSampleCount;
894     return (((uint64_t)colorAttachmentKey) << 32) | dsAttachmentKey;
895 }
896 
makeGraphicsPipelineKey(const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc) const897 UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
898                                             const RenderPassDesc& renderPassDesc) const {
899     UniqueKey pipelineKey;
900     {
901         static const skgpu::UniqueKey::Domain kGraphicsPipelineDomain = UniqueKey::GenerateDomain();
902         // 5 uint32_t's (render step id, paint id, uint64 RenderPass desc, uint16 write swizzle)
903         UniqueKey::Builder builder(&pipelineKey, kGraphicsPipelineDomain, 5, "GraphicsPipeline");
904         // add GraphicsPipelineDesc key
905         builder[0] = pipelineDesc.renderStepID();
906         builder[1] = pipelineDesc.paintParamsID().asUInt();
907 
908         // Add RenderPassDesc key.
909         uint64_t renderPassKey = this->getRenderPassDescKeyForPipeline(renderPassDesc);
910         builder[2] = renderPassKey & 0xFFFFFFFF;
911         builder[3] = (renderPassKey >> 32) & 0xFFFFFFFF;
912         builder[4] = renderPassDesc.fWriteSwizzle.asKey();
913         builder.finish();
914     }
915 
916     return pipelineKey;
917 }
918 
makeComputePipelineKey(const ComputePipelineDesc & pipelineDesc) const919 UniqueKey DawnCaps::makeComputePipelineKey(const ComputePipelineDesc& pipelineDesc) const {
920     UniqueKey pipelineKey;
921     {
922         static const skgpu::UniqueKey::Domain kComputePipelineDomain = UniqueKey::GenerateDomain();
923         // The key is made up of a single uint32_t corresponding to the compute step ID.
924         UniqueKey::Builder builder(&pipelineKey, kComputePipelineDomain, 1, "ComputePipeline");
925         builder[0] = pipelineDesc.computeStep()->uniqueID();
926 
927         // TODO(b/240615224): The local work group size should factor into the key here since it is
928         // specified in the shader text on Dawn/SPIR-V. This is not a problem right now since
929         // ComputeSteps don't vary their workgroup size dynamically.
930 
931         builder.finish();
932     }
933     return pipelineKey;
934 }
935 
buildKeyForTexture(SkISize dimensions,const TextureInfo & info,ResourceType type,Shareable shareable,GraphiteResourceKey * key) const936 void DawnCaps::buildKeyForTexture(SkISize dimensions,
937                                   const TextureInfo& info,
938                                   ResourceType type,
939                                   Shareable shareable,
940                                   GraphiteResourceKey* key) const {
941     const DawnTextureSpec& dawnSpec = info.dawnTextureSpec();
942 
943     SkASSERT(!dimensions.isEmpty());
944 
945     SkASSERT(dawnSpec.getViewFormat() != wgpu::TextureFormat::Undefined);
946     uint32_t formatKey = static_cast<uint32_t>(dawnSpec.getViewFormat());
947 
948     uint32_t samplesKey = SamplesToKey(info.numSamples());
949     // We don't have to key the number of mip levels because it is inherit in the combination of
950     // isMipped and dimensions.
951     bool isMipped = info.mipmapped() == Mipmapped::kYes;
952 
953     // Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
954     // amounts in the asserts must be less than or equal to 32.
955     SkASSERT(samplesKey                             < (1u << 3));  // sample key is first 3 bits
956     SkASSERT(static_cast<uint32_t>(isMipped)        < (1u << 1));  // isMapped is 4th bit
957     SkASSERT(static_cast<uint32_t>(dawnSpec.fUsage) < (1u << 28)); // usage is remaining 28 bits
958 
959     // We need two uint32_ts for dimensions, 1 for format, and 1 for the rest of the key;
960     static int kNum32DataCnt = 2 + 1 + 1;
961 
962     GraphiteResourceKey::Builder builder(key, type, kNum32DataCnt, shareable);
963 
964     builder[0] = dimensions.width();
965     builder[1] = dimensions.height();
966     builder[2] = formatKey;
967     builder[3] = (samplesKey                                   << 0) |
968                  (static_cast<uint32_t>(isMipped)              << 3) |
969                  (static_cast<uint32_t>(dawnSpec.fUsage)       << 4);
970 }
971 
972 } // namespace skgpu::graphite
973