From 0b3b15291825c1dd5fe965365e215782f1ac868e Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Sep 2023 21:20:50 -0600 Subject: [PATCH] Add new Queue query API The original queue query API has shortcomings which cause confusion and make it harder to do the things people want with vk-bootstrap. Design features of the new API: * Choose a queue given a combination of queue types - Instead of only choosing based on a single queue type * Allow picking the first or 'best' queue for any given combination of queue types * Allow picking a present queue with a specific surface handle - Instead of only using the surface the PhysicalDevice was selected with * Dont need to update the library to support new queue types over time --- src/VkBootstrap.cpp | 110 +++++++++++++----- src/VkBootstrap.h | 47 ++++++-- tests/bootstrap_tests.cpp | 3 +- tests/unit_tests.cpp | 227 +++++++++++++++++++++++++++++++++++--- 4 files changed, 338 insertions(+), 49 deletions(-) diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 2566c59..d4a8751 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -410,6 +410,7 @@ const char* to_string(QueueError err) { CASE_TO_STRING(QueueError, graphics_unavailable) CASE_TO_STRING(QueueError, compute_unavailable) CASE_TO_STRING(QueueError, transfer_unavailable) + CASE_TO_STRING(QueueError, queue_type_unavailable) CASE_TO_STRING(QueueError, queue_index_out_of_range) CASE_TO_STRING(QueueError, invalid_queue_family_index) default: @@ -932,6 +933,7 @@ bool supports_features(VkPhysicalDeviceFeatures supported, return true; } // clang-format on + // Finds the first queue which supports the desired operations. Returns QUEUE_INDEX_MAX_VALUE if none is found uint32_t get_first_queue_index(std::vector const& families, VkQueueFlags desired_flags) { for (uint32_t i = 0; i < static_cast(families.size()); i++) { @@ -939,21 +941,33 @@ uint32_t get_first_queue_index(std::vector const& famil } return QUEUE_INDEX_MAX_VALUE; } -// 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. + +// Finds the that has the desired flag and the least number of undesired flags. +// Any queue family with invalid_flags will be ignored. +// 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) { + std::vector const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags, VkQueueFlags invalid_flags) { uint32_t index = QUEUE_INDEX_MAX_VALUE; + uint32_t least_undesired_flags_count = QUEUE_INDEX_MAX_VALUE; for (uint32_t i = 0; i < static_cast(families.size()); i++) { - if ((families[i].queueFlags & desired_flags) == desired_flags && ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { - if ((families[i].queueFlags & undesired_flags) == 0) { - return i; - } else { + if ((invalid_flags != 0) && (families[i].queueFlags & invalid_flags) == invalid_flags) { + continue; + } + if ((families[i].queueFlags & desired_flags) == desired_flags) { + uint32_t undesired_flag_count = 0; + // Count the number of flags which are supported by the queue family but are undesired + for (uint32_t j = VK_QUEUE_FLAG_BITS_MAX_ENUM; j != 0; j >>= 1) { + if (families[i].queueFlags & undesired_flags & j) { + undesired_flag_count++; + } + } + if (undesired_flag_count < least_undesired_flags_count) { + least_undesired_flags_count = undesired_flag_count; index = i; } } } + return index; } @@ -961,8 +975,7 @@ uint32_t get_separate_queue_index( 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) == desired_flags && - (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && (families[i].queueFlags & undesired_flags) == 0) + if ((families[i].queueFlags & desired_flags) == desired_flags && ((families[i].queueFlags & undesired_flags) == 0)) return i; } return QUEUE_INDEX_MAX_VALUE; @@ -1047,13 +1060,17 @@ PhysicalDevice::Suitable PhysicalDeviceSelector::is_device_suitable(PhysicalDevi if (criteria.required_version > pd.properties.apiVersion) return PhysicalDevice::Suitable::no; if (criteria.desired_version > pd.properties.apiVersion) suitable = PhysicalDevice::Suitable::partial; - 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) != + bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families, + VK_QUEUE_COMPUTE_BIT, + VK_QUEUE_GRAPHICS_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_GRAPHICS_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, VK_QUEUE_GRAPHICS_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) != + bool separate_transfer = detail::get_separate_queue_index( + pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT) != detail::QUEUE_INDEX_MAX_VALUE; bool present_queue = detail::get_present_queue_index(pd.physical_device, instance_info.surface, pd.queue_families) != @@ -1356,16 +1373,20 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditiona // 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_GRAPHICS_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, VK_QUEUE_GRAPHICS_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_GRAPHICS_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; + return detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT) != + detail::QUEUE_INDEX_MAX_VALUE; } std::vector PhysicalDevice::get_queue_families() const { return queue_families; } std::vector PhysicalDevice::get_extensions() const { return extensions; } @@ -1373,6 +1394,7 @@ PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device // ---- Queues ---- // +// Legacy queue function Result Device::get_queue_index(QueueType type) const { uint32_t index = detail::QUEUE_INDEX_MAX_VALUE; switch (type) { @@ -1385,11 +1407,11 @@ Result Device::get_queue_index(QueueType type) const { if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::graphics_unavailable }; break; case QueueType::compute: - index = detail::get_separate_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT); + index = detail::get_separate_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::compute_unavailable }; break; case QueueType::transfer: - index = detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT); + index = detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT); if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::transfer_unavailable }; break; default: @@ -1397,15 +1419,17 @@ Result Device::get_queue_index(QueueType type) const { } return index; } + +// Legacy queue function Result Device::get_dedicated_queue_index(QueueType type) const { uint32_t index = detail::QUEUE_INDEX_MAX_VALUE; switch (type) { case QueueType::compute: - index = detail::get_dedicated_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT); + index = detail::get_dedicated_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT); if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::compute_unavailable }; break; case QueueType::transfer: - index = detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT); + index = detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::transfer_unavailable }; break; default: @@ -1414,6 +1438,7 @@ Result Device::get_dedicated_queue_index(QueueType type) const { return index; } +// Legacy queue function Result Device::get_queue(QueueType type) const { auto index = get_queue_index(type); if (!index.has_value()) return { index.error() }; @@ -1421,14 +1446,49 @@ Result Device::get_queue(QueueType type) const { internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue); return out_queue; } + +// Legacy queue function Result Device::get_dedicated_queue(QueueType type) const { auto index = get_dedicated_queue_index(type); if (!index.has_value()) return { index.error() }; - VkQueue out_queue; + VkQueue out_queue{}; internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue); return out_queue; } + +Result Device::get_first_queue_and_index(VkQueueFlags queue_flags) const { + uint32_t index = detail::get_first_queue_index(queue_families, queue_flags); + if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::queue_type_unavailable }; + + VkQueue out_queue{}; + internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue); + return QueueAndIndex{ out_queue, index }; +} + +Result Device::get_preferred_queue_and_index(VkQueueFlags queue_flags) const { + uint32_t index = detail::get_separate_queue_index(queue_families, queue_flags, ~queue_flags, 0); + if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::queue_type_unavailable }; + + VkQueue out_queue{}; + internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue); + return QueueAndIndex{ out_queue, index }; +} + +Result Device::get_first_presentation_queue_and_index(VkSurfaceKHR surface_to_check) const { + VkSurfaceKHR surface_to_use = surface; + if (surface_to_check != VK_NULL_HANDLE) { + surface_to_use = surface_to_check; + } + uint32_t index = detail::get_present_queue_index(physical_device.physical_device, surface_to_use, queue_families); + if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result{ QueueError::present_unavailable }; + + VkQueue out_queue{}; + internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue); + return QueueAndIndex{ out_queue, index }; +} + + // ---- Dispatch ---- // DispatchTable Device::make_table() const { return { device, fp_vkGetDeviceProcAddr }; } diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index f655d91..d39144c 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -200,6 +200,7 @@ enum class QueueError { graphics_unavailable, compute_unavailable, transfer_unavailable, + queue_type_unavailable, queue_index_out_of_range, invalid_queue_family_index }; @@ -675,13 +676,25 @@ class PhysicalDeviceSelector { }; // ---- Queue ---- // -enum class QueueType { present, graphics, compute, transfer }; + +enum class QueueType { + present, + graphics, + compute, + transfer, +}; namespace detail { // Sentinel value, used in implementation only const uint32_t QUEUE_INDEX_MAX_VALUE = 65536; } // namespace detail + +struct QueueAndIndex { + VkQueue queue = VK_NULL_HANDLE; + uint32_t index = detail::QUEUE_INDEX_MAX_VALUE; +}; + // ---- Device ---- // struct Device { @@ -693,13 +706,31 @@ struct Device { PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr; uint32_t instance_version = VKB_VK_API_VERSION_1_0; - Result get_queue_index(QueueType type) const; - // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue index - Result get_dedicated_queue_index(QueueType type) const; - - Result get_queue(QueueType type) const; - // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue - Result get_dedicated_queue(QueueType type) const; + // Legacy function - gets the queue which supports queue_type operations using the following rules: + // If queue_type is graphics, return the first queue that supports graphics operations + // If queue_type is compute, return a queue that supports compute operations but not graphics, if one exists + // If queue_type is transfer, return a queue that supports transfer operations but not graphics, if one exists + // If queue_type is present, return the first queue that supports presentation with the VkSurfaceKHR given during physical device selection + Result get_queue_index(QueueType queue_type) const; + Result get_queue(QueueType queue_type) const; + + // Legacy function - gets the queue which supports only the queue_type operations using the following rules: + // If queue_type is compute, return a queue that supports compute operations but not graphics or transfer, if one + // exists If queue_type is transfer, return a queue that supports transfer operations but not graphics or compute, + // if one exists If queue_type is any other type, return an error code + Result get_dedicated_queue(QueueType queue_type) const; + Result get_dedicated_queue_index(QueueType queue_type) const; + + // Returns the first queue which supports the operations specified by queue_flags + Result get_first_queue_and_index(VkQueueFlags queue_flags) const; + + // Returns the queue which supports the operations specified by queue_flags and the least number of operations not specified by queue_flags + Result get_preferred_queue_and_index(VkQueueFlags queue_flags) const; + + // Get the first queue which supports presentation + // If surface_to_check is not VK_NULL_HANDLE, it will make sure the surface is compatible with the queue + // Otherwise if surface_to_check is VK_NULL_HANDLE, it will use the VkSurfaceKHR used duing physical device selection + Result get_first_presentation_queue_and_index(VkSurfaceKHR surface_to_check = VK_NULL_HANDLE) const; // Return a loaded dispatch table DispatchTable make_table() const; diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index 7531540..b2184e7 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -727,7 +727,7 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]" } TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") { - [[maybe_unused]] VulkanMock& mock = get_and_setup_default(); + VulkanMock& mock = get_and_setup_default(); mock.api_version = VK_API_VERSION_1_2; mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2; @@ -740,7 +740,6 @@ TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") { mock.physical_devices_details[0].add_features_pNext_struct(mock_vulkan_12_features); GIVEN("A working instance") { - vkb::InstanceBuilder builder; auto instance = get_headless_instance(2); // make sure we use 1.2 SECTION("Requires a device that supports multiview and bufferDeviceAddress") { VkPhysicalDeviceVulkan11Features features_11{}; diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp index c067328..075af1b 100644 --- a/tests/unit_tests.cpp +++ b/tests/unit_tests.cpp @@ -2,6 +2,7 @@ #include "VkBootstrap.cpp" +#include "vulkan_mock.hpp" TEST_CASE("Single Queue Device", "[UnitTests.queue_selection_logic]") { std::vector families = { VkQueueFamilyProperties{ @@ -9,9 +10,9 @@ TEST_CASE("Single Queue Device", "[UnitTests.queue_selection_logic]") { REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == - vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); + vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == - vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); + vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == @@ -22,14 +23,16 @@ TEST_CASE("Dedicated Compute Queue, Separate Transfer", "[UnitTests.queue_select SECTION("Dedicated Queue First") { std::vector families = { VkQueueFamilyProperties{ - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } } }; REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); - REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); - REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); + REQUIRE(1 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT, 0)); + REQUIRE(2 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0)); REQUIRE(1 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); @@ -37,14 +40,16 @@ TEST_CASE("Dedicated Compute Queue, Separate Transfer", "[UnitTests.queue_select SECTION("Dedicated Queue Last") { std::vector families = { VkQueueFamilyProperties{ - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } } }; REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); - REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); - REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); + REQUIRE(2 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT, 0)); + REQUIRE(1 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0)); REQUIRE(2 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); @@ -55,14 +60,16 @@ TEST_CASE("Dedicated Transfer Queue, Separate Compute", "[UnitTests.queue_select SECTION("Dedicated Queue First") { std::vector families = { VkQueueFamilyProperties{ - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } } }; REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); - REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); - REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); + REQUIRE(2 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT, 0)); + REQUIRE(1 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); REQUIRE(1 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); @@ -70,16 +77,208 @@ TEST_CASE("Dedicated Transfer Queue, Separate Compute", "[UnitTests.queue_select SECTION("Dedicated Queue Last") { std::vector families = { VkQueueFamilyProperties{ - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }, VkQueueFamilyProperties{ VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } } }; REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); - REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); - REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); + REQUIRE(1 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT, 0)); + REQUIRE(2 == vkb::detail::get_separate_queue_index( + families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0)); REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); REQUIRE(2 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); } } + +vkb::Device setup_mock_for_queue_selection(std::vector const& queue_families) { + VulkanMock& mock = get_vulkan_mock(); + VulkanMock::PhysicalDeviceDetails physical_device_details{}; + physical_device_details.properties.apiVersion = VK_API_VERSION_1_0; + physical_device_details.queue_family_properties = queue_families; + mock.add_physical_device(std::move(physical_device_details)); + + return vkb::DeviceBuilder{ + vkb::PhysicalDeviceSelector{ vkb::InstanceBuilder{}.set_headless().build().value() }.select().value() + } + .build() + .value(); +} + +TEST_CASE("Single Uber Queue", "[UnitTests.get_first_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_first_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_first_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 0 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_first_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 0 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_first_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_first_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_first_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Compute + Transfer", "[UnitTests.get_first_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_first_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_first_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 0 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_first_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 0 && q2.queue != nullptr)); +} + +TEST_CASE("Single Uber Queue", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 0 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 0 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Compute + Transfer", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 1 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 1 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Compute + Transfer, Compute", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 2 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 1 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Compute, Compute + Transfer", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 1 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 2 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Transfer, Compute + Transfer", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 2 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 1 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +} + +TEST_CASE("Uber, Compute + Transfer, Transfer", "[UnitTests.get_preferred_queue_and_index]") { + vkb::Device d = setup_mock_for_queue_selection( + { VkQueueFamilyProperties{ VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, 1 }, + VkQueueFamilyProperties{ VK_QUEUE_TRANSFER_BIT, 1 } }); + + vkb::QueueAndIndex q0 = d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT).value(); + CHECK((q0.index == 0 && q0.queue != nullptr)); + + vkb::QueueAndIndex q1 = d.get_preferred_queue_and_index(VK_QUEUE_COMPUTE_BIT).value(); + CHECK((q1.index == 1 && q1.queue != nullptr)); + + vkb::QueueAndIndex q2 = d.get_preferred_queue_and_index(VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q2.index == 2 && q2.queue != nullptr)); + + vkb::QueueAndIndex q3 = + d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT).value(); + CHECK((q3.index == 0 && q3.queue != nullptr)); + + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_PROTECTED_BIT).matches_error(vkb::QueueError::queue_type_unavailable)); + CHECK(d.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_PROTECTED_BIT) + .matches_error(vkb::QueueError::queue_type_unavailable)); +}