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/gpu/graphite/TextureInfo.h"
13 #include "src/gpu/graphite/AttachmentTypes.h"
14 #include "src/gpu/graphite/ComputePipelineDesc.h"
15 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
16 #include "src/gpu/graphite/GraphiteResourceKey.h"
17 #include "src/gpu/graphite/UniformManager.h"
18 #include "src/gpu/graphite/dawn/DawnUtilsPriv.h"
19 #include "src/sksl/SkSLUtil.h"
20
21 namespace {
22
23 // These are all the valid wgpu::TextureFormat that we currently support in Skia.
24 // They are roughly ordered from most frequently used to least to improve look
25 // up times in arrays.
26 static constexpr wgpu::TextureFormat kFormats[] = {
27 wgpu::TextureFormat::RGBA8Unorm,
28 wgpu::TextureFormat::R8Unorm,
29 wgpu::TextureFormat::BGRA8Unorm,
30 wgpu::TextureFormat::RGBA16Float,
31
32 wgpu::TextureFormat::Stencil8,
33 wgpu::TextureFormat::Depth32Float,
34 wgpu::TextureFormat::Depth32FloatStencil8,
35
36 wgpu::TextureFormat::Undefined,
37 };
38
39 }
40
41 namespace skgpu::graphite {
42
DawnCaps(const wgpu::Device & device,const ContextOptions & options)43 DawnCaps::DawnCaps(const wgpu::Device& device, const ContextOptions& options)
44 : Caps() {
45 this->initCaps(device);
46 this->initShaderCaps();
47 this->initFormatTable(device);
48 this->finishInitialization(options);
49 }
50
51 DawnCaps::~DawnCaps() = default;
52
onIsTexturable(const TextureInfo & info) const53 bool DawnCaps::onIsTexturable(const TextureInfo& info) const {
54 if (!(info.dawnTextureSpec().fUsage & wgpu::TextureUsage::TextureBinding)) {
55 return false;
56 }
57 return this->isTexturable(info.dawnTextureSpec().fFormat);
58 }
59
isTexturable(wgpu::TextureFormat format) const60 bool DawnCaps::isTexturable(wgpu::TextureFormat format) const {
61 const FormatInfo& formatInfo = this->getFormatInfo(format);
62 return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags);
63 }
64
isRenderable(const TextureInfo & info) const65 bool DawnCaps::isRenderable(const TextureInfo& info) const {
66 return info.dawnTextureSpec().fUsage & wgpu::TextureUsage::RenderAttachment &&
67 this->isRenderable(info.dawnTextureSpec().fFormat, info.numSamples());
68 }
69
maxRenderTargetSampleCount(wgpu::TextureFormat format) const70 uint32_t DawnCaps::maxRenderTargetSampleCount(wgpu::TextureFormat format) const {
71 const FormatInfo& formatInfo = this->getFormatInfo(format);
72 if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) {
73 return 0;
74 }
75 if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) {
76 return 8;
77 } else {
78 return 1;
79 }
80 }
81
isRenderable(wgpu::TextureFormat format,uint32_t sampleCount) const82 bool DawnCaps::isRenderable(wgpu::TextureFormat format, uint32_t sampleCount) const {
83 return sampleCount <= this->maxRenderTargetSampleCount(format);
84 }
85
getDefaultSampledTextureInfo(SkColorType colorType,Mipmapped mipmapped,Protected,Renderable renderable) const86 TextureInfo DawnCaps::getDefaultSampledTextureInfo(SkColorType colorType,
87 Mipmapped mipmapped,
88 Protected,
89 Renderable renderable) const {
90 wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
91 wgpu::TextureUsage::CopyDst |
92 wgpu::TextureUsage::CopySrc;
93 if (renderable == Renderable::kYes) {
94 usage |= wgpu::TextureUsage::RenderAttachment;
95 }
96
97 wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
98 if (format == wgpu::TextureFormat::Undefined) {
99 SkDebugf("colorType=%d is not supported\n", static_cast<int>(colorType));
100 return {};
101 }
102
103 DawnTextureInfo info;
104 info.fSampleCount = 1;
105 info.fMipmapped = mipmapped;
106 info.fFormat = format;
107 info.fUsage = usage;
108
109 return info;
110 }
111
getDefaultMSAATextureInfo(const TextureInfo & singleSampledInfo,Discardable discardable) const112 TextureInfo DawnCaps::getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
113 Discardable discardable) const {
114 const DawnTextureSpec& singleSpec = singleSampledInfo.dawnTextureSpec();
115
116 DawnTextureInfo info;
117 info.fSampleCount = this->defaultMSAASamples();
118 info.fMipmapped = Mipmapped::kNo;
119 info.fFormat = singleSpec.fFormat;
120 info.fUsage = wgpu::TextureUsage::RenderAttachment;
121 return info;
122 }
123
getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags> depthStencilType,uint32_t sampleCount,Protected) const124 TextureInfo DawnCaps::getDefaultDepthStencilTextureInfo(
125 SkEnumBitMask<DepthStencilFlags> depthStencilType,
126 uint32_t sampleCount,
127 Protected) const {
128 DawnTextureInfo info;
129 info.fSampleCount = sampleCount;
130 info.fMipmapped = Mipmapped::kNo;
131 info.fFormat = DawnDepthStencilFlagsToFormat(depthStencilType);
132 info.fUsage = wgpu::TextureUsage::RenderAttachment;
133 return info;
134 }
135
getColorTypeInfo(SkColorType colorType,const TextureInfo & textureInfo) const136 const Caps::ColorTypeInfo* DawnCaps::getColorTypeInfo(SkColorType colorType,
137 const TextureInfo& textureInfo) const {
138 auto dawnFormat = textureInfo.dawnTextureSpec().fFormat;
139 if (dawnFormat == wgpu::TextureFormat::Undefined) {
140 SkASSERT(false);
141 return nullptr;
142 }
143
144 const FormatInfo& info = this->getFormatInfo(dawnFormat);
145 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
146 const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
147 if (ctInfo.fColorType == colorType) {
148 return &ctInfo;
149 }
150 }
151
152 SkASSERT(false);
153 return nullptr;
154 }
155
supportsWritePixels(const TextureInfo & textureInfo) const156 bool DawnCaps::supportsWritePixels(const TextureInfo& textureInfo) const {
157 const auto& spec = textureInfo.dawnTextureSpec();
158 return spec.fUsage & wgpu::TextureUsage::CopyDst;
159 }
160
supportsReadPixels(const TextureInfo & textureInfo) const161 bool DawnCaps::supportsReadPixels(const TextureInfo& textureInfo) const {
162 const auto& spec = textureInfo.dawnTextureSpec();
163 return spec.fUsage & wgpu::TextureUsage::CopySrc;
164 }
165
supportedWritePixelsColorType(SkColorType dstColorType,const TextureInfo & dstTextureInfo,SkColorType srcColorType) const166 SkColorType DawnCaps::supportedWritePixelsColorType(SkColorType dstColorType,
167 const TextureInfo& dstTextureInfo,
168 SkColorType srcColorType) const {
169 SkASSERT(false);
170 return kUnknown_SkColorType;
171 }
172
supportedReadPixelsColorType(SkColorType srcColorType,const TextureInfo & srcTextureInfo,SkColorType dstColorType) const173 SkColorType DawnCaps::supportedReadPixelsColorType(SkColorType srcColorType,
174 const TextureInfo& srcTextureInfo,
175 SkColorType dstColorType) const {
176 auto dawnFormat = getFormatFromColorType(srcColorType);
177 const FormatInfo& info = this->getFormatInfo(dawnFormat);
178 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
179 const auto& ctInfo = info.fColorTypeInfos[i];
180 if (ctInfo.fColorType == srcColorType) {
181 return srcColorType;
182 }
183 }
184 return kUnknown_SkColorType;
185 }
186
initCaps(const wgpu::Device & device)187 void DawnCaps::initCaps(const wgpu::Device& device) {
188 wgpu::SupportedLimits limits;
189 if (!device.GetLimits(&limits)) {
190 SkASSERT(false);
191 }
192 fMaxTextureSize = limits.limits.maxTextureDimension2D;
193
194 fRequiredTransferBufferAlignment = 4;
195 fRequiredUniformBufferAlignment = 256;
196 fRequiredStorageBufferAlignment = fRequiredUniformBufferAlignment;
197
198 // Dawn requires 256 bytes per row alignment for buffer texture copies.
199 fTextureDataRowBytesAlignment = 256;
200
201 fResourceBindingReqs.fUniformBufferLayout = Layout::kStd140;
202 fResourceBindingReqs.fStorageBufferLayout = Layout::kStd430;
203 fResourceBindingReqs.fSeparateTextureAndSamplerBinding = true;
204
205 // TODO: support storage buffer
206 fStorageBufferSupport = false;
207 fStorageBufferPreferred = false;
208
209 fDrawBufferCanBeMapped = false;
210
211 // TODO: support clamp to border.
212 fClampToBorderSupport = false;
213 }
214
initShaderCaps()215 void DawnCaps::initShaderCaps() {
216 SkSL::ShaderCaps* shaderCaps = fShaderCaps.get();
217
218 // WGSL does not support infinities regardless of hardware support. There are discussions around
219 // enabling it using an extension in the future.
220 shaderCaps->fInfinitySupport = false;
221 }
222
initFormatTable(const wgpu::Device & device)223 void DawnCaps::initFormatTable(const wgpu::Device& device) {
224 FormatInfo* info;
225 // Format: RGBA8Unorm
226 {
227 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA8Unorm)];
228 info->fFlags = FormatInfo::kAllFlags;
229 info->fColorTypeInfoCount = 2;
230 info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]());
231 int ctIdx = 0;
232 // Format: RGBA8Unorm, Surface: kRGBA_8888
233 {
234 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
235 ctInfo.fColorType = kRGBA_8888_SkColorType;
236 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
237 }
238 // Format: RGBA8Unorm, Surface: kRGB_888x
239 {
240 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
241 ctInfo.fColorType = kRGB_888x_SkColorType;
242 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
243 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
244 }
245 }
246
247
248 // Format: R8Unorm
249 {
250 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R8Unorm)];
251 info->fFlags = FormatInfo::kAllFlags;
252 info->fColorTypeInfoCount = 3;
253 info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]());
254 int ctIdx = 0;
255 // Format: R8Unorm, Surface: kR8_unorm
256 {
257 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
258 ctInfo.fColorType = kR8_unorm_SkColorType;
259 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
260 }
261 // Format: R8Unorm, Surface: kAlpha_8
262 {
263 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
264 ctInfo.fColorType = kAlpha_8_SkColorType;
265 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
266 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
267 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
268 }
269 // Format: R8Unorm, Surface: kGray_8
270 {
271 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
272 ctInfo.fColorType = kGray_8_SkColorType;
273 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
274 ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
275 }
276 }
277
278 // Format: BGRA8Unorm
279 {
280 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BGRA8Unorm)];
281 info->fFlags = FormatInfo::kAllFlags;
282 info->fColorTypeInfoCount = 1;
283 info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]());
284 int ctIdx = 0;
285 // Format: BGRA8Unorm, Surface: kBGRA_8888
286 {
287 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
288 ctInfo.fColorType = kBGRA_8888_SkColorType;
289 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
290 }
291 }
292
293 // Format: RGBA16Float
294 {
295 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA16Float)];
296 info->fFlags = FormatInfo::kAllFlags;
297 info->fColorTypeInfoCount = 1;
298 info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]());
299 int ctIdx = 0;
300 // Format: RGBA16Float, Surface: RGBA_F16
301 {
302 auto& ctInfo = info->fColorTypeInfos[ctIdx++];
303 ctInfo.fColorType = kRGBA_F16_SkColorType;
304 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
305 }
306 }
307
308 /*
309 * Non-color formats
310 */
311
312 // Format: Stencil8
313 {
314 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Stencil8)];
315 info->fFlags = FormatInfo::kMSAA_Flag;
316 info->fColorTypeInfoCount = 0;
317 }
318
319 // Format: Depth32Float
320 {
321 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth32Float)];
322 info->fFlags = FormatInfo::kMSAA_Flag;
323 info->fColorTypeInfoCount = 0;
324 }
325
326 // Format: Depth32Float_Stencil8
327 {
328 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth32FloatStencil8)];
329 info->fFlags = FormatInfo::kMSAA_Flag;
330 info->fColorTypeInfoCount = 0;
331 }
332
333 // Format: Undefined
334 {
335 info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Undefined)];
336 info->fFlags = 0;
337 info->fColorTypeInfoCount = 0;
338 }
339
340 ////////////////////////////////////////////////////////////////////////////
341 // Map SkColorTypes (used for creating SkSurfaces) to wgpu::TextureFormat.
342 // The order in which the formats are passed into the setColorType function
343 // indicates the priority in selecting which format we use for a given
344 // SkColorType.
345
346 std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, wgpu::TextureFormat::Undefined);
347
348 this->setColorType(kAlpha_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
349 this->setColorType(kRGBA_8888_SkColorType, { wgpu::TextureFormat::RGBA8Unorm });
350 this->setColorType(kRGB_888x_SkColorType, { wgpu::TextureFormat::RGBA8Unorm });
351 this->setColorType(kBGRA_8888_SkColorType, { wgpu::TextureFormat::BGRA8Unorm });
352 this->setColorType(kGray_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
353 this->setColorType(kR8_unorm_SkColorType, { wgpu::TextureFormat::R8Unorm });
354 this->setColorType(kRGBA_F16_SkColorType, { wgpu::TextureFormat::RGBA16Float });
355 }
356
357 // static
GetFormatIndex(wgpu::TextureFormat format)358 size_t DawnCaps::GetFormatIndex(wgpu::TextureFormat format) {
359 for (size_t i = 0; i < std::size(kFormats); ++i) {
360 if (format == kFormats[i]) {
361 return i;
362 }
363 if (kFormats[i] == wgpu::TextureFormat::Undefined) {
364 SkDEBUGFAILF("Not supported wgpu::TextureFormat: %d\n", format);
365 return i;
366 }
367 }
368 SkUNREACHABLE;
369 return 0;
370 }
371
setColorType(SkColorType colorType,std::initializer_list<wgpu::TextureFormat> formats)372 void DawnCaps::setColorType(SkColorType colorType,
373 std::initializer_list<wgpu::TextureFormat> formats) {
374 static_assert(std::tuple_size<decltype(fFormatTable)>::value == std::size(kFormats),
375 "Size is not same for DawnCaps::fFormatTable and kFormats");
376 #ifdef SK_DEBUG
377 for (size_t i = 0; i < std::size(fFormatTable); ++i) {
378 const auto& formatInfo = fFormatTable[i];
379 for (int j = 0; j < formatInfo.fColorTypeInfoCount; ++j) {
380 const auto& ctInfo = formatInfo.fColorTypeInfos[j];
381 if (ctInfo.fColorType == colorType) {
382 bool found = false;
383 for (auto it = formats.begin(); it != formats.end(); ++it) {
384 if (kFormats[i] == *it) {
385 found = true;
386 }
387 }
388 SkASSERT(found);
389 }
390 }
391 }
392 #endif
393 int idx = static_cast<int>(colorType);
394 for (auto it = formats.begin(); it != formats.end(); ++it) {
395 const auto& info = this->getFormatInfo(*it);
396 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
397 if (info.fColorTypeInfos[i].fColorType == colorType) {
398 fColorTypeToFormatTable[idx] = *it;
399 return;
400 }
401 }
402 }
403 }
404
getRenderPassDescKey(const RenderPassDesc & renderPassDesc) const405 uint64_t DawnCaps::getRenderPassDescKey(const RenderPassDesc& renderPassDesc) const {
406 DawnTextureInfo colorInfo, depthStencilInfo;
407 renderPassDesc.fColorAttachment.fTextureInfo.getDawnTextureInfo(&colorInfo);
408 renderPassDesc.fDepthStencilAttachment.fTextureInfo.getDawnTextureInfo(&depthStencilInfo);
409 SkASSERT(static_cast<uint32_t>(colorInfo.fFormat) <= 0xffff &&
410 static_cast<uint32_t>(depthStencilInfo.fFormat) <= 0xffff);
411 uint32_t colorAttachmentKey =
412 static_cast<uint32_t>(colorInfo.fFormat) << 16 | colorInfo.fSampleCount;
413 uint32_t dsAttachmentKey =
414 static_cast<uint32_t>(depthStencilInfo.fFormat) << 16 | depthStencilInfo.fSampleCount;
415 return (((uint64_t) colorAttachmentKey) << 32) | dsAttachmentKey;
416 }
417
makeGraphicsPipelineKey(const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc) const418 UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
419 const RenderPassDesc& renderPassDesc) const {
420 UniqueKey pipelineKey;
421 {
422 static const skgpu::UniqueKey::Domain kGraphicsPipelineDomain = UniqueKey::GenerateDomain();
423 // 4 uint32_t's (render step id, paint id, uint64 RenderPass desc)
424 UniqueKey::Builder builder(&pipelineKey, kGraphicsPipelineDomain, 4, "GraphicsPipeline");
425 // add GraphicsPipelineDesc key
426 builder[0] = pipelineDesc.renderStepID();
427 builder[1] = pipelineDesc.paintParamsID().asUInt();
428
429 // add RenderPassDesc key
430 uint64_t renderPassKey = this->getRenderPassDescKey(renderPassDesc);
431 builder[2] = renderPassKey & 0xFFFFFFFF;
432 builder[3] = (renderPassKey >> 32) & 0xFFFFFFFF;
433 builder.finish();
434 }
435
436 return pipelineKey;
437 }
438
makeComputePipelineKey(const ComputePipelineDesc & pipelineDesc) const439 UniqueKey DawnCaps::makeComputePipelineKey(const ComputePipelineDesc& pipelineDesc) const {
440 SkASSERT(false);
441 return {};
442 }
443
buildKeyForTexture(SkISize dimensions,const TextureInfo & info,ResourceType type,Shareable shareable,GraphiteResourceKey * key) const444 void DawnCaps::buildKeyForTexture(SkISize dimensions,
445 const TextureInfo& info,
446 ResourceType type,
447 Shareable shareable,
448 GraphiteResourceKey* key) const {
449 const DawnTextureSpec& dawnSpec = info.dawnTextureSpec();
450
451 SkASSERT(!dimensions.isEmpty());
452
453 SkASSERT(dawnSpec.fFormat != wgpu::TextureFormat::Undefined);
454 uint32_t formatKey = static_cast<uint32_t>(dawnSpec.fFormat);
455
456 uint32_t samplesKey = SamplesToKey(info.numSamples());
457 // We don't have to key the number of mip levels because it is inherit in the combination of
458 // isMipped and dimensions.
459 bool isMipped = info.mipmapped() == Mipmapped::kYes;
460
461 // Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
462 // amounts in the asserts must be less than or equal to 32.
463 SkASSERT(samplesKey < (1u << 3));
464 SkASSERT(static_cast<uint32_t>(isMipped) < (1u << 1));
465 SkASSERT(static_cast<uint32_t>(dawnSpec.fUsage) < (1u << 5));
466
467 // We need two uint32_ts for dimensions, 1 for format, and 1 for the rest of the key;
468 static int kNum32DataCnt = 2 + 1 + 1;
469
470 GraphiteResourceKey::Builder builder(key, type, kNum32DataCnt, shareable);
471
472 builder[0] = dimensions.width();
473 builder[1] = dimensions.height();
474 builder[2] = formatKey;
475 builder[3] = (samplesKey << 0) |
476 (static_cast<uint32_t>(isMipped) << 3) |
477 (static_cast<uint32_t>(dawnSpec.fUsage) << 4);
478 }
479
480 } // namespace skgpu::graphite
481