diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 86e960f..9a81c8b 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -846,8 +846,8 @@ std::vector check_device_extension_support( // clang-format off bool supports_features(VkPhysicalDeviceFeatures supported, VkPhysicalDeviceFeatures requested, - const std::vector& extension_supported, - const std::vector& extension_requested) { + std::vector const& extension_supported, + std::vector const& extension_requested) { if (requested.robustBufferAccess && !supported.robustBufferAccess) return false; if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) return false; if (requested.imageCubeArray && !supported.imageCubeArray) return false; @@ -992,7 +992,7 @@ uint32_t get_present_queue_index(VkPhysicalDevice const phys_device, PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details( uint32_t instance_version, VkPhysicalDevice phys_device, - std::vector extension_features_as_template) const { + std::vector extension_features_as_template) const { PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; desc.phys_device = phys_device; auto queue_families = detail::get_vector_noerror( @@ -1007,7 +1007,7 @@ PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_devi desc.device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; desc.extension_features = extension_features_as_template; if (instance_version >= VK_API_VERSION_1_1) { - ExtensionFeatures* prev = nullptr; + detail::ExtensionFeatures* prev = nullptr; for(auto& extension : desc.extension_features) { if(prev != nullptr) { prev->structure->pNext = extension.structure; @@ -1260,12 +1260,12 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::set_desired_version(uint32_t maj // Just calls add_required_features PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_11( VkPhysicalDeviceVulkan11Features const& features_11) { - add_required_features(features_11); + add_required_extension_features(features_11); return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_12( VkPhysicalDeviceVulkan12Features const& features_12) { - add_required_features(features_12); + add_required_extension_features(features_12); return *this; } #endif @@ -1401,21 +1401,24 @@ detail::Result DeviceBuilder::build() const { bool has_phys_dev_features_2 = false; -// Setup the pNexts of all the extension features #if defined(VK_API_VERSION_1_1) + // Setup the pNexts of all the extension features + std::vector match = physical_device.extension_features; VkPhysicalDeviceFeatures2 local_features2{}; + VkBaseOutStructure* tail = nullptr; if (physical_device.instance_version >= VK_MAKE_VERSION(1, 1, 0) && - physical_device.extension_features.size() > 0) { - ExtensionFeatures* prev = nullptr; - for(auto& extension : physical_device.extension_features) { + match.size() > 0) { + detail::ExtensionFeatures* prev = nullptr; + for(auto& extension : match) { if(prev != nullptr) { prev->structure->pNext = extension.structure; } prev = &extension; + tail = prev->structure; } local_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; local_features2.features = physical_device.features; - local_features2.pNext = physical_device.extension_features[0].structure; + local_features2.pNext = match.front().structure; has_phys_dev_features_2 = true; } #endif @@ -1427,19 +1430,31 @@ detail::Result DeviceBuilder::build() const { device_create_info.pQueueCreateInfos = queueCreateInfos.data(); device_create_info.enabledExtensionCount = static_cast(extensions.size()); device_create_info.ppEnabledExtensionNames = extensions.data(); + + detail::setup_pNext_chain(device_create_info, info.pNext_chain); + +#if defined(VK_API_VERSION_1_1) // VUID-VkDeviceCreateInfo-pNext-00373 - don't add pEnabledFeatures if the phys_dev_features_2 is present - if (!has_phys_dev_features_2) { - device_create_info.pEnabledFeatures = &physical_device.features; - } else { + if (has_phys_dev_features_2) { device_create_info.pNext = &local_features2; - } + if(info.pNext_chain.size() > 0) { + match.back().structure->pNext = info.pNext_chain.front(); + } + } else { + device_create_info.pEnabledFeatures = &physical_device.features; + } +#else + device_create_info.pEnabledFeatures = &physical_device.features; +#endif Device device; + VkResult res = detail::vulkan_functions().fp_vkCreateDevice( physical_device.physical_device, &device_create_info, info.allocation_callbacks, &device.device); if (res != VK_SUCCESS) { return { DeviceError::failed_create_device, res }; } + device.physical_device = physical_device; device.surface = physical_device.surface; device.queue_families = physical_device.queue_families; diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index e2fb789..8111cf9 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -115,86 +115,6 @@ template class Result { bool m_init; }; -} // namespace detail - -enum class InstanceError { - vulkan_unavailable, - vulkan_version_unavailable, - vulkan_version_1_1_unavailable, - vulkan_version_1_2_unavailable, - failed_create_instance, - failed_create_debug_messenger, - requested_layers_not_present, - requested_extensions_not_present, - windowing_extensions_not_present, -}; -enum class PhysicalDeviceError { - no_surface_provided, - failed_enumerate_physical_devices, - no_physical_devices_found, - no_suitable_device, -}; -enum class QueueError { - present_unavailable, - graphics_unavailable, - compute_unavailable, - transfer_unavailable, - queue_index_out_of_range, - invalid_queue_family_index -}; -enum class DeviceError { - failed_create_device, -}; -enum class SwapchainError { - surface_handle_not_provided, - failed_query_surface_support_details, - failed_create_swapchain, - failed_get_swapchain_images, - failed_create_swapchain_image_views, -}; - -std::error_code make_error_code(InstanceError instance_error); -std::error_code make_error_code(PhysicalDeviceError physical_device_error); -std::error_code make_error_code(QueueError queue_error); -std::error_code make_error_code(DeviceError device_error); -std::error_code make_error_code(SwapchainError swapchain_error); - -const char* to_string_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT s); -const char* to_string_message_type(VkDebugUtilsMessageTypeFlagsEXT s); - -const char* to_string(InstanceError err); -const char* to_string(PhysicalDeviceError err); -const char* to_string(QueueError err); -const char* to_string(DeviceError err); -const char* to_string(SwapchainError err); - -// Gathers useful information about the available vulkan capabilities, like layers and instance -// extensions. Use this for enabling features conditionally, ie if you would like an extension but -// can use a fallback if it isn't supported but need to know if support is available first. -struct SystemInfo { - private: - SystemInfo(); - - public: - // Use get_system_info to create a SystemInfo struct. This is because loading vulkan could fail. - static detail::Result get_system_info(); - static detail::Result get_system_info(PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); - - // Returns true if a layer is available - 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; - - std::vector available_layers; - std::vector available_extensions; - bool validation_layers_available = false; - bool debug_utils_available = false; -}; - - -class InstanceBuilder; -class PhysicalDeviceSelector; - struct ExtensionFeatures { using DeleteProc = void(*)(ExtensionFeatures&); @@ -313,6 +233,86 @@ struct ExtensionFeatures { CopyProc copy_proc = nullptr; }; +} // namespace detail + +enum class InstanceError { + vulkan_unavailable, + vulkan_version_unavailable, + vulkan_version_1_1_unavailable, + vulkan_version_1_2_unavailable, + failed_create_instance, + failed_create_debug_messenger, + requested_layers_not_present, + requested_extensions_not_present, + windowing_extensions_not_present, +}; +enum class PhysicalDeviceError { + no_surface_provided, + failed_enumerate_physical_devices, + no_physical_devices_found, + no_suitable_device, +}; +enum class QueueError { + present_unavailable, + graphics_unavailable, + compute_unavailable, + transfer_unavailable, + queue_index_out_of_range, + invalid_queue_family_index +}; +enum class DeviceError { + failed_create_device, +}; +enum class SwapchainError { + surface_handle_not_provided, + failed_query_surface_support_details, + failed_create_swapchain, + failed_get_swapchain_images, + failed_create_swapchain_image_views, +}; + +std::error_code make_error_code(InstanceError instance_error); +std::error_code make_error_code(PhysicalDeviceError physical_device_error); +std::error_code make_error_code(QueueError queue_error); +std::error_code make_error_code(DeviceError device_error); +std::error_code make_error_code(SwapchainError swapchain_error); + +const char* to_string_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT s); +const char* to_string_message_type(VkDebugUtilsMessageTypeFlagsEXT s); + +const char* to_string(InstanceError err); +const char* to_string(PhysicalDeviceError err); +const char* to_string(QueueError err); +const char* to_string(DeviceError err); +const char* to_string(SwapchainError err); + +// Gathers useful information about the available vulkan capabilities, like layers and instance +// extensions. Use this for enabling features conditionally, ie if you would like an extension but +// can use a fallback if it isn't supported but need to know if support is available first. +struct SystemInfo { + private: + SystemInfo(); + + public: + // Use get_system_info to create a SystemInfo struct. This is because loading vulkan could fail. + static detail::Result get_system_info(); + static detail::Result get_system_info(PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); + + // Returns true if a layer is available + 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; + + std::vector available_layers; + std::vector available_extensions; + bool validation_layers_available = false; + bool debug_utils_available = false; +}; + + +class InstanceBuilder; +class PhysicalDeviceSelector; + struct Instance { VkInstance instance = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE; @@ -473,7 +473,7 @@ struct PhysicalDevice { uint32_t instance_version = VK_MAKE_VERSION(1, 0, 0); std::vector extensions_to_enable; std::vector queue_families; - mutable std::vector extension_features; + std::vector extension_features; bool defer_surface_initialization = false; friend class PhysicalDeviceSelector; friend class DeviceBuilder; @@ -534,16 +534,15 @@ class PhysicalDeviceSelector { // Require a physical device that supports a (major, minor) version of vulkan. PhysicalDeviceSelector& set_minimum_version(uint32_t major, uint32_t minor); -// Require a physical device which supports a specific set of general/extension features. - template - PhysicalDeviceSelector& add_required_features(T const& features) { + // Require a physical device which supports a specific set of general/extension features. #if defined(VK_API_VERSION_1_1) - criteria.extension_features.push_back(ExtensionFeatures::make(features)); -#endif + template + PhysicalDeviceSelector& add_required_extension_features(T const& features) { + criteria.extension_features.push_back(detail::ExtensionFeatures::make(features)); return *this; } - template<> - PhysicalDeviceSelector& add_required_features(VkPhysicalDeviceFeatures const& features) { +#endif + PhysicalDeviceSelector& set_required_features(VkPhysicalDeviceFeatures const& features) { criteria.required_features = features; return *this; } @@ -581,7 +580,7 @@ class PhysicalDeviceSelector { VkPhysicalDeviceMemoryProperties mem_properties{}; #if defined(VK_API_VERSION_1_1) VkPhysicalDeviceFeatures2 device_features2{}; - std::vector extension_features; + std::vector extension_features; #endif }; @@ -590,7 +589,7 @@ class PhysicalDeviceSelector { PhysicalDeviceDesc populate_device_details(uint32_t instance_version, VkPhysicalDevice phys_device, - std::vector extension_features_as_template) const; + std::vector extension_features_as_template) const; struct SelectionCriteria { PreferredDeviceType preferred_type = PreferredDeviceType::discrete; @@ -612,7 +611,7 @@ class PhysicalDeviceSelector { VkPhysicalDeviceFeatures required_features{}; #if defined(VK_API_VERSION_1_1) VkPhysicalDeviceFeatures2 required_features2{}; - std::vector extension_features; + std::vector extension_features; #endif bool defer_surface_initialization = false; bool use_first_gpu_unconditionally = false; @@ -670,6 +669,13 @@ class DeviceBuilder { // If a custom queue setup is provided, getting the queues and queue indexes is up to the application. DeviceBuilder& custom_queue_setup(std::vector queue_descriptions); + // Add a structure to the pNext chain of VkDeviceCreateInfo. + // The structure must be valid when DeviceBuilder::build() is called. + template DeviceBuilder& add_pNext(T* structure) { + info.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + // Provide custom allocation callbacks. DeviceBuilder& set_allocation_callbacks(VkAllocationCallbacks* callbacks); @@ -677,6 +683,7 @@ class DeviceBuilder { PhysicalDevice physical_device; struct DeviceInfo { VkDeviceCreateFlags flags = 0; + std::vector pNext_chain; std::vector queue_descriptions; VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; } info; diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index 81aee38..b60ae02 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -411,6 +411,38 @@ TEST_CASE("ReLoading Vulkan Manually", "[VkBootstrap.loading]") { } } +#if defined(VK_API_VERSION_1_1) +TEST_CASE("Querying Required Extension Features", "[VkBootstrap.version]") { + GIVEN("A working instance") { + vkb::InstanceBuilder builder; + + auto instance_ret = + builder.request_validation_layers().require_api_version(1, 2).set_headless().build(); + REQUIRE(instance_ret.has_value()); + // Requires a device that supports runtime descriptor arrays via descriptor indexing extension. + { + VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing_features{}; + descriptor_indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT; + descriptor_indexing_features.runtimeDescriptorArray = true; + + vkb::PhysicalDeviceSelector selector(instance_ret.value()); + auto phys_dev_ret = + selector.add_required_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME). + add_required_extension_features(descriptor_indexing_features).select(); + // Ignore if hardware support isn't true + REQUIRE(phys_dev_ret.has_value()); + + vkb::DeviceBuilder device_builder(phys_dev_ret.value()); + auto device_ret = device_builder.build(); + REQUIRE(device_ret.has_value()); + vkb::destroy_device(device_ret.value()); + } + vkb::destroy_instance(instance_ret.value()); + } +} + +#endif + #if defined(VK_API_VERSION_1_2) TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") { GIVEN("A working instance") {