From 6a458fae5677ef1754b574fb817fa9bd7c8f897b Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 6 Feb 2022 19:17:53 -0700 Subject: [PATCH] Allow apps to query devices based on the name This lets applications see all the devices which meet a set of requirements then pick whichever of these devices is most appropriate for them. The intent is for applications that want to find all the suitable devices and let the user of the application pick the physical device from a list. --- .clang-format | 2 +- src/VkBootstrap.cpp | 608 ++++++++++++++++++-------------------- src/VkBootstrap.h | 83 ++++-- tests/bootstrap_tests.cpp | 25 ++ 4 files changed, 368 insertions(+), 350 deletions(-) diff --git a/.clang-format b/.clang-format index 2ef5b38..e38d62b 100644 --- a/.clang-format +++ b/.clang-format @@ -12,7 +12,7 @@ BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false -ColumnLimit: 100 +ColumnLimit: 120 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 0 ContinuationIndentWidth: 4 diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index e3cfd3e..62eb3f6 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -31,17 +31,15 @@ #endif #include +#include namespace vkb { namespace detail { -GenericFeaturesPNextNode::GenericFeaturesPNextNode() { - memset(fields, UINT8_MAX, sizeof(VkBool32) * field_capacity); -} +GenericFeaturesPNextNode::GenericFeaturesPNextNode() { memset(fields, UINT8_MAX, sizeof(VkBool32) * field_capacity); } -bool GenericFeaturesPNextNode::match( - GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept { +bool GenericFeaturesPNextNode::match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept { assert(requested.sType == supported.sType && "Non-matching sTypes in features nodes!"); for (uint32_t i = 0; i < field_capacity; i++) { if (requested.fields[i] && !supported.fields[i]) return false; @@ -115,8 +113,8 @@ class VulkanFunctions { ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties")); fp_vkEnumerateInstanceVersion = reinterpret_cast( ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion")); - fp_vkCreateInstance = reinterpret_cast( - ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); + fp_vkCreateInstance = + reinterpret_cast(ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); } public: @@ -124,8 +122,7 @@ class VulkanFunctions { out_ptr = reinterpret_cast(ptr_vkGetInstanceProcAddr(instance, func_name)); } - template - void get_device_proc_addr(VkDevice device, T& out_ptr, const char* func_name) { + template void get_device_proc_addr(VkDevice device, T& out_ptr, const char* func_name) { out_ptr = reinterpret_cast(fp_vkGetDeviceProcAddr(device, func_name)); } @@ -204,8 +201,7 @@ VulkanFunctions& vulkan_functions() { } // Helper for robustly executing the two-call pattern -template -auto get_vector(std::vector& out, F&& f, Ts&&... ts) -> VkResult { +template auto get_vector(std::vector& out, F&& f, Ts&&... ts) -> VkResult { uint32_t count = 0; VkResult err; do { @@ -220,8 +216,7 @@ auto get_vector(std::vector& out, F&& f, Ts&&... ts) -> VkResult { return err; } -template -auto get_vector_noerror(F&& f, Ts&&... ts) -> std::vector { +template auto get_vector_noerror(F&& f, Ts&&... ts) -> std::vector { uint32_t count = 0; std::vector results; f(ts..., &count, nullptr); @@ -306,8 +301,7 @@ bool check_layer_supported(std::vector const& available_layer return false; } -bool check_layers_supported(std::vector const& available_layers, - std::vector const& layer_names) { +bool check_layers_supported(std::vector const& available_layers, std::vector const& layer_names) { bool all_found = true; for (const auto& layer_name : layer_names) { bool found = check_layer_supported(available_layers, layer_name); @@ -316,8 +310,7 @@ bool check_layers_supported(std::vector const& available_laye return all_found; } -bool check_extension_supported( - std::vector const& available_extensions, const char* extension_name) { +bool check_extension_supported(std::vector const& available_extensions, const char* extension_name) { if (!extension_name) return false; for (const auto& extension_properties : available_extensions) { if (strcmp(extension_name, extension_properties.extensionName) == 0) { @@ -327,8 +320,8 @@ bool check_extension_supported( return false; } -bool check_extensions_supported(std::vector const& available_extensions, - std::vector const& extension_names) { +bool check_extensions_supported( + std::vector const& available_extensions, std::vector const& extension_names) { bool all_found = true; for (const auto& extension_name : extension_names) { bool found = check_extension_supported(available_extensions, extension_name); @@ -337,8 +330,7 @@ bool check_extensions_supported(std::vector const& availa return all_found; } -template -void setup_pNext_chain(T& structure, std::vector const& structs) { +template void setup_pNext_chain(T& structure, std::vector const& structs) { structure.pNext = nullptr; if (structs.size() <= 0) return; for (size_t i = 0; i < structs.size() - 1; i++) { @@ -350,17 +342,13 @@ const char* validation_layer_name = "VK_LAYER_KHRONOS_validation"; struct InstanceErrorCategory : std::error_category { const char* name() const noexcept override { return "vkb_instance"; } - std::string message(int err) const override { - return to_string(static_cast(err)); - } + std::string message(int err) const override { return to_string(static_cast(err)); } }; const InstanceErrorCategory instance_error_category; struct PhysicalDeviceErrorCategory : std::error_category { const char* name() const noexcept override { return "vkb_physical_device"; } - std::string message(int err) const override { - return to_string(static_cast(err)); - } + std::string message(int err) const override { return to_string(static_cast(err)); } }; const PhysicalDeviceErrorCategory physical_device_error_category; @@ -378,9 +366,7 @@ const DeviceErrorCategory device_error_category; struct SwapchainErrorCategory : std::error_category { const char* name() const noexcept override { return "vbk_swapchain"; } - std::string message(int err) const override { - return to_string(static_cast(err)); - } + std::string message(int err) const override { return to_string(static_cast(err)); } }; const SwapchainErrorCategory swapchain_error_category; @@ -506,8 +492,7 @@ SystemInfo::SystemInfo() { } for (auto& layer : this->available_layers) - if (strcmp(layer.layerName, detail::validation_layer_name) == 0) - validation_layers_available = true; + if (strcmp(layer.layerName, detail::validation_layer_name) == 0) validation_layers_available = true; auto available_extensions_ret = detail::get_vector( this->available_extensions, detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, nullptr); @@ -523,9 +508,8 @@ SystemInfo::SystemInfo() { for (auto& layer : this->available_layers) { std::vector layer_extensions; - auto layer_extensions_ret = detail::get_vector(layer_extensions, - detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, - layer.layerName); + auto layer_extensions_ret = detail::get_vector( + layer_extensions, detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, layer.layerName); if (layer_extensions_ret == VK_SUCCESS) { this->available_extensions.insert( this->available_extensions.end(), layer_extensions.begin(), layer_extensions.end()); @@ -580,8 +564,7 @@ detail::Result InstanceBuilder::build() const { if (info.minimum_instance_version > VKB_VK_API_VERSION_1_0 || info.required_api_version > VKB_VK_API_VERSION_1_0 || info.desired_api_version > VKB_VK_API_VERSION_1_0) { - PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = - detail::vulkan_functions().fp_vkEnumerateInstanceVersion; + PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = detail::vulkan_functions().fp_vkEnumerateInstanceVersion; if (pfn_vkEnumerateInstanceVersion != nullptr) { VkResult res = pfn_vkEnumerateInstanceVersion(&instance_version); @@ -624,8 +607,8 @@ detail::Result InstanceBuilder::build() const { if (info.debug_callback != nullptr && system.debug_utils_available) { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - bool supports_properties2_ext = detail::check_extension_supported( - system.available_extensions, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + bool supports_properties2_ext = + detail::check_extension_supported(system.available_extensions, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); if (supports_properties2_ext) { extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); @@ -688,11 +671,9 @@ detail::Result InstanceBuilder::build() const { if (info.enabled_validation_features.size() != 0 || info.disabled_validation_features.size()) { features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; features.pNext = nullptr; - features.enabledValidationFeatureCount = - static_cast(info.enabled_validation_features.size()); + features.enabledValidationFeatureCount = static_cast(info.enabled_validation_features.size()); features.pEnabledValidationFeatures = info.enabled_validation_features.data(); - features.disabledValidationFeatureCount = - static_cast(info.disabled_validation_features.size()); + features.disabledValidationFeatureCount = static_cast(info.disabled_validation_features.size()); features.pDisabledValidationFeatures = info.disabled_validation_features.data(); pNext_chain.push_back(reinterpret_cast(&features)); } @@ -722,10 +703,9 @@ detail::Result InstanceBuilder::build() const { instance_create_info.ppEnabledLayerNames = layers.data(); Instance instance; - VkResult res = detail::vulkan_functions().fp_vkCreateInstance( - &instance_create_info, info.allocation_callbacks, &instance.instance); - if (res != VK_SUCCESS) - return detail::Result(InstanceError::failed_create_instance, res); + VkResult res = + detail::vulkan_functions().fp_vkCreateInstance(&instance_create_info, info.allocation_callbacks, &instance.instance); + if (res != VK_SUCCESS) return detail::Result(InstanceError::failed_create_instance, res); detail::vulkan_functions().init_instance_funcs(instance.instance); @@ -878,8 +858,7 @@ void destroy_debug_messenger(VkInstance const instance, VkDebugUtilsMessengerEXT namespace detail { -std::vector check_device_extension_support( - VkPhysicalDevice device, std::vector desired_extensions) { +std::vector check_device_extension_support(VkPhysicalDevice device, std::vector desired_extensions) { std::vector available_extensions; auto available_extensions_ret = detail::get_vector( available_extensions, detail::vulkan_functions().fp_vkEnumerateDeviceExtensionProperties, device, nullptr); @@ -899,71 +878,71 @@ std::vector check_device_extension_support( // clang-format off bool supports_features(VkPhysicalDeviceFeatures supported, - VkPhysicalDeviceFeatures 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; - if (requested.independentBlend && !supported.independentBlend) return false; - if (requested.geometryShader && !supported.geometryShader) return false; - if (requested.tessellationShader && !supported.tessellationShader) return false; - if (requested.sampleRateShading && !supported.sampleRateShading) return false; - if (requested.dualSrcBlend && !supported.dualSrcBlend) return false; - if (requested.logicOp && !supported.logicOp) return false; - if (requested.multiDrawIndirect && !supported.multiDrawIndirect) return false; - if (requested.drawIndirectFirstInstance && !supported.drawIndirectFirstInstance) return false; - if (requested.depthClamp && !supported.depthClamp) return false; - if (requested.depthBiasClamp && !supported.depthBiasClamp) return false; - if (requested.fillModeNonSolid && !supported.fillModeNonSolid) return false; - if (requested.depthBounds && !supported.depthBounds) return false; - if (requested.wideLines && !supported.wideLines) return false; - if (requested.largePoints && !supported.largePoints) return false; - if (requested.alphaToOne && !supported.alphaToOne) return false; - if (requested.multiViewport && !supported.multiViewport) return false; - if (requested.samplerAnisotropy && !supported.samplerAnisotropy) return false; - if (requested.textureCompressionETC2 && !supported.textureCompressionETC2) return false; - if (requested.textureCompressionASTC_LDR && !supported.textureCompressionASTC_LDR) return false; - if (requested.textureCompressionBC && !supported.textureCompressionBC) return false; - if (requested.occlusionQueryPrecise && !supported.occlusionQueryPrecise) return false; - if (requested.pipelineStatisticsQuery && !supported.pipelineStatisticsQuery) return false; - if (requested.vertexPipelineStoresAndAtomics && !supported.vertexPipelineStoresAndAtomics) return false; - if (requested.fragmentStoresAndAtomics && !supported.fragmentStoresAndAtomics) return false; - if (requested.shaderTessellationAndGeometryPointSize && !supported.shaderTessellationAndGeometryPointSize) return false; - if (requested.shaderImageGatherExtended && !supported.shaderImageGatherExtended) return false; - if (requested.shaderStorageImageExtendedFormats && !supported.shaderStorageImageExtendedFormats) return false; - if (requested.shaderStorageImageMultisample && !supported.shaderStorageImageMultisample) return false; - if (requested.shaderStorageImageReadWithoutFormat && !supported.shaderStorageImageReadWithoutFormat) return false; - if (requested.shaderStorageImageWriteWithoutFormat && !supported.shaderStorageImageWriteWithoutFormat) return false; - if (requested.shaderUniformBufferArrayDynamicIndexing && !supported.shaderUniformBufferArrayDynamicIndexing) return false; - if (requested.shaderSampledImageArrayDynamicIndexing && !supported.shaderSampledImageArrayDynamicIndexing) return false; - if (requested.shaderStorageBufferArrayDynamicIndexing && !supported.shaderStorageBufferArrayDynamicIndexing) return false; - if (requested.shaderStorageImageArrayDynamicIndexing && !supported.shaderStorageImageArrayDynamicIndexing) return false; - if (requested.shaderClipDistance && !supported.shaderClipDistance) return false; - if (requested.shaderCullDistance && !supported.shaderCullDistance) return false; - if (requested.shaderFloat64 && !supported.shaderFloat64) return false; - if (requested.shaderInt64 && !supported.shaderInt64) return false; - if (requested.shaderInt16 && !supported.shaderInt16) return false; - if (requested.shaderResourceResidency && !supported.shaderResourceResidency) return false; - if (requested.shaderResourceMinLod && !supported.shaderResourceMinLod) return false; - if (requested.sparseBinding && !supported.sparseBinding) return false; - if (requested.sparseResidencyBuffer && !supported.sparseResidencyBuffer) return false; - if (requested.sparseResidencyImage2D && !supported.sparseResidencyImage2D) return false; - if (requested.sparseResidencyImage3D && !supported.sparseResidencyImage3D) return false; - if (requested.sparseResidency2Samples && !supported.sparseResidency2Samples) return false; - if (requested.sparseResidency4Samples && !supported.sparseResidency4Samples) return false; - if (requested.sparseResidency8Samples && !supported.sparseResidency8Samples) return false; - if (requested.sparseResidency16Samples && !supported.sparseResidency16Samples) return false; - if (requested.sparseResidencyAliased && !supported.sparseResidencyAliased) return false; - if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false; - if (requested.inheritedQueries && !supported.inheritedQueries) return false; + VkPhysicalDeviceFeatures 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; + if (requested.independentBlend && !supported.independentBlend) return false; + if (requested.geometryShader && !supported.geometryShader) return false; + if (requested.tessellationShader && !supported.tessellationShader) return false; + if (requested.sampleRateShading && !supported.sampleRateShading) return false; + if (requested.dualSrcBlend && !supported.dualSrcBlend) return false; + if (requested.logicOp && !supported.logicOp) return false; + if (requested.multiDrawIndirect && !supported.multiDrawIndirect) return false; + if (requested.drawIndirectFirstInstance && !supported.drawIndirectFirstInstance) return false; + if (requested.depthClamp && !supported.depthClamp) return false; + if (requested.depthBiasClamp && !supported.depthBiasClamp) return false; + if (requested.fillModeNonSolid && !supported.fillModeNonSolid) return false; + if (requested.depthBounds && !supported.depthBounds) return false; + if (requested.wideLines && !supported.wideLines) return false; + if (requested.largePoints && !supported.largePoints) return false; + if (requested.alphaToOne && !supported.alphaToOne) return false; + if (requested.multiViewport && !supported.multiViewport) return false; + if (requested.samplerAnisotropy && !supported.samplerAnisotropy) return false; + if (requested.textureCompressionETC2 && !supported.textureCompressionETC2) return false; + if (requested.textureCompressionASTC_LDR && !supported.textureCompressionASTC_LDR) return false; + if (requested.textureCompressionBC && !supported.textureCompressionBC) return false; + if (requested.occlusionQueryPrecise && !supported.occlusionQueryPrecise) return false; + if (requested.pipelineStatisticsQuery && !supported.pipelineStatisticsQuery) return false; + if (requested.vertexPipelineStoresAndAtomics && !supported.vertexPipelineStoresAndAtomics) return false; + if (requested.fragmentStoresAndAtomics && !supported.fragmentStoresAndAtomics) return false; + if (requested.shaderTessellationAndGeometryPointSize && !supported.shaderTessellationAndGeometryPointSize) return false; + if (requested.shaderImageGatherExtended && !supported.shaderImageGatherExtended) return false; + if (requested.shaderStorageImageExtendedFormats && !supported.shaderStorageImageExtendedFormats) return false; + if (requested.shaderStorageImageMultisample && !supported.shaderStorageImageMultisample) return false; + if (requested.shaderStorageImageReadWithoutFormat && !supported.shaderStorageImageReadWithoutFormat) return false; + if (requested.shaderStorageImageWriteWithoutFormat && !supported.shaderStorageImageWriteWithoutFormat) return false; + if (requested.shaderUniformBufferArrayDynamicIndexing && !supported.shaderUniformBufferArrayDynamicIndexing) return false; + if (requested.shaderSampledImageArrayDynamicIndexing && !supported.shaderSampledImageArrayDynamicIndexing) return false; + if (requested.shaderStorageBufferArrayDynamicIndexing && !supported.shaderStorageBufferArrayDynamicIndexing) return false; + if (requested.shaderStorageImageArrayDynamicIndexing && !supported.shaderStorageImageArrayDynamicIndexing) return false; + if (requested.shaderClipDistance && !supported.shaderClipDistance) return false; + if (requested.shaderCullDistance && !supported.shaderCullDistance) return false; + if (requested.shaderFloat64 && !supported.shaderFloat64) return false; + if (requested.shaderInt64 && !supported.shaderInt64) return false; + if (requested.shaderInt16 && !supported.shaderInt16) return false; + if (requested.shaderResourceResidency && !supported.shaderResourceResidency) return false; + if (requested.shaderResourceMinLod && !supported.shaderResourceMinLod) return false; + if (requested.sparseBinding && !supported.sparseBinding) return false; + if (requested.sparseResidencyBuffer && !supported.sparseResidencyBuffer) return false; + if (requested.sparseResidencyImage2D && !supported.sparseResidencyImage2D) return false; + if (requested.sparseResidencyImage3D && !supported.sparseResidencyImage3D) return false; + if (requested.sparseResidency2Samples && !supported.sparseResidency2Samples) return false; + if (requested.sparseResidency4Samples && !supported.sparseResidency4Samples) return false; + if (requested.sparseResidency8Samples && !supported.sparseResidency8Samples) return false; + if (requested.sparseResidency16Samples && !supported.sparseResidency16Samples) return false; + if (requested.sparseResidencyAliased && !supported.sparseResidencyAliased) return false; + if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false; + if (requested.inheritedQueries && !supported.inheritedQueries) return false; - for(size_t i = 0; i < extension_requested.size(); ++i) { - auto res = GenericFeaturesPNextNode::match(extension_requested[i], extension_supported[i]); - if(!res) return false; - } + for(size_t i = 0; i < extension_requested.size(); ++i) { + auto res = GenericFeaturesPNextNode::match(extension_requested[i], extension_supported[i]); + if(!res) return false; + } - return true; + return true; } // clang-format on // Finds the first queue which supports the desired operations. Returns QUEUE_INDEX_MAX_VALUE if none is found @@ -976,13 +955,11 @@ uint32_t get_first_queue_index(std::vector const& famil // Finds the queue which is separate from the graphics queue and has the desired flag and not the // undesired flag, but will select it if no better options are available compute support. Returns // QUEUE_INDEX_MAX_VALUE if none is found. -uint32_t get_separate_queue_index(std::vector const& families, - VkQueueFlags desired_flags, - VkQueueFlags undesired_flags) { +uint32_t get_separate_queue_index( + std::vector const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) { uint32_t index = QUEUE_INDEX_MAX_VALUE; for (uint32_t i = 0; i < static_cast(families.size()); i++) { - if ((families[i].queueFlags & desired_flags) && - ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { + if ((families[i].queueFlags & desired_flags) && ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { if ((families[i].queueFlags & undesired_flags) == 0) { return i; } else { @@ -994,9 +971,8 @@ uint32_t get_separate_queue_index(std::vector const& fa } // finds the first queue which supports only the desired flag (not graphics or transfer). Returns QUEUE_INDEX_MAX_VALUE if none is found. -uint32_t get_dedicated_queue_index(std::vector const& families, - VkQueueFlags desired_flags, - VkQueueFlags undesired_flags) { +uint32_t get_dedicated_queue_index( + std::vector const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) { for (uint32_t i = 0; i < static_cast(families.size()); i++) { if ((families[i].queueFlags & desired_flags) && (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && (families[i].queueFlags & undesired_flags) == 0) @@ -1006,16 +982,13 @@ uint32_t get_dedicated_queue_index(std::vector const& f } // finds the first queue which supports presenting. returns QUEUE_INDEX_MAX_VALUE if none is found -uint32_t get_present_queue_index(VkPhysicalDevice const phys_device, - VkSurfaceKHR const surface, - std::vector const& families) { +uint32_t get_present_queue_index( + VkPhysicalDevice const phys_device, VkSurfaceKHR const surface, std::vector const& families) { for (uint32_t i = 0; i < static_cast(families.size()); i++) { VkBool32 presentSupport = false; if (surface != VK_NULL_HANDLE) { - VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceSupportKHR( - phys_device, i, surface, &presentSupport); - if (res != VK_SUCCESS) - return QUEUE_INDEX_MAX_VALUE; // TODO: determine if this should fail another way + VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceSupportKHR(phys_device, i, surface, &presentSupport); + if (res != VK_SUCCESS) return QUEUE_INDEX_MAX_VALUE; // TODO: determine if this should fail another way } if (presentSupport == VK_TRUE) return i; } @@ -1023,29 +996,32 @@ uint32_t get_present_queue_index(VkPhysicalDevice const phys_device, } } // namespace detail - -PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice phys_device, +PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice vk_phys_device, std::vector const& src_extended_features_chain) const { - PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; - desc.phys_device = phys_device; + PhysicalDevice physical_device{}; + physical_device.physical_device = vk_phys_device; + physical_device.surface = instance_info.surface; + physical_device.defer_surface_initialization = criteria.defer_surface_initialization; + physical_device.instance_version = instance_info.version; auto queue_families = detail::get_vector_noerror( - detail::vulkan_functions().fp_vkGetPhysicalDeviceQueueFamilyProperties, phys_device); - desc.queue_families = queue_families; + detail::vulkan_functions().fp_vkGetPhysicalDeviceQueueFamilyProperties, vk_phys_device); + physical_device.queue_families = queue_families; - detail::vulkan_functions().fp_vkGetPhysicalDeviceProperties(phys_device, &desc.device_properties); - detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(phys_device, &desc.device_features); - detail::vulkan_functions().fp_vkGetPhysicalDeviceMemoryProperties(phys_device, &desc.mem_properties); + detail::vulkan_functions().fp_vkGetPhysicalDeviceProperties(vk_phys_device, &physical_device.properties); + detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(vk_phys_device, &physical_device.features); + detail::vulkan_functions().fp_vkGetPhysicalDeviceMemoryProperties(vk_phys_device, &physical_device.memory_properties); + + physical_device.name = physical_device.properties.deviceName; #if defined(VKB_VK_API_VERSION_1_1) - desc.device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; #else - desc.device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; #endif auto fill_chain = src_extended_features_chain; - if (!fill_chain.empty() && - (instance_info.version >= VKB_VK_API_VERSION_1_1 || instance_info.supports_properties2_ext)) { + if (!fill_chain.empty() && (instance_info.version >= VKB_VK_API_VERSION_1_1 || instance_info.supports_properties2_ext)) { detail::GenericFeaturesPNextNode* prev = nullptr; for (auto& extension : fill_chain) { @@ -1056,213 +1032,229 @@ PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_devi } #if defined(VKB_VK_API_VERSION_1_1) - if (instance_info.version >= VKB_VK_API_VERSION_1_1 && desc.device_properties.apiVersion >= VKB_VK_API_VERSION_1_1) { + if (instance_info.version >= VKB_VK_API_VERSION_1_1 && physical_device.properties.apiVersion >= VKB_VK_API_VERSION_1_1) { VkPhysicalDeviceFeatures2 local_features{}; local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; local_features.pNext = &fill_chain.front(); - detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(phys_device, &local_features); + detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(vk_phys_device, &local_features); } else if (instance_info.supports_properties2_ext) { VkPhysicalDeviceFeatures2KHR local_features_khr{}; local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; local_features_khr.pNext = &fill_chain.front(); - detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(phys_device, &local_features_khr); + detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(vk_phys_device, &local_features_khr); } #else VkPhysicalDeviceFeatures2KHR local_features_khr{}; local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; local_features_khr.pNext = &fill_chain.front(); if (instance_info.supports_properties2_ext) { - detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(phys_device, &local_features_khr); + detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(vk_phys_device, &local_features_khr); } #endif - desc.extended_features_chain = fill_chain; + physical_device.extended_features_chain = fill_chain; } - return desc; + return physical_device; } -PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable(PhysicalDeviceDesc pd) const { - Suitable suitable = Suitable::yes; +PhysicalDevice::Suitable PhysicalDeviceSelector::is_device_suitable(PhysicalDevice const& pd) const { + PhysicalDevice::Suitable suitable = PhysicalDevice::Suitable::yes; - if (criteria.required_version > pd.device_properties.apiVersion) return Suitable::no; - if (criteria.desired_version > pd.device_properties.apiVersion) suitable = Suitable::partial; + if (criteria.name.size() > 0 && criteria.name != pd.properties.deviceName) return PhysicalDevice::Suitable::no; - bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families, - VK_QUEUE_COMPUTE_BIT, - VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE; - bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families, - VK_QUEUE_TRANSFER_BIT, - VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE; - bool separate_compute = - detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; - bool separate_transfer = - detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; + if (criteria.required_version > pd.properties.apiVersion) return PhysicalDevice::Suitable::no; + if (criteria.desired_version > pd.properties.apiVersion) suitable = PhysicalDevice::Suitable::partial; - bool present_queue = - detail::get_present_queue_index(pd.phys_device, instance_info.surface, pd.queue_families) != - detail::QUEUE_INDEX_MAX_VALUE; + bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != + detail::QUEUE_INDEX_MAX_VALUE; + bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != + detail::QUEUE_INDEX_MAX_VALUE; + bool separate_compute = detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != + detail::QUEUE_INDEX_MAX_VALUE; + bool separate_transfer = detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != + detail::QUEUE_INDEX_MAX_VALUE; - if (criteria.require_dedicated_compute_queue && !dedicated_compute) return Suitable::no; - if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return Suitable::no; - if (criteria.require_separate_compute_queue && !separate_compute) return Suitable::no; - if (criteria.require_separate_transfer_queue && !separate_transfer) return Suitable::no; + bool present_queue = detail::get_present_queue_index(pd.physical_device, instance_info.surface, pd.queue_families) != + detail::QUEUE_INDEX_MAX_VALUE; + + if (criteria.require_dedicated_compute_queue && !dedicated_compute) return PhysicalDevice::Suitable::no; + if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return PhysicalDevice::Suitable::no; + if (criteria.require_separate_compute_queue && !separate_compute) return PhysicalDevice::Suitable::no; + if (criteria.require_separate_transfer_queue && !separate_transfer) return PhysicalDevice::Suitable::no; if (criteria.require_present && !present_queue && !criteria.defer_surface_initialization) - return Suitable::no; + return PhysicalDevice::Suitable::no; - auto required_extensions_supported = - detail::check_device_extension_support(pd.phys_device, criteria.required_extensions); + auto required_extensions_supported = detail::check_device_extension_support(pd.physical_device, criteria.required_extensions); if (required_extensions_supported.size() != criteria.required_extensions.size()) - return Suitable::no; + return PhysicalDevice::Suitable::no; - auto desired_extensions_supported = - detail::check_device_extension_support(pd.phys_device, criteria.desired_extensions); + auto desired_extensions_supported = detail::check_device_extension_support(pd.physical_device, criteria.desired_extensions); if (desired_extensions_supported.size() != criteria.desired_extensions.size()) - suitable = Suitable::partial; + suitable = PhysicalDevice::Suitable::partial; - bool swapChainAdequate = false; - if (criteria.defer_surface_initialization) { - swapChainAdequate = true; - } else if (!instance_info.headless) { + if (!criteria.defer_surface_initialization && criteria.require_present) { std::vector formats; std::vector present_modes; auto formats_ret = detail::get_vector(formats, detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceFormatsKHR, - pd.phys_device, + pd.physical_device, instance_info.surface); auto present_modes_ret = detail::get_vector(present_modes, detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfacePresentModesKHR, - pd.phys_device, + pd.physical_device, instance_info.surface); - if (formats_ret == VK_SUCCESS && present_modes_ret == VK_SUCCESS) { - swapChainAdequate = !formats.empty() && !present_modes.empty(); + if (formats_ret != VK_SUCCESS || present_modes_ret != VK_SUCCESS || formats.empty() || present_modes.empty()) { + return PhysicalDevice::Suitable::no; } } - if (criteria.require_present && !swapChainAdequate) return Suitable::no; - if (pd.device_properties.deviceType != static_cast(criteria.preferred_type)) { - if (criteria.allow_any_type) - suitable = Suitable::partial; - else - return Suitable::no; + if (!criteria.allow_any_type && pd.properties.deviceType != static_cast(criteria.preferred_type)) { + suitable = PhysicalDevice::Suitable::partial; } bool required_features_supported = detail::supports_features( - pd.device_features, criteria.required_features, pd.extended_features_chain, criteria.extended_features_chain); - if (!required_features_supported) return Suitable::no; + pd.features, criteria.required_features, pd.extended_features_chain, criteria.extended_features_chain); + if (!required_features_supported) return PhysicalDevice::Suitable::no; - bool has_required_memory = false; - bool has_preferred_memory = false; - for (uint32_t i = 0; i < pd.mem_properties.memoryHeapCount; i++) { - if (pd.mem_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { - if (pd.mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { - has_required_memory = true; - } - if (pd.mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) { - has_preferred_memory = true; + for (uint32_t i = 0; i < pd.memory_properties.memoryHeapCount; i++) { + if (pd.memory_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + if (pd.memory_properties.memoryHeaps[i].size < criteria.required_mem_size) { + return PhysicalDevice::Suitable::no; + } else if (pd.memory_properties.memoryHeaps[i].size < criteria.desired_mem_size) { + suitable = PhysicalDevice::Suitable::partial; } } } - if (!has_required_memory) return Suitable::no; - if (!has_preferred_memory) suitable = Suitable::partial; return suitable; } +// delegate construction to the one with an explicit surface parameter +PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance) +: PhysicalDeviceSelector(instance, VK_NULL_HANDLE) {} -PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance) { +PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance, VkSurfaceKHR surface) { instance_info.instance = instance.instance; - instance_info.headless = instance.headless; instance_info.version = instance.instance_version; instance_info.supports_properties2_ext = instance.supports_properties2_ext; + instance_info.surface = surface; criteria.require_present = !instance.headless; criteria.required_version = instance.api_version; criteria.desired_version = instance.api_version; } -detail::Result PhysicalDeviceSelector::select() const { - +detail::Result> PhysicalDeviceSelector::select_impl(DeviceSelectionMode selection) const { #if !defined(NDEBUG) // Validation for (const auto& node : criteria.extended_features_chain) { assert(node.sType != static_cast(0) && "Features struct sType must be filled with the struct's " "corresponding VkStructureType enum"); - assert( - node.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 && - "Do not pass VkPhysicalDeviceFeatures2 as a required extension feature structure. An " - "instance of this is managed internally for selection criteria and device creation."); + assert(node.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 && + "Do not pass VkPhysicalDeviceFeatures2 as a required extension feature structure. An " + "instance of this is managed internally for selection criteria and device creation."); } #endif - if (!instance_info.headless && !criteria.defer_surface_initialization) { + if (criteria.require_present && !criteria.defer_surface_initialization) { if (instance_info.surface == VK_NULL_HANDLE) - return detail::Result{ PhysicalDeviceError::no_surface_provided }; + return detail::Result>{ PhysicalDeviceError::no_surface_provided }; } - std::vector physical_devices; + // Get the VkPhysicalDevice handles on the system + std::vector vk_physical_devices; - auto physical_devices_ret = detail::get_vector( - physical_devices, detail::vulkan_functions().fp_vkEnumeratePhysicalDevices, instance_info.instance); - if (physical_devices_ret != VK_SUCCESS) { - return detail::Result{ PhysicalDeviceError::failed_enumerate_physical_devices, - physical_devices_ret }; + auto vk_physical_devices_ret = detail::get_vector( + vk_physical_devices, detail::vulkan_functions().fp_vkEnumeratePhysicalDevices, instance_info.instance); + if (vk_physical_devices_ret != VK_SUCCESS) { + return detail::Result>{ PhysicalDeviceError::failed_enumerate_physical_devices, + vk_physical_devices_ret }; } - if (physical_devices.size() == 0) { - return detail::Result{ PhysicalDeviceError::no_physical_devices_found }; + if (vk_physical_devices.size() == 0) { + return detail::Result>{ PhysicalDeviceError::no_physical_devices_found }; } - std::vector phys_device_descriptions; - for (auto& phys_device : physical_devices) { - phys_device_descriptions.push_back(populate_device_details(phys_device, criteria.extended_features_chain)); + auto fill_out_phys_dev_with_criteria = [&](PhysicalDevice& phys_dev) { + phys_dev.features = criteria.required_features; + phys_dev.extended_features_chain = criteria.extended_features_chain; + phys_dev.extensions_to_enable.insert( + phys_dev.extensions_to_enable.end(), criteria.required_extensions.begin(), criteria.required_extensions.end()); + auto desired_extensions_supported = + detail::check_device_extension_support(phys_dev.physical_device, criteria.desired_extensions); + phys_dev.extensions_to_enable.insert( + phys_dev.extensions_to_enable.end(), desired_extensions_supported.begin(), desired_extensions_supported.end()); + }; + + // if this option is set, always return only the first physical device found + if (criteria.use_first_gpu_unconditionally && vk_physical_devices.size() > 0) { + PhysicalDevice physical_device = populate_device_details(vk_physical_devices[0], criteria.extended_features_chain); + fill_out_phys_dev_with_criteria(physical_device); + return std::vector{ physical_device }; } - PhysicalDeviceDesc selected_device{}; - - if (criteria.use_first_gpu_unconditionally) { - selected_device = phys_device_descriptions.at(0); - } else { - for (const auto& device : phys_device_descriptions) { - auto suitable = is_device_suitable(device); - if (suitable == Suitable::yes) { - selected_device = device; - break; - } else if (suitable == Suitable::partial) { - selected_device = device; - } + // Populate their details and check their suitability + std::vector physical_devices; + for (auto& vk_physical_device : vk_physical_devices) { + PhysicalDevice phys_dev = populate_device_details(vk_physical_device, criteria.extended_features_chain); + phys_dev.suitable = is_device_suitable(phys_dev); + if (phys_dev.suitable != PhysicalDevice::Suitable::no) { + physical_devices.push_back(phys_dev); } } - if (selected_device.phys_device == VK_NULL_HANDLE) { - return detail::Result{ PhysicalDeviceError::no_suitable_device }; - } - PhysicalDevice out_device{}; - out_device.physical_device = selected_device.phys_device; - out_device.surface = instance_info.surface; - out_device.features = criteria.required_features; - out_device.extended_features_chain = criteria.extended_features_chain; - out_device.properties = selected_device.device_properties; - out_device.memory_properties = selected_device.mem_properties; - out_device.queue_families = selected_device.queue_families; - out_device.defer_surface_initialization = criteria.defer_surface_initialization; - out_device.instance_version = instance_info.version; + // sort the list into fully and partially suitable devices. use stable_partition to maintain relative order + const auto partition_index = std::stable_partition(physical_devices.begin(), physical_devices.end(), [](auto const& pd) { + return pd.suitable == PhysicalDevice::Suitable::yes; + }); - out_device.extensions_to_enable.insert(out_device.extensions_to_enable.end(), - criteria.required_extensions.begin(), - criteria.required_extensions.end()); - auto desired_extensions_supported = - detail::check_device_extension_support(out_device.physical_device, criteria.desired_extensions); - out_device.extensions_to_enable.insert(out_device.extensions_to_enable.end(), - desired_extensions_supported.begin(), - desired_extensions_supported.end()); - return out_device; + // Remove the partially suitable elements if they aren't desired + if (selection == DeviceSelectionMode::only_fully_suitable) { + physical_devices.erase(partition_index, physical_devices.end() - 1); + } + + return physical_devices; } +detail::Result PhysicalDeviceSelector::select(DeviceSelectionMode selection) const { + auto const selected_devices = select_impl(selection); + + if (!selected_devices) return detail::Result{ selected_devices.error() }; + if (selected_devices.value().size() == 0) { + return detail::Result{ PhysicalDeviceError::no_suitable_device }; + } + + return selected_devices.value().at(0); +} + +// Return all devices which are considered suitable - intended for applications which want to let the user pick the physical device +detail::Result> PhysicalDeviceSelector::select_devices(DeviceSelectionMode selection) const { + auto const selected_devices = select_impl(selection); + if (!selected_devices) return detail::Result>{ selected_devices.error() }; + if (selected_devices.value().size() == 0) { + return detail::Result>{ PhysicalDeviceError::no_suitable_device }; + } + return selected_devices.value(); +} + +detail::Result> PhysicalDeviceSelector::select_device_names(DeviceSelectionMode selection) const { + auto const selected_devices = select_impl(selection); + if (!selected_devices) return detail::Result>{ selected_devices.error() }; + if (selected_devices.value().size() == 0) { + return detail::Result>{ PhysicalDeviceError::no_suitable_device }; + } + std::vector names; + for (const auto& pd : selected_devices.value()) { + names.push_back(pd.name); + } + return names; +} PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface(VkSurfaceKHR surface) { instance_info.surface = surface; - instance_info.headless = false; + return *this; +} +PhysicalDeviceSelector& PhysicalDeviceSelector::set_name(std::string const& name) { + criteria.name = name; return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type(PreferredDeviceType type) { @@ -1306,8 +1298,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extension(const cha return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extensions(std::vector extensions) { - criteria.required_extensions.insert( - criteria.required_extensions.end(), extensions.begin(), extensions.end()); + criteria.required_extensions.insert(criteria.required_extensions.end(), extensions.begin(), extensions.end()); return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension(const char* extension) { @@ -1315,8 +1306,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension(const char return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extensions(std::vector extensions) { - criteria.desired_extensions.insert( - criteria.desired_extensions.end(), extensions.begin(), extensions.end()); + criteria.desired_extensions.insert(criteria.desired_extensions.end(), extensions.begin(), extensions.end()); return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::set_minimum_version(uint32_t major, uint32_t minor) { @@ -1333,22 +1323,19 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysical } #if defined(VKB_VK_API_VERSION_1_2) // Just calls add_required_features -PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_11( - VkPhysicalDeviceVulkan11Features features_11) { +PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_11(VkPhysicalDeviceVulkan11Features features_11) { features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; add_required_extension_features(features_11); return *this; } -PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_12( - VkPhysicalDeviceVulkan12Features features_12) { +PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_12(VkPhysicalDeviceVulkan12Features features_12) { features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; add_required_extension_features(features_12); return *this; } #endif #if defined(VKB_VK_API_VERSION_1_3) -PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_13( - VkPhysicalDeviceVulkan13Features features_13) { +PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_13(VkPhysicalDeviceVulkan13Features features_13) { features_13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; add_required_extension_features(features_13); return *this; @@ -1363,25 +1350,20 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditiona return *this; } +// PhysicalDevice bool PhysicalDevice::has_dedicated_compute_queue() const { - return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; + return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE; } bool PhysicalDevice::has_separate_compute_queue() const { - return detail::get_separate_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; + return detail::get_separate_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE; } bool PhysicalDevice::has_dedicated_transfer_queue() const { - return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; + return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE; } bool PhysicalDevice::has_separate_transfer_queue() const { - return detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != - detail::QUEUE_INDEX_MAX_VALUE; -} -std::vector PhysicalDevice::get_queue_families() const { - return queue_families; + return detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE; } +std::vector PhysicalDevice::get_queue_families() const { return queue_families; } std::vector PhysicalDevice::get_extensions() const { return extensions_to_enable; } PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; } @@ -1471,8 +1453,7 @@ DeviceBuilder::DeviceBuilder(PhysicalDevice phys_device) { physical_device = phy detail::Result DeviceBuilder::build() const { std::vector queue_descriptions; - queue_descriptions.insert( - queue_descriptions.end(), info.queue_descriptions.begin(), info.queue_descriptions.end()); + queue_descriptions.insert(queue_descriptions.end(), info.queue_descriptions.begin(), info.queue_descriptions.end()); if (queue_descriptions.size() == 0) { for (uint32_t i = 0; i < physical_device.queue_families.size(); i++) { @@ -1560,10 +1541,8 @@ detail::Result DeviceBuilder::build() const { device.queue_families = physical_device.queue_families; device.allocation_callbacks = info.allocation_callbacks; device.fp_vkGetDeviceProcAddr = detail::vulkan_functions().fp_vkGetDeviceProcAddr; - detail::vulkan_functions().get_device_proc_addr( - device.device, device.internal_table.fp_vkGetDeviceQueue, "vkGetDeviceQueue"); - detail::vulkan_functions().get_device_proc_addr( - device.device, device.internal_table.fp_vkDestroyDevice, "vkDestroyDevice"); + detail::vulkan_functions().get_device_proc_addr(device.device, device.internal_table.fp_vkGetDeviceQueue, "vkGetDeviceQueue"); + detail::vulkan_functions().get_device_proc_addr(device.device, device.internal_table.fp_vkDestroyDevice, "vkDestroyDevice"); return device; } DeviceBuilder& DeviceBuilder::custom_queue_setup(std::vector queue_descriptions) { @@ -1618,8 +1597,7 @@ Result query_surface_support_details(VkPhysicalDevice phy if (surface == VK_NULL_HANDLE) return make_error_code(SurfaceSupportError::surface_handle_null); VkSurfaceCapabilitiesKHR capabilities; - VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - phys_device, surface, &capabilities); + VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface, &capabilities); if (res != VK_SUCCESS) { return { make_error_code(SurfaceSupportError::failed_get_surface_capabilities), res }; } @@ -1646,13 +1624,10 @@ VkSurfaceFormatKHR find_surface_format(VkPhysicalDevice phys_device, for (auto const& desired_format : desired_formats) { for (auto const& available_format : available_formats) { // finds the first format that is desired and available - if (desired_format.format == available_format.format && - desired_format.colorSpace == available_format.colorSpace) { + if (desired_format.format == available_format.format && desired_format.colorSpace == available_format.colorSpace) { VkFormatProperties properties; - detail::vulkan_functions().fp_vkGetPhysicalDeviceFormatProperties( - phys_device, desired_format.format, &properties); - if ((properties.optimalTilingFeatures & feature_flags) == feature_flags) - return desired_format; + detail::vulkan_functions().fp_vkGetPhysicalDeviceFormatProperties(phys_device, desired_format.format, &properties); + if ((properties.optimalTilingFeatures & feature_flags) == feature_flags) return desired_format; } } } @@ -1682,10 +1657,10 @@ VkExtent2D find_extent(VkSurfaceCapabilitiesKHR const& capabilities, uint32_t de } else { VkExtent2D actualExtent = { desired_width, desired_height }; - actualExtent.width = maximum(capabilities.minImageExtent.width, - minimum(capabilities.maxImageExtent.width, actualExtent.width)); - actualExtent.height = maximum(capabilities.minImageExtent.height, - minimum(capabilities.maxImageExtent.height, actualExtent.height)); + actualExtent.width = + maximum(capabilities.minImageExtent.width, minimum(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = + maximum(capabilities.minImageExtent.height, minimum(capabilities.maxImageExtent.height, actualExtent.height)); return actualExtent; } @@ -1694,8 +1669,7 @@ VkExtent2D find_extent(VkSurfaceCapabilitiesKHR const& capabilities, uint32_t de void destroy_swapchain(Swapchain const& swapchain) { if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE) { - swapchain.internal_table.fp_vkDestroySwapchainKHR( - swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks); + swapchain.internal_table.fp_vkDestroySwapchainKHR(swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks); } } @@ -1754,19 +1728,17 @@ detail::Result SwapchainBuilder::build() const { auto surface_support_ret = detail::query_surface_support_details(info.physical_device, info.surface); if (!surface_support_ret.has_value()) - return detail::Error{ SwapchainError::failed_query_surface_support_details, - surface_support_ret.vk_result() }; + return detail::Error{ SwapchainError::failed_query_surface_support_details, surface_support_ret.vk_result() }; auto surface_support = surface_support_ret.value(); uint32_t image_count = surface_support.capabilities.minImageCount + 1; if (surface_support.capabilities.maxImageCount > 0 && image_count > surface_support.capabilities.maxImageCount) { image_count = surface_support.capabilities.maxImageCount; } - VkSurfaceFormatKHR surface_format = detail::find_surface_format( - info.physical_device, surface_support.formats, desired_formats, info.format_feature_flags); + VkSurfaceFormatKHR surface_format = + detail::find_surface_format(info.physical_device, surface_support.formats, desired_formats, info.format_feature_flags); - VkExtent2D extent = - detail::find_extent(surface_support.capabilities, info.desired_width, info.desired_height); + VkExtent2D extent = detail::find_extent(surface_support.capabilities, info.desired_width, info.desired_height); uint32_t image_array_layers = info.array_layer_count; if (surface_support.capabilities.maxImageArrayLayers < info.array_layer_count) @@ -1776,8 +1748,7 @@ detail::Result SwapchainBuilder::build() const { uint32_t queue_family_indices[] = { info.graphics_queue_index, info.present_queue_index }; - VkPresentModeKHR present_mode = - detail::find_present_mode(surface_support.present_modes, desired_present_modes); + VkPresentModeKHR present_mode = detail::find_present_mode(surface_support.present_modes, desired_present_modes); VkSurfaceTransformFlagBitsKHR pre_transform = info.pre_transform; if (info.pre_transform == static_cast(0)) @@ -1816,8 +1787,7 @@ detail::Result SwapchainBuilder::build() const { Swapchain swapchain{}; PFN_vkCreateSwapchainKHR swapchain_create_proc; detail::vulkan_functions().get_device_proc_addr(info.device, swapchain_create_proc, "vkCreateSwapchainKHR"); - auto res = swapchain_create_proc( - info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain); + auto res = swapchain_create_proc(info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain); if (res != VK_SUCCESS) { return detail::Error{ SwapchainError::failed_create_swapchain, res }; @@ -1827,10 +1797,8 @@ detail::Result SwapchainBuilder::build() const { swapchain.extent = extent; detail::vulkan_functions().get_device_proc_addr( info.device, swapchain.internal_table.fp_vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR"); - detail::vulkan_functions().get_device_proc_addr( - info.device, swapchain.internal_table.fp_vkCreateImageView, "vkCreateImageView"); - detail::vulkan_functions().get_device_proc_addr( - info.device, swapchain.internal_table.fp_vkDestroyImageView, "vkDestroyImageView"); + detail::vulkan_functions().get_device_proc_addr(info.device, swapchain.internal_table.fp_vkCreateImageView, "vkCreateImageView"); + detail::vulkan_functions().get_device_proc_addr(info.device, swapchain.internal_table.fp_vkDestroyImageView, "vkDestroyImageView"); detail::vulkan_functions().get_device_proc_addr( info.device, swapchain.internal_table.fp_vkDestroySwapchainKHR, "vkDestroySwapchainKHR"); auto images = swapchain.get_images(); @@ -1844,8 +1812,8 @@ detail::Result SwapchainBuilder::build() const { detail::Result> Swapchain::get_images() { std::vector swapchain_images; - auto swapchain_images_ret = detail::get_vector( - swapchain_images, internal_table.fp_vkGetSwapchainImagesKHR, device, swapchain); + auto swapchain_images_ret = + detail::get_vector(swapchain_images, internal_table.fp_vkGetSwapchainImagesKHR, device, swapchain); if (swapchain_images_ret != VK_SUCCESS) { return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret }; } @@ -1875,10 +1843,8 @@ detail::Result> Swapchain::get_image_views() { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - VkResult res = - internal_table.fp_vkCreateImageView(device, &createInfo, allocation_callbacks, &views[i]); - if (res != VK_SUCCESS) - return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; + VkResult res = internal_table.fp_vkCreateImageView(device, &createInfo, allocation_callbacks, &views[i]); + if (res != VK_SUCCESS) return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; } return views; } diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 5acc8b5..cd60a93 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -28,25 +29,25 @@ #include "VkBootstrapDispatch.h" #ifdef VK_MAKE_API_VERSION - #define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_API_VERSION(variant, major, minor, patch) +#define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_API_VERSION(variant, major, minor, patch) #elif defined(VK_MAKE_VERSION) - #define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch) +#define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch) #endif #if defined(VK_API_VERSION_1_3) || defined(VK_VERSION_1_3) - #define VKB_VK_API_VERSION_1_3 VKB_MAKE_VK_VERSION(0, 1, 3, 0) +#define VKB_VK_API_VERSION_1_3 VKB_MAKE_VK_VERSION(0, 1, 3, 0) #endif #if defined(VK_API_VERSION_1_2) || defined(VK_VERSION_1_2) - #define VKB_VK_API_VERSION_1_2 VKB_MAKE_VK_VERSION(0, 1, 2, 0) +#define VKB_VK_API_VERSION_1_2 VKB_MAKE_VK_VERSION(0, 1, 2, 0) #endif #if defined(VK_API_VERSION_1_1) || defined(VK_VERSION_1_1) - #define VKB_VK_API_VERSION_1_1 VKB_MAKE_VK_VERSION(0, 1, 1, 0) +#define VKB_VK_API_VERSION_1_1 VKB_MAKE_VK_VERSION(0, 1, 1, 0) #endif #if defined(VK_API_VERSION_1_0) || defined(VK_VERSION_1_0) - #define VKB_VK_API_VERSION_1_0 VKB_MAKE_VK_VERSION(0, 1, 0, 0) +#define VKB_VK_API_VERSION_1_0 VKB_MAKE_VK_VERSION(0, 1, 0, 0) #endif namespace vkb { @@ -65,8 +66,7 @@ template class Result { Result(Error error) : m_error{ error }, m_init{ false } {} - Result(std::error_code error_code, VkResult result = VK_SUCCESS) - : m_error{ error_code, result }, m_init{ false } {} + Result(std::error_code error_code, VkResult result = VK_SUCCESS) : m_error{ error_code, result }, m_init{ false } {} ~Result() { destroy(); } Result(Result const& expected) : m_init(expected.m_init) { @@ -423,9 +423,9 @@ class InstanceBuilder { PFN_vkDebugUtilsMessengerCallbackEXT debug_callback = default_debug_callback; VkDebugUtilsMessageSeverityFlagsEXT debug_message_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - VkDebugUtilsMessageTypeFlagsEXT debug_message_type = - VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + VkDebugUtilsMessageTypeFlagsEXT debug_message_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; void* debug_user_data_pointer = nullptr; // validation features @@ -450,15 +450,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback(VkDebugUtilsMessageSeverit const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); -void destroy_debug_utils_messenger(VkInstance const instance, - VkDebugUtilsMessengerEXT const messenger, - VkAllocationCallbacks* allocation_callbacks = nullptr); +void destroy_debug_utils_messenger( + VkInstance const instance, VkDebugUtilsMessengerEXT const messenger, VkAllocationCallbacks* allocation_callbacks = nullptr); // ---- Physical Device ---- // class PhysicalDeviceSelector; class DeviceBuilder; struct PhysicalDevice { + std::string name; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE; @@ -492,28 +492,55 @@ struct PhysicalDevice { std::vector extensions_to_enable; std::vector queue_families; std::vector extended_features_chain; +#if defined(VKB_VK_API_VERSION_1_1) + VkPhysicalDeviceFeatures2 features2{}; +#else + VkPhysicalDeviceFeatures2KHR features2{}; +#endif bool defer_surface_initialization = false; + enum class Suitable { yes, partial, no }; + Suitable suitable = Suitable::yes; friend class PhysicalDeviceSelector; friend class DeviceBuilder; }; -enum class PreferredDeviceType { - other = 0, - integrated = 1, - discrete = 2, - virtual_gpu = 3, - cpu = 4 +enum class PreferredDeviceType { other = 0, integrated = 1, discrete = 2, virtual_gpu = 3, cpu = 4 }; + +enum class DeviceSelectionMode { + // return all suitable and partially suitable devices + partially_and_fully_suitable, + // return only physical devices which are fully suitable + only_fully_suitable }; +// Enumerates the physical devices on the system, and based on the added criteria, returns a physical device or list of physical devies +// A device is considered suitable if it meets all the 'required' and 'desired' criteria. +// A device is considered partially suitable if it meets only the 'required' criteria. class PhysicalDeviceSelector { public: // Requires a vkb::Instance to construct, needed to pass instance creation info. explicit PhysicalDeviceSelector(Instance const& instance); + // Requires a vkb::Instance to construct, needed to pass instance creation info, optionally specify the surface here + explicit PhysicalDeviceSelector(Instance const& instance, VkSurfaceKHR surface); - detail::Result select() const; + // Return the first device which is suitable + // use the `selection` parameter to configure if partially + detail::Result select(DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const; + + // Return all devices which are considered suitable - intended for applications which want to let the user pick the physical device + detail::Result> select_devices( + DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const; + + // Return the names of all devices which are considered suitable - intended for applications which want to let the user pick the physical device + detail::Result> select_device_names( + DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const; // Set the surface in which the physical device should render to. + // Be sure to set it if swapchain functionality is to be used. PhysicalDeviceSelector& set_surface(VkSurfaceKHR surface); + + // Set the name of the device to select. + PhysicalDeviceSelector& set_name(std::string const& name); // Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete. PhysicalDeviceSelector& prefer_gpu_device_type(PreferredDeviceType type = PreferredDeviceType::discrete); // Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true. @@ -555,8 +582,7 @@ class PhysicalDeviceSelector { // Require a physical device which supports a specific set of general/extension features. #if defined(VKB_VK_API_VERSION_1_1) - template - PhysicalDeviceSelector& add_required_extension_features(T const& features) { + template PhysicalDeviceSelector& add_required_extension_features(T const& features) { criteria.extended_features_chain.push_back(features); return *this; } @@ -614,10 +640,8 @@ class PhysicalDeviceSelector { // We copy the extension features stored in the selector criteria under the prose of a // "template" to ensure that after fetching everything is compared 1:1 during a match. - PhysicalDeviceDesc populate_device_details(VkPhysicalDevice phys_device, - std::vector const& src_extended_features_chain) const; - struct SelectionCriteria { + std::string name; PreferredDeviceType preferred_type = PreferredDeviceType::discrete; bool allow_any_type = true; bool require_present = true; @@ -643,9 +667,12 @@ class PhysicalDeviceSelector { bool use_first_gpu_unconditionally = false; } criteria; - enum class Suitable { yes, partial, no }; + PhysicalDevice populate_device_details(VkPhysicalDevice phys_device, + std::vector const& src_extended_features_chain) const; - Suitable is_device_suitable(PhysicalDeviceDesc phys_device) const; + PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const; + + detail::Result> select_impl(DeviceSelectionMode selection) const; }; // ---- Queue ---- // diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index 03a0ba6..b964e61 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -204,6 +204,31 @@ TEST_CASE("Device Configuration", "[VkBootstrap.bootstrap]") { vkb::destroy_instance(instance); } + +TEST_CASE("Select all Physical Devices", "[VkBootstrap.bootstrap]") { + + auto window = create_window_glfw("Select all Physical Devices"); + auto instance = get_instance(1); + auto surface = create_surface_glfw(instance.instance, window); + + vkb::PhysicalDeviceSelector phys_device_selector(instance, surface); + + auto phys_device_ret = phys_device_selector.select_devices(); + REQUIRE(phys_device_ret.has_value()); + auto phys_devices = phys_device_ret.value(); + + REQUIRE(phys_devices.at(0).name.size() > 0); + + auto phys_device_names_ret = phys_device_selector.select_device_names(); + REQUIRE(phys_device_names_ret.has_value()); + REQUIRE(phys_device_names_ret.value().at(0).size() > 0); + + vkb::DeviceBuilder device_builder(phys_devices.at(0)); + auto device_ret = device_builder.build(); + REQUIRE(device_ret.has_value()); + auto dispatch_table = device_ret.value().make_table(); +} + TEST_CASE("Loading Dispatch Table", "[VkBootstrap.bootstrap]") { auto instance = get_headless_instance(0); {