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