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/vk/VulkanCaps.h"
9
10 #include "include/core/SkTextureCompressionType.h"
11 #include "include/gpu/graphite/ContextOptions.h"
12 #include "include/gpu/graphite/TextureInfo.h"
13 #include "include/gpu/graphite/vk/VulkanGraphiteTypes.h"
14 #include "include/gpu/vk/VulkanExtensions.h"
15 #include "include/gpu/vk/VulkanTypes.h"
16 #include "src/gpu/graphite/ContextUtils.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/RendererProvider.h"
21 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
22 #include "src/gpu/graphite/vk/VulkanGraphiteTypesPriv.h"
23 #include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
24 #include "src/gpu/graphite/vk/VulkanRenderPass.h"
25 #include "src/gpu/graphite/vk/VulkanSharedContext.h"
26 #include "src/gpu/graphite/vk/VulkanYcbcrConversion.h"
27 #include "src/gpu/vk/VulkanUtilsPriv.h"
28 #include "src/sksl/SkSLUtil.h"
29
30 #ifdef SK_BUILD_FOR_ANDROID
31 #include <sys/system_properties.h>
32 #endif
33
34 namespace skgpu::graphite {
35
VulkanCaps(const ContextOptions & contextOptions,const skgpu::VulkanInterface * vkInterface,VkPhysicalDevice physDev,uint32_t physicalDeviceVersion,const VkPhysicalDeviceFeatures2 * features,const skgpu::VulkanExtensions * extensions,Protected isProtected)36 VulkanCaps::VulkanCaps(const ContextOptions& contextOptions,
37 const skgpu::VulkanInterface* vkInterface,
38 VkPhysicalDevice physDev,
39 uint32_t physicalDeviceVersion,
40 const VkPhysicalDeviceFeatures2* features,
41 const skgpu::VulkanExtensions* extensions,
42 Protected isProtected)
43 : Caps() {
44 this->init(contextOptions, vkInterface, physDev, physicalDeviceVersion, features, extensions,
45 isProtected);
46 }
47
~VulkanCaps()48 VulkanCaps::~VulkanCaps() {}
49
init(const ContextOptions & contextOptions,const skgpu::VulkanInterface * vkInterface,VkPhysicalDevice physDev,uint32_t physicalDeviceVersion,const VkPhysicalDeviceFeatures2 * features,const skgpu::VulkanExtensions * extensions,Protected isProtected)50 void VulkanCaps::init(const ContextOptions& contextOptions,
51 const skgpu::VulkanInterface* vkInterface,
52 VkPhysicalDevice physDev,
53 uint32_t physicalDeviceVersion,
54 const VkPhysicalDeviceFeatures2* features,
55 const skgpu::VulkanExtensions* extensions,
56 Protected isProtected) {
57 VkPhysicalDeviceProperties physDevProperties;
58 VULKAN_CALL(vkInterface, GetPhysicalDeviceProperties(physDev, &physDevProperties));
59
60 #if defined(GPU_TEST_UTILS)
61 this->setDeviceName(physDevProperties.deviceName);
62 #endif
63
64 // Graphite requires Vulkan version 1.1 or later, which always has protected support.
65 if (isProtected == Protected::kYes) {
66 fProtectedSupport = true;
67 fShouldAlwaysUseDedicatedImageMemory = true;
68 }
69
70 fPhysicalDeviceMemoryProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
71 fPhysicalDeviceMemoryProperties2.pNext = nullptr;
72 VULKAN_CALL(vkInterface,
73 GetPhysicalDeviceMemoryProperties2(physDev, &fPhysicalDeviceMemoryProperties2));
74
75 // We could actually query and get a max size for each config, however maxImageDimension2D will
76 // give the minimum max size across all configs. So for simplicity we will use that for now.
77 fMaxTextureSize = std::min(physDevProperties.limits.maxImageDimension2D, (uint32_t)INT_MAX);
78
79 fRequiredUniformBufferAlignment = physDevProperties.limits.minUniformBufferOffsetAlignment;
80 fRequiredStorageBufferAlignment = physDevProperties.limits.minStorageBufferOffsetAlignment;
81 fRequiredTransferBufferAlignment = 4;
82
83 // Unlike D3D, WebGPU, and Metal, the Vulkan NDC coordinate space is aligned with the top-left
84 // Y-down coordinate space of the viewport.
85 fNDCYAxisPointsDown = true;
86
87 fResourceBindingReqs.fUniformBufferLayout = Layout::kStd140;
88 // We can enable std430 and ensure no array stride mismatch in functions because all bound
89 // buffers will either be a UBO or SSBO, depending on if storage buffers are enabled or not.
90 // Although intrinsic uniforms always use uniform buffers, they do not contain any arrays.
91 fResourceBindingReqs.fStorageBufferLayout = Layout::kStd430;
92 fResourceBindingReqs.fSeparateTextureAndSamplerBinding = false;
93 fResourceBindingReqs.fDistinctIndexRanges = false;
94
95 fResourceBindingReqs.fIntrinsicBufferBinding =
96 VulkanGraphicsPipeline::kIntrinsicUniformBufferIndex;
97 fResourceBindingReqs.fRenderStepBufferBinding =
98 VulkanGraphicsPipeline::kRenderStepUniformBufferIndex;
99 fResourceBindingReqs.fPaintParamsBufferBinding =
100 VulkanGraphicsPipeline::kPaintUniformBufferIndex;
101 fResourceBindingReqs.fGradientBufferBinding =
102 VulkanGraphicsPipeline::kGradientBufferIndex;
103
104 // TODO(b/353983969): Enable storage buffers once perf regressions are addressed.
105 fStorageBufferSupport = false;
106
107 VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
108 VULKAN_CALL(vkInterface, GetPhysicalDeviceMemoryProperties(physDev, &deviceMemoryProperties));
109 fSupportsMemorylessAttachments = false;
110 VkMemoryPropertyFlags requiredLazyFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
111 if (fProtectedSupport) {
112 // If we have a protected context we can only use memoryless images if they also support
113 // being protected. With current devices we don't actually expect this combination to be
114 // supported, but this at least covers us for future devices that may allow it.
115 requiredLazyFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
116 }
117 for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; ++i) {
118 const uint32_t& supportedFlags = deviceMemoryProperties.memoryTypes[i].propertyFlags;
119 if ((supportedFlags & requiredLazyFlags) == requiredLazyFlags) {
120 fSupportsMemorylessAttachments = true;
121 }
122 }
123
124 #ifdef SK_BUILD_FOR_UNIX
125 if (kNvidia_VkVendor == physDevProperties.vendorID) {
126 // On NVIDIA linux we see a big perf regression when not using dedicated image allocations.
127 fShouldAlwaysUseDedicatedImageMemory = true;
128 }
129 #endif
130
131 if (physDevProperties.vendorID == kNvidia_VkVendor ||
132 physDevProperties.vendorID == kAMD_VkVendor) {
133 // On discrete GPUs, it can be faster to read gpu-only memory compared to memory that is
134 // also mappable on the host.
135 fGpuOnlyBuffersMorePerformant = true;
136
137 // On discrete GPUs we try to use special DEVICE_LOCAL and HOST_VISIBLE memory for our
138 // cpu write, gpu read buffers. This memory is not ideal to be kept persistently mapped.
139 // Some discrete GPUs do not expose this special memory, however we still disable
140 // persistently mapped buffers for all of them since most GPUs with updated drivers do
141 // expose it. If this becomes an issue we can try to be more fine grained.
142 fShouldPersistentlyMapCpuToGpuBuffers = false;
143 }
144
145 if (!contextOptions.fDisableDriverCorrectnessWorkarounds) {
146 this->applyDriverCorrectnessWorkarounds(physDevProperties);
147 }
148
149 if (physDevProperties.vendorID == kAMD_VkVendor) {
150 // AMD advertises support for MAX_UINT vertex attributes but in reality only supports 32.
151 fMaxVertexAttributes = 32;
152 } else {
153 fMaxVertexAttributes = physDevProperties.limits.maxVertexInputAttributes;
154 }
155 fMaxUniformBufferRange = physDevProperties.limits.maxUniformBufferRange;
156 fMaxStorageBufferRange = physDevProperties.limits.maxStorageBufferRange;
157
158 #ifdef SK_BUILD_FOR_ANDROID
159 if (extensions->hasExtension(
160 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, 2)) {
161 fSupportsAHardwareBufferImages = true;
162 }
163 #endif
164
165 // Determine whether the client enabled certain physical device features.
166 if (features) {
167 auto ycbcrFeatures =
168 skgpu::GetExtensionFeatureStruct<VkPhysicalDeviceSamplerYcbcrConversionFeatures>(
169 *features,
170 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES);
171 if (ycbcrFeatures && ycbcrFeatures->samplerYcbcrConversion) {
172 fSupportsYcbcrConversion = true;
173 }
174 }
175
176 if (extensions->hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
177 fSupportsDeviceFaultInfo = true;
178 }
179
180 // TODO(skia:14639): We must force std430 array stride when using SSBOs since SPIR-V generation
181 // cannot handle mixed array strides being passed into functions.
182 fShaderCaps->fForceStd430ArrayLayout =
183 fStorageBufferSupport && fResourceBindingReqs.fStorageBufferLayout == Layout::kStd430;
184
185 if (features && features->features.dualSrcBlend) {
186 fShaderCaps->fDualSourceBlendingSupport = true;
187 }
188
189 // Note that format table initialization should be performed at the end of this method to ensure
190 // all capability determinations are completed prior to populating the format tables.
191 this->initFormatTable(vkInterface, physDev, physDevProperties);
192 this->initDepthStencilFormatTable(vkInterface, physDev, physDevProperties);
193
194 this->finishInitialization(contextOptions);
195 }
196
applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties & properties)197 void VulkanCaps::applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties& properties) {
198 // By default, we initialize the Android API version to 0 since we consider certain things
199 // "fixed" only once above a certain version. This way, we default to enabling the workarounds.
200 int androidAPIVersion = 0;
201 #if defined(SK_BUILD_FOR_ANDROID)
202 char androidAPIVersionStr[PROP_VALUE_MAX];
203 int strLength = __system_property_get("ro.build.version.sdk", androidAPIVersionStr);
204 // Defaults to zero since most checks care if it is greater than a specific value. So this will
205 // just default to it being less.
206 androidAPIVersion = (strLength == 0) ? 0 : atoi(androidAPIVersionStr);
207 #endif
208
209 // On Mali galaxy s7 we see lots of rendering issues when we suballocate VkImages.
210 if (kARM_VkVendor == properties.vendorID && androidAPIVersion <= 28) {
211 fShouldAlwaysUseDedicatedImageMemory = true;
212 }
213
214 // On Qualcomm the gpu resolves an area larger than the render pass bounds when using
215 // discardable msaa attachments. This causes the resolve to resolve uninitialized data from the
216 // msaa image into the resolve image. This was reproed on a Pixel4 using the DstReadShuffle GM
217 // where the top half of the GM would drop out. In Ganesh we had also seen this on Arm devices,
218 // but the issue hasn't appeared yet in Graphite. It may just have occured on older Arm drivers
219 // that we don't even test any more. This also occurs on swiftshader: b/303705884 in Ganesh, but
220 // we aren't currently testing that in Graphite yet so leaving that off the workaround for now
221 // until we run into it.
222 if (kQualcomm_VkVendor == properties.vendorID) {
223 fMustLoadFullImageForMSAA = true;
224 }
225 }
226
227 // These are all the valid VkFormats that we support in Skia. They are roughly ordered from most
228 // frequently used to least to improve look up times in arrays.
229 static constexpr VkFormat kVkFormats[] = {
230 VK_FORMAT_R8G8B8A8_UNORM,
231 VK_FORMAT_R8_UNORM,
232 VK_FORMAT_B8G8R8A8_UNORM,
233 VK_FORMAT_R5G6B5_UNORM_PACK16,
234 VK_FORMAT_R16G16B16A16_SFLOAT,
235 VK_FORMAT_R16_SFLOAT,
236 VK_FORMAT_R8G8B8_UNORM,
237 VK_FORMAT_R8G8_UNORM,
238 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
239 VK_FORMAT_A2R10G10B10_UNORM_PACK32,
240 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
241 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
242 VK_FORMAT_R8G8B8A8_SRGB,
243 VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,
244 VK_FORMAT_BC1_RGB_UNORM_BLOCK,
245 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
246 VK_FORMAT_R16_UNORM,
247 VK_FORMAT_R16G16_UNORM,
248 VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
249 VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
250 VK_FORMAT_R16G16B16A16_UNORM,
251 VK_FORMAT_R16G16_SFLOAT,
252 };
253 // These are all the valid depth/stencil formats that we support in Skia.
254 static constexpr VkFormat kDepthStencilVkFormats[] = {
255 VK_FORMAT_S8_UINT,
256 VK_FORMAT_D16_UNORM,
257 VK_FORMAT_D32_SFLOAT,
258 VK_FORMAT_D24_UNORM_S8_UINT,
259 VK_FORMAT_D32_SFLOAT_S8_UINT,
260 };
261
getDefaultSampledTextureInfo(SkColorType ct,Mipmapped mipmapped,Protected isProtected,Renderable isRenderable) const262 TextureInfo VulkanCaps::getDefaultSampledTextureInfo(SkColorType ct,
263 Mipmapped mipmapped,
264 Protected isProtected,
265 Renderable isRenderable) const {
266 VkFormat format = this->getFormatFromColorType(ct);
267 const FormatInfo& formatInfo = this->getFormatInfo(format);
268 static constexpr int defaultSampleCount = 1;
269 if ((isProtected == Protected::kYes && !this->protectedSupport()) ||
270 !formatInfo.isTexturable(VK_IMAGE_TILING_OPTIMAL) ||
271 (isRenderable == Renderable::kYes &&
272 !formatInfo.isRenderable(VK_IMAGE_TILING_OPTIMAL, defaultSampleCount)) ) {
273 return {};
274 }
275
276 VulkanTextureInfo info;
277 info.fSampleCount = defaultSampleCount;
278 info.fMipmapped = mipmapped;
279 info.fFlags = (isProtected == Protected::kYes) ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
280 info.fFormat = format;
281 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
282 info.fImageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
283 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
284 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
285 if (isRenderable == Renderable::kYes) {
286 // We make all renderable images support being used as input attachment
287 info.fImageUsageFlags = info.fImageUsageFlags |
288 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
289 VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
290 }
291 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
292 info.fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
293
294 return TextureInfos::MakeVulkan(info);
295 }
296
getTextureInfoForSampledCopy(const TextureInfo & textureInfo,Mipmapped mipmapped) const297 TextureInfo VulkanCaps::getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
298 Mipmapped mipmapped) const {
299 VulkanTextureInfo info;
300 if (!TextureInfos::GetVulkanTextureInfo(textureInfo, &info)) {
301 return {};
302 }
303
304 info.fSampleCount = 1;
305 info.fMipmapped = mipmapped;
306 info.fFlags =
307 (textureInfo.isProtected() == Protected::kYes) ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
308 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
309 info.fImageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
310 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
311 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
312
313 return TextureInfos::MakeVulkan(info);
314 }
315
316 namespace {
format_from_compression(SkTextureCompressionType compression)317 VkFormat format_from_compression(SkTextureCompressionType compression) {
318 switch (compression) {
319 case SkTextureCompressionType::kETC2_RGB8_UNORM:
320 return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
321 case SkTextureCompressionType::kBC1_RGB8_UNORM:
322 return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
323 case SkTextureCompressionType::kBC1_RGBA8_UNORM:
324 return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
325 default:
326 return VK_FORMAT_UNDEFINED;
327 }
328 }
329 }
330
getDefaultCompressedTextureInfo(SkTextureCompressionType compression,Mipmapped mipmapped,Protected isProtected) const331 TextureInfo VulkanCaps::getDefaultCompressedTextureInfo(SkTextureCompressionType compression,
332 Mipmapped mipmapped,
333 Protected isProtected) const {
334 VkFormat format = format_from_compression(compression);
335 const FormatInfo& formatInfo = this->getFormatInfo(format);
336 static constexpr int defaultSampleCount = 1;
337 if ((isProtected == Protected::kYes && !this->protectedSupport()) ||
338 !formatInfo.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
339 return {};
340 }
341
342 VulkanTextureInfo info;
343 info.fSampleCount = defaultSampleCount;
344 info.fMipmapped = mipmapped;
345 info.fFlags = (isProtected == Protected::kYes) ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
346 info.fFormat = format;
347 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
348 info.fImageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
349 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
350 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
351 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
352 info.fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
353
354 return TextureInfos::MakeVulkan(info);
355 }
356
getDefaultMSAATextureInfo(const TextureInfo & singleSampledInfo,Discardable discardable) const357 TextureInfo VulkanCaps::getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
358 Discardable discardable) const {
359 if (fDefaultMSAASamples <= 1) {
360 return {};
361 }
362
363 const VkFormat singleSpecFormat = TextureInfos::GetVkFormat(singleSampledInfo);
364 const FormatInfo& formatInfo = this->getFormatInfo(singleSpecFormat);
365 if ((singleSampledInfo.isProtected() == Protected::kYes && !this->protectedSupport()) ||
366 !formatInfo.isRenderable(VK_IMAGE_TILING_OPTIMAL, fDefaultMSAASamples)) {
367 return {};
368 }
369
370 VulkanTextureInfo info;
371 info.fSampleCount = fDefaultMSAASamples;
372 info.fMipmapped = Mipmapped::kNo;
373 info.fFlags = (singleSampledInfo.isProtected() == Protected::kYes) ?
374 VK_IMAGE_CREATE_PROTECTED_BIT : 0;
375 info.fFormat = singleSpecFormat;
376 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
377
378 /**
379 * Graphite, unlike ganesh, does not require a dedicated MSAA attachment on every surface.
380 * MSAA textures now get resolved within the scope of a render pass, which can be done simply
381 * with the color attachment usage flag. So we no longer require transfer src/dst usage flags.
382 */
383 VkImageUsageFlags flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
384 if (discardable == Discardable::kYes && fSupportsMemorylessAttachments) {
385 flags = flags | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
386 }
387
388 info.fImageUsageFlags = flags;
389 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
390 info.fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
391
392 return TextureInfos::MakeVulkan(info);
393 }
394
getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags> flags,uint32_t sampleCount,Protected isProtected) const395 TextureInfo VulkanCaps::getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags> flags,
396 uint32_t sampleCount,
397 Protected isProtected) const {
398 VkFormat format = this->getFormatFromDepthStencilFlags(flags);
399 const DepthStencilFormatInfo& formatInfo = this->getDepthStencilFormatInfo(format);
400 if ( (isProtected == Protected::kYes && !this->protectedSupport()) ||
401 !formatInfo.isDepthStencilSupported(formatInfo.fFormatProperties.optimalTilingFeatures) ||
402 !formatInfo.fSupportedSampleCounts.isSampleCountSupported(sampleCount)) {
403 return {};
404 }
405
406 VulkanTextureInfo info;
407 info.fSampleCount = sampleCount;
408 info.fMipmapped = Mipmapped::kNo;
409 info.fFlags = (isProtected == Protected::kYes) ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
410 info.fFormat = format;
411 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
412 // TODO: Passing in a discardable flag to this method, and if true, add the TRANSIENT bit.
413 info.fImageUsageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
414 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
415 info.fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
416
417 return TextureInfos::MakeVulkan(info);
418 }
419
getDefaultStorageTextureInfo(SkColorType colorType) const420 TextureInfo VulkanCaps::getDefaultStorageTextureInfo(SkColorType colorType) const {
421 VkFormat format = this->getFormatFromColorType(colorType);
422 const FormatInfo& formatInfo = this->getFormatInfo(format);
423 if (!formatInfo.isTexturable(VK_IMAGE_TILING_OPTIMAL) ||
424 !formatInfo.isStorage(VK_IMAGE_TILING_OPTIMAL)) {
425 return {};
426 }
427
428 VulkanTextureInfo info;
429 info.fSampleCount = 1;
430 info.fMipmapped = Mipmapped::kNo;
431 info.fFlags = 0;
432 info.fFormat = format;
433 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
434 // Storage textures are currently always sampleable from a shader
435 info.fImageUsageFlags = VK_IMAGE_USAGE_STORAGE_BIT |
436 VK_IMAGE_USAGE_SAMPLED_BIT |
437 VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
438 info.fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
439 info.fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
440
441 return TextureInfos::MakeVulkan(info);
442 }
443
channelMask(const TextureInfo & textureInfo) const444 uint32_t VulkanCaps::channelMask(const TextureInfo& textureInfo) const {
445 return skgpu::VkFormatChannels(TextureInfos::GetVkFormat(textureInfo));
446 }
447
initFormatTable(const skgpu::VulkanInterface * interface,VkPhysicalDevice physDev,const VkPhysicalDeviceProperties & properties)448 void VulkanCaps::initFormatTable(const skgpu::VulkanInterface* interface,
449 VkPhysicalDevice physDev,
450 const VkPhysicalDeviceProperties& properties) {
451 static_assert(std::size(kVkFormats) == VulkanCaps::kNumVkFormats,
452 "Size of VkFormats array must match static value in header");
453
454 std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, VK_FORMAT_UNDEFINED);
455
456 // Go through all the formats and init their support surface and data ColorTypes.
457 // Format: VK_FORMAT_R8G8B8A8_UNORM
458 {
459 constexpr VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
460 auto& info = this->getFormatInfo(format);
461 info.init(interface, physDev, properties, format);
462 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
463 info.fColorTypeInfoCount = 2;
464 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
465 int ctIdx = 0;
466 // Format: VK_FORMAT_R8G8B8A8_UNORM, Surface: kRGBA_8888
467 {
468 constexpr SkColorType ct = SkColorType::kRGBA_8888_SkColorType;
469 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
470 ctInfo.fColorType = ct;
471 ctInfo.fTransferColorType = ct;
472 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
473 }
474 // Format: VK_FORMAT_R8G8B8A8_UNORM, Surface: kRGB_888x
475 {
476 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
477 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
478 ctInfo.fColorType = ct;
479 ctInfo.fTransferColorType = ct;
480 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
481 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
482 }
483 }
484 }
485
486 // Format: VK_FORMAT_R8_UNORM
487 {
488 constexpr VkFormat format = VK_FORMAT_R8_UNORM;
489 auto& info = this->getFormatInfo(format);
490 info.init(interface, physDev, properties, format);
491 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
492 info.fColorTypeInfoCount = 3;
493 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
494 int ctIdx = 0;
495 // Format: VK_FORMAT_R8_UNORM, Surface: kR_8
496 {
497 constexpr SkColorType ct = SkColorType::kR8_unorm_SkColorType;
498 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
499 ctInfo.fColorType = ct;
500 ctInfo.fTransferColorType = ct;
501 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
502 }
503 // Format: VK_FORMAT_R8_UNORM, Surface: kAlpha_8
504 {
505 constexpr SkColorType ct = SkColorType::kAlpha_8_SkColorType;
506 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
507 ctInfo.fColorType = ct;
508 ctInfo.fTransferColorType = ct;
509 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
510 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
511 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
512 }
513 // Format: VK_FORMAT_R8_UNORM, Surface: kGray_8
514 {
515 constexpr SkColorType ct = SkColorType::kGray_8_SkColorType;
516 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
517 ctInfo.fColorType = ct;
518 ctInfo.fTransferColorType = ct;
519 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
520 ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
521 }
522 }
523 }
524
525 // Format: VK_FORMAT_B8G8R8A8_UNORM
526 {
527 constexpr VkFormat format = VK_FORMAT_B8G8R8A8_UNORM;
528 auto& info = this->getFormatInfo(format);
529 info.init(interface, physDev, properties, format);
530 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
531 info.fColorTypeInfoCount = 1;
532 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
533 int ctIdx = 0;
534 // Format: VK_FORMAT_B8G8R8A8_UNORM, Surface: kBGRA_8888
535 {
536 constexpr SkColorType ct = SkColorType::kBGRA_8888_SkColorType;
537 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
538 ctInfo.fColorType = ct;
539 ctInfo.fTransferColorType = ct;
540 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
541 }
542 }
543 }
544 // Format: VK_FORMAT_R5G6B5_UNORM_PACK16
545 {
546 constexpr VkFormat format = VK_FORMAT_R5G6B5_UNORM_PACK16;
547 auto& info = this->getFormatInfo(format);
548 info.init(interface, physDev, properties, format);
549 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
550 info.fColorTypeInfoCount = 1;
551 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
552 int ctIdx = 0;
553 // Format: VK_FORMAT_R5G6B5_UNORM_PACK16, Surface: kRGB_565_SkColorType
554 {
555 constexpr SkColorType ct = SkColorType::kRGB_565_SkColorType;
556 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
557 ctInfo.fColorType = ct;
558 ctInfo.fTransferColorType = ct;
559 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
560 }
561 }
562 }
563 // Format: VK_FORMAT_R16G16B16A16_SFLOAT
564 {
565 constexpr VkFormat format = VK_FORMAT_R16G16B16A16_SFLOAT;
566 auto& info = this->getFormatInfo(format);
567 info.init(interface, physDev, properties, format);
568 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
569 info.fColorTypeInfoCount = 2;
570 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
571 int ctIdx = 0;
572 // Format: VK_FORMAT_R16G16B16A16_SFLOAT, Surface: kRGBA_F16_SkColorType
573 {
574 constexpr SkColorType ct = SkColorType::kRGBA_F16_SkColorType;
575 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
576 ctInfo.fColorType = ct;
577 ctInfo.fTransferColorType = ct;
578 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
579 }
580 // Format: VK_FORMAT_R16G16B16A16_SFLOAT, Surface: kRGB_F16F16F16x_SkColorType
581 {
582 constexpr SkColorType ct = SkColorType::kRGB_F16F16F16x_SkColorType;
583 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
584 ctInfo.fColorType = ct;
585 ctInfo.fTransferColorType = ct;
586 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
587 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
588 }
589 }
590 }
591 // Format: VK_FORMAT_R16_SFLOAT
592 {
593 constexpr VkFormat format = VK_FORMAT_R16_SFLOAT;
594 auto& info = this->getFormatInfo(format);
595 info.init(interface, physDev, properties, format);
596 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
597 info.fColorTypeInfoCount = 1;
598 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
599 int ctIdx = 0;
600 // Format: VK_FORMAT_R16_SFLOAT, Surface: kAlpha_F16
601 {
602 constexpr SkColorType ct = SkColorType::kA16_float_SkColorType;
603 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
604 ctInfo.fColorType = ct;
605 ctInfo.fTransferColorType = ct;
606 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
607 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
608 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
609 }
610 }
611 }
612 // Format: VK_FORMAT_R8G8B8_UNORM
613 {
614 constexpr VkFormat format = VK_FORMAT_R8G8B8_UNORM;
615 auto& info = this->getFormatInfo(format);
616 info.init(interface, physDev, properties, format);
617 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
618 info.fColorTypeInfoCount = 1;
619 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
620 int ctIdx = 0;
621 // Format: VK_FORMAT_R8G8B8_UNORM, Surface: kRGB_888x
622 {
623 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
624 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
625 ctInfo.fColorType = ct;
626 // This SkColorType is a lie, but we don't have a kRGB_888_SkColorType. The Vulkan
627 // format is 3 bpp so we must manualy convert to/from this and kRGB_888x when doing
628 // transfers. We signal this need for manual conversions in the
629 // supportedRead/WriteColorType calls.
630 ctInfo.fTransferColorType = SkColorType::kRGB_888x_SkColorType;
631 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
632 }
633 }
634 }
635 // Format: VK_FORMAT_R8G8_UNORM
636 {
637 constexpr VkFormat format = VK_FORMAT_R8G8_UNORM;
638 auto& info = this->getFormatInfo(format);
639 info.init(interface, physDev, properties, format);
640 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
641 info.fColorTypeInfoCount = 1;
642 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
643 int ctIdx = 0;
644 // Format: VK_FORMAT_R8G8_UNORM, Surface: kR8G8_unorm
645 {
646 constexpr SkColorType ct = SkColorType::kR8G8_unorm_SkColorType;
647 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
648 ctInfo.fColorType = ct;
649 ctInfo.fTransferColorType = ct;
650 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
651 }
652 }
653 }
654 // Format: VK_FORMAT_A2B10G10R10_UNORM_PACK32
655 {
656 constexpr VkFormat format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
657 auto& info = this->getFormatInfo(format);
658 info.init(interface, physDev, properties, format);
659 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
660 info.fColorTypeInfoCount = 2;
661 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
662 int ctIdx = 0;
663 // Format: VK_FORMAT_A2B10G10R10_UNORM_PACK32, Surface: kRGBA_1010102
664 {
665 constexpr SkColorType ct = SkColorType::kRGBA_1010102_SkColorType;
666 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
667 ctInfo.fColorType = ct;
668 ctInfo.fTransferColorType = ct;
669 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
670 }
671 // Format: VK_FORMAT_A2B10G10R10_UNORM_PACK32, Surface: kRGB_101010x
672 {
673 constexpr SkColorType ct = SkColorType::kRGB_101010x_SkColorType;
674 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
675 ctInfo.fColorType = ct;
676 ctInfo.fTransferColorType = ct;
677 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
678 ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
679 }
680 }
681 }
682 // Format: VK_FORMAT_A2R10G10B10_UNORM_PACK32
683 {
684 constexpr VkFormat format = VK_FORMAT_A2R10G10B10_UNORM_PACK32;
685 auto& info = this->getFormatInfo(format);
686 info.init(interface, physDev, properties, format);
687 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
688 info.fColorTypeInfoCount = 1;
689 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
690 int ctIdx = 0;
691 // Format: VK_FORMAT_A2R10G10B10_UNORM_PACK32, Surface: kBGRA_1010102
692 {
693 constexpr SkColorType ct = SkColorType::kBGRA_1010102_SkColorType;
694 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
695 ctInfo.fColorType = ct;
696 ctInfo.fTransferColorType = ct;
697 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
698 }
699 }
700 }
701 // Format: VK_FORMAT_B4G4R4A4_UNORM_PACK16
702 {
703 constexpr VkFormat format = VK_FORMAT_B4G4R4A4_UNORM_PACK16;
704 auto& info = this->getFormatInfo(format);
705 info.init(interface, physDev, properties, format);
706 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
707 info.fColorTypeInfoCount = 1;
708 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
709 int ctIdx = 0;
710 // Format: VK_FORMAT_B4G4R4A4_UNORM_PACK16, Surface: kARGB_4444_SkColorType
711 {
712 constexpr SkColorType ct = SkColorType::kARGB_4444_SkColorType;
713 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
714 ctInfo.fColorType = ct;
715 ctInfo.fTransferColorType = ct;
716 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
717 ctInfo.fReadSwizzle = skgpu::Swizzle::BGRA();
718 ctInfo.fWriteSwizzle = skgpu::Swizzle::BGRA();
719 }
720 }
721 }
722
723 // Format: VK_FORMAT_R4G4B4A4_UNORM_PACK16
724 {
725 constexpr VkFormat format = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
726 auto& info = this->getFormatInfo(format);
727 info.init(interface, physDev, properties, format);
728 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
729 info.fColorTypeInfoCount = 1;
730 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
731 int ctIdx = 0;
732 // Format: VK_FORMAT_R4G4B4A4_UNORM_PACK16, Surface: kARGB_4444_SkColorType
733 {
734 constexpr SkColorType ct = SkColorType::kARGB_4444_SkColorType;
735 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
736 ctInfo.fColorType = ct;
737 ctInfo.fTransferColorType = ct;
738 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
739 }
740 }
741 }
742 // Format: VK_FORMAT_R8G8B8A8_SRGB
743 {
744 constexpr VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
745 auto& info = this->getFormatInfo(format);
746 info.init(interface, physDev, properties, format);
747 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
748 info.fColorTypeInfoCount = 1;
749 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
750 int ctIdx = 0;
751 // Format: VK_FORMAT_R8G8B8A8_SRGB, Surface: kRGBA_8888_SRGB
752 {
753 constexpr SkColorType ct = SkColorType::kSRGBA_8888_SkColorType;
754 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
755 ctInfo.fColorType = ct;
756 ctInfo.fTransferColorType = ct;
757 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
758 }
759 }
760 }
761 // Format: VK_FORMAT_R16_UNORM
762 {
763 constexpr VkFormat format = VK_FORMAT_R16_UNORM;
764 auto& info = this->getFormatInfo(format);
765 info.init(interface, physDev, properties, format);
766 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
767 info.fColorTypeInfoCount = 1;
768 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
769 int ctIdx = 0;
770 // Format: VK_FORMAT_R16_UNORM, Surface: kAlpha_16
771 {
772 constexpr SkColorType ct = SkColorType::kA16_unorm_SkColorType;
773 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
774 ctInfo.fColorType = ct;
775 ctInfo.fTransferColorType = ct;
776 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
777 ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
778 ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
779 }
780 }
781 }
782 // Format: VK_FORMAT_R16G16_UNORM
783 {
784 constexpr VkFormat format = VK_FORMAT_R16G16_UNORM;
785 auto& info = this->getFormatInfo(format);
786 info.init(interface, physDev, properties, format);
787 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
788 info.fColorTypeInfoCount = 1;
789 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
790 int ctIdx = 0;
791 // Format: VK_FORMAT_R16G16_UNORM, Surface: kRG_1616
792 {
793 constexpr SkColorType ct = SkColorType::kR16G16_unorm_SkColorType;
794 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
795 ctInfo.fColorType = ct;
796 ctInfo.fTransferColorType = ct;
797 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
798 }
799 }
800 }
801 // Format: VK_FORMAT_R16G16B16A16_UNORM
802 {
803 constexpr VkFormat format = VK_FORMAT_R16G16B16A16_UNORM;
804 auto& info = this->getFormatInfo(format);
805 info.init(interface, physDev, properties, format);
806 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
807 info.fColorTypeInfoCount = 1;
808 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
809 int ctIdx = 0;
810 // Format: VK_FORMAT_R16G16B16A16_UNORM, Surface: kRGBA_16161616
811 {
812 constexpr SkColorType ct = SkColorType::kR16G16B16A16_unorm_SkColorType;
813 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
814 ctInfo.fColorType = ct;
815 ctInfo.fTransferColorType = ct;
816 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
817 }
818 }
819 }
820 // Format: VK_FORMAT_R16G16_SFLOAT
821 {
822 constexpr VkFormat format = VK_FORMAT_R16G16_SFLOAT;
823 auto& info = this->getFormatInfo(format);
824 info.init(interface, physDev, properties, format);
825 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
826 info.fColorTypeInfoCount = 1;
827 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
828 int ctIdx = 0;
829 // Format: VK_FORMAT_R16G16_SFLOAT, Surface: kRG_F16
830 {
831 constexpr SkColorType ct = SkColorType::kR16G16_float_SkColorType;
832 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
833 ctInfo.fColorType = ct;
834 ctInfo.fTransferColorType = ct;
835 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
836 }
837 }
838 }
839 // Format: VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM
840 {
841 constexpr VkFormat format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
842 auto& info = this->getFormatInfo(format);
843 if (fSupportsYcbcrConversion) {
844 info.init(interface, physDev, properties, format);
845 }
846 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
847 info.fColorTypeInfoCount = 1;
848 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
849 int ctIdx = 0;
850 // Format: VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, Surface: kRGB_888x
851 {
852 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
853 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
854 ctInfo.fColorType = ct;
855 ctInfo.fTransferColorType = ct;
856 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
857 }
858 SkDEBUGCODE(info.fIsWrappedOnly = true;)
859 }
860 }
861 // Format: VK_FORMAT_G8_B8R8_2PLANE_420_UNORM
862 {
863 constexpr VkFormat format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
864 auto& info = this->getFormatInfo(format);
865 if (fSupportsYcbcrConversion) {
866 info.init(interface, physDev, properties, format);
867 }
868 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
869 info.fColorTypeInfoCount = 1;
870 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
871 int ctIdx = 0;
872 // Format: VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, Surface: kRGB_888x
873 {
874 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
875 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
876 ctInfo.fColorType = ct;
877 ctInfo.fTransferColorType = ct;
878 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
879 }
880 SkDEBUGCODE(info.fIsWrappedOnly = true;)
881 }
882 }
883 // Format: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK
884 {
885 constexpr VkFormat format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
886 auto& info = this->getFormatInfo(format);
887 info.init(interface, physDev, properties, format);
888 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
889 info.fColorTypeInfoCount = 1;
890 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
891 int ctIdx = 0;
892 // Format: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, Surface: kRGB_888x
893 {
894 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
895 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
896 ctInfo.fColorType = ct;
897 ctInfo.fTransferColorType = ct;
898 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
899 }
900 }
901 }
902
903 // Format: VK_FORMAT_BC1_RGB_UNORM_BLOCK
904 {
905 constexpr VkFormat format = VK_FORMAT_BC1_RGB_UNORM_BLOCK;
906 auto& info = this->getFormatInfo(format);
907 info.init(interface, physDev, properties, format);
908 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
909 info.fColorTypeInfoCount = 1;
910 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
911 int ctIdx = 0;
912 // Format: VK_FORMAT_BC1_RGB_UNORM_BLOCK, Surface: kRGB_888x
913 {
914 constexpr SkColorType ct = SkColorType::kRGB_888x_SkColorType;
915 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
916 ctInfo.fColorType = ct;
917 ctInfo.fTransferColorType = ct;
918 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
919 }
920 }
921 }
922
923 // Format: VK_FORMAT_BC1_RGBA_UNORM_BLOCK
924 {
925 constexpr VkFormat format = VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
926 auto& info = this->getFormatInfo(format);
927 info.init(interface, physDev, properties, format);
928 if (info.isTexturable(VK_IMAGE_TILING_OPTIMAL)) {
929 info.fColorTypeInfoCount = 1;
930 info.fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info.fColorTypeInfoCount);
931 int ctIdx = 0;
932 // Format: VK_FORMAT_BC1_RGBA_UNORM_BLOCK, Surface: kRGB_888x
933 {
934 constexpr SkColorType ct = SkColorType::kRGBA_8888_SkColorType;
935 auto& ctInfo = info.fColorTypeInfos[ctIdx++];
936 ctInfo.fColorType = ct;
937 ctInfo.fTransferColorType = ct;
938 ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
939 }
940 }
941 }
942
943 ////////////////////////////////////////////////////////////////////////////
944 // Map SkColorType (used for creating Surfaces) to VkFormats. The order in which the formats are
945 // passed into the setColorType function indicates the priority in selecting which format we use
946 // for a given SkColorType.
947 typedef SkColorType ct;
948
949 this->setColorType(ct::kAlpha_8_SkColorType, { VK_FORMAT_R8_UNORM });
950 this->setColorType(ct::kRGB_565_SkColorType, { VK_FORMAT_R5G6B5_UNORM_PACK16 });
951 this->setColorType(ct::kARGB_4444_SkColorType, { VK_FORMAT_R4G4B4A4_UNORM_PACK16,
952 VK_FORMAT_B4G4R4A4_UNORM_PACK16 });
953 this->setColorType(ct::kRGBA_8888_SkColorType, { VK_FORMAT_R8G8B8A8_UNORM });
954 this->setColorType(ct::kSRGBA_8888_SkColorType, { VK_FORMAT_R8G8B8A8_SRGB });
955 this->setColorType(ct::kRGB_888x_SkColorType, { VK_FORMAT_R8G8B8_UNORM,
956 VK_FORMAT_R8G8B8A8_UNORM });
957 this->setColorType(ct::kR8G8_unorm_SkColorType, { VK_FORMAT_R8G8_UNORM });
958 this->setColorType(ct::kBGRA_8888_SkColorType, { VK_FORMAT_B8G8R8A8_UNORM });
959 this->setColorType(ct::kRGBA_1010102_SkColorType, { VK_FORMAT_A2B10G10R10_UNORM_PACK32 });
960 this->setColorType(ct::kBGRA_1010102_SkColorType, { VK_FORMAT_A2R10G10B10_UNORM_PACK32 });
961 this->setColorType(ct::kRGB_101010x_SkColorType, { VK_FORMAT_A2B10G10R10_UNORM_PACK32 });
962 this->setColorType(ct::kGray_8_SkColorType, { VK_FORMAT_R8_UNORM });
963 this->setColorType(ct::kA16_float_SkColorType, { VK_FORMAT_R16_SFLOAT });
964 this->setColorType(ct::kRGBA_F16_SkColorType, { VK_FORMAT_R16G16B16A16_SFLOAT });
965 this->setColorType(ct::kRGB_F16F16F16x_SkColorType, { VK_FORMAT_R16G16B16A16_SFLOAT });
966 this->setColorType(ct::kA16_unorm_SkColorType, { VK_FORMAT_R16_UNORM });
967 this->setColorType(ct::kR16G16_unorm_SkColorType, { VK_FORMAT_R16G16_UNORM });
968 this->setColorType(ct::kR16G16B16A16_unorm_SkColorType, { VK_FORMAT_R16G16B16A16_UNORM });
969 this->setColorType(ct::kR16G16_float_SkColorType, { VK_FORMAT_R16G16_SFLOAT });
970 }
971
972 namespace {
set_ds_flags_to_format(VkFormat & slot,VkFormat format)973 void set_ds_flags_to_format(VkFormat& slot, VkFormat format) {
974 if (slot == VK_FORMAT_UNDEFINED) {
975 slot = format;
976 }
977 }
978 } // namespace
979
initDepthStencilFormatTable(const skgpu::VulkanInterface * interface,VkPhysicalDevice physDev,const VkPhysicalDeviceProperties & properties)980 void VulkanCaps::initDepthStencilFormatTable(const skgpu::VulkanInterface* interface,
981 VkPhysicalDevice physDev,
982 const VkPhysicalDeviceProperties& properties) {
983 static_assert(std::size(kDepthStencilVkFormats) == VulkanCaps::kNumDepthStencilVkFormats,
984 "Size of DepthStencilVkFormats array must match static value in header");
985
986 using DSFlags = SkEnumBitMask<DepthStencilFlags>;
987 constexpr DSFlags stencilFlags = DepthStencilFlags::kStencil;
988 constexpr DSFlags depthFlags = DepthStencilFlags::kDepth;
989 constexpr DSFlags dsFlags = DepthStencilFlags::kDepthStencil;
990
991 std::fill_n(fDepthStencilFlagsToFormatTable, kNumDepthStencilFlags, VK_FORMAT_UNDEFINED);
992 // Format: VK_FORMAT_S8_UINT
993 {
994 constexpr VkFormat format = VK_FORMAT_S8_UINT;
995 auto& info = this->getDepthStencilFormatInfo(format);
996 info.init(interface, physDev, properties, format);
997 if (info.fFormatProperties.optimalTilingFeatures &
998 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
999 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[stencilFlags.value()], format);
1000 }
1001 }
1002 // Format: VK_FORMAT_D16_UNORM
1003 {
1004 // Qualcomm drivers will report OUT_OF_HOST_MEMORY when binding memory to a VkImage with
1005 // D16_UNORM in a protected context. Using D32_SFLOAT succeeds, so clearly it's not actually
1006 // out of memory. D16_UNORM appears to function correctly in unprotected contexts.
1007 const bool disableD16InProtected = this->protectedSupport() &&
1008 kQualcomm_VkVendor == properties.vendorID;
1009 if (!disableD16InProtected) {
1010 constexpr VkFormat format = VK_FORMAT_D16_UNORM;
1011 auto& info = this->getDepthStencilFormatInfo(format);
1012 info.init(interface, physDev, properties, format);
1013 if (info.fFormatProperties.optimalTilingFeatures &
1014 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
1015 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[depthFlags.value()], format);
1016 }
1017 }
1018 }
1019 // Format: VK_FORMAT_D32_SFLOAT
1020 {
1021 constexpr VkFormat format = VK_FORMAT_D32_SFLOAT;
1022 auto& info = this->getDepthStencilFormatInfo(format);
1023 info.init(interface, physDev, properties, format);
1024 if (info.fFormatProperties.optimalTilingFeatures &
1025 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
1026 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[depthFlags.value()], format);
1027 }
1028 }
1029 // Format: VK_FORMAT_D24_UNORM_S8_UINT
1030 {
1031 constexpr VkFormat format = VK_FORMAT_D24_UNORM_S8_UINT;
1032 auto& info = this->getDepthStencilFormatInfo(format);
1033 info.init(interface, physDev, properties, format);
1034 if (info.fFormatProperties.optimalTilingFeatures &
1035 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
1036 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[stencilFlags.value()], format);
1037 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[depthFlags.value()], format);
1038 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[dsFlags.value()], format);
1039 }
1040 }
1041 // Format: VK_FORMAT_D32_SFLOAT_S8_UINT
1042 {
1043 constexpr VkFormat format = VK_FORMAT_D32_SFLOAT_S8_UINT;
1044 auto& info = this->getDepthStencilFormatInfo(format);
1045 info.init(interface, physDev, properties, format);
1046 if (info.fFormatProperties.optimalTilingFeatures &
1047 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
1048 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[stencilFlags.value()], format);
1049 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[depthFlags.value()], format);
1050 set_ds_flags_to_format(fDepthStencilFlagsToFormatTable[dsFlags.value()], format);
1051 }
1052 }
1053 }
1054
initSampleCounts(const skgpu::VulkanInterface * interface,VkPhysicalDevice physDev,const VkPhysicalDeviceProperties & physProps,VkFormat format,VkImageUsageFlags usage)1055 void VulkanCaps::SupportedSampleCounts::initSampleCounts(const skgpu::VulkanInterface* interface,
1056 VkPhysicalDevice physDev,
1057 const VkPhysicalDeviceProperties& physProps,
1058 VkFormat format,
1059 VkImageUsageFlags usage) {
1060 VkImageFormatProperties properties;
1061
1062 VkResult result;
1063 // VULKAN_CALL_RESULT requires a VulkanSharedContext for tracking DEVICE_LOST, but VulkanCaps
1064 // are initialized before a VulkanSharedContext is available. The _NOCHECK variant only requires
1065 // a VulkanInterface, so we can use that and log failures manually.
1066 VULKAN_CALL_RESULT_NOCHECK(interface,
1067 result,
1068 GetPhysicalDeviceImageFormatProperties(physDev,
1069 format,
1070 VK_IMAGE_TYPE_2D,
1071 VK_IMAGE_TILING_OPTIMAL,
1072 usage,
1073 0, // createFlags
1074 &properties));
1075 if (result != VK_SUCCESS) {
1076 SKGPU_LOG_W("Vulkan call GetPhysicalDeviceImageFormatProperties failed: %d", result);
1077 return;
1078 }
1079
1080 VkSampleCountFlags flags = properties.sampleCounts;
1081 if (flags & VK_SAMPLE_COUNT_1_BIT) {
1082 fSampleCounts.push_back(1);
1083 }
1084 if (kImagination_VkVendor == physProps.vendorID) {
1085 // MSAA does not work on imagination
1086 return;
1087 }
1088 if (kIntel_VkVendor == physProps.vendorID) {
1089 // MSAA doesn't work well on Intel GPUs chromium:527565, chromium:983926
1090 return;
1091 }
1092 if (flags & VK_SAMPLE_COUNT_2_BIT) {
1093 fSampleCounts.push_back(2);
1094 }
1095 if (flags & VK_SAMPLE_COUNT_4_BIT) {
1096 fSampleCounts.push_back(4);
1097 }
1098 if (flags & VK_SAMPLE_COUNT_8_BIT) {
1099 fSampleCounts.push_back(8);
1100 }
1101 if (flags & VK_SAMPLE_COUNT_16_BIT) {
1102 fSampleCounts.push_back(16);
1103 }
1104 // Standard sample locations are not defined for more than 16 samples, and we don't need more
1105 // than 16. Omit 32 and 64.
1106 }
1107
isSampleCountSupported(int requestedCount) const1108 bool VulkanCaps::SupportedSampleCounts::isSampleCountSupported(int requestedCount) const {
1109 requestedCount = std::max(1, requestedCount);
1110 for (int i = 0; i < fSampleCounts.size(); i++) {
1111 if (fSampleCounts[i] == requestedCount) {
1112 return true;
1113 } else if (requestedCount < fSampleCounts[i]) {
1114 return false;
1115 }
1116 }
1117 return false;
1118 }
1119
1120
1121 namespace {
is_texturable(VkFormatFeatureFlags flags)1122 bool is_texturable(VkFormatFeatureFlags flags) {
1123 return SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & flags) &&
1124 SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT & flags);
1125 }
1126
is_renderable(VkFormatFeatureFlags flags)1127 bool is_renderable(VkFormatFeatureFlags flags) {
1128 return SkToBool(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT & flags);
1129 }
1130
is_storage(VkFormatFeatureFlags flags)1131 bool is_storage(VkFormatFeatureFlags flags) {
1132 return SkToBool(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT & flags);
1133 }
1134
is_transfer_src(VkFormatFeatureFlags flags)1135 bool is_transfer_src(VkFormatFeatureFlags flags) {
1136 return SkToBool(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT & flags);
1137 }
1138
is_transfer_dst(VkFormatFeatureFlags flags)1139 bool is_transfer_dst(VkFormatFeatureFlags flags) {
1140 return SkToBool(VK_FORMAT_FEATURE_TRANSFER_DST_BIT & flags);
1141 }
1142 }
1143
init(const skgpu::VulkanInterface * interface,VkPhysicalDevice physDev,const VkPhysicalDeviceProperties & properties,VkFormat format)1144 void VulkanCaps::FormatInfo::init(const skgpu::VulkanInterface* interface,
1145 VkPhysicalDevice physDev,
1146 const VkPhysicalDeviceProperties& properties,
1147 VkFormat format) {
1148 memset(&fFormatProperties, 0, sizeof(VkFormatProperties));
1149 VULKAN_CALL(interface, GetPhysicalDeviceFormatProperties(physDev, format, &fFormatProperties));
1150
1151 if (is_renderable(fFormatProperties.optimalTilingFeatures)) {
1152 // We make all renderable images support being used as input attachment
1153 VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
1154 VK_IMAGE_USAGE_TRANSFER_DST_BIT |
1155 VK_IMAGE_USAGE_SAMPLED_BIT |
1156 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
1157 VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
1158 this->fSupportedSampleCounts.initSampleCounts(interface, physDev, properties, format,
1159 usageFlags);
1160 }
1161 }
1162
isTexturable(VkImageTiling imageTiling) const1163 bool VulkanCaps::FormatInfo::isTexturable(VkImageTiling imageTiling) const {
1164 switch (imageTiling) {
1165 case VK_IMAGE_TILING_OPTIMAL:
1166 return is_texturable(fFormatProperties.optimalTilingFeatures);
1167 case VK_IMAGE_TILING_LINEAR:
1168 return is_texturable(fFormatProperties.linearTilingFeatures);
1169 default:
1170 return false;
1171 }
1172 SkUNREACHABLE;
1173 }
1174
isRenderable(VkImageTiling imageTiling,uint32_t sampleCount) const1175 bool VulkanCaps::FormatInfo::isRenderable(VkImageTiling imageTiling,
1176 uint32_t sampleCount) const {
1177 if (!fSupportedSampleCounts.isSampleCountSupported(sampleCount)) {
1178 return false;
1179 }
1180 switch (imageTiling) {
1181 case VK_IMAGE_TILING_OPTIMAL:
1182 return is_renderable(fFormatProperties.optimalTilingFeatures);
1183 case VK_IMAGE_TILING_LINEAR:
1184 return is_renderable(fFormatProperties.linearTilingFeatures);
1185 default:
1186 return false;
1187 }
1188 SkUNREACHABLE;
1189 }
1190
isStorage(VkImageTiling imageTiling) const1191 bool VulkanCaps::FormatInfo::isStorage(VkImageTiling imageTiling) const {
1192 switch (imageTiling) {
1193 case VK_IMAGE_TILING_OPTIMAL:
1194 return is_storage(fFormatProperties.optimalTilingFeatures);
1195 case VK_IMAGE_TILING_LINEAR:
1196 return is_storage(fFormatProperties.linearTilingFeatures);
1197 default:
1198 return false;
1199 }
1200 SkUNREACHABLE;
1201 }
1202
isTransferSrc(VkImageTiling imageTiling) const1203 bool VulkanCaps::FormatInfo::isTransferSrc(VkImageTiling imageTiling) const {
1204 switch (imageTiling) {
1205 case VK_IMAGE_TILING_OPTIMAL:
1206 return is_transfer_src(fFormatProperties.optimalTilingFeatures);
1207 case VK_IMAGE_TILING_LINEAR:
1208 return is_transfer_src(fFormatProperties.linearTilingFeatures);
1209 default:
1210 return false;
1211 }
1212 SkUNREACHABLE;
1213 }
1214
isTransferDst(VkImageTiling imageTiling) const1215 bool VulkanCaps::FormatInfo::isTransferDst(VkImageTiling imageTiling) const {
1216 switch (imageTiling) {
1217 case VK_IMAGE_TILING_OPTIMAL:
1218 return is_transfer_dst(fFormatProperties.optimalTilingFeatures);
1219 case VK_IMAGE_TILING_LINEAR:
1220 return is_transfer_dst(fFormatProperties.linearTilingFeatures);
1221 default:
1222 return false;
1223 }
1224 SkUNREACHABLE;
1225 }
1226
setColorType(SkColorType colorType,std::initializer_list<VkFormat> formats)1227 void VulkanCaps::setColorType(SkColorType colorType, std::initializer_list<VkFormat> formats) {
1228 int idx = static_cast<int>(colorType);
1229 for (auto it = formats.begin(); it != formats.end(); ++it) {
1230 const auto& info = this->getFormatInfo(*it);
1231 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
1232 if (info.fColorTypeInfos[i].fColorType == colorType) {
1233 fColorTypeToFormatTable[idx] = *it;
1234 return;
1235 }
1236 }
1237 }
1238 }
1239
getFormatFromColorType(SkColorType colorType) const1240 VkFormat VulkanCaps::getFormatFromColorType(SkColorType colorType) const {
1241 int idx = static_cast<int>(colorType);
1242 return fColorTypeToFormatTable[idx];
1243 }
1244
getFormatInfo(VkFormat format)1245 VulkanCaps::FormatInfo& VulkanCaps::getFormatInfo(VkFormat format) {
1246 static_assert(std::size(kVkFormats) == VulkanCaps::kNumVkFormats,
1247 "Size of VkFormats array must match static value in header");
1248 for (size_t i = 0; i < std::size(kVkFormats); ++i) {
1249 if (kVkFormats[i] == format) {
1250 return fFormatTable[i];
1251 }
1252 }
1253 static FormatInfo kInvalidFormat;
1254 return kInvalidFormat;
1255 }
1256
getFormatInfo(VkFormat format) const1257 const VulkanCaps::FormatInfo& VulkanCaps::getFormatInfo(VkFormat format) const {
1258 VulkanCaps* nonConstThis = const_cast<VulkanCaps*>(this);
1259 return nonConstThis->getFormatInfo(format);
1260 }
1261
init(const skgpu::VulkanInterface * interface,VkPhysicalDevice physDev,const VkPhysicalDeviceProperties & properties,VkFormat format)1262 void VulkanCaps::DepthStencilFormatInfo::init(const skgpu::VulkanInterface* interface,
1263 VkPhysicalDevice physDev,
1264 const VkPhysicalDeviceProperties& properties,
1265 VkFormat format) {
1266 memset(&fFormatProperties, 0, sizeof(VkFormatProperties));
1267 VULKAN_CALL(interface, GetPhysicalDeviceFormatProperties(physDev, format, &fFormatProperties));
1268
1269 if (this->isDepthStencilSupported(fFormatProperties.optimalTilingFeatures)) {
1270 VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1271 fSupportedSampleCounts.initSampleCounts(interface, physDev, properties, format, usageFlags);
1272 }
1273 }
1274
isDepthStencilSupported(VkFormatFeatureFlags flags) const1275 bool VulkanCaps::DepthStencilFormatInfo::isDepthStencilSupported(VkFormatFeatureFlags flags) const {
1276 return SkToBool(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT & flags);
1277 }
1278
getFormatFromDepthStencilFlags(const SkEnumBitMask<DepthStencilFlags> & flags) const1279 VkFormat VulkanCaps::getFormatFromDepthStencilFlags(const SkEnumBitMask<DepthStencilFlags>& flags)
1280 const {
1281 return fDepthStencilFlagsToFormatTable[flags.value()];
1282 }
1283
getDepthStencilFormatInfo(VkFormat format)1284 VulkanCaps::DepthStencilFormatInfo& VulkanCaps::getDepthStencilFormatInfo(VkFormat format) {
1285 static_assert(std::size(kDepthStencilVkFormats) == VulkanCaps::kNumDepthStencilVkFormats,
1286 "Size of VkFormats array must match static value in header");
1287 for (size_t i = 0; i < std::size(kDepthStencilVkFormats); ++i) {
1288 if (kVkFormats[i] == format) {
1289 return fDepthStencilFormatTable[i];
1290 }
1291 }
1292 static DepthStencilFormatInfo kInvalidFormat;
1293 return kInvalidFormat;
1294 }
1295
getDepthStencilFormatInfo(VkFormat format) const1296 const VulkanCaps::DepthStencilFormatInfo& VulkanCaps::getDepthStencilFormatInfo(VkFormat format)
1297 const {
1298 VulkanCaps* nonConstThis = const_cast<VulkanCaps*>(this);
1299 return nonConstThis->getDepthStencilFormatInfo(format);
1300 }
1301
getColorTypeInfo(SkColorType ct,const TextureInfo & textureInfo) const1302 const Caps::ColorTypeInfo* VulkanCaps::getColorTypeInfo(SkColorType ct,
1303 const TextureInfo& textureInfo) const {
1304 VkFormat vkFormat = TextureInfos::GetVkFormat(textureInfo);
1305 if (vkFormat == VK_FORMAT_UNDEFINED) {
1306 // If VkFormat is undefined but there is a valid YCbCr conversion associated with the
1307 // texture, then we know we are using an external format and can return color type
1308 // info representative of external format color information.
1309 return TextureInfos::GetVulkanYcbcrConversionInfo(textureInfo).isValid()
1310 ? &fExternalFormatColorTypeInfo
1311 : nullptr;
1312 }
1313
1314 const FormatInfo& info = this->getFormatInfo(vkFormat);
1315 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
1316 const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
1317 if (ctInfo.fColorType == ct) {
1318 return &ctInfo;
1319 }
1320 }
1321
1322 return nullptr;
1323 }
1324
onIsTexturable(const TextureInfo & texInfo) const1325 bool VulkanCaps::onIsTexturable(const TextureInfo& texInfo) const {
1326 VulkanTextureInfo vkInfo;
1327 if (!TextureInfos::GetVulkanTextureInfo(texInfo, &vkInfo)) {
1328 return false;
1329 }
1330 return this->isTexturable(vkInfo);
1331 }
1332
isTexturable(const VulkanTextureInfo & vkInfo) const1333 bool VulkanCaps::isTexturable(const VulkanTextureInfo& vkInfo) const {
1334 // All images using external formats are required to be able to be sampled per Vulkan spec.
1335 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAndroidHardwareBufferFormatPropertiesANDROID.html#_description
1336 if (vkInfo.fFormat == VK_FORMAT_UNDEFINED && vkInfo.fYcbcrConversionInfo.isValid()) {
1337 return true;
1338 }
1339
1340 // Otherwise, we are working with a known format and can simply reference the format table info.
1341 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1342 return info.isTexturable(vkInfo.fImageTiling);
1343 }
1344
isRenderable(const TextureInfo & texInfo) const1345 bool VulkanCaps::isRenderable(const TextureInfo& texInfo) const {
1346 VulkanTextureInfo vkInfo;
1347 if (!TextureInfos::GetVulkanTextureInfo(texInfo, &vkInfo)) {
1348 return false;
1349 }
1350 return this->isRenderable(vkInfo);
1351 }
1352
isRenderable(const VulkanTextureInfo & vkInfo) const1353 bool VulkanCaps::isRenderable(const VulkanTextureInfo& vkInfo) const {
1354 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1355 return info.isRenderable(vkInfo.fImageTiling, vkInfo.fSampleCount);
1356 }
1357
isStorage(const TextureInfo & texInfo) const1358 bool VulkanCaps::isStorage(const TextureInfo& texInfo) const {
1359 VulkanTextureInfo vkInfo;
1360 if (!TextureInfos::GetVulkanTextureInfo(texInfo, &vkInfo)) {
1361 return false;
1362 }
1363
1364 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1365 return info.isStorage(vkInfo.fImageTiling);
1366 }
1367
isTransferSrc(const VulkanTextureInfo & vkInfo) const1368 bool VulkanCaps::isTransferSrc(const VulkanTextureInfo& vkInfo) const {
1369 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1370 return info.isTransferSrc(vkInfo.fImageTiling);
1371 }
1372
isTransferDst(const VulkanTextureInfo & vkInfo) const1373 bool VulkanCaps::isTransferDst(const VulkanTextureInfo& vkInfo) const {
1374 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1375 return info.isTransferDst(vkInfo.fImageTiling);
1376 }
1377
supportsWritePixels(const TextureInfo & texInfo) const1378 bool VulkanCaps::supportsWritePixels(const TextureInfo& texInfo) const {
1379 VulkanTextureInfo vkInfo;
1380 if (!TextureInfos::GetVulkanTextureInfo(texInfo, &vkInfo)) {
1381 return false;
1382 }
1383
1384 // Can't write if it needs a YCbCr sampler
1385 if (VkFormatNeedsYcbcrSampler(vkInfo.fFormat)) {
1386 return false;
1387 }
1388
1389 if (vkInfo.fSampleCount > 1) {
1390 return false;
1391 }
1392
1393 if (!SkToBool(vkInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
1394 return false;
1395 }
1396
1397 return true;
1398 }
1399
supportsReadPixels(const TextureInfo & texInfo) const1400 bool VulkanCaps::supportsReadPixels(const TextureInfo& texInfo) const {
1401 if (texInfo.isProtected() == Protected::kYes) {
1402 return false;
1403 }
1404
1405 VulkanTextureInfo vkInfo;
1406 if (!TextureInfos::GetVulkanTextureInfo(texInfo, &vkInfo)) {
1407 return false;
1408 }
1409
1410 // Can't read if it needs a YCbCr sampler
1411 if (VkFormatNeedsYcbcrSampler(vkInfo.fFormat)) {
1412 return false;
1413 }
1414
1415 if (VkFormatIsCompressed(vkInfo.fFormat)) {
1416 return false;
1417 }
1418
1419 if (vkInfo.fSampleCount > 1) {
1420 return false;
1421 }
1422
1423 if (!SkToBool(vkInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) {
1424 return false;
1425 }
1426
1427 return true;
1428 }
1429
supportedWritePixelsColorType(SkColorType dstColorType,const TextureInfo & dstTextureInfo,SkColorType srcColorType) const1430 std::pair<SkColorType, bool /*isRGBFormat*/> VulkanCaps::supportedWritePixelsColorType(
1431 SkColorType dstColorType,
1432 const TextureInfo& dstTextureInfo,
1433 SkColorType srcColorType) const {
1434 VulkanTextureInfo vkInfo;
1435 if (!TextureInfos::GetVulkanTextureInfo(dstTextureInfo, &vkInfo)) {
1436 return {kUnknown_SkColorType, false};
1437 }
1438
1439 // Can't write to YCbCr formats
1440 // TODO: Can't write to external formats, either
1441 if (VkFormatNeedsYcbcrSampler(vkInfo.fFormat)) {
1442 return {kUnknown_SkColorType, false};
1443 }
1444
1445 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1446 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
1447 const auto& ctInfo = info.fColorTypeInfos[i];
1448 if (ctInfo.fColorType == dstColorType) {
1449 return {ctInfo.fTransferColorType, vkInfo.fFormat == VK_FORMAT_R8G8B8_UNORM};
1450 }
1451 }
1452
1453 return {kUnknown_SkColorType, false};
1454 }
1455
supportedReadPixelsColorType(SkColorType srcColorType,const TextureInfo & srcTextureInfo,SkColorType dstColorType) const1456 std::pair<SkColorType, bool /*isRGBFormat*/> VulkanCaps::supportedReadPixelsColorType(
1457 SkColorType srcColorType,
1458 const TextureInfo& srcTextureInfo,
1459 SkColorType dstColorType) const {
1460 VulkanTextureInfo vkInfo;
1461 if (!TextureInfos::GetVulkanTextureInfo(srcTextureInfo, &vkInfo)) {
1462 return {kUnknown_SkColorType, false};
1463 }
1464
1465 // Can't read from YCbCr formats
1466 // TODO: external formats?
1467 if (VkFormatNeedsYcbcrSampler(vkInfo.fFormat)) {
1468 return {kUnknown_SkColorType, false};
1469 }
1470
1471 // TODO: handle compressed formats
1472 if (VkFormatIsCompressed(vkInfo.fFormat)) {
1473 SkASSERT(this->isTexturable(vkInfo));
1474 return {kUnknown_SkColorType, false};
1475 }
1476
1477 const FormatInfo& info = this->getFormatInfo(vkInfo.fFormat);
1478 for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
1479 const auto& ctInfo = info.fColorTypeInfos[i];
1480 if (ctInfo.fColorType == srcColorType) {
1481 return {ctInfo.fTransferColorType, vkInfo.fFormat == VK_FORMAT_R8G8B8_UNORM};
1482 }
1483 }
1484
1485 return {kUnknown_SkColorType, false};
1486 }
1487
makeGraphicsPipelineKey(const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc) const1488 UniqueKey VulkanCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
1489 const RenderPassDesc& renderPassDesc) const {
1490 UniqueKey pipelineKey;
1491 {
1492 static const skgpu::UniqueKey::Domain kGraphicsPipelineDomain =
1493 UniqueKey::GenerateDomain();
1494
1495 VulkanRenderPass::VulkanRenderPassMetaData rpMetaData {renderPassDesc};
1496
1497 // Reserve 3 uint32s for the render step id, paint id, and write swizzle.
1498 static constexpr int kUint32sNeededForPipelineInfo = 3;
1499 // The uint32s needed for a RenderPass is variable number, so consult rpMetaData to
1500 // determine how many to reserve.
1501 UniqueKey::Builder builder(&pipelineKey,
1502 kGraphicsPipelineDomain,
1503 kUint32sNeededForPipelineInfo + rpMetaData.fUint32DataCnt,
1504 "GraphicsPipeline");
1505
1506 int idx = 0;
1507 // Add GraphicsPipelineDesc information
1508 builder[idx++] = pipelineDesc.renderStepID();
1509 builder[idx++] = pipelineDesc.paintParamsID().asUInt();
1510 // Add RenderPass info relevant for pipeline creation that's not captured in RenderPass keys
1511 builder[idx++] = renderPassDesc.fWriteSwizzle.asKey();
1512 // Add RenderPassDesc information
1513 VulkanRenderPass::AddRenderPassInfoToKey(rpMetaData, builder, idx, /*compatibleOnly=*/true);
1514
1515 builder.finish();
1516 }
1517
1518 return pipelineKey;
1519 }
1520
makeSamplerKey(const SamplerDesc & samplerDesc) const1521 GraphiteResourceKey VulkanCaps::makeSamplerKey(const SamplerDesc& samplerDesc) const {
1522 GraphiteResourceKey samplerKey;
1523 const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan();
1524 static const ResourceType kSamplerType = GraphiteResourceKey::GenerateResourceType();
1525 // Non-format ycbcr and sampler information are guaranteed to fit within one uint32, so the size
1526 // of the returned span accurately captures the quantity of uint32s needed whether the sampler
1527 // is immutable or not.
1528 GraphiteResourceKey::Builder builder(&samplerKey, kSamplerType, samplerData.size(),
1529 Shareable::kYes);
1530
1531 for (size_t i = 0; i < samplerData.size(); i++) {
1532 builder[i] = samplerData[i];
1533 }
1534
1535 builder.finish();
1536 return samplerKey;
1537 }
1538
buildKeyForTexture(SkISize dimensions,const TextureInfo & info,ResourceType type,Shareable shareable,GraphiteResourceKey * key) const1539 void VulkanCaps::buildKeyForTexture(SkISize dimensions,
1540 const TextureInfo& info,
1541 ResourceType type,
1542 Shareable shareable,
1543 GraphiteResourceKey* key) const {
1544 SkASSERT(!dimensions.isEmpty());
1545
1546 const VulkanTextureSpec vkSpec = TextureInfos::GetVulkanTextureSpec(info);
1547 // We expect that the VkFormat enum is at most a 32-bit value.
1548 static_assert(VK_FORMAT_MAX_ENUM == 0x7FFFFFFF);
1549 // We should either be using a known VkFormat or have a valid ycbcr conversion.
1550 SkASSERT(vkSpec.fFormat != VK_FORMAT_UNDEFINED || vkSpec.fYcbcrConversionInfo.isValid());
1551
1552 uint32_t format = static_cast<uint32_t>(vkSpec.fFormat);
1553 uint32_t samples = SamplesToKey(info.numSamples());
1554 // We don't have to key the number of mip levels because it is inherit in the combination of
1555 // isMipped and dimensions.
1556 bool isMipped = info.mipmapped() == Mipmapped::kYes;
1557 Protected isProtected = info.isProtected();
1558
1559 // Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
1560 // amounts in the asserts must be less than or equal to 32. vkSpec.fFlags will go into its
1561 // own 32-bit block.
1562 SkASSERT(samples < (1u << 3)); // sample key is first 3 bits
1563 SkASSERT(static_cast<uint32_t>(isMipped) < (1u << 1)); // isMapped is 4th bit
1564 SkASSERT(static_cast<uint32_t>(isProtected) < (1u << 1)); // isProtected is 5th bit
1565 SkASSERT(vkSpec.fImageTiling < (1u << 1)); // imageTiling is 6th bit
1566 SkASSERT(vkSpec.fSharingMode < (1u << 1)); // sharingMode is 7th bit
1567 SkASSERT(vkSpec.fAspectMask < (1u << 11)); // aspectMask is bits 8 - 19
1568 SkASSERT(vkSpec.fImageUsageFlags < (1u << 12)); // imageUsageFlags are bits 20-32
1569
1570 // We need two uint32_ts for dimensions, 1 for format, and 2 for the rest of the information.
1571 static constexpr int kNum32DataCntNoYcbcr = 2 + 1 + 2;
1572 int num32DataCnt = kNum32DataCntNoYcbcr;
1573
1574 // If a texture w/ an external format is being used, that information must also be appended.
1575 const VulkanYcbcrConversionInfo& ycbcrInfo = TextureInfos::GetVulkanYcbcrConversionInfo(info);
1576 num32DataCnt += ycbcrPackaging::numInt32sNeeded(ycbcrInfo);
1577
1578 GraphiteResourceKey::Builder builder(key, type, num32DataCnt, shareable);
1579
1580 int i = 0;
1581 builder[i++] = dimensions.width();
1582 builder[i++] = dimensions.height();
1583
1584 if (ycbcrInfo.isValid()) {
1585 SkASSERT(ycbcrInfo.fFormat != VK_FORMAT_UNDEFINED || ycbcrInfo.fExternalFormat != 0);
1586 bool useExternalFormat = ycbcrInfo.fFormat == VK_FORMAT_UNDEFINED;
1587 builder[i++] = ycbcrPackaging::nonFormatInfoAsUInt32(ycbcrInfo);
1588 if (useExternalFormat) {
1589 builder[i++] = (uint32_t)ycbcrInfo.fExternalFormat;
1590 builder[i++] = (uint32_t)(ycbcrInfo.fExternalFormat >> 32);
1591 } else {
1592 builder[i++] = ycbcrInfo.fFormat;
1593 }
1594 } else {
1595 builder[i++] = format;
1596 }
1597
1598 builder[i++] = (static_cast<uint32_t>(vkSpec.fFlags));
1599 builder[i++] = (samples << 0 ) |
1600 (static_cast<uint32_t>(isMipped) << 3 ) |
1601 (static_cast<uint32_t>(isProtected) << 4 ) |
1602 (static_cast<uint32_t>(vkSpec.fImageTiling) << 5 ) |
1603 (static_cast<uint32_t>(vkSpec.fSharingMode) << 6 ) |
1604 (static_cast<uint32_t>(vkSpec.fAspectMask) << 7 ) |
1605 (static_cast<uint32_t>(vkSpec.fImageUsageFlags) << 19);
1606 SkASSERT(i == num32DataCnt);
1607 }
1608
getImmutableSamplerInfo(const TextureProxy * proxy) const1609 ImmutableSamplerInfo VulkanCaps::getImmutableSamplerInfo(const TextureProxy* proxy) const {
1610 if (proxy) {
1611 const skgpu::VulkanYcbcrConversionInfo& ycbcrConversionInfo =
1612 TextureInfos::GetVulkanYcbcrConversionInfo(proxy->textureInfo());
1613
1614 if (ycbcrConversionInfo.isValid()) {
1615 ImmutableSamplerInfo immutableSamplerInfo;
1616 // ycbcrConversionInfo's fFormat being VK_FORMAT_UNDEFINED indicates we are using an
1617 // external format rather than a known VkFormat.
1618 immutableSamplerInfo.fFormat = ycbcrConversionInfo.fFormat == VK_FORMAT_UNDEFINED
1619 ? ycbcrConversionInfo.fExternalFormat
1620 : ycbcrConversionInfo.fFormat;
1621 immutableSamplerInfo.fNonFormatYcbcrConversionInfo =
1622 ycbcrPackaging::nonFormatInfoAsUInt32(ycbcrConversionInfo);
1623 return immutableSamplerInfo;
1624 }
1625 }
1626
1627 // If the proxy is null or the YCbCr conversion for that proxy is invalid, then return a
1628 // default ImmutableSamplerInfo struct.
1629 return {};
1630 }
1631
1632 } // namespace skgpu::graphite
1633