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 'preferred' 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
This commit is contained in:
Charles Giessen 2023-09-24 21:20:50 -06:00
parent be0df09b3a
commit f290028cb5
6 changed files with 409 additions and 87 deletions

View File

@ -1,6 +1,6 @@
# Getting Started # Getting Started
`vk-bootstrap` reduces the complexity of setting up a vulkan application by simplifying the three initial steps; instance creation, Physical device selection, and device creation. `vk-bootstrap` reduces the complexity of setting up a vulkan application by simplifying the three initial steps; instance creation, Physical device selection, and device creation.
## Instance Creation ## Instance Creation
@ -16,17 +16,17 @@ Because creating an instance may fail, the builder returns an 'Result' type. Thi
if (!instance_builder_return) { if (!instance_builder_return) {
std::cerr << "Failed to create Vulkan instance. Error: " << instance_builder_return.error().message() << "\n"; std::cerr << "Failed to create Vulkan instance. Error: " << instance_builder_return.error().message() << "\n";
return -1; return -1;
} }
``` ```
Once any possible errors have been dealt with, we can pull the `vkb::Instance` struct out of the `Result`. Once any possible errors have been dealt with, we can pull the `vkb::Instance` struct out of the `Result`.
```cpp ```cpp
vkb::Instance vkb_instance = instance_builder_return.value(); vkb::Instance vkb_instance = instance_builder_return.value();
``` ```
This is enough to create a usable `VkInstance` handle but many will want to customize it a bit. To configure instance creation, simply call the member functions on the `vkb::InstanceBuilder` object before `build()` is called. This is enough to create a usable `VkInstance` handle but many will want to customize it a bit. To configure instance creation, simply call the member functions on the `vkb::InstanceBuilder` object before `build()` is called.
The most common customization to instance creation is enabling the "Validation Layers", an invaluable tool for any vulkan application developer. The most common customization to instance creation is enabling the "Validation Layers", an invaluable tool for any vulkan application developer.
```cpp ```cpp
instance_builder.request_validation_layers (); instance_builder.request_validation_layers();
``` ```
The other common customization point is setting up the `Debug Messenger Callback`, the mechanism in which an application can control what and where the "Validation Layers" log its output. The other common customization point is setting up the `Debug Messenger Callback`, the mechanism in which an application can control what and where the "Validation Layers" log its output.
```cpp ```cpp
@ -34,7 +34,7 @@ instance_builder.set_debug_callback (
[] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, [] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void *pUserData) void *pUserData)
-> VkBool32 { -> VkBool32 {
auto severity = vkb::to_string_message_severity (messageSeverity); auto severity = vkb::to_string_message_severity (messageSeverity);
auto type = vkb::to_string_message_type (messageType); auto type = vkb::to_string_message_type (messageType);
@ -49,7 +49,7 @@ instance_builder.use_default_debug_messenger();
``` ```
Configuration can be chained together and done inline with building, like so. Configuration can be chained together and done inline with building, like so.
```cpp ```cpp
auto inst_builder_ret = instance_builder auto inst_builder_ret = instance_builder
.set_app_name ("Awesome Vulkan Application") .set_app_name ("Awesome Vulkan Application")
.set_engine_name("Excellent Game Engine") .set_engine_name("Excellent Game Engine")
.require_api_version(1,0,0) .require_api_version(1,0,0)
@ -83,9 +83,9 @@ CustomVulkanWrapper custom_vk_class;
custom_vk_class.instance = vkb_instance.instance; custom_vk_class.instance = vkb_instance.instance;
``` ```
When the application is finished with the vulkan, call `vkb::destroy_instance()` to dispose of the instance and associated data. When the application is finished with the vulkan, call `vkb::destroy_instance()` to dispose of the instance and associated data.
```cpp ```cpp
// cleanup // cleanup
vkb::destroy_instance(vkb_instance); vkb::destroy_instance(vkb_instance);
``` ```
### Instance Creation Summary ### Instance Creation Summary
@ -98,7 +98,7 @@ auto instance_builder_return = instance_builder
.build (); .build ();
if (!instance_builder_return) { if (!instance_builder_return) {
// Handle error // Handle error
} }
vkb::Instance vkb_instance = instance_builder_return.value (); vkb::Instance vkb_instance = instance_builder_return.value ();
// at program end // at program end
@ -106,11 +106,11 @@ vkb::destroy_instance(vkb_instance);
``` ```
## Surface Creation ## Surface Creation
Presenting images to the screen Vulkan requires creating a surface, encapsulated in a `VkSurfaceKHR` handle. Creating a surface is the responsibility of the windowing system, thus is out of scope for `vk-bootstrap`. However, `vk-bootstrap` does try to make the process as painless as possible by automatically enabling the correct windowing extensions in `VkInstance` creation. Presenting images to the screen Vulkan requires creating a surface, encapsulated in a `VkSurfaceKHR` handle. Creating a surface is the responsibility of the windowing system, thus is out of scope for `vk-bootstrap`. However, `vk-bootstrap` does try to make the process as painless as possible by automatically enabling the correct windowing extensions in `VkInstance` creation.
Windowing libraries which support Vulkan usually provide a way of getting the `VkSurfaceKHR` handle for the window. These methods require a valid Vulkan instance, thus must be done after instance creation. Windowing libraries which support Vulkan usually provide a way of getting the `VkSurfaceKHR` handle for the window. These methods require a valid Vulkan instance, thus must be done after instance creation.
Examples for GLFW and SDL2 are listed below. Examples for GLFW and SDL2 are listed below.
```cpp ```cpp
vkb::Instance vkb_instance; //valid vkb::Instance vkb::Instance vkb_instance; //valid vkb::Instance
VkSurfaceKHR surface = nullptr; VkSurfaceKHR surface = nullptr;
@ -134,7 +134,7 @@ Creating a `vkb::PhysicalDeviceSelector` requires a valid `vkb::Instance` to con
It follows the same pattern laid out by `vkb::InstanceBuilder`. It follows the same pattern laid out by `vkb::InstanceBuilder`.
```cpp ```cpp
vkb::PhysicalDeviceSelector phys_device_selector (vkb_instance); vkb::PhysicalDeviceSelector phys_device_selector (vkb_instance);
auto physical_device_selector_return = phys_device_selector auto physical_device_selector_return = phys_device_selector
.set_surface(surface_handle) .set_surface(surface_handle)
.select (); .select ();
@ -149,21 +149,19 @@ By default, this will prefer a discrete GPU.
No cleanup is required for `vkb::PhysicalDevice`. No cleanup is required for `vkb::PhysicalDevice`.
The `vkb::PhysicalDeviceSelector` will look for the first device in the list that satisfied all the specified criteria, and if none is found, will return the first device that partially satisfies the criteria. The `vkb::PhysicalDeviceSelector` will look for the first device in the list that satisfied all the specified criteria, and if none is found, will return the first device that partially satisfies the criteria.
The various "require" and "desire" pairs of functions indicate to `vk-bootstrap` what features and capabilities are necessary for an application and what are simply preferred. A "require" function will fail any `VkPhysicalDevice` that doesn't satisfy the constraint, while any criteria that doesn't satisfy the "desire" functions will make the `VkPhysicalDevice` only 'partially satisfy'. The various "require" functions indicate to `vk-bootstrap` what features and capabilities are necessary for an application. A "require" function will fail any `VkPhysicalDevice` that doesn't satisfy the constraint.
```c ```c
// Application cannot function without this extension // Application cannot function without this extension
phys_device_selector.add_required_extension("VK_KHR_timeline_semaphore"); phys_device_selector.add_required_extension("VK_KHR_timeline_semaphore");
// Application can deal with the lack of this extension
phys_device_selector.add_desired_extension("VK_KHR_imageless_framebuffer");
``` ```
Note: Note:
Because `vk-bootstrap` does not manage creating a `VkSurfaceKHR` handle, it is explicitly passed into the `vkb::PhysicalDeviceSelector` for proper querying of surface support details. Unless the `vkb::InstanceBuilder::set_headless()` function was called, the physical device selector will emit `no_surface_provided` error. If an application does intend to present but cannot create a `VkSurfaceKHR` handle before physical device selection, use `defer_surface_initialization()` to disable the `no_surface_provided` error. Because `vk-bootstrap` does not manage creating a `VkSurfaceKHR` handle, it is explicitly passed into the `vkb::PhysicalDeviceSelector` for proper querying of surface support details. Unless the `vkb::InstanceBuilder::set_headless()` function was called, the physical device selector will emit `no_surface_provided` error. If an application does intend to present but cannot create a `VkSurfaceKHR` handle before physical device selection, use `defer_surface_initialization()` to disable the `no_surface_provided` error.
## Device Creation ## Device Creation
@ -198,17 +196,30 @@ vkb::destroy_device(vkb_device);
By default, `vkb::DeviceBuilder` will enable one queue from each queue family available on the `VkPhysicalDevice`. This is done because in practice, most use cases only need a single queue from each family. By default, `vkb::DeviceBuilder` will enable one queue from each queue family available on the `VkPhysicalDevice`. This is done because in practice, most use cases only need a single queue from each family.
To get a `VkQueue` or the index of a `VkQueue`, use the `get_queue(QueueType type)` and `get_queue_index(QueueType type)` functions of `vkb::Device`. These will return the appropriate `VkQueue` or `uint32_t` if they exist and were enabled, else they will return an error. To get a `VkQueue` and the index of a `VkQueue`, use the `get_preferred_queue_and_index(VkQueueFlags flags)` functions of `vkb::Device`. These will return the appropriate `VkQueue` handle and `uint32_t` index stored in a `vkb::QueueAndIndex` struct if they exist and were enabled, else they will return an error.
```cpp ```cpp
auto queue_ret = vkb_device.get_queue (vkb::QueueType::graphics); auto queue_ret = vkb_device.get_preferred_queue_and_index (VK_QUEUE_GRAPHICS_BIT);
if (!queue_ret) { if (!queue_ret) {
// handle error // handle error
} }
graphics_queue = queue_ret.value (); graphics_queue = queue_ret.value ();
``` ```
Queue families represent a set of queues with similar operations, such as graphics, transfer, and compute. Because not all Vulkan hardware has queue families for each operation category, an application should be able to handle the presence or lack of certain queue families. For this reason the `get_dedicated_queue` and `get_dedicated_queue_index` functions of `vkb::Device` exist to allow applications to easily know if there is a queue dedicated to a particular operation, such as compute or transfer operations. To query a queue capable of multiple operations, use multiple `VkQueueFlags` combined with bitwise-or to look for a queue that supports all specified flags, if such a queue exists.
```cpp
vkb_device.get_preferred_queue_and_index(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT);
```
To get a `VkQueue` capable of presentation, use `get_first_presentation_queue_and_index()`.
To check if any given queue family index is capable of presentation operations, use `queue_family_index_supports_presentation(VkQueue queue)`.
`get_preferred_queue_and_index(VkQueueFlags flags)` looks for a queue that supports the given queue flags while minimizing unsupported flags.
It does not require that the queue exclusively supports only the given queue flags, which may result in the same queue handle being returns by different queries.
Because not all Vulkan hardware has queue families for each operation category, an application needs to be careful that they didn't get the same queue handle multiple times.
While it is fine to query the same queue multiple times, it is not fine to use that queue in multiple threads at the same time, or to ignore other synchronization requirements of VkQueue's in the Vulkan API.
#### Custom queue setup #### Custom queue setup
@ -222,14 +233,14 @@ for (uint32_t i = 0; i < static_cast<uint32_t>(queue_families.size ()); i++) {
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
// Find the first queue family with graphics operations supported // Find the first queue family with graphics operations supported
queue_descriptions.push_back (vkb::CustomQueueDescription ( queue_descriptions.push_back (vkb::CustomQueueDescription (
i, queue_families[i].queueCount, i, queue_families[i].queueCount,
std::vector<float> (queue_families[i].queueCount, 1.0f))); std::vector<float> (queue_families[i].queueCount, 1.0f)));
} }
} }
``` ```
## Swapchain ## Swapchain
Creating a swapchain follows the same form outlined by `vkb::InstanceBuilder` and `vkb::DeviceBuilder`. Create the `vkb::SwapchainBuilder`, provide `vkb::Device`, call the appropriate builder functions, and call `build()`. Creating a swapchain follows the same form outlined by `vkb::InstanceBuilder` and `vkb::DeviceBuilder`. Create the `vkb::SwapchainBuilder`, provide `vkb::Device`, call the appropriate builder functions, and call `build()`.
```cpp ```cpp
vkb::SwapchainBuilder swapchain_builder{ device }; vkb::SwapchainBuilder swapchain_builder{ device };

View File

@ -410,6 +410,7 @@ const char* to_string(QueueError err) {
CASE_TO_STRING(QueueError, graphics_unavailable) CASE_TO_STRING(QueueError, graphics_unavailable)
CASE_TO_STRING(QueueError, compute_unavailable) CASE_TO_STRING(QueueError, compute_unavailable)
CASE_TO_STRING(QueueError, transfer_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, queue_index_out_of_range)
CASE_TO_STRING(QueueError, invalid_queue_family_index) CASE_TO_STRING(QueueError, invalid_queue_family_index)
default: default:
@ -932,6 +933,7 @@ bool supports_features(VkPhysicalDeviceFeatures supported,
return true; return true;
} }
// clang-format on // clang-format on
// Finds the first queue which supports the desired operations. Returns QUEUE_INDEX_MAX_VALUE if none is found // 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<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags) { uint32_t get_first_queue_index(std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags) {
for (uint32_t i = 0; i < static_cast<uint32_t>(families.size()); i++) { for (uint32_t i = 0; i < static_cast<uint32_t>(families.size()); i++) {
@ -939,21 +941,33 @@ uint32_t get_first_queue_index(std::vector<VkQueueFamilyProperties> const& famil
} }
return QUEUE_INDEX_MAX_VALUE; 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 // Finds the that has the desired flag and the least number of undesired flags.
// QUEUE_INDEX_MAX_VALUE if none is found. // Any queue family with invalid_flags will be ignored.
// Returns QUEUE_INDEX_MAX_VALUE if none is found.
uint32_t get_separate_queue_index( uint32_t get_separate_queue_index(
std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) { std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags, VkQueueFlags invalid_flags) {
uint32_t index = QUEUE_INDEX_MAX_VALUE; 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<uint32_t>(families.size()); i++) { for (uint32_t i = 0; i < static_cast<uint32_t>(families.size()); i++) {
if ((families[i].queueFlags & desired_flags) == desired_flags && ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { if ((invalid_flags != 0) && (families[i].queueFlags & invalid_flags) == invalid_flags) {
if ((families[i].queueFlags & undesired_flags) == 0) { continue;
return i; }
} else { 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; index = i;
} }
} }
} }
return index; return index;
} }
@ -961,8 +975,7 @@ uint32_t get_separate_queue_index(
uint32_t get_dedicated_queue_index( uint32_t get_dedicated_queue_index(
std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) { std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) {
for (uint32_t i = 0; i < static_cast<uint32_t>(families.size()); i++) { for (uint32_t i = 0; i < static_cast<uint32_t>(families.size()); i++) {
if ((families[i].queueFlags & desired_flags) == desired_flags && if ((families[i].queueFlags & desired_flags) == desired_flags && ((families[i].queueFlags & undesired_flags) == 0))
(families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && (families[i].queueFlags & undesired_flags) == 0)
return i; return i;
} }
return QUEUE_INDEX_MAX_VALUE; 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.required_version > pd.properties.apiVersion) return PhysicalDevice::Suitable::no;
if (criteria.desired_version > pd.properties.apiVersion) suitable = PhysicalDevice::Suitable::partial; 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) != bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families,
detail::QUEUE_INDEX_MAX_VALUE; VK_QUEUE_COMPUTE_BIT,
bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
detail::QUEUE_INDEX_MAX_VALUE; bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families,
bool separate_compute = detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != 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; 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; detail::QUEUE_INDEX_MAX_VALUE;
bool present_queue = detail::get_present_queue_index(pd.physical_device, instance_info.surface, pd.queue_families) != 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 // PhysicalDevice
bool PhysicalDevice::has_dedicated_compute_queue() const { 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 { 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 { 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 { 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<VkQueueFamilyProperties> PhysicalDevice::get_queue_families() const { return queue_families; } std::vector<VkQueueFamilyProperties> PhysicalDevice::get_queue_families() const { return queue_families; }
std::vector<std::string> PhysicalDevice::get_extensions() const { return extensions; } std::vector<std::string> PhysicalDevice::get_extensions() const { return extensions; }
@ -1373,6 +1394,7 @@ PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device
// ---- Queues ---- // // ---- Queues ---- //
// Legacy queue function
Result<uint32_t> Device::get_queue_index(QueueType type) const { Result<uint32_t> Device::get_queue_index(QueueType type) const {
uint32_t index = detail::QUEUE_INDEX_MAX_VALUE; uint32_t index = detail::QUEUE_INDEX_MAX_VALUE;
switch (type) { switch (type) {
@ -1385,27 +1407,11 @@ Result<uint32_t> Device::get_queue_index(QueueType type) const {
if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::graphics_unavailable }; if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::graphics_unavailable };
break; break;
case QueueType::compute: 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<uint32_t>{ QueueError::compute_unavailable }; if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::compute_unavailable };
break; break;
case QueueType::transfer: 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<uint32_t>{ QueueError::transfer_unavailable };
break;
default:
return Result<uint32_t>{ QueueError::invalid_queue_family_index };
}
return index;
}
Result<uint32_t> 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);
if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::compute_unavailable };
break;
case QueueType::transfer:
index = detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT);
if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::transfer_unavailable }; if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::transfer_unavailable };
break; break;
default: default:
@ -1414,6 +1420,25 @@ Result<uint32_t> Device::get_dedicated_queue_index(QueueType type) const {
return index; return index;
} }
// Legacy queue function
Result<uint32_t> 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_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT);
if (index == detail::QUEUE_INDEX_MAX_VALUE) return Result<uint32_t>{ QueueError::compute_unavailable };
break;
case QueueType::transfer:
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<uint32_t>{ QueueError::transfer_unavailable };
break;
default:
return Result<uint32_t>{ QueueError::invalid_queue_family_index };
}
return index;
}
// Legacy queue function
Result<VkQueue> Device::get_queue(QueueType type) const { Result<VkQueue> Device::get_queue(QueueType type) const {
auto index = get_queue_index(type); auto index = get_queue_index(type);
if (!index.has_value()) return { index.error() }; if (!index.has_value()) return { index.error() };
@ -1421,14 +1446,63 @@ Result<VkQueue> Device::get_queue(QueueType type) const {
internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue); internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue);
return out_queue; return out_queue;
} }
// Legacy queue function
Result<VkQueue> Device::get_dedicated_queue(QueueType type) const { Result<VkQueue> Device::get_dedicated_queue(QueueType type) const {
auto index = get_dedicated_queue_index(type); auto index = get_dedicated_queue_index(type);
if (!index.has_value()) return { index.error() }; if (!index.has_value()) return { index.error() };
VkQueue out_queue; VkQueue out_queue{};
internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue); internal_table.fp_vkGetDeviceQueue(device, index.value(), 0, &out_queue);
return out_queue; return out_queue;
} }
Result<QueueAndIndex> 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<QueueAndIndex>{ QueueError::queue_type_unavailable };
VkQueue out_queue{};
internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue);
return QueueAndIndex{ out_queue, index };
}
Result<QueueAndIndex> 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<QueueAndIndex>{ QueueError::queue_type_unavailable };
VkQueue out_queue{};
internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue);
return QueueAndIndex{ out_queue, index };
}
Result<QueueAndIndex> 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<QueueAndIndex>{ QueueError::present_unavailable };
VkQueue out_queue{};
internal_table.fp_vkGetDeviceQueue(device, index, 0, &out_queue);
return QueueAndIndex{ out_queue, index };
}
bool Device::queue_family_index_supports_presentation(uint32_t index, VkSurfaceKHR surface_to_check) const {
VkBool32 presentSupport = 0;
VkSurfaceKHR surface_to_use = surface;
if (surface_to_check != VK_NULL_HANDLE) {
surface_to_use = surface_to_check;
}
if (surface_to_use != VK_NULL_HANDLE) {
VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceSupportKHR(
physical_device, index, surface_to_use, &presentSupport);
if (res != VK_SUCCESS) return false;
}
return presentSupport == 1;
}
// ---- Dispatch ---- // // ---- Dispatch ---- //
DispatchTable Device::make_table() const { return { device, fp_vkGetDeviceProcAddr }; } DispatchTable Device::make_table() const { return { device, fp_vkGetDeviceProcAddr }; }

View File

@ -200,6 +200,7 @@ enum class QueueError {
graphics_unavailable, graphics_unavailable,
compute_unavailable, compute_unavailable,
transfer_unavailable, transfer_unavailable,
queue_type_unavailable,
queue_index_out_of_range, queue_index_out_of_range,
invalid_queue_family_index invalid_queue_family_index
}; };
@ -675,13 +676,25 @@ class PhysicalDeviceSelector {
}; };
// ---- Queue ---- // // ---- Queue ---- //
enum class QueueType { present, graphics, compute, transfer };
enum class QueueType {
present,
graphics,
compute,
transfer,
};
namespace detail { namespace detail {
// Sentinel value, used in implementation only // Sentinel value, used in implementation only
const uint32_t QUEUE_INDEX_MAX_VALUE = 65536; const uint32_t QUEUE_INDEX_MAX_VALUE = 65536;
} // namespace detail } // namespace detail
struct QueueAndIndex {
VkQueue queue = VK_NULL_HANDLE;
uint32_t index = detail::QUEUE_INDEX_MAX_VALUE;
};
// ---- Device ---- // // ---- Device ---- //
struct Device { struct Device {
@ -693,13 +706,34 @@ struct Device {
PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr; PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr;
uint32_t instance_version = VKB_VK_API_VERSION_1_0; uint32_t instance_version = VKB_VK_API_VERSION_1_0;
Result<uint32_t> get_queue_index(QueueType type) const; // Legacy function - gets the queue which supports queue_type operations using the following rules:
// Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue index // If queue_type is graphics, return the first queue that supports graphics operations
Result<uint32_t> get_dedicated_queue_index(QueueType type) const; // 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<uint32_t> get_queue_index(QueueType queue_type) const;
Result<VkQueue> get_queue(QueueType queue_type) const;
Result<VkQueue> get_queue(QueueType type) const; // Legacy function - gets the queue which supports only the queue_type operations using the following rules:
// Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue // If queue_type is compute, return a queue that supports compute operations but not graphics or transfer, if one
Result<VkQueue> get_dedicated_queue(QueueType type) const; // 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<VkQueue> get_dedicated_queue(QueueType queue_type) const;
Result<uint32_t> get_dedicated_queue_index(QueueType queue_type) const;
// Returns the first queue which supports the operations specified by queue_flags
Result<QueueAndIndex> 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<QueueAndIndex> get_preferred_queue_and_index(VkQueueFlags queue_flags) const;
// Get the first queue which supports presentation. Very unlikely to be unique.
// 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<QueueAndIndex> get_first_presentation_queue_and_index(VkSurfaceKHR surface_to_check = VK_NULL_HANDLE) const;
// Returns true if the given queue family index is capable of supporting presentation operations.
bool queue_family_index_supports_presentation(uint32_t index, VkSurfaceKHR surface_to_check = VK_NULL_HANDLE) const;
// Return a loaded dispatch table // Return a loaded dispatch table
DispatchTable make_table() const; DispatchTable make_table() const;

View File

@ -334,6 +334,9 @@ TEST_CASE("Swapchain", "[VkBootstrap.bootstrap]") {
auto graphics_queue_index = device.get_queue_index(vkb::QueueType::graphics).value(); auto graphics_queue_index = device.get_queue_index(vkb::QueueType::graphics).value();
auto present_queue_index = device.get_queue_index(vkb::QueueType::present).value(); auto present_queue_index = device.get_queue_index(vkb::QueueType::present).value();
REQUIRE(device.get_first_presentation_queue_and_index().has_value());
REQUIRE(device.queue_family_index_supports_presentation(present_queue_index));
THEN("Swapchain can be made") { THEN("Swapchain can be made") {
vkb::SwapchainBuilder swapchain_builder(device); vkb::SwapchainBuilder swapchain_builder(device);
auto swapchain_ret = swapchain_builder.build(); auto swapchain_ret = swapchain_builder.build();
@ -727,7 +730,7 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]"
} }
TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[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.api_version = VK_API_VERSION_1_2;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2; mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2;
@ -740,7 +743,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); mock.physical_devices_details[0].add_features_pNext_struct(mock_vulkan_12_features);
GIVEN("A working instance") { GIVEN("A working instance") {
vkb::InstanceBuilder builder;
auto instance = get_headless_instance(2); // make sure we use 1.2 auto instance = get_headless_instance(2); // make sure we use 1.2
SECTION("Requires a device that supports multiview and bufferDeviceAddress") { SECTION("Requires a device that supports multiview and bufferDeviceAddress") {
VkPhysicalDeviceVulkan11Features features_11{}; VkPhysicalDeviceVulkan11Features features_11{};

View File

@ -2,6 +2,7 @@
#include "VkBootstrap.cpp" #include "VkBootstrap.cpp"
#include "vulkan_mock.hpp"
TEST_CASE("Single Queue Device", "[UnitTests.queue_selection_logic]") { TEST_CASE("Single Queue Device", "[UnitTests.queue_selection_logic]") {
std::vector<VkQueueFamilyProperties> families = { VkQueueFamilyProperties{ std::vector<VkQueueFamilyProperties> 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(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT));
REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == 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 == 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 == REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE ==
vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT));
REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == 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") { SECTION("Dedicated Queue First") {
std::vector<VkQueueFamilyProperties> families = { std::vector<VkQueueFamilyProperties> families = {
VkQueueFamilyProperties{ 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, 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 | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }
}; };
REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); 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(1 == vkb::detail::get_separate_queue_index(
REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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(1 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT));
REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE ==
vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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") { SECTION("Dedicated Queue Last") {
std::vector<VkQueueFamilyProperties> families = { std::vector<VkQueueFamilyProperties> families = {
VkQueueFamilyProperties{ 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 | 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, 1, 0, VkExtent3D{ 1, 1, 1 } }
}; };
REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); 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(2 == vkb::detail::get_separate_queue_index(
REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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(2 == vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT));
REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE == REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE ==
vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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") { SECTION("Dedicated Queue First") {
std::vector<VkQueueFamilyProperties> families = { std::vector<VkQueueFamilyProperties> families = {
VkQueueFamilyProperties{ 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_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 | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } }
}; };
REQUIRE(0 == vkb::detail::get_first_queue_index(families, VK_QUEUE_GRAPHICS_BIT)); 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(2 == vkb::detail::get_separate_queue_index(
REQUIRE(1 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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 == REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE ==
vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); 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)); 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") { SECTION("Dedicated Queue Last") {
std::vector<VkQueueFamilyProperties> families = { std::vector<VkQueueFamilyProperties> families = {
VkQueueFamilyProperties{ 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 | VK_QUEUE_TRANSFER_BIT, 1, 0, VkExtent3D{ 1, 1, 1 } },
VkQueueFamilyProperties{ 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(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(1 == vkb::detail::get_separate_queue_index(
REQUIRE(2 == vkb::detail::get_separate_queue_index(families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT)); 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 == REQUIRE(vkb::detail::QUEUE_INDEX_MAX_VALUE ==
vkb::detail::get_dedicated_queue_index(families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT)); 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)); 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<VkQueueFamilyProperties> 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));
}

View File

@ -296,7 +296,9 @@ VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceSupportKHR(
} }
} }
} }
if (surface && pSupported) *pSupported = true; if (surface && pSupported) {
*pSupported = true;
}
return VK_SUCCESS; return VK_SUCCESS;
} }
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceFormatsKHR( VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceFormatsKHR(