From 2b711f30d50f140a6582d8abdb2f81f9fe934d6e Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 10 Feb 2020 18:01:58 -0700 Subject: [PATCH] Finished basic rewrite of queue handling logic Premise is to let the easy creation of a compute or transfer queue. If the user supplies a custom list they want to use, defer to that. Else, enable the basic requirements and be on your merry way. --- example/triangle.cpp | 14 ++- src/VkBootstrap.cpp | 204 +++++++++++++++++++++++++++++-------------- src/VkBootstrap.h | 47 +++++++--- 3 files changed, 189 insertions(+), 76 deletions(-) diff --git a/example/triangle.cpp b/example/triangle.cpp index 34e4c02..f9758bd 100644 --- a/example/triangle.cpp +++ b/example/triangle.cpp @@ -75,8 +75,18 @@ int device_initialization (Init& init) { } int get_queues (Init& init, RenderData& data) { - data.graphics_queue = vkb::get_graphics_queue (init.device).value (); - data.present_queue = vkb::get_present_queue (init.device).value (); + auto gq = vkb::get_graphics_queue (init.device); + if (!gq.has_value ()) { + std::cout << "failed to get graphics queue\n"; + return -1; + } + data.graphics_queue = gq.value (); + auto pq = vkb::get_present_queue (init.device); + if (!pq.has_value ()) { + std::cout << "failed to get present queue\n"; + return -1; + } + data.present_queue = pq.value (); return 0; } diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 4d0ba74..9bfeb58 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -126,6 +126,27 @@ bool check_layers_supported (std::vector layer_names) { return all_found; } +bool check_extensions_supported (std::vector extension_names) { + auto available_extensions = + detail::get_vector (vkEnumerateInstanceExtensionProperties, nullptr); + if (!available_extensions.has_value ()) { + return false; // maybe report error? + } + bool all_found = true; + for (const auto& extension_name : extension_names) { + bool found = false; + for (const auto& layer_properties : available_extensions.value ()) { + if (strcmp (extension_name, layer_properties.extensionName) == 0) { + found = true; + break; + } + } + if (!found) all_found = false; + } + + return all_found; +} + template void setup_pNext_chain (T& structure, std::vector& structs) { structure.pNext = nullptr; @@ -180,6 +201,11 @@ detail::Expected> InstanceBuilder::build extensions.push_back ("VK_KHR_metal_surface"); #endif } + bool all_extensions_supported = detail::check_extensions_supported (extensions); + if (!all_extensions_supported) { + return detail::Error{ InstanceError::requested_extensions_not_present }; + } + std::vector layers; for (auto& layer : info.layers) layers.push_back (layer.c_str ()); @@ -499,33 +525,46 @@ int get_present_queue_index (VkPhysicalDevice const phys_device, return -1; } -PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkPhysicalDevice phys_device) { - Suitable suitable = Suitable::yes; - +PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details ( + VkPhysicalDevice phys_device) { + PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; + desc.phys_device = phys_device; auto queue_families = detail::get_vector_noerror ( vkGetPhysicalDeviceQueueFamilyProperties, phys_device); - bool dedicated_compute = get_distinct_compute_queue_index (queue_families); - bool dedicated_transfer = get_distinct_transfer_queue_index (queue_families); - bool present_queue = get_present_queue_index (phys_device, info.surface, queue_families); + desc.queue_families = queue_families; + + vkGetPhysicalDeviceProperties (phys_device, &desc.device_properties); + vkGetPhysicalDeviceFeatures (phys_device, &desc.device_features); + vkGetPhysicalDeviceMemoryProperties (phys_device, &desc.mem_properties); + return desc; +} + +PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (PhysicalDeviceDesc pd) { + Suitable suitable = Suitable::yes; + + bool dedicated_compute = get_distinct_compute_queue_index (pd.queue_families); + bool dedicated_transfer = get_distinct_transfer_queue_index (pd.queue_families); + bool present_queue = get_present_queue_index (pd.phys_device, system_info.surface, pd.queue_families); if (criteria.require_dedicated_compute_queue && !dedicated_compute) suitable = Suitable::no; if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) suitable = Suitable::no; if (criteria.require_present && !present_queue) suitable = Suitable::no; auto required_extensions_supported = - detail::check_device_extension_support (phys_device, criteria.required_extensions); + detail::check_device_extension_support (pd.phys_device, criteria.required_extensions); if (required_extensions_supported.size () != criteria.required_extensions.size ()) suitable = Suitable::no; auto desired_extensions_supported = - detail::check_device_extension_support (phys_device, criteria.desired_extensions); + detail::check_device_extension_support (pd.phys_device, criteria.desired_extensions); if (desired_extensions_supported.size () != criteria.desired_extensions.size ()) suitable = Suitable::partial; bool swapChainAdequate = false; - if (!info.headless) { - auto swapChainSupport_ret = detail::query_surface_support_details (phys_device, info.surface); + if (!system_info.headless) { + auto swapChainSupport_ret = + detail::query_surface_support_details (pd.phys_device, system_info.surface); if (swapChainSupport_ret.has_value ()) { auto swapchain_support = swapChainSupport_ret.value (); swapChainAdequate = @@ -534,17 +573,33 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP } if (criteria.require_present && !swapChainAdequate) suitable = Suitable::no; - VkPhysicalDeviceMemoryProperties mem_properties; - vkGetPhysicalDeviceMemoryProperties (phys_device, &mem_properties); + if ((criteria.preferred_type == PreferredDeviceType::discrete && + pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) || + (criteria.preferred_type == PreferredDeviceType::integrated && + pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) || + (criteria.preferred_type == PreferredDeviceType::virtual_gpu && + pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)) { + if (criteria.allow_fallback) + suitable = Suitable::partial; + else + suitable = Suitable::no; + } + + if (criteria.required_version < pd.device_properties.apiVersion) suitable = Suitable::no; + if (criteria.desired_version < pd.device_properties.apiVersion) suitable = Suitable::partial; + + bool required_features_supported = + detail::supports_features (pd.device_features, criteria.required_features); + if (!required_features_supported) suitable = Suitable::no; bool has_required_memory = false; bool has_preferred_memory = false; - for (int i = 0; i < mem_properties.memoryHeapCount; i++) { - if (mem_properties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { - if (mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { + for (int i = 0; i < pd.mem_properties.memoryHeapCount; i++) { + if (pd.mem_properties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + if (pd.mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { has_required_memory = true; } - if (mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) { + if (pd.mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) { has_preferred_memory = true; } } @@ -552,40 +607,18 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP if (!has_required_memory) suitable = Suitable::no; if (!has_preferred_memory) suitable = Suitable::partial; - VkPhysicalDeviceProperties device_properties; - vkGetPhysicalDeviceProperties (phys_device, &device_properties); - if ((criteria.preferred_type == PreferredDeviceType::discrete && - device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) || - (criteria.preferred_type == PreferredDeviceType::integrated && - device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) || - (criteria.preferred_type == PreferredDeviceType::virtual_gpu && - device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)) { - if (criteria.allow_fallback) - suitable = Suitable::partial; - else - suitable = Suitable::no; - } - - if (criteria.required_version < device_properties.apiVersion) suitable = Suitable::no; - if (criteria.desired_version < device_properties.apiVersion) suitable = Suitable::partial; - - VkPhysicalDeviceFeatures supported_features{}; - vkGetPhysicalDeviceFeatures (phys_device, &supported_features); - bool required_features_supported = - detail::supports_features (supported_features, criteria.required_features); - if (!required_features_supported) suitable = Suitable::no; - return suitable; } PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) { - info.instance = instance.instance; - info.headless = instance.headless; + system_info.instance = instance.instance; + system_info.headless = instance.headless; criteria.require_present = !instance.headless; } detail::Expected> PhysicalDeviceSelector::select () { - auto physical_devices = detail::get_vector (vkEnumeratePhysicalDevices, info.instance); + auto physical_devices = + detail::get_vector (vkEnumeratePhysicalDevices, system_info.instance); if (!physical_devices.has_value ()) { return detail::Error{ PhysicalDeviceError::failed_enumerate_physical_devices, physical_devices.error () }; @@ -594,42 +627,50 @@ detail::Expected> PhysicalDev return detail::Error{ PhysicalDeviceError::no_physical_devices_found }; } - PhysicalDevice physical_device; + std::vector phys_device_descriptions; + for (auto& phys_device : physical_devices.value ()) { + phys_device_descriptions.push_back (populate_device_details (phys_device)); + } + + PhysicalDeviceDesc selected_device{}; + if (criteria.use_first_gpu_unconditionally) { - physical_device.phys_device = physical_devices.value ().at (0); + selected_device = phys_device_descriptions.at (0); } else { - for (const auto& device : physical_devices.value ()) { + for (const auto& device : phys_device_descriptions) { auto suitable = is_device_suitable (device); if (suitable == Suitable::yes) { - physical_device.phys_device = device; + selected_device = device; break; } else if (suitable == Suitable::partial) { - physical_device.phys_device = device; + selected_device = device; } } } - if (physical_device.phys_device == VK_NULL_HANDLE) { + if (selected_device.phys_device == VK_NULL_HANDLE) { return detail::Error{ PhysicalDeviceError::no_suitable_device }; } + PhysicalDevice out_device{}; + out_device.phys_device = selected_device.phys_device; + out_device.surface = system_info.surface; + out_device.features = criteria.required_features; + out_device.queue_families = selected_device.queue_families; - physical_device.surface = info.surface; - physical_device.features = criteria.required_features; - - physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (), + 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 (physical_device.phys_device, criteria.desired_extensions); - physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (), + detail::check_device_extension_support (out_device.phys_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 physical_device; + return out_device; } PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface (VkSurfaceKHR surface) { - info.surface = surface; - info.headless = false; + system_info.surface = surface; + system_info.headless = false; return *this; } PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type (PreferredDeviceType type) { @@ -706,21 +747,41 @@ void destroy_device (Device device) { vkDestroyDevice (device.device, nullptr); DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) { info.physical_device = phys_device; info.extensions = phys_device.extensions_to_enable; + info.queue_families = phys_device.queue_families; } detail::Expected> DeviceBuilder::build () { - auto queue_families = detail::get_vector_noerror ( - vkGetPhysicalDeviceQueueFamilyProperties, info.physical_device.phys_device); + std::vector queue_descriptions; + queue_descriptions.insert ( + queue_descriptions.end (), info.queue_descriptions.begin (), info.queue_descriptions.end ()); + + if (queue_descriptions.size () == 0) { + int graphics = get_graphics_queue_index (info.queue_families); + if (graphics >= 0) { + queue_descriptions.push_back ({ static_cast (graphics), 1, { 1.0f } }); + } + if (info.request_compute_queue) { + int compute = get_distinct_compute_queue_index (info.queue_families); + if (compute >= 0) { + queue_descriptions.push_back ({ static_cast (compute), 1, { 1.0f } }); + } + } + if (info.request_transfer_queue) { + int transfer = get_distinct_transfer_queue_index (info.queue_families); + if (transfer >= 0) { + queue_descriptions.push_back ({ static_cast (transfer), 1, { 1.0f } }); + } + } + } std::vector queueCreateInfos; - float priority = 1.0f; - for (int i = 0; i < queue_families.size (); i++) { + for (auto& desc : queue_descriptions) { VkDeviceQueueCreateInfo queue_create_info = {}; queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_info.queueFamilyIndex = static_cast (i); - queue_create_info.queueCount = 1; - queue_create_info.pQueuePriorities = &priority; + queue_create_info.queueFamilyIndex = desc.index; + queue_create_info.queueCount = desc.count; + queue_create_info.pQueuePriorities = desc.priorities.data (); queueCreateInfos.push_back (queue_create_info); } @@ -748,6 +809,7 @@ detail::Expected> DeviceBuilder::build () { } device.physical_device = info.physical_device; device.surface = info.physical_device.surface; + device.queue_families = info.queue_families; return device; } @@ -756,6 +818,20 @@ template DeviceBuilder& DeviceBuilder::add_pNext (T* structure) { return *this; } +DeviceBuilder& DeviceBuilder::request_dedicated_compute_queue (bool compute) { + info.request_compute_queue = compute; + return *this; +} +DeviceBuilder& DeviceBuilder::request_dedicated_transfer_queue (bool transfer) { + info.request_transfer_queue = transfer; + return *this; +} +DeviceBuilder& DeviceBuilder::custom_queue_setup (std::vector queue_descriptions) { + info.queue_descriptions = queue_descriptions; + return *this; +} + + // ---- Getting Queues ---- // detail::Expected> get_present_queue_index (Device const& device) { diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 9cac10b..603ae12 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -124,6 +124,8 @@ struct Instance { void destroy_instance (Instance instance); // release instance resources +// TODO utility function for users to check if layer/extension is supported + class InstanceBuilder { public: detail::Expected> build (); // use builder pattern @@ -138,9 +140,14 @@ class InstanceBuilder { InstanceBuilder& add_layer (std::string app_name); InstanceBuilder& add_extension (std::string app_name); + bool check_and_add_layer (std::string app_name); + bool check_and_add_extension (std::string app_name); + InstanceBuilder& setup_validation_layers (bool enable_validation = true); InstanceBuilder& set_headless (bool headless = false); + bool check_and_setup_validation_layers (bool enable_validation = true); + InstanceBuilder& set_default_debug_messenger (); InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback); InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); @@ -222,6 +229,7 @@ struct PhysicalDevice { private: VkPhysicalDeviceFeatures features{}; std::vector extensions_to_enable; + std::vector queue_families; friend class PhysicalDeviceSelector; friend class DeviceBuilder; }; @@ -259,11 +267,21 @@ struct PhysicalDeviceSelector { PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); private: - struct PhysicalDeviceInfo { + struct SystemInfo { VkInstance instance = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE; bool headless = false; - } info; + } system_info; + + struct PhysicalDeviceDesc { + VkPhysicalDevice phys_device = VK_NULL_HANDLE; + std::vector queue_families; + + VkPhysicalDeviceFeatures device_features; + VkPhysicalDeviceProperties device_properties; + VkPhysicalDeviceMemoryProperties mem_properties; + }; + PhysicalDeviceDesc populate_device_details (VkPhysicalDevice phys_device); struct SelectionCriteria { PreferredDeviceType preferred_type = PreferredDeviceType::discrete; @@ -287,13 +305,9 @@ struct PhysicalDeviceSelector { enum class Suitable { yes, partial, no }; - Suitable is_device_suitable (VkPhysicalDevice phys_device); + Suitable is_device_suitable (PhysicalDeviceDesc phys_device); }; -// ---- Queue Selection ---- // - -enum class QueueType : uint8_t { primary, compute, transfer }; - // ---- Device ---- // enum class DeviceError { failed_create_device, @@ -308,6 +322,12 @@ struct Device { void destroy_device (Device device); +struct CustomQueueDescription { + uint32_t index; + uint32_t count; + std::vector priorities; +}; + class DeviceBuilder { public: DeviceBuilder (PhysicalDevice device); @@ -315,12 +335,22 @@ class DeviceBuilder { template DeviceBuilder& add_pNext (T* structure); + DeviceBuilder& request_dedicated_compute_queue (bool compute = true); + DeviceBuilder& request_dedicated_transfer_queue (bool transfer = true); + + /* For advanced users */ + DeviceBuilder& custom_queue_setup (std::vector queue_descriptions); + private: struct DeviceInfo { VkDeviceCreateFlags flags = 0; std::vector pNext_chain; PhysicalDevice physical_device; std::vector extensions; + std::vector queue_families; + std::vector queue_descriptions; + bool request_compute_queue = true; + bool request_transfer_queue = true; } info; }; @@ -381,9 +411,6 @@ class SwapchainBuilder { detail::Expected> build (); detail::Expected> recreate (Swapchain const& swapchain); - // SwapchainBuilder& set_desired_image_count (uint32_t count); - // SwapchainBuilder& set_maximum_image_count (uint32_t count); - SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format); SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format); SwapchainBuilder& use_default_format_selection ();