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