Vulkan is much more capable now than it was in the time of Vulkan 1.0 release in 2016. Some examples of new functionalities can be seen in the following output produced by the example code of this article. The new functionalities are marked by the red tint:
Vulkan instance: Version: 1.3.275 Extensions (18 in total): VK_KHR_device_group_creation VK_KHR_external_fence_capabilities VK_KHR_external_memory_capabilities VK_KHR_external_semaphore_capabilities VK_KHR_get_physical_device_properties2 VK_KHR_get_surface_capabilities2 VK_KHR_surface VK_KHR_surface_protected_capabilities VK_KHR_win32_surface VK_EXT_debug_report VK_EXT_debug_utils VK_EXT_surface_maintenance1 VK_EXT_swapchain_colorspace VK_NV_external_memory_capabilities VK_KHR_portability_enumeration VK_LUNARG_direct_driver_loading VK_EXT_layer_settings VK_EXT_validation_features Vulkan devices: Quadro RTX 3000 Vulkan version: 1.3.277 Device type: DiscreteGpu VendorID: 0x10de DeviceID: 0x1f36 Device UUID: e0e7b72b-a391-a141-9bbf-2059ba86ca79 Driver name: NVIDIA Driver info: 552.74 PCI bus info: domain: 0, bus: 1, device: 0, function: 0 MaxTextureSize: 32768 Geometry shader: supported Double precision: supported Half precision: supported Vulkan Video: supported Vulkan Ray Tracing: supported Memory heaps: 0: 5955MiB (device local) 1: 16200MiB 2: 214MiB (device local) Queue families: 0: gcts (count: 16) 1: ts (count: 2) 2: cts (count: 8) 3: tsv (count: 3, decode H264, decode H265) 4: tsv (count: 1) Format support for compressed textures: BC7 (BC7_SRGB): yes ETC2 (ETC2_R8G8B8A8_SRGB): no ASTC (ASTC_4x4_SRGB): no ASTC (ASTC_4x4_SFLOAT): yes Extensions (215 in total): VK_KHR_16bit_storage, [...]
We can see new extensions for both - instance and devices. If list of extensions floods your screen too much, you might disable it by --no-extension-list. As we can see, the instance supports 18 extensions and the device 215. Using the device extension list, we can find out Vulkan video, Vulkan raytracing and PCI bus info support:
// supported extensions vk::vector<vk::ExtensionProperties> extensionList = vk::enumerateDeviceExtensionProperties(pd, nullptr); bool videoQueueSupported = vk::isExtensionSupported(extensionList, "VK_KHR_video_queue") && instanceVersion >= vk::ApiVersion11; bool raytracingSupported = vk::isExtensionSupported(extensionList, "VK_KHR_acceleration_structure") && vk::isExtensionSupported(extensionList, "VK_KHR_ray_tracing_pipeline") && vk::isExtensionSupported(extensionList, "VK_KHR_ray_query") && instanceVersion >= vk::ApiVersion11; bool pciBusInfoSupported = vk::isExtensionSupported(extensionList, "VK_EXT_pci_bus_info") && instanceVersion >= vk::ApiVersion11;
The important rule is: Before we use some Vulkan functionality, we should make sure it is supported. The support can be provided by:
Let's analyze the output of this article example:
Vulkan instance: |
|
Extensions (18 in total): |
extension enumeration since Vulkan 1.0
|
Vulkan devices: |
|
Geometry shader: supported |
optional, Vulkan 1.0 |
Memory heaps: |
Vulkan 1.0
|
Queue families: |
queue info: Vulkan 1.0 |
Extensions (215 in total): [...]
|
Vulkan 1.0
|
Vulkan 1.1 introduces vk::PhysicalDeviceProperties2 structure that allows for extending vk::PhysicalDeviceProperties by pNext pointer:
struct PhysicalDeviceProperties2 { StructureType sType = StructureType::ePhysicalDeviceProperties2; void* pNext = nullptr; PhysicalDeviceProperties properties; };
Now, we can chain structures using pNext pointer. Before we do that, let's start with declaring the structures and getting device version:
// get device properties vk::PhysicalDeviceProperties2 properties2; vk::PhysicalDeviceProperties& properties = properties2.properties; properties = vk::getPhysicalDeviceProperties(pd);
We follow with chaining of the structures using pNext pointer:
// extended device properties vk::PhysicalDevicePCIBusInfoPropertiesEXT pciBusInfo; // requires VK_EXT_pci_bus_info vk::PhysicalDeviceVulkan12Properties properties12; // requires Vulkan 1.2 vk::PhysicalDeviceVulkan11Properties properties11; // requires Vulkan 1.2 (really 1.2, not 1.1) if(properties.apiVersion >= vk::ApiVersion11) { void** lastPNext = &properties2.pNext; if(properties.apiVersion >= vk::ApiVersion12) { properties2.pNext = &properties11; properties11.pNext = &properties12; lastPNext = &properties12.pNext; } if(pciBusInfoSupported) *lastPNext = &pciBusInfo; vk::getPhysicalDeviceProperties2(pd, properties2); }
During vk::getPhysicalDeviceProperties2() call, all the structures in the structure chain are filled with the data. Now, we can print additional info from the structures:
if(properties.apiVersion >= vk::ApiVersion12) { cout << " Driver name: " << properties12.driverName << endl; cout << " Driver info: " << properties12.driverInfo << endl; } else { cout << " Driver name: < unknown >" << endl; cout << " Driver info: < unknown >" << endl; }
If VK_EXT_pci_bus_info extension is supported, we can also print pci bus information:
// PCI bus info cout << " PCI bus info:" << endl; if(pciBusInfoSupported) cout << " domain: " << pciBusInfo.pciDomain << ", bus: " << pciBusInfo.pciBus << ", device: " << pciBusInfo.pciDevice << ", function: " << pciBusInfo.pciFunction << endl; else cout << " not available" << endl;
Vulkan features from newer Vulkan versions are retrieved in the similar way as properties:
// device features vk::PhysicalDeviceVulkan12Features features12; vk::PhysicalDeviceFeatures2 features2{ .pNext = (properties.apiVersion>=vk::ApiVersion12) ? &features12 : nullptr, }; vk::PhysicalDeviceFeatures& features = features2.features; if(properties.apiVersion >= vk::ApiVersion11) vk::getPhysicalDeviceFeatures2(pd, features2); else features = vk::getPhysicalDeviceFeatures(pd);
Then, we can, for example, find out if half precision floats are supported:
cout << " Half precision: "; if(properties.apiVersion >= vk::ApiVersion12 && features12.shaderFloat16) cout << "supported" << endl; else cout << "not supported" << endl; cout << " Half precision: ";
To get extended info for queue families, we use vector of vk::QueueFamilyProperties2 and vector of vk::QueueFamilyVideoPropertiesKHR:
// queue family properties cout << " Queue families:" << endl; vk::vector<vk::QueueFamilyProperties2> queueFamilyList; vk::vector<vk::QueueFamilyVideoPropertiesKHR> queueVideoPropertiesList; if(properties.apiVersion >= vk::ApiVersion11) queueFamilyList = vk::getPhysicalDeviceQueueFamilyProperties2( pd, queueVideoPropertiesList, videoQueueSupported); else { vk::vector<vk::QueueFamilyProperties> v = vk::getPhysicalDeviceQueueFamilyProperties(pd); queueFamilyList.resize(v.size()); for(size_t i=0; i<v.size(); i++) queueFamilyList[i].queueFamilyProperties = v[i]; }
If we are on Vulkan 1.1+, we call vk::getPhysicalDeviceQueueFamilyProperties2. Otherwise, we use old Vulkan 1.0 function and copy the results from the old structures into the new ones.
Then, we can process the information about the queue families. The extension provided functionality is marked by bold text:
for(uint32_t i=0, c=uint32_t(queueFamilyList.size()); i<c; i++) { cout << " " << i << ": "; vk::QueueFamilyProperties& queueFamilyProperties = queueFamilyList[i].queueFamilyProperties; if(queueFamilyProperties.queueFlags & vk::QueueFlagBits::eGraphics) cout << "g"; if(queueFamilyProperties.queueFlags & vk::QueueFlagBits::eCompute) cout << "c"; if(queueFamilyProperties.queueFlags & vk::QueueFlagBits::eTransfer) cout << "t"; if(queueFamilyProperties.queueFlags & vk::QueueFlagBits::eSparseBinding) cout << "s"; if(queueFamilyProperties.queueFlags & (vk::QueueFlagBits::eVideoDecodeKHR | vk::QueueFlagBits::eVideoEncodeKHR)) cout << "v"; cout << " (count: " << queueFamilyProperties.queueCount; if(videoQueueSupported) { if(queueVideoPropertiesList[i].videoCodecOperations & vk::VideoCodecOperationFlagBitsKHR::eDecodeH264) cout << ", decode H264"; if(queueVideoPropertiesList[i].videoCodecOperations & vk::VideoCodecOperationFlagBitsKHR::eDecodeH265) cout << ", decode H265"; if(queueVideoPropertiesList[i].videoCodecOperations & vk::VideoCodecOperationFlagBitsKHR::eDecodeAV1) cout << ", decode AV1"; } cout << ")" << endl; }
We can test, for example, for compressed texture support. The compressed textures reduce memory consumption, lower bandwidth, but might reduce visual quality. Memory consumption and bandwidth requirements might be of issue for example, on mobile devices. But checking of the visual quality of compressed textures might be sometimes important.
As of 2025, we can query for these Vulkan core formats:
So, we query for Bc7Srgb, Etc2R8G8B8A8Srgb, Astc4x4Srgb formats. If Vulkan 1.3 is supported, we query for Astc4x4Sfloat as well. In all the cases, we test for SampledImageFilterLinear bit:
cout << " Format support for compressed textures:" << endl; vk::FormatProperties formatProperties = vk::getPhysicalDeviceFormatProperties(pd, vk::Format::eBc7SrgbBlock); if(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear) cout << " BC7 (BC7_SRGB): yes" << endl; else cout << " BC7 (BC7_SRGB): no" << endl; formatProperties = vk::getPhysicalDeviceFormatProperties(pd, vk::Format::eEtc2R8G8B8A8SrgbBlock); if(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear) cout << " ETC2 (ETC2_R8G8B8A8_SRGB): yes" << endl; else cout << " ETC2 (ETC2_R8G8B8A8_SRGB): no" << endl; formatProperties = vk::getPhysicalDeviceFormatProperties(pd, vk::Format::eAstc4x4SrgbBlock); if(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear) cout << " ASTC (ASTC_4x4_SRGB): yes" << endl; else cout << " ASTC (ASTC_4x4_SRGB): no" << endl; if(properties.apiVersion >= vk::ApiVersion13) { formatProperties = vk::getPhysicalDeviceFormatProperties(pd, vk::Format::eAstc4x4SfloatBlock); if(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear) cout << " ASTC (ASTC_4x4_SFLOAT): yes" << endl; else cout << " ASTC (ASTC_4x4_SFLOAT): no" << endl; } else cout << " ASTC (ASTC_4x4_SFLOAT): no" << endl;