From 4c5f3310d9f90a4e3983bc9b7ef6dff94329a7a1 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 17 Feb 2025 14:27:24 -0600 Subject: [PATCH 1/5] Add vkb::SystemInfo::is_instance_version_available Allows users to check the Instance API version before calling vkb::InstanceBuilder::build(). --- src/VkBootstrap.cpp | 14 ++++++++++++++ src/VkBootstrap.h | 7 +++++++ tests/bootstrap_tests.cpp | 19 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index c52459b..bd6a291 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -551,6 +551,15 @@ SystemInfo::SystemInfo() { } } } + + PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = detail::vulkan_functions().fp_vkEnumerateInstanceVersion; + + if (pfn_vkEnumerateInstanceVersion != nullptr) { + VkResult res = pfn_vkEnumerateInstanceVersion(&instance_api_version); + if (res != VK_SUCCESS) { + instance_api_version = VKB_VK_API_VERSION_1_0; + } + } } bool SystemInfo::is_extension_available(const char* extension_name) const { if (!extension_name) return false; @@ -560,6 +569,11 @@ bool SystemInfo::is_layer_available(const char* layer_name) const { if (!layer_name) return false; return detail::check_layer_supported(available_layers, layer_name); } +bool SystemInfo::is_instance_version_available(uint32_t major_api_version, uint32_t minor_api_version) { + return instance_api_version >= VKB_MAKE_VK_VERSION(0, major_api_version, minor_api_version, 0); +} +bool SystemInfo::is_instance_version_available(uint32_t api_version) { return instance_api_version >= api_version; } + void destroy_surface(Instance const& instance, VkSurfaceKHR surface) { if (instance.instance != VK_NULL_HANDLE && surface != VK_NULL_HANDLE) { detail::vulkan_functions().fp_vkDestroySurfaceKHR(instance.instance, surface, instance.allocation_callbacks); diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 6c24921..9565db0 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -278,11 +278,18 @@ struct SystemInfo { bool is_layer_available(const char* layer_name) const; // Returns true if an extension is available bool is_extension_available(const char* extension_name) const; + // Returns true if the Instance API Version is greater than or equal to the specified version + bool is_instance_version_available(uint32_t major_api_version, uint32_t minor_api_version); + // Returns true if the Instance API Version is greater than or equal to the specified version. + // Should be constructed with VK_MAKE_VERSION or VK_MAKE_API_VERSION. + bool is_instance_version_available(uint32_t api_version); std::vector available_layers; std::vector available_extensions; bool validation_layers_available = false; bool debug_utils_available = false; + + uint32_t instance_api_version = VKB_VK_API_VERSION_1_0; }; // Forward declared - check VkBoostrap.cpp for implementations diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index fc08f35..fcbfb69 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -455,6 +455,25 @@ TEST_CASE("SystemInfo Loading Vulkan Automatically", "[VkBootstrap.loading]") { REQUIRE(ret); } +TEST_CASE("SystemInfo Check Instance API Version", "[VkBootstrap.instance_api_version]") { + VulkanMock& mock = get_and_setup_default(); + mock.api_version = VK_API_VERSION_1_2; + auto info_ret = vkb::SystemInfo::get_system_info(); + REQUIRE(info_ret); + auto system_info = info_ret.value(); + REQUIRE(system_info.is_instance_version_available(VK_MAKE_API_VERSION(0, 1, 0, 0))); + REQUIRE(system_info.is_instance_version_available(VK_MAKE_API_VERSION(0, 1, 1, 0))); + REQUIRE(system_info.is_instance_version_available(VK_MAKE_API_VERSION(0, 1, 2, 0))); + REQUIRE(!system_info.is_instance_version_available(VK_MAKE_API_VERSION(0, 1, 3, 0))); + REQUIRE(!system_info.is_instance_version_available(VK_MAKE_API_VERSION(0, 1, 4, 0))); + + REQUIRE(system_info.is_instance_version_available(1, 0)); + REQUIRE(system_info.is_instance_version_available(1, 1)); + REQUIRE(system_info.is_instance_version_available(1, 2)); + REQUIRE(!system_info.is_instance_version_available(1, 3)); + REQUIRE(!system_info.is_instance_version_available(1, 4)); +} + TEST_CASE("SystemInfo Loading Vulkan Manually", "[VkBootstrap.loading]") { [[maybe_unused]] VulkanMock& mock = get_and_setup_default(); VulkanLibrary vk_lib; From 3c47257bfea671cd57519d965abcce7322a53a2a Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 17 Feb 2025 14:35:56 -0600 Subject: [PATCH 2/5] Clean up getting started and tests usage of set_minimum_instance_version --- docs/getting_started.md | 2 -- tests/bootstrap_tests.cpp | 5 +---- tests/vulkan_hpp_tests.cpp | 5 +---- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 131978d..e2f1879 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -74,10 +74,8 @@ instance_builder.set_app_version(3, 0, 0); instance_builder.set_engine_version(5, 0, 2); // The Instance Version (aka the version of your loader) -// TODO - which? // This will make sure you require a 1.3 Vulkan Loader instance_builder.require_api_version(1, 3, 0); -instance_builder.set_minimum_instance_version(1, 3, 0); ``` ## Enabling Instance Level Extensions and Layers diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index fcbfb69..158efa7 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -20,10 +20,7 @@ TEST_CASE("Instance with surface", "[VkBootstrap.bootstrap]") { REQUIRE(sys_info_ret); vkb::InstanceBuilder instance_builder; - auto instance_ret = instance_builder.require_api_version(1, 1, 0) - .set_minimum_instance_version(1, 0, 0) - .use_default_debug_messenger() - .build(); + auto instance_ret = instance_builder.require_api_version(1, 1, 0).use_default_debug_messenger().build(); REQUIRE(instance_ret); vkb::Instance instance = instance_ret.value(); diff --git a/tests/vulkan_hpp_tests.cpp b/tests/vulkan_hpp_tests.cpp index 738f316..92cd3b7 100644 --- a/tests/vulkan_hpp_tests.cpp +++ b/tests/vulkan_hpp_tests.cpp @@ -34,10 +34,7 @@ TEST_CASE("VulkanHpp Instance with surface", "[VkBootstrap.vulkan_hpp]") { REQUIRE(sys_info_ret); vkb::InstanceBuilder instance_builder; - auto instance_ret = instance_builder.require_api_version(1, 1, 0) - .set_minimum_instance_version(1, 0, 0) - .use_default_debug_messenger() - .build(); + auto instance_ret = instance_builder.require_api_version(1, 1, 0).use_default_debug_messenger().build(); REQUIRE(instance_ret); vkb::Instance instance = instance_ret.value(); vk::Instance hpp_instance{ instance }; From 130d84cf68d12f8c1d7ce017dcef0b4f3fd0527c Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 17 Feb 2025 14:36:31 -0600 Subject: [PATCH 3/5] Format getting_started.md table of contents --- docs/getting_started.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index e2f1879..8ebd2f5 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -2,20 +2,21 @@ `vk-bootstrap` reduces the complexity of dealing with `VkInstance`, `VkPhysicalDevice`, and `VkDevice`, the three things every Vulkan app needs to start. +- [Getting Started](#getting-started) - [Build Pattern](#build-pattern) - [Instance Creation](#instance-creation) - * [Versions](#versions) - * [Enabling Instance Level Extensions and Layers](#enabling-instance-level-extensions-and-layers) + - [Versions](#versions) + - [Enabling Instance Level Extensions and Layers](#enabling-instance-level-extensions-and-layers) - [Physical Device Selection](#physical-device-selection) - [Physical Device Object](#physical-device-object) - [Device Creation](#device-creation) - * [Queues](#queues) - + [Custom queue setup](#custom-queue-setup) + - [Queues](#queues) + - [Custom queue setup](#custom-queue-setup) - [Dispatch Table](#dispatch-table) - [WSI](#wsi) - * [For those who don't want a surface](#for-those-who-dont-want-a-surface) - * [Surface Creation](#surface-creation) - * [Swapchain](#swapchain) + - [For those who don't want a surface](#for-those-who-dont-want-a-surface) + - [Surface Creation](#surface-creation) + - [Swapchain](#swapchain) - [Cleanup](#cleanup) - [Creating your own debug callback](#creating-your-own-debug-callback) From c78b7bcb3bf2a8505302b9adec6e64c41c2c28a4 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 17 Feb 2025 18:13:36 -0600 Subject: [PATCH 4/5] Check glfwCreateWindow error code properly --- example/basic_usage.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/example/basic_usage.cpp b/example/basic_usage.cpp index 6902d9d..4007775 100644 --- a/example/basic_usage.cpp +++ b/example/basic_usage.cpp @@ -5,10 +5,8 @@ bool init_vulkan() { vkb::InstanceBuilder builder; - auto inst_ret = builder.set_app_name("Example Vulkan Application") - .request_validation_layers() - .use_default_debug_messenger() - .build(); + auto inst_ret = + builder.set_app_name("Example Vulkan Application").request_validation_layers().use_default_debug_messenger().build(); if (!inst_ret) { std::cerr << "Failed to create Vulkan instance. Error: " << inst_ret.error().message() << "\n"; return false; @@ -20,8 +18,9 @@ bool init_vulkan() { GLFWwindow* window = glfwCreateWindow(1024, 1024, "Vulkan Triangle", NULL, NULL); VkSurfaceKHR surface = VK_NULL_HANDLE; - if (VkResult err = glfwCreateWindowSurface(vkb_inst, window, nullptr, &surface)) { - std::cerr << "Failed to select create windows surface\n"; + VkResult glfw_result = glfwCreateWindowSurface(vkb_inst, window, nullptr, &surface); + if (glfw_result != VK_SUCCESS) { + std::cerr << "Failed to select create window surface. Error: " << std::to_string(glfw_result) << "\n"; return false; } @@ -79,4 +78,4 @@ int main() { if (init_vulkan()) { std::cout << "Boilerplate done, time to write you application!\n"; } -} \ No newline at end of file +} From 1ed3275bc9a44b287085e64e743122eba3c49a4c Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Wed, 19 Feb 2025 12:41:29 -0600 Subject: [PATCH 5/5] Document set_minimum_instance_version in getting_started.md --- docs/getting_started.md | 7 +++++-- tests/bootstrap_tests.cpp | 38 +++++++++++++++++++++++++++++++++++++ tests/vulkan_mock_setup.cpp | 7 ++++++- tests/vulkan_mock_setup.hpp | 2 ++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 8ebd2f5..81210d0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -74,9 +74,12 @@ instance_builder.set_app_version(3, 0, 0); // Sets VkApplicationInfo::engineVersion instance_builder.set_engine_version(5, 0, 2); -// The Instance Version (aka the version of your loader) -// This will make sure you require a 1.3 Vulkan Loader +// Set the required API version of your Vulkan app +// This will make sure you have a 1.3 Loader and 1.3 Physical Devices instance_builder.require_api_version(1, 3, 0); + +// Lower the required Instance version - used when you need a higher Physical Device version than Instance version +instance_builder.set_minimum_instance_version(1, 2, 0) ``` ## Enabling Instance Level Extensions and Layers diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index 158efa7..369663b 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -532,6 +532,44 @@ TEST_CASE("ReLoading Vulkan Manually", "[VkBootstrap.loading]") { } } +TEST_CASE("Minimum instance API version", "[VkBootstrap.api_version]") { + VulkanMock& mock = get_and_setup_default(); + mock.api_version = VK_API_VERSION_1_2; + mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_3; + mock.physical_devices_details[0].properties.deviceID = 1; + add_basic_physical_device(mock).properties.apiVersion = VK_API_VERSION_1_4; + mock.physical_devices_details[1].properties.deviceID = 2; + { + auto ret = vkb::InstanceBuilder{}.set_headless().require_api_version(1, 4).set_minimum_instance_version(1, 2).build(); + REQUIRE(ret); + auto pd_ret = vkb::PhysicalDeviceSelector{ ret.value() }.select(); + REQUIRE(pd_ret); + REQUIRE(pd_ret.value().properties.deviceID == 2); + } + { + auto ret = vkb::InstanceBuilder{} + .set_headless() + .require_api_version(VK_MAKE_API_VERSION(0, 1, 4, 0)) + .set_minimum_instance_version(VK_MAKE_API_VERSION(0, 1, 2, 0)) + .build(); + REQUIRE(ret); + auto pd_ret = vkb::PhysicalDeviceSelector{ ret.value() }.select(); + REQUIRE(pd_ret); + REQUIRE(pd_ret.value().properties.deviceID == 2); + } +} +TEST_CASE("Minimum instance API version lower than VkPhysicalDevice", "[VkBootstrap.api_version]") { + VulkanMock& mock = get_and_setup_default(); + mock.api_version = VK_API_VERSION_1_2; + mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_3; + mock.physical_devices_details[0].properties.deviceID = 1; + { + auto ret = vkb::InstanceBuilder{}.set_headless().require_api_version(1, 4).set_minimum_instance_version(1, 2).build(); + REQUIRE(ret); + auto pd_ret = vkb::PhysicalDeviceSelector{ ret.value() }.select(); + REQUIRE(pd_ret.error() == vkb::PhysicalDeviceError::no_suitable_device); + } +} TEST_CASE("Querying Required Extension Features but with 1.0", "[VkBootstrap.select_features]") { VulkanMock& mock = get_and_setup_default(); mock.instance_extensions.push_back(get_extension_properties("VK_KHR_get_physical_device_properties2")); diff --git a/tests/vulkan_mock_setup.cpp b/tests/vulkan_mock_setup.cpp index 101e49d..1466a39 100644 --- a/tests/vulkan_mock_setup.cpp +++ b/tests/vulkan_mock_setup.cpp @@ -36,6 +36,11 @@ VulkanMock& get_and_setup_default() { mock.instance_extensions.push_back(get_extension_properties("VK_EXT_metal_surface")); #endif mock.instance_extensions.push_back(get_extension_properties(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)); + add_basic_physical_device(mock); + return mock; +} + +VulkanMock::PhysicalDeviceDetails& add_basic_physical_device(VulkanMock& mock) { VulkanMock::PhysicalDeviceDetails physical_device_details{}; physical_device_details.extensions.push_back(get_extension_properties(VK_KHR_SWAPCHAIN_EXTENSION_NAME)); physical_device_details.properties.apiVersion = VK_API_VERSION_1_0; @@ -45,7 +50,7 @@ VulkanMock& get_and_setup_default() { queue_family_properties.minImageTransferGranularity = { 1, 1, 1 }; physical_device_details.queue_family_properties.push_back(queue_family_properties); mock.add_physical_device(std::move(physical_device_details)); - return mock; + return mock.physical_devices_details.back(); } VulkanMock::SurfaceDetails get_basic_surface_details() { diff --git a/tests/vulkan_mock_setup.hpp b/tests/vulkan_mock_setup.hpp index cc99e69..1890033 100644 --- a/tests/vulkan_mock_setup.hpp +++ b/tests/vulkan_mock_setup.hpp @@ -15,4 +15,6 @@ VkExtensionProperties get_extension_properties(const char* extName); VulkanMock& get_and_setup_default(); +VulkanMock::PhysicalDeviceDetails& add_basic_physical_device(VulkanMock& mock); + VulkanMock::SurfaceDetails get_basic_surface_details();