如何使用Vulkan API查询GPU支持的所有纹理格式?
问题场景
我在用Vulkan API查询GPU支持的纹理格式时,使用vkGetPhysicalDeviceSurfaceFormatsKHR得到的结果仅4种格式,但我的AMD Radeon高性能GPU实际支持VK_FORMAT_BC1_RGBA_UNORM_BLOCK这类压缩格式(已成功用该格式创建图像)。怀疑这段代码只枚举了KHR Surface的支持格式,而非GPU的全部纹理格式,请问如何正确查询?
附原查询代码:
uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); if (formatCount != 0) { details.formats.resize(formatCount); vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); int counter = 0; int numFormats = details.formats.size(); for (const auto& format : details.formats) { VkFormat vkFormat = format.format; VkColorSpaceKHR colorSpace = format.colorSpace; std::cout << "[" << counter << "] Format: " << vkFormatToString(vkFormat) << ", Color Space: " << colorSpace << std::endl; counter++; } }
附成功创建BC1格式图像的代码:
imageInfo.format = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; imageInfo.tiling = tiling; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); }
问题原因
你判断的完全正确:vkGetPhysicalDeviceSurfaceFormatsKHR仅返回与指定窗口表面兼容的交换链图像格式——这些是GPU能直接输出到显示器的格式,和GPU本身支持的所有纹理格式不是一个概念。BC1这类压缩格式属于GPU内部处理的纹理格式,不属于交换链输出兼容格式,因此不会被该API返回。
正确查询方法
要获取GPU支持的所有纹理格式,需根据纹理的使用场景,调用以下两个核心API:
1. 查询格式的基础支持属性
使用vkGetPhysicalDeviceFormatProperties可以获取指定格式的通用支持能力,比如能否作为采样图像、存储图像、颜色附件等:
VkPhysicalDevice physicalDevice = ...; // 你的物理设备实例 VkFormat targetFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(physicalDevice, targetFormat, &formatProps); // 检查是否支持作为采样图像(最常见的纹理用途) if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { std::cout << "BC1格式支持作为采样图像" << std::endl; } // 检查是否支持作为存储图像 if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) { std::cout << "BC1格式支持作为存储图像" << std::endl; }
2. 查询特定图像参数下的格式兼容性
如果要确认某格式能否配合你创建图像时的具体参数(如tiling、usage、样本数)使用,vkGetPhysicalDeviceImageFormatProperties更准确,它直接对应vkCreateImage的参数校验逻辑:
VkPhysicalDevice physicalDevice = ...; VkFormat format = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; VkImageType imageType = VK_IMAGE_TYPE_2D; VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; // 压缩格式通常仅支持OPTIMAL tiling VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT; VkImageCreateFlags flags = 0; VkImageFormatProperties imageFormatProps; VkResult result = vkGetPhysicalDeviceImageFormatProperties( physicalDevice, format, imageType, tiling, usage, flags, &imageFormatProps ); if (result == VK_SUCCESS) { std::cout << "BC1格式支持指定参数创建图像,最大分辨率:" << imageFormatProps.maxExtent.width << "x" << imageFormatProps.maxExtent.height << std::endl; } else { std::cout << "BC1格式不支持当前指定的图像参数" << std::endl; }
3. 枚举GPU支持的所有纹理格式
如果要遍历GPU支持的所有可用纹理格式,可以循环所有VkFormat枚举值,逐个检查是否满足纹理相关的用途:
VkPhysicalDevice physicalDevice = ...; // 遍历所有VkFormat(跳过VK_FORMAT_UNDEFINED) for (uint32_t formatEnum = 1; formatEnum < VK_FORMAT_MAX_ENUM; ++formatEnum) { VkFormat format = static_cast<VkFormat>(formatEnum); VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); // 判断是否为可用的纹理格式(支持采样、存储或颜色附件) bool isValidTextureFormat = false; // 检查最优平铺下的支持能力 if (props.optimalTilingFeatures & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { isValidTextureFormat = true; } // 检查线性平铺下的支持能力 if (props.linearTilingFeatures & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { isValidTextureFormat = true; } if (isValidTextureFormat) { std::cout << "支持的纹理格式:" << vkFormatToString(format) << std::endl; } }
注意事项
- 压缩格式(BC、ASTC、ETC等)几乎都只支持
VK_IMAGE_TILING_OPTIMAL,不支持线性平铺,查询时要对应正确的tiling参数。 - 不同GPU厂商对格式的支持差异较大,务必通过API动态查询,不要硬编码格式列表。
内容的提问来源于stack exchange,提问作者gmmo

