Skip to content

Commit

Permalink
Update profiles sample to use recent profiles library and roadmap 202…
Browse files Browse the repository at this point in the history
…2 profile (#1189)

* Update profiles sample to use recent profiles library

Use roadmap 2022 profile instead of no longer available LunarG profile

* Integrate review feedback
Check profile before cereating device
  • Loading branch information
SaschaWillems authored Oct 21, 2024
1 parent e2c3f56 commit f7e97a1
Show file tree
Hide file tree
Showing 7 changed files with 10,640 additions and 4,898 deletions.
118 changes: 63 additions & 55 deletions samples/tooling/profiles/README.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
////
- Copyright (c) 2022-2023, Sascha Willems
- Copyright (c) 2022-2024, Sascha Willems
-
- SPDX-License-Identifier: Apache-2.0
-
Expand Down Expand Up @@ -28,10 +28,12 @@ Profiles define a common requirement baseline of properties, features, extension
to make Vulkan applications more portable.
Instead of having to check all of these in your app one by one at runtime, you use the profiles library to check if the selected device supports all the requirements of that given profile.
If that's the case, you then use the same library to create the device and/or instance.
The library then takes care of enabling all the required features, extensions, etc.
The library then takes care of enabling all the required features, extensions, etc. saving a lot of the otherwise common boilerplate.

We'll show this in a sample that makes use of descriptor indexing.
We'll be using the `VP_LUNARG_desktop_portability_2021` profile that defines feature sets for common desktop platforms with drivers supporting Vulkan 1.1 on Windows and Linux.

The sample uses the `VP_KHR_roadmap_2022` profile that enables a baseline of features and extensions for "newer mid-to-high-end devices shipping in 2022 or shortly thereafter across mainstream smartphone, tablet, laptops, console and desktop devices.".
Details on what this profile contains can be found link:https://docs.vulkan.org/spec/latest/appendices/roadmap.html#roadmap-2022[here].

== Without profiles

Expand All @@ -40,47 +42,39 @@ This could look something like this:

[,cpp]
----
if (!device_extension_supported(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)) || (...)) {
app_terminate('Extension not supported');
}
// Instance creation
// If using Vulkan 1.0, VK_KHR_get_physical_device_properties2 is a requirement for descriptor indexing
// See https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html#_extension_and_version_dependencies
add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
vkCreateInstance(...);
// Device creation
add_device_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
// VK_KHR_maintenance3 is a requirement for descriptor indexing
// See https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html#_extension_and_version_dependencies
add_device_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing_features{};
descriptor_indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
descriptor_indexing_features.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
descriptor_indexing_features.runtimeDescriptorArray = VK_TRUE;
descriptor_indexing_features.descriptorBindingVariableDescriptorCount = VK_TRUE;
VkPhysicalDeviceFeatures2 physical_device_features2{};
physical_device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
physical_device_features2.pNext = &descriptor_indexing_features;
vkGetPhysicalDeviceFeatures2(physicalDevice, &physical_device_features2);
if (!descriptor_indexing_features.runtimeDescriptorArray) {
app_terminate('runtime descriptor arrays not supported!');
}
// ... do the same for all other extensions and features
VkPhysicalDeviceFeatures enabled_features{};
VkPhysicalDeviceVulkan11Features enabled_features_11{};
VkPhysicalDeviceVulkan12Features enabled_features_12{};
enabled_features.fullDrawIndexUint32 = VK_TRUE;
enabled_features.imageCubeArray = VK_TRUE;
enabled_features.independentBlend = VK_TRUE;
enabled_features.ampleRateShading = VK_TRUE;
enabled_features.rawIndirectFirstInstance = VK_TRUE;
enabled_features.depthClamp = VK_TRUE;
enabled_features.depthBiasClamp = VK_TRUE;
enabled_features.samplerAnisotropy = VK_TRUE;
enabled_features.occlusionQueryPrecise = VK_TRUE;
enabled_features.fragmentStoresAndAtomics = VK_TRUE;
enabled_features.shaderStorageImageExtendedFormats = VK_TRUE;
enabled_features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE;
enabled_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
enabled_features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE;
enabled_features.shaderStorageImageArrayDynamicIndexing = VK_TRUE;
...
enabled_features_11.samplerYcbcrConversion = VK_TRUE;
enabled_features_11.pNext = &enabled_features_12;
...
enabled_features_12.samplerMirrorClampToEdge = VK_TRUE;
enabled_features_12.descriptorIndexing = VK_TRUE;
enabled_features_12.shaderUniformTexelBufferArrayDynamicIndexing = VK_TRUE;
...
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.pNext = &physical_device_features2;
device_create_info.pNext = &enabled_features_11;
VkResult result = vkCreateDevice(...);
if (!result) {
Expand All @@ -97,7 +91,7 @@ Using the profile library and a profile that requires all the above features and
[,cpp]
----
// Profile to enable
const VpProfileProperties profile_properties = {PROFILE_NAME, PROFILE_SPEC_VERSION};
const VpProfileProperties profile_properties = {VP_KHR_ROADMAP_2022_NAME, VP_KHR_ROADMAP_2022_SPEC_VERSION};
// Instance creation
Expand All @@ -113,38 +107,52 @@ create_info.ppEnabledExtensionNames = enabled_extensions.data();
create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
VpInstanceCreateInfo instance_create_info{};
instance_create_info.pProfile = &profile_properties;
instance_create_info.pCreateInfo = &create_info;
instance_create_info.flags = VP_INSTANCE_CREATE_MERGE_EXTENSIONS_BIT;
instance_create_info.pEnabledFullProfiles = &profile_properties;
instance_create_info.enabledFullProfileCount = 1;
instance_create_info.pCreateInfo = &create_info;
result = vpCreateInstance(&instance_create_info, nullptr, &vulkan_instance);
// Device creation
std::vector<const char *> enabled_extensions;
enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
VkDeviceCreateInfo create_info{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
create_info.pNext = gpu.get_extension_feature_chain();
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;
create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
create_info.ppEnabledExtensionNames = enabled_extensions.data();
VkBool32 profile_supported;
vpGetPhysicalDeviceProfileSupport(instance->get_handle(), gpu.get_handle(), &profile_properties, &profile_supported);
if (!profile_supported) {
app_terminate("The selected profile is not supported (error at creating the device)!");
}
VpDeviceCreateInfo deviceCreateInfo{};
deviceCreateInfo.pCreateInfo = &create_info;
deviceCreateInfo.pProfile = &profile_properties;
deviceCreateInfo.flags = VP_DEVICE_CREATE_MERGE_EXTENSIONS_BIT;
deviceCreateInfo.pCreateInfo = &create_info;
deviceCreateInfo.pEnabledFullProfiles = &profile_properties;
deviceCreateInfo.enabledFullProfileCount = 1;
VkResult result = vpCreateDevice(gpu.get_handle(), &deviceCreateInfo, nullptr, &vulkan_device);
----

This will enable all features and extensions defined in the selected profile, including descriptor indexing.
This will enable all features and extensions defined in the selected profile, including descriptor indexing. By passing `pCreateInfo` of the instance and/or device you can also enable additional extensions not part of the profile.

Inspecting the device created using a profile in a graphics debugger like https://renderdoc.org/[RenderDoc] we can see that the profile library did the following based on the link:https://docs.vulkan.org/spec/latest/appendices/roadmap.html#roadmap-2022[profile] we selected:

Set the appropriate Vulkan version at instance creation:

image::./renderdoc_instance.png[RenderDoc instance Api version]

Inspecting the device created using a profile in a graphics debugger like https://renderdoc.org/[RenderDoc] we can see that the profile library did create the physical device pNext feature chain:
Enabled device features and setup the pNext chain:

image::./renderdoc_device_1.png[RenderDoc device pNext]

And also enabled all required extensions:
And also enabled all required extensions (incl. explicitly requested ones):

image::./renderdoc_device_2.png[RenderDoc device extensions]

Esp.
the physical device pNext chain for the different features is a lot of boiler plate code when done manually.
=== Conclusion

The `VP_LUNARG_desktop_portability_2021` profile we use for this sample enables many more features and extensions.
You can find a comparison table for the profiles in https://vulkan.lunarg.com/doc/sdk/latest/windows/profiles_definitions.html[LunarG's SDK documentation].
Using profiles makes setting up a baseline of extensions and features a lot easier. It also saves lots of code as you no longer have to worry about enabling extensions, setting up their feature structs and properly chaining the `pNext` structures.
63 changes: 30 additions & 33 deletions samples/tooling/profiles/profiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,9 @@
// The Vulkan Profiles library is part of the SDK and has been copied to the sample's folder for convenience
#include "vulkan_profiles.hpp"

// This sample uses the VP_LUNARG_desktop_portability_2021 profile that defines feature sets for common desktop platforms with drivers supporting Vulkan 1.1 on Windows and Linux, and the VP_LUNARG_desktop_portability_2021_subset profile on portability platforms like macOS
#if (defined(VKB_ENABLE_PORTABILITY) && defined(VP_LUNARG_desktop_portability_2021_subset))
# define PROFILE_NAME VP_LUNARG_DESKTOP_PORTABILITY_2021_SUBSET_NAME
# define PROFILE_SPEC_VERSION VP_LUNARG_DESKTOP_PORTABILITY_2021_SUBSET_SPEC_VERSION
#else
# define PROFILE_NAME VP_LUNARG_DESKTOP_PORTABILITY_2021_NAME
# define PROFILE_SPEC_VERSION VP_LUNARG_DESKTOP_PORTABILITY_2021_SPEC_VERSION
#endif
// This sample will use the Khronos roadmap 2022 profile which requires Vulkan 1.3
// For details on what this profile requires/enables, see https://docs.vulkan.org/spec/latest/appendices/roadmap.html#roadmap-2022
const VpProfileProperties profile_properties = {VP_KHR_ROADMAP_2022_NAME, VP_KHR_ROADMAP_2022_SPEC_VERSION};

Profiles::Profiles()
{
Expand All @@ -63,6 +58,16 @@ Profiles::~Profiles()
// Instead of manually setting up all extensions, features, etc. we use the Vulkan Profiles library to simplify device setup
std::unique_ptr<vkb::Device> Profiles::create_device(vkb::PhysicalDevice &gpu)
{
// Check if the profile is supported at device level
VkBool32 profile_supported;
vpGetPhysicalDeviceProfileSupport(get_instance().get_handle(), gpu.get_handle(), &profile_properties, &profile_supported);
if (!profile_supported)
{
throw std::runtime_error{"The selected profile is not supported (error at creating the device)!"};
}

// If the profile is supported, we can start setting things up and use the profiles library for that

// Simplified queue setup (only graphics)
uint32_t selected_queue_family = 0;
const auto &queue_family_properties = gpu.get_queue_family_properties();
Expand All @@ -81,26 +86,21 @@ std::unique_ptr<vkb::Device> Profiles::create_device(vkb::PhysicalDevice &gpu)
}
}

VkDeviceCreateInfo create_info{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
create_info.pNext = gpu.get_extension_feature_chain();
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;

const VpProfileProperties profile_properties = {PROFILE_NAME, PROFILE_SPEC_VERSION};
std::vector<const char *> enabled_extensions;
enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);

// Check if the profile is supported at device level
VkBool32 profile_supported;
vpGetPhysicalDeviceProfileSupport(get_instance().get_handle(), gpu.get_handle(), &profile_properties, &profile_supported);
if (!profile_supported)
{
throw std::runtime_error{"The selected profile is not supported (error at creating the device)!"};
}
VkDeviceCreateInfo create_info{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
create_info.pNext = gpu.get_extension_feature_chain();
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;
create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
create_info.ppEnabledExtensionNames = enabled_extensions.data();

// Create the device using the profile tool library
// Create the device using the profiles library
VpDeviceCreateInfo deviceCreateInfo{};
deviceCreateInfo.pCreateInfo = &create_info;
deviceCreateInfo.pProfile = &profile_properties;
deviceCreateInfo.flags = VP_DEVICE_CREATE_MERGE_EXTENSIONS_BIT;
deviceCreateInfo.pCreateInfo = &create_info;
deviceCreateInfo.pEnabledFullProfiles = &profile_properties;
deviceCreateInfo.enabledFullProfileCount = 1;
VkDevice vulkan_device;
VkResult result = vpCreateDevice(gpu.get_handle(), &deviceCreateInfo, nullptr, &vulkan_device);

Expand Down Expand Up @@ -130,8 +130,6 @@ std::unique_ptr<vkb::Instance> Profiles::create_instance(bool headless)
throw vkb::VulkanException(result, "Failed to initialize volk.");
}

const VpProfileProperties profile_properties = {PROFILE_NAME, PROFILE_SPEC_VERSION};

// Check if the profile is supported at instance level
VkBool32 profile_supported;
vpGetInstanceProfileSupport(nullptr, &profile_properties, &profile_supported);
Expand Down Expand Up @@ -205,12 +203,11 @@ std::unique_ptr<vkb::Instance> Profiles::create_instance(bool headless)
// Note: We don't explicitly set an application info here so the one from the profile is used
// This also defines the api version to be used

// Create the instance using the profile tool library
// We set VP_INSTANCE_CREATE_MERGE_EXTENSIONS_BIT so the extensions defined in the profile will be merged with the extensions we specified manually
// Create the instance using the profiles library
VpInstanceCreateInfo instance_create_info{};
instance_create_info.pProfile = &profile_properties;
instance_create_info.pCreateInfo = &create_info;
instance_create_info.flags = VP_INSTANCE_CREATE_MERGE_EXTENSIONS_BIT;
instance_create_info.pEnabledFullProfiles = &profile_properties;
instance_create_info.enabledFullProfileCount = 1;
instance_create_info.pCreateInfo = &create_info;
VkInstance vulkan_instance;

result = vpCreateInstance(&instance_create_info, nullptr, &vulkan_instance);
Expand Down Expand Up @@ -748,7 +745,7 @@ void Profiles::view_changed()

void Profiles::on_update_ui_overlay(vkb::Drawer &drawer)
{
drawer.text("Enabled profile: %s", PROFILE_NAME);
drawer.text("Enabled profile: %s", profile_properties.profileName);
}

std::unique_ptr<vkb::Application> create_profiles()
Expand Down
Binary file modified samples/tooling/profiles/renderdoc_device_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified samples/tooling/profiles/renderdoc_device_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/tooling/profiles/renderdoc_instance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f7e97a1

Please sign in to comment.