Allow apps to query devices based on the name

This lets applications see all the devices which meet a set of requirements
then pick whichever of these devices is most appropriate for them. The intent
is for applications that want to find all the suitable devices and let the
user of the application pick the physical device from a list.
This commit is contained in:
Charles Giessen 2022-02-06 19:17:53 -07:00 committed by Charles Giessen
parent f4d3f916f1
commit 5f45b1019a
4 changed files with 368 additions and 350 deletions

View File

@ -12,7 +12,7 @@ BinPackParameters: false
BreakBeforeBinaryOperators: false BreakBeforeBinaryOperators: false
BreakBeforeTernaryOperators: false BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100 ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 0 ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4

View File

@ -31,17 +31,15 @@
#endif #endif
#include <mutex> #include <mutex>
#include <algorithm>
namespace vkb { namespace vkb {
namespace detail { namespace detail {
GenericFeaturesPNextNode::GenericFeaturesPNextNode() { GenericFeaturesPNextNode::GenericFeaturesPNextNode() { memset(fields, UINT8_MAX, sizeof(VkBool32) * field_capacity); }
memset(fields, UINT8_MAX, sizeof(VkBool32) * field_capacity);
}
bool GenericFeaturesPNextNode::match( bool GenericFeaturesPNextNode::match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept {
GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept {
assert(requested.sType == supported.sType && "Non-matching sTypes in features nodes!"); assert(requested.sType == supported.sType && "Non-matching sTypes in features nodes!");
for (uint32_t i = 0; i < field_capacity; i++) { for (uint32_t i = 0; i < field_capacity; i++) {
if (requested.fields[i] && !supported.fields[i]) return false; if (requested.fields[i] && !supported.fields[i]) return false;
@ -115,8 +113,8 @@ class VulkanFunctions {
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties")); ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"));
fp_vkEnumerateInstanceVersion = reinterpret_cast<PFN_vkEnumerateInstanceVersion>( fp_vkEnumerateInstanceVersion = reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion")); ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
fp_vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>( fp_vkCreateInstance =
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); reinterpret_cast<PFN_vkCreateInstance>(ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
} }
public: public:
@ -124,8 +122,7 @@ class VulkanFunctions {
out_ptr = reinterpret_cast<T>(ptr_vkGetInstanceProcAddr(instance, func_name)); out_ptr = reinterpret_cast<T>(ptr_vkGetInstanceProcAddr(instance, func_name));
} }
template <typename T> template <typename T> void get_device_proc_addr(VkDevice device, T& out_ptr, const char* func_name) {
void get_device_proc_addr(VkDevice device, T& out_ptr, const char* func_name) {
out_ptr = reinterpret_cast<T>(fp_vkGetDeviceProcAddr(device, func_name)); out_ptr = reinterpret_cast<T>(fp_vkGetDeviceProcAddr(device, func_name));
} }
@ -204,8 +201,7 @@ VulkanFunctions& vulkan_functions() {
} }
// Helper for robustly executing the two-call pattern // Helper for robustly executing the two-call pattern
template <typename T, typename F, typename... Ts> template <typename T, typename F, typename... Ts> auto get_vector(std::vector<T>& out, F&& f, Ts&&... ts) -> VkResult {
auto get_vector(std::vector<T>& out, F&& f, Ts&&... ts) -> VkResult {
uint32_t count = 0; uint32_t count = 0;
VkResult err; VkResult err;
do { do {
@ -220,8 +216,7 @@ auto get_vector(std::vector<T>& out, F&& f, Ts&&... ts) -> VkResult {
return err; return err;
} }
template <typename T, typename F, typename... Ts> template <typename T, typename F, typename... Ts> auto get_vector_noerror(F&& f, Ts&&... ts) -> std::vector<T> {
auto get_vector_noerror(F&& f, Ts&&... ts) -> std::vector<T> {
uint32_t count = 0; uint32_t count = 0;
std::vector<T> results; std::vector<T> results;
f(ts..., &count, nullptr); f(ts..., &count, nullptr);
@ -306,8 +301,7 @@ bool check_layer_supported(std::vector<VkLayerProperties> const& available_layer
return false; return false;
} }
bool check_layers_supported(std::vector<VkLayerProperties> const& available_layers, bool check_layers_supported(std::vector<VkLayerProperties> const& available_layers, std::vector<const char*> const& layer_names) {
std::vector<const char*> const& layer_names) {
bool all_found = true; bool all_found = true;
for (const auto& layer_name : layer_names) { for (const auto& layer_name : layer_names) {
bool found = check_layer_supported(available_layers, layer_name); bool found = check_layer_supported(available_layers, layer_name);
@ -316,8 +310,7 @@ bool check_layers_supported(std::vector<VkLayerProperties> const& available_laye
return all_found; return all_found;
} }
bool check_extension_supported( bool check_extension_supported(std::vector<VkExtensionProperties> const& available_extensions, const char* extension_name) {
std::vector<VkExtensionProperties> const& available_extensions, const char* extension_name) {
if (!extension_name) return false; if (!extension_name) return false;
for (const auto& extension_properties : available_extensions) { for (const auto& extension_properties : available_extensions) {
if (strcmp(extension_name, extension_properties.extensionName) == 0) { if (strcmp(extension_name, extension_properties.extensionName) == 0) {
@ -327,8 +320,8 @@ bool check_extension_supported(
return false; return false;
} }
bool check_extensions_supported(std::vector<VkExtensionProperties> const& available_extensions, bool check_extensions_supported(
std::vector<const char*> const& extension_names) { std::vector<VkExtensionProperties> const& available_extensions, std::vector<const char*> const& extension_names) {
bool all_found = true; bool all_found = true;
for (const auto& extension_name : extension_names) { for (const auto& extension_name : extension_names) {
bool found = check_extension_supported(available_extensions, extension_name); bool found = check_extension_supported(available_extensions, extension_name);
@ -337,8 +330,7 @@ bool check_extensions_supported(std::vector<VkExtensionProperties> const& availa
return all_found; return all_found;
} }
template <typename T> template <typename T> void setup_pNext_chain(T& structure, std::vector<VkBaseOutStructure*> const& structs) {
void setup_pNext_chain(T& structure, std::vector<VkBaseOutStructure*> const& structs) {
structure.pNext = nullptr; structure.pNext = nullptr;
if (structs.size() <= 0) return; if (structs.size() <= 0) return;
for (size_t i = 0; i < structs.size() - 1; i++) { for (size_t i = 0; i < structs.size() - 1; i++) {
@ -350,17 +342,13 @@ const char* validation_layer_name = "VK_LAYER_KHRONOS_validation";
struct InstanceErrorCategory : std::error_category { struct InstanceErrorCategory : std::error_category {
const char* name() const noexcept override { return "vkb_instance"; } const char* name() const noexcept override { return "vkb_instance"; }
std::string message(int err) const override { std::string message(int err) const override { return to_string(static_cast<InstanceError>(err)); }
return to_string(static_cast<InstanceError>(err));
}
}; };
const InstanceErrorCategory instance_error_category; const InstanceErrorCategory instance_error_category;
struct PhysicalDeviceErrorCategory : std::error_category { struct PhysicalDeviceErrorCategory : std::error_category {
const char* name() const noexcept override { return "vkb_physical_device"; } const char* name() const noexcept override { return "vkb_physical_device"; }
std::string message(int err) const override { std::string message(int err) const override { return to_string(static_cast<PhysicalDeviceError>(err)); }
return to_string(static_cast<PhysicalDeviceError>(err));
}
}; };
const PhysicalDeviceErrorCategory physical_device_error_category; const PhysicalDeviceErrorCategory physical_device_error_category;
@ -378,9 +366,7 @@ const DeviceErrorCategory device_error_category;
struct SwapchainErrorCategory : std::error_category { struct SwapchainErrorCategory : std::error_category {
const char* name() const noexcept override { return "vbk_swapchain"; } const char* name() const noexcept override { return "vbk_swapchain"; }
std::string message(int err) const override { std::string message(int err) const override { return to_string(static_cast<SwapchainError>(err)); }
return to_string(static_cast<SwapchainError>(err));
}
}; };
const SwapchainErrorCategory swapchain_error_category; const SwapchainErrorCategory swapchain_error_category;
@ -506,8 +492,7 @@ SystemInfo::SystemInfo() {
} }
for (auto& layer : this->available_layers) for (auto& layer : this->available_layers)
if (strcmp(layer.layerName, detail::validation_layer_name) == 0) if (strcmp(layer.layerName, detail::validation_layer_name) == 0) validation_layers_available = true;
validation_layers_available = true;
auto available_extensions_ret = detail::get_vector<VkExtensionProperties>( auto available_extensions_ret = detail::get_vector<VkExtensionProperties>(
this->available_extensions, detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, nullptr); this->available_extensions, detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, nullptr);
@ -523,9 +508,8 @@ SystemInfo::SystemInfo() {
for (auto& layer : this->available_layers) { for (auto& layer : this->available_layers) {
std::vector<VkExtensionProperties> layer_extensions; std::vector<VkExtensionProperties> layer_extensions;
auto layer_extensions_ret = detail::get_vector<VkExtensionProperties>(layer_extensions, auto layer_extensions_ret = detail::get_vector<VkExtensionProperties>(
detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, layer_extensions, detail::vulkan_functions().fp_vkEnumerateInstanceExtensionProperties, layer.layerName);
layer.layerName);
if (layer_extensions_ret == VK_SUCCESS) { if (layer_extensions_ret == VK_SUCCESS) {
this->available_extensions.insert( this->available_extensions.insert(
this->available_extensions.end(), layer_extensions.begin(), layer_extensions.end()); this->available_extensions.end(), layer_extensions.begin(), layer_extensions.end());
@ -580,8 +564,7 @@ detail::Result<Instance> InstanceBuilder::build() const {
if (info.minimum_instance_version > VKB_VK_API_VERSION_1_0 || info.required_api_version > VKB_VK_API_VERSION_1_0 || if (info.minimum_instance_version > VKB_VK_API_VERSION_1_0 || info.required_api_version > VKB_VK_API_VERSION_1_0 ||
info.desired_api_version > VKB_VK_API_VERSION_1_0) { info.desired_api_version > VKB_VK_API_VERSION_1_0) {
PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = detail::vulkan_functions().fp_vkEnumerateInstanceVersion;
detail::vulkan_functions().fp_vkEnumerateInstanceVersion;
if (pfn_vkEnumerateInstanceVersion != nullptr) { if (pfn_vkEnumerateInstanceVersion != nullptr) {
VkResult res = pfn_vkEnumerateInstanceVersion(&instance_version); VkResult res = pfn_vkEnumerateInstanceVersion(&instance_version);
@ -624,8 +607,8 @@ detail::Result<Instance> InstanceBuilder::build() const {
if (info.debug_callback != nullptr && system.debug_utils_available) { if (info.debug_callback != nullptr && system.debug_utils_available) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
} }
bool supports_properties2_ext = detail::check_extension_supported( bool supports_properties2_ext =
system.available_extensions, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); detail::check_extension_supported(system.available_extensions, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (supports_properties2_ext) { if (supports_properties2_ext) {
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
@ -688,11 +671,9 @@ detail::Result<Instance> InstanceBuilder::build() const {
if (info.enabled_validation_features.size() != 0 || info.disabled_validation_features.size()) { if (info.enabled_validation_features.size() != 0 || info.disabled_validation_features.size()) {
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.pNext = nullptr; features.pNext = nullptr;
features.enabledValidationFeatureCount = features.enabledValidationFeatureCount = static_cast<uint32_t>(info.enabled_validation_features.size());
static_cast<uint32_t>(info.enabled_validation_features.size());
features.pEnabledValidationFeatures = info.enabled_validation_features.data(); features.pEnabledValidationFeatures = info.enabled_validation_features.data();
features.disabledValidationFeatureCount = features.disabledValidationFeatureCount = static_cast<uint32_t>(info.disabled_validation_features.size());
static_cast<uint32_t>(info.disabled_validation_features.size());
features.pDisabledValidationFeatures = info.disabled_validation_features.data(); features.pDisabledValidationFeatures = info.disabled_validation_features.data();
pNext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&features)); pNext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&features));
} }
@ -722,10 +703,9 @@ detail::Result<Instance> InstanceBuilder::build() const {
instance_create_info.ppEnabledLayerNames = layers.data(); instance_create_info.ppEnabledLayerNames = layers.data();
Instance instance; Instance instance;
VkResult res = detail::vulkan_functions().fp_vkCreateInstance( VkResult res =
&instance_create_info, info.allocation_callbacks, &instance.instance); detail::vulkan_functions().fp_vkCreateInstance(&instance_create_info, info.allocation_callbacks, &instance.instance);
if (res != VK_SUCCESS) if (res != VK_SUCCESS) return detail::Result<Instance>(InstanceError::failed_create_instance, res);
return detail::Result<Instance>(InstanceError::failed_create_instance, res);
detail::vulkan_functions().init_instance_funcs(instance.instance); detail::vulkan_functions().init_instance_funcs(instance.instance);
@ -878,8 +858,7 @@ void destroy_debug_messenger(VkInstance const instance, VkDebugUtilsMessengerEXT
namespace detail { namespace detail {
std::vector<const char*> check_device_extension_support( std::vector<const char*> check_device_extension_support(VkPhysicalDevice device, std::vector<const char*> desired_extensions) {
VkPhysicalDevice device, std::vector<const char*> desired_extensions) {
std::vector<VkExtensionProperties> available_extensions; std::vector<VkExtensionProperties> available_extensions;
auto available_extensions_ret = detail::get_vector<VkExtensionProperties>( auto available_extensions_ret = detail::get_vector<VkExtensionProperties>(
available_extensions, detail::vulkan_functions().fp_vkEnumerateDeviceExtensionProperties, device, nullptr); available_extensions, detail::vulkan_functions().fp_vkEnumerateDeviceExtensionProperties, device, nullptr);
@ -976,13 +955,11 @@ uint32_t get_first_queue_index(std::vector<VkQueueFamilyProperties> const& famil
// Finds the queue which is separate from the graphics queue and has the desired flag and not the // 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 // undesired flag, but will select it if no better options are available compute support. Returns
// QUEUE_INDEX_MAX_VALUE if none is found. // QUEUE_INDEX_MAX_VALUE if none is found.
uint32_t get_separate_queue_index(std::vector<VkQueueFamilyProperties> const& families, uint32_t get_separate_queue_index(
VkQueueFlags desired_flags, std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_flags) {
VkQueueFlags undesired_flags) {
uint32_t index = QUEUE_INDEX_MAX_VALUE; uint32_t index = 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) && if ((families[i].queueFlags & desired_flags) && ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
if ((families[i].queueFlags & undesired_flags) == 0) { if ((families[i].queueFlags & undesired_flags) == 0) {
return i; return i;
} else { } else {
@ -994,9 +971,8 @@ uint32_t get_separate_queue_index(std::vector<VkQueueFamilyProperties> const& fa
} }
// finds the first queue which supports only the desired flag (not graphics or transfer). Returns QUEUE_INDEX_MAX_VALUE if none is found. // finds the first queue which supports only the desired flag (not graphics or transfer). Returns QUEUE_INDEX_MAX_VALUE if none is found.
uint32_t get_dedicated_queue_index(std::vector<VkQueueFamilyProperties> const& families, uint32_t get_dedicated_queue_index(
VkQueueFlags desired_flags, std::vector<VkQueueFamilyProperties> const& families, VkQueueFlags desired_flags, VkQueueFlags undesired_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) && (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && if ((families[i].queueFlags & desired_flags) && (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 &&
(families[i].queueFlags & undesired_flags) == 0) (families[i].queueFlags & undesired_flags) == 0)
@ -1006,16 +982,13 @@ uint32_t get_dedicated_queue_index(std::vector<VkQueueFamilyProperties> const& f
} }
// finds the first queue which supports presenting. returns QUEUE_INDEX_MAX_VALUE if none is found // finds the first queue which supports presenting. returns QUEUE_INDEX_MAX_VALUE if none is found
uint32_t get_present_queue_index(VkPhysicalDevice const phys_device, uint32_t get_present_queue_index(
VkSurfaceKHR const surface, VkPhysicalDevice const phys_device, VkSurfaceKHR const surface, std::vector<VkQueueFamilyProperties> const& families) {
std::vector<VkQueueFamilyProperties> const& families) {
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++) {
VkBool32 presentSupport = false; VkBool32 presentSupport = false;
if (surface != VK_NULL_HANDLE) { if (surface != VK_NULL_HANDLE) {
VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceSupportKHR( VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceSupportKHR(phys_device, i, surface, &presentSupport);
phys_device, i, surface, &presentSupport); if (res != VK_SUCCESS) return QUEUE_INDEX_MAX_VALUE; // TODO: determine if this should fail another way
if (res != VK_SUCCESS)
return QUEUE_INDEX_MAX_VALUE; // TODO: determine if this should fail another way
} }
if (presentSupport == VK_TRUE) return i; if (presentSupport == VK_TRUE) return i;
} }
@ -1023,29 +996,32 @@ uint32_t get_present_queue_index(VkPhysicalDevice const phys_device,
} }
} // namespace detail } // namespace detail
PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice vk_phys_device,
PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice phys_device,
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const { std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const {
PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; PhysicalDevice physical_device{};
desc.phys_device = phys_device; physical_device.physical_device = vk_phys_device;
physical_device.surface = instance_info.surface;
physical_device.defer_surface_initialization = criteria.defer_surface_initialization;
physical_device.instance_version = instance_info.version;
auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties>( auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties>(
detail::vulkan_functions().fp_vkGetPhysicalDeviceQueueFamilyProperties, phys_device); detail::vulkan_functions().fp_vkGetPhysicalDeviceQueueFamilyProperties, vk_phys_device);
desc.queue_families = queue_families; physical_device.queue_families = queue_families;
detail::vulkan_functions().fp_vkGetPhysicalDeviceProperties(phys_device, &desc.device_properties); detail::vulkan_functions().fp_vkGetPhysicalDeviceProperties(vk_phys_device, &physical_device.properties);
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(phys_device, &desc.device_features); detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(vk_phys_device, &physical_device.features);
detail::vulkan_functions().fp_vkGetPhysicalDeviceMemoryProperties(phys_device, &desc.mem_properties); detail::vulkan_functions().fp_vkGetPhysicalDeviceMemoryProperties(vk_phys_device, &physical_device.memory_properties);
physical_device.name = physical_device.properties.deviceName;
#if defined(VKB_VK_API_VERSION_1_1) #if defined(VKB_VK_API_VERSION_1_1)
desc.device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
#else #else
desc.device_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
#endif #endif
auto fill_chain = src_extended_features_chain; auto fill_chain = src_extended_features_chain;
if (!fill_chain.empty() && if (!fill_chain.empty() && (instance_info.version >= VKB_VK_API_VERSION_1_1 || instance_info.supports_properties2_ext)) {
(instance_info.version >= VKB_VK_API_VERSION_1_1 || instance_info.supports_properties2_ext)) {
detail::GenericFeaturesPNextNode* prev = nullptr; detail::GenericFeaturesPNextNode* prev = nullptr;
for (auto& extension : fill_chain) { for (auto& extension : fill_chain) {
@ -1056,213 +1032,229 @@ PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_devi
} }
#if defined(VKB_VK_API_VERSION_1_1) #if defined(VKB_VK_API_VERSION_1_1)
if (instance_info.version >= VKB_VK_API_VERSION_1_1 && desc.device_properties.apiVersion >= VKB_VK_API_VERSION_1_1) { if (instance_info.version >= VKB_VK_API_VERSION_1_1 && physical_device.properties.apiVersion >= VKB_VK_API_VERSION_1_1) {
VkPhysicalDeviceFeatures2 local_features{}; VkPhysicalDeviceFeatures2 local_features{};
local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
local_features.pNext = &fill_chain.front(); local_features.pNext = &fill_chain.front();
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(phys_device, &local_features); detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(vk_phys_device, &local_features);
} else if (instance_info.supports_properties2_ext) { } else if (instance_info.supports_properties2_ext) {
VkPhysicalDeviceFeatures2KHR local_features_khr{}; VkPhysicalDeviceFeatures2KHR local_features_khr{};
local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
local_features_khr.pNext = &fill_chain.front(); local_features_khr.pNext = &fill_chain.front();
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(phys_device, &local_features_khr); detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(vk_phys_device, &local_features_khr);
} }
#else #else
VkPhysicalDeviceFeatures2KHR local_features_khr{}; VkPhysicalDeviceFeatures2KHR local_features_khr{};
local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; local_features_khr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
local_features_khr.pNext = &fill_chain.front(); local_features_khr.pNext = &fill_chain.front();
if (instance_info.supports_properties2_ext) { if (instance_info.supports_properties2_ext) {
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(phys_device, &local_features_khr); detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2KHR(vk_phys_device, &local_features_khr);
} }
#endif #endif
desc.extended_features_chain = fill_chain; physical_device.extended_features_chain = fill_chain;
} }
return desc; return physical_device;
} }
PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable(PhysicalDeviceDesc pd) const { PhysicalDevice::Suitable PhysicalDeviceSelector::is_device_suitable(PhysicalDevice const& pd) const {
Suitable suitable = Suitable::yes; PhysicalDevice::Suitable suitable = PhysicalDevice::Suitable::yes;
if (criteria.required_version > pd.device_properties.apiVersion) return Suitable::no; if (criteria.name.size() > 0 && criteria.name != pd.properties.deviceName) return PhysicalDevice::Suitable::no;
if (criteria.desired_version > pd.device_properties.apiVersion) suitable = Suitable::partial;
bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families, if (criteria.required_version > pd.properties.apiVersion) return PhysicalDevice::Suitable::no;
VK_QUEUE_COMPUTE_BIT, if (criteria.desired_version > pd.properties.apiVersion) suitable = PhysicalDevice::Suitable::partial;
VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families, bool dedicated_compute = detail::get_dedicated_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) !=
VK_QUEUE_TRANSFER_BIT,
VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
bool separate_compute =
detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) !=
detail::QUEUE_INDEX_MAX_VALUE; detail::QUEUE_INDEX_MAX_VALUE;
bool separate_transfer = bool dedicated_transfer = detail::get_dedicated_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) !=
detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
bool separate_compute = detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) !=
detail::QUEUE_INDEX_MAX_VALUE;
bool separate_transfer = detail::get_separate_queue_index(pd.queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) !=
detail::QUEUE_INDEX_MAX_VALUE; detail::QUEUE_INDEX_MAX_VALUE;
bool present_queue = bool present_queue = detail::get_present_queue_index(pd.physical_device, instance_info.surface, pd.queue_families) !=
detail::get_present_queue_index(pd.phys_device, instance_info.surface, pd.queue_families) !=
detail::QUEUE_INDEX_MAX_VALUE; detail::QUEUE_INDEX_MAX_VALUE;
if (criteria.require_dedicated_compute_queue && !dedicated_compute) return Suitable::no; if (criteria.require_dedicated_compute_queue && !dedicated_compute) return PhysicalDevice::Suitable::no;
if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return Suitable::no; if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return PhysicalDevice::Suitable::no;
if (criteria.require_separate_compute_queue && !separate_compute) return Suitable::no; if (criteria.require_separate_compute_queue && !separate_compute) return PhysicalDevice::Suitable::no;
if (criteria.require_separate_transfer_queue && !separate_transfer) return Suitable::no; if (criteria.require_separate_transfer_queue && !separate_transfer) return PhysicalDevice::Suitable::no;
if (criteria.require_present && !present_queue && !criteria.defer_surface_initialization) if (criteria.require_present && !present_queue && !criteria.defer_surface_initialization)
return Suitable::no; return PhysicalDevice::Suitable::no;
auto required_extensions_supported = auto required_extensions_supported = detail::check_device_extension_support(pd.physical_device, criteria.required_extensions);
detail::check_device_extension_support(pd.phys_device, criteria.required_extensions);
if (required_extensions_supported.size() != criteria.required_extensions.size()) if (required_extensions_supported.size() != criteria.required_extensions.size())
return Suitable::no; return PhysicalDevice::Suitable::no;
auto desired_extensions_supported = auto desired_extensions_supported = detail::check_device_extension_support(pd.physical_device, criteria.desired_extensions);
detail::check_device_extension_support(pd.phys_device, criteria.desired_extensions);
if (desired_extensions_supported.size() != criteria.desired_extensions.size()) if (desired_extensions_supported.size() != criteria.desired_extensions.size())
suitable = Suitable::partial; suitable = PhysicalDevice::Suitable::partial;
bool swapChainAdequate = false; if (!criteria.defer_surface_initialization && criteria.require_present) {
if (criteria.defer_surface_initialization) {
swapChainAdequate = true;
} else if (!instance_info.headless) {
std::vector<VkSurfaceFormatKHR> formats; std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> present_modes; std::vector<VkPresentModeKHR> present_modes;
auto formats_ret = detail::get_vector<VkSurfaceFormatKHR>(formats, auto formats_ret = detail::get_vector<VkSurfaceFormatKHR>(formats,
detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceFormatsKHR, detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceFormatsKHR,
pd.phys_device, pd.physical_device,
instance_info.surface); instance_info.surface);
auto present_modes_ret = detail::get_vector<VkPresentModeKHR>(present_modes, auto present_modes_ret = detail::get_vector<VkPresentModeKHR>(present_modes,
detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfacePresentModesKHR, detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfacePresentModesKHR,
pd.phys_device, pd.physical_device,
instance_info.surface); instance_info.surface);
if (formats_ret == VK_SUCCESS && present_modes_ret == VK_SUCCESS) { if (formats_ret != VK_SUCCESS || present_modes_ret != VK_SUCCESS || formats.empty() || present_modes.empty()) {
swapChainAdequate = !formats.empty() && !present_modes.empty(); return PhysicalDevice::Suitable::no;
} }
} }
if (criteria.require_present && !swapChainAdequate) return Suitable::no;
if (pd.device_properties.deviceType != static_cast<VkPhysicalDeviceType>(criteria.preferred_type)) { if (!criteria.allow_any_type && pd.properties.deviceType != static_cast<VkPhysicalDeviceType>(criteria.preferred_type)) {
if (criteria.allow_any_type) suitable = PhysicalDevice::Suitable::partial;
suitable = Suitable::partial;
else
return Suitable::no;
} }
bool required_features_supported = detail::supports_features( bool required_features_supported = detail::supports_features(
pd.device_features, criteria.required_features, pd.extended_features_chain, criteria.extended_features_chain); pd.features, criteria.required_features, pd.extended_features_chain, criteria.extended_features_chain);
if (!required_features_supported) return Suitable::no; if (!required_features_supported) return PhysicalDevice::Suitable::no;
bool has_required_memory = false; for (uint32_t i = 0; i < pd.memory_properties.memoryHeapCount; i++) {
bool has_preferred_memory = false; if (pd.memory_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
for (uint32_t i = 0; i < pd.mem_properties.memoryHeapCount; i++) { if (pd.memory_properties.memoryHeaps[i].size < criteria.required_mem_size) {
if (pd.mem_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { return PhysicalDevice::Suitable::no;
if (pd.mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { } else if (pd.memory_properties.memoryHeaps[i].size < criteria.desired_mem_size) {
has_required_memory = true; suitable = PhysicalDevice::Suitable::partial;
}
if (pd.mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) {
has_preferred_memory = true;
} }
} }
} }
if (!has_required_memory) return Suitable::no;
if (!has_preferred_memory) suitable = Suitable::partial;
return suitable; return suitable;
} }
// delegate construction to the one with an explicit surface parameter
PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance)
: PhysicalDeviceSelector(instance, VK_NULL_HANDLE) {}
PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance) { PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance, VkSurfaceKHR surface) {
instance_info.instance = instance.instance; instance_info.instance = instance.instance;
instance_info.headless = instance.headless;
instance_info.version = instance.instance_version; instance_info.version = instance.instance_version;
instance_info.supports_properties2_ext = instance.supports_properties2_ext; instance_info.supports_properties2_ext = instance.supports_properties2_ext;
instance_info.surface = surface;
criteria.require_present = !instance.headless; criteria.require_present = !instance.headless;
criteria.required_version = instance.api_version; criteria.required_version = instance.api_version;
criteria.desired_version = instance.api_version; criteria.desired_version = instance.api_version;
} }
detail::Result<PhysicalDevice> PhysicalDeviceSelector::select() const { detail::Result<std::vector<PhysicalDevice>> PhysicalDeviceSelector::select_impl(DeviceSelectionMode selection) const {
#if !defined(NDEBUG) #if !defined(NDEBUG)
// Validation // Validation
for (const auto& node : criteria.extended_features_chain) { for (const auto& node : criteria.extended_features_chain) {
assert(node.sType != static_cast<VkStructureType>(0) && assert(node.sType != static_cast<VkStructureType>(0) &&
"Features struct sType must be filled with the struct's " "Features struct sType must be filled with the struct's "
"corresponding VkStructureType enum"); "corresponding VkStructureType enum");
assert( assert(node.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 &&
node.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 &&
"Do not pass VkPhysicalDeviceFeatures2 as a required extension feature structure. An " "Do not pass VkPhysicalDeviceFeatures2 as a required extension feature structure. An "
"instance of this is managed internally for selection criteria and device creation."); "instance of this is managed internally for selection criteria and device creation.");
} }
#endif #endif
if (!instance_info.headless && !criteria.defer_surface_initialization) { if (criteria.require_present && !criteria.defer_surface_initialization) {
if (instance_info.surface == VK_NULL_HANDLE) if (instance_info.surface == VK_NULL_HANDLE)
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_surface_provided }; return detail::Result<std::vector<PhysicalDevice>>{ PhysicalDeviceError::no_surface_provided };
} }
std::vector<VkPhysicalDevice> physical_devices; // Get the VkPhysicalDevice handles on the system
std::vector<VkPhysicalDevice> vk_physical_devices;
auto physical_devices_ret = detail::get_vector<VkPhysicalDevice>( auto vk_physical_devices_ret = detail::get_vector<VkPhysicalDevice>(
physical_devices, detail::vulkan_functions().fp_vkEnumeratePhysicalDevices, instance_info.instance); vk_physical_devices, detail::vulkan_functions().fp_vkEnumeratePhysicalDevices, instance_info.instance);
if (physical_devices_ret != VK_SUCCESS) { if (vk_physical_devices_ret != VK_SUCCESS) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::failed_enumerate_physical_devices, return detail::Result<std::vector<PhysicalDevice>>{ PhysicalDeviceError::failed_enumerate_physical_devices,
physical_devices_ret }; vk_physical_devices_ret };
} }
if (physical_devices.size() == 0) { if (vk_physical_devices.size() == 0) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_physical_devices_found }; return detail::Result<std::vector<PhysicalDevice>>{ PhysicalDeviceError::no_physical_devices_found };
} }
std::vector<PhysicalDeviceDesc> phys_device_descriptions; auto fill_out_phys_dev_with_criteria = [&](PhysicalDevice& phys_dev) {
for (auto& phys_device : physical_devices) { phys_dev.features = criteria.required_features;
phys_device_descriptions.push_back(populate_device_details(phys_device, criteria.extended_features_chain)); phys_dev.extended_features_chain = criteria.extended_features_chain;
} phys_dev.extensions_to_enable.insert(
phys_dev.extensions_to_enable.end(), criteria.required_extensions.begin(), criteria.required_extensions.end());
PhysicalDeviceDesc selected_device{};
if (criteria.use_first_gpu_unconditionally) {
selected_device = phys_device_descriptions.at(0);
} else {
for (const auto& device : phys_device_descriptions) {
auto suitable = is_device_suitable(device);
if (suitable == Suitable::yes) {
selected_device = device;
break;
} else if (suitable == Suitable::partial) {
selected_device = device;
}
}
}
if (selected_device.phys_device == VK_NULL_HANDLE) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_suitable_device };
}
PhysicalDevice out_device{};
out_device.physical_device = selected_device.phys_device;
out_device.surface = instance_info.surface;
out_device.features = criteria.required_features;
out_device.extended_features_chain = criteria.extended_features_chain;
out_device.properties = selected_device.device_properties;
out_device.memory_properties = selected_device.mem_properties;
out_device.queue_families = selected_device.queue_families;
out_device.defer_surface_initialization = criteria.defer_surface_initialization;
out_device.instance_version = instance_info.version;
out_device.extensions_to_enable.insert(out_device.extensions_to_enable.end(),
criteria.required_extensions.begin(),
criteria.required_extensions.end());
auto desired_extensions_supported = auto desired_extensions_supported =
detail::check_device_extension_support(out_device.physical_device, criteria.desired_extensions); detail::check_device_extension_support(phys_dev.physical_device, criteria.desired_extensions);
out_device.extensions_to_enable.insert(out_device.extensions_to_enable.end(), phys_dev.extensions_to_enable.insert(
desired_extensions_supported.begin(), phys_dev.extensions_to_enable.end(), desired_extensions_supported.begin(), desired_extensions_supported.end());
desired_extensions_supported.end()); };
return out_device;
// if this option is set, always return only the first physical device found
if (criteria.use_first_gpu_unconditionally && vk_physical_devices.size() > 0) {
PhysicalDevice physical_device = populate_device_details(vk_physical_devices[0], criteria.extended_features_chain);
fill_out_phys_dev_with_criteria(physical_device);
return std::vector<PhysicalDevice>{ physical_device };
}
// Populate their details and check their suitability
std::vector<PhysicalDevice> physical_devices;
for (auto& vk_physical_device : vk_physical_devices) {
PhysicalDevice phys_dev = populate_device_details(vk_physical_device, criteria.extended_features_chain);
phys_dev.suitable = is_device_suitable(phys_dev);
if (phys_dev.suitable != PhysicalDevice::Suitable::no) {
physical_devices.push_back(phys_dev);
}
}
// sort the list into fully and partially suitable devices. use stable_partition to maintain relative order
const auto partition_index = std::stable_partition(physical_devices.begin(), physical_devices.end(), [](auto const& pd) {
return pd.suitable == PhysicalDevice::Suitable::yes;
});
// Remove the partially suitable elements if they aren't desired
if (selection == DeviceSelectionMode::only_fully_suitable) {
physical_devices.erase(partition_index, physical_devices.end() - 1);
}
return physical_devices;
} }
detail::Result<PhysicalDevice> PhysicalDeviceSelector::select(DeviceSelectionMode selection) const {
auto const selected_devices = select_impl(selection);
if (!selected_devices) return detail::Result<PhysicalDevice>{ selected_devices.error() };
if (selected_devices.value().size() == 0) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_suitable_device };
}
return selected_devices.value().at(0);
}
// Return all devices which are considered suitable - intended for applications which want to let the user pick the physical device
detail::Result<std::vector<PhysicalDevice>> PhysicalDeviceSelector::select_devices(DeviceSelectionMode selection) const {
auto const selected_devices = select_impl(selection);
if (!selected_devices) return detail::Result<std::vector<PhysicalDevice>>{ selected_devices.error() };
if (selected_devices.value().size() == 0) {
return detail::Result<std::vector<PhysicalDevice>>{ PhysicalDeviceError::no_suitable_device };
}
return selected_devices.value();
}
detail::Result<std::vector<std::string>> PhysicalDeviceSelector::select_device_names(DeviceSelectionMode selection) const {
auto const selected_devices = select_impl(selection);
if (!selected_devices) return detail::Result<std::vector<std::string>>{ selected_devices.error() };
if (selected_devices.value().size() == 0) {
return detail::Result<std::vector<std::string>>{ PhysicalDeviceError::no_suitable_device };
}
std::vector<std::string> names;
for (const auto& pd : selected_devices.value()) {
names.push_back(pd.name);
}
return names;
}
PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface(VkSurfaceKHR surface) { PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface(VkSurfaceKHR surface) {
instance_info.surface = surface; instance_info.surface = surface;
instance_info.headless = false; return *this;
}
PhysicalDeviceSelector& PhysicalDeviceSelector::set_name(std::string const& name) {
criteria.name = name;
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type(PreferredDeviceType type) { PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type(PreferredDeviceType type) {
@ -1306,8 +1298,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extension(const cha
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extensions(std::vector<const char*> extensions) { PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extensions(std::vector<const char*> extensions) {
criteria.required_extensions.insert( criteria.required_extensions.insert(criteria.required_extensions.end(), extensions.begin(), extensions.end());
criteria.required_extensions.end(), extensions.begin(), extensions.end());
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension(const char* extension) { PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension(const char* extension) {
@ -1315,8 +1306,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension(const char
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extensions(std::vector<const char*> extensions) { PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extensions(std::vector<const char*> extensions) {
criteria.desired_extensions.insert( criteria.desired_extensions.insert(criteria.desired_extensions.end(), extensions.begin(), extensions.end());
criteria.desired_extensions.end(), extensions.begin(), extensions.end());
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::set_minimum_version(uint32_t major, uint32_t minor) { PhysicalDeviceSelector& PhysicalDeviceSelector::set_minimum_version(uint32_t major, uint32_t minor) {
@ -1333,22 +1323,19 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysical
} }
#if defined(VKB_VK_API_VERSION_1_2) #if defined(VKB_VK_API_VERSION_1_2)
// Just calls add_required_features // Just calls add_required_features
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_11( PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_11(VkPhysicalDeviceVulkan11Features features_11) {
VkPhysicalDeviceVulkan11Features features_11) {
features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
add_required_extension_features(features_11); add_required_extension_features(features_11);
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_12( PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_12(VkPhysicalDeviceVulkan12Features features_12) {
VkPhysicalDeviceVulkan12Features features_12) {
features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
add_required_extension_features(features_12); add_required_extension_features(features_12);
return *this; return *this;
} }
#endif #endif
#if defined(VKB_VK_API_VERSION_1_3) #if defined(VKB_VK_API_VERSION_1_3)
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_13( PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features_13(VkPhysicalDeviceVulkan13Features features_13) {
VkPhysicalDeviceVulkan13Features features_13) {
features_13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; features_13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
add_required_extension_features(features_13); add_required_extension_features(features_13);
return *this; return *this;
@ -1363,25 +1350,20 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditiona
return *this; return *this;
} }
// 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) != return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
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) != return detail::get_separate_queue_index(queue_families, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_TRANSFER_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
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) != return detail::get_dedicated_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
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) != return detail::get_separate_queue_index(queue_families, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_COMPUTE_BIT) != detail::QUEUE_INDEX_MAX_VALUE;
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<const char*> PhysicalDevice::get_extensions() const { return extensions_to_enable; } std::vector<const char*> PhysicalDevice::get_extensions() const { return extensions_to_enable; }
PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; } PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; }
@ -1471,8 +1453,7 @@ DeviceBuilder::DeviceBuilder(PhysicalDevice phys_device) { physical_device = phy
detail::Result<Device> DeviceBuilder::build() const { detail::Result<Device> DeviceBuilder::build() const {
std::vector<CustomQueueDescription> queue_descriptions; std::vector<CustomQueueDescription> queue_descriptions;
queue_descriptions.insert( queue_descriptions.insert(queue_descriptions.end(), info.queue_descriptions.begin(), info.queue_descriptions.end());
queue_descriptions.end(), info.queue_descriptions.begin(), info.queue_descriptions.end());
if (queue_descriptions.size() == 0) { if (queue_descriptions.size() == 0) {
for (uint32_t i = 0; i < physical_device.queue_families.size(); i++) { for (uint32_t i = 0; i < physical_device.queue_families.size(); i++) {
@ -1560,10 +1541,8 @@ detail::Result<Device> DeviceBuilder::build() const {
device.queue_families = physical_device.queue_families; device.queue_families = physical_device.queue_families;
device.allocation_callbacks = info.allocation_callbacks; device.allocation_callbacks = info.allocation_callbacks;
device.fp_vkGetDeviceProcAddr = detail::vulkan_functions().fp_vkGetDeviceProcAddr; device.fp_vkGetDeviceProcAddr = detail::vulkan_functions().fp_vkGetDeviceProcAddr;
detail::vulkan_functions().get_device_proc_addr( detail::vulkan_functions().get_device_proc_addr(device.device, device.internal_table.fp_vkGetDeviceQueue, "vkGetDeviceQueue");
device.device, device.internal_table.fp_vkGetDeviceQueue, "vkGetDeviceQueue"); detail::vulkan_functions().get_device_proc_addr(device.device, device.internal_table.fp_vkDestroyDevice, "vkDestroyDevice");
detail::vulkan_functions().get_device_proc_addr(
device.device, device.internal_table.fp_vkDestroyDevice, "vkDestroyDevice");
return device; return device;
} }
DeviceBuilder& DeviceBuilder::custom_queue_setup(std::vector<CustomQueueDescription> queue_descriptions) { DeviceBuilder& DeviceBuilder::custom_queue_setup(std::vector<CustomQueueDescription> queue_descriptions) {
@ -1618,8 +1597,7 @@ Result<SurfaceSupportDetails> query_surface_support_details(VkPhysicalDevice phy
if (surface == VK_NULL_HANDLE) return make_error_code(SurfaceSupportError::surface_handle_null); if (surface == VK_NULL_HANDLE) return make_error_code(SurfaceSupportError::surface_handle_null);
VkSurfaceCapabilitiesKHR capabilities; VkSurfaceCapabilitiesKHR capabilities;
VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( VkResult res = detail::vulkan_functions().fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface, &capabilities);
phys_device, surface, &capabilities);
if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
return { make_error_code(SurfaceSupportError::failed_get_surface_capabilities), res }; return { make_error_code(SurfaceSupportError::failed_get_surface_capabilities), res };
} }
@ -1646,13 +1624,10 @@ VkSurfaceFormatKHR find_surface_format(VkPhysicalDevice phys_device,
for (auto const& desired_format : desired_formats) { for (auto const& desired_format : desired_formats) {
for (auto const& available_format : available_formats) { for (auto const& available_format : available_formats) {
// finds the first format that is desired and available // finds the first format that is desired and available
if (desired_format.format == available_format.format && if (desired_format.format == available_format.format && desired_format.colorSpace == available_format.colorSpace) {
desired_format.colorSpace == available_format.colorSpace) {
VkFormatProperties properties; VkFormatProperties properties;
detail::vulkan_functions().fp_vkGetPhysicalDeviceFormatProperties( detail::vulkan_functions().fp_vkGetPhysicalDeviceFormatProperties(phys_device, desired_format.format, &properties);
phys_device, desired_format.format, &properties); if ((properties.optimalTilingFeatures & feature_flags) == feature_flags) return desired_format;
if ((properties.optimalTilingFeatures & feature_flags) == feature_flags)
return desired_format;
} }
} }
} }
@ -1682,10 +1657,10 @@ VkExtent2D find_extent(VkSurfaceCapabilitiesKHR const& capabilities, uint32_t de
} else { } else {
VkExtent2D actualExtent = { desired_width, desired_height }; VkExtent2D actualExtent = { desired_width, desired_height };
actualExtent.width = maximum(capabilities.minImageExtent.width, actualExtent.width =
minimum(capabilities.maxImageExtent.width, actualExtent.width)); maximum(capabilities.minImageExtent.width, minimum(capabilities.maxImageExtent.width, actualExtent.width));
actualExtent.height = maximum(capabilities.minImageExtent.height, actualExtent.height =
minimum(capabilities.maxImageExtent.height, actualExtent.height)); maximum(capabilities.minImageExtent.height, minimum(capabilities.maxImageExtent.height, actualExtent.height));
return actualExtent; return actualExtent;
} }
@ -1694,8 +1669,7 @@ VkExtent2D find_extent(VkSurfaceCapabilitiesKHR const& capabilities, uint32_t de
void destroy_swapchain(Swapchain const& swapchain) { void destroy_swapchain(Swapchain const& swapchain) {
if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE) { if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE) {
swapchain.internal_table.fp_vkDestroySwapchainKHR( swapchain.internal_table.fp_vkDestroySwapchainKHR(swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks);
swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks);
} }
} }
@ -1754,19 +1728,17 @@ detail::Result<Swapchain> SwapchainBuilder::build() const {
auto surface_support_ret = detail::query_surface_support_details(info.physical_device, info.surface); auto surface_support_ret = detail::query_surface_support_details(info.physical_device, info.surface);
if (!surface_support_ret.has_value()) if (!surface_support_ret.has_value())
return detail::Error{ SwapchainError::failed_query_surface_support_details, return detail::Error{ SwapchainError::failed_query_surface_support_details, surface_support_ret.vk_result() };
surface_support_ret.vk_result() };
auto surface_support = surface_support_ret.value(); auto surface_support = surface_support_ret.value();
uint32_t image_count = surface_support.capabilities.minImageCount + 1; uint32_t image_count = surface_support.capabilities.minImageCount + 1;
if (surface_support.capabilities.maxImageCount > 0 && image_count > surface_support.capabilities.maxImageCount) { if (surface_support.capabilities.maxImageCount > 0 && image_count > surface_support.capabilities.maxImageCount) {
image_count = surface_support.capabilities.maxImageCount; image_count = surface_support.capabilities.maxImageCount;
} }
VkSurfaceFormatKHR surface_format = detail::find_surface_format( VkSurfaceFormatKHR surface_format =
info.physical_device, surface_support.formats, desired_formats, info.format_feature_flags); detail::find_surface_format(info.physical_device, surface_support.formats, desired_formats, info.format_feature_flags);
VkExtent2D extent = VkExtent2D extent = detail::find_extent(surface_support.capabilities, info.desired_width, info.desired_height);
detail::find_extent(surface_support.capabilities, info.desired_width, info.desired_height);
uint32_t image_array_layers = info.array_layer_count; uint32_t image_array_layers = info.array_layer_count;
if (surface_support.capabilities.maxImageArrayLayers < info.array_layer_count) if (surface_support.capabilities.maxImageArrayLayers < info.array_layer_count)
@ -1776,8 +1748,7 @@ detail::Result<Swapchain> SwapchainBuilder::build() const {
uint32_t queue_family_indices[] = { info.graphics_queue_index, info.present_queue_index }; uint32_t queue_family_indices[] = { info.graphics_queue_index, info.present_queue_index };
VkPresentModeKHR present_mode = VkPresentModeKHR present_mode = detail::find_present_mode(surface_support.present_modes, desired_present_modes);
detail::find_present_mode(surface_support.present_modes, desired_present_modes);
VkSurfaceTransformFlagBitsKHR pre_transform = info.pre_transform; VkSurfaceTransformFlagBitsKHR pre_transform = info.pre_transform;
if (info.pre_transform == static_cast<VkSurfaceTransformFlagBitsKHR>(0)) if (info.pre_transform == static_cast<VkSurfaceTransformFlagBitsKHR>(0))
@ -1816,8 +1787,7 @@ detail::Result<Swapchain> SwapchainBuilder::build() const {
Swapchain swapchain{}; Swapchain swapchain{};
PFN_vkCreateSwapchainKHR swapchain_create_proc; PFN_vkCreateSwapchainKHR swapchain_create_proc;
detail::vulkan_functions().get_device_proc_addr(info.device, swapchain_create_proc, "vkCreateSwapchainKHR"); detail::vulkan_functions().get_device_proc_addr(info.device, swapchain_create_proc, "vkCreateSwapchainKHR");
auto res = swapchain_create_proc( auto res = swapchain_create_proc(info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain);
info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain);
if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
return detail::Error{ SwapchainError::failed_create_swapchain, res }; return detail::Error{ SwapchainError::failed_create_swapchain, res };
@ -1827,10 +1797,8 @@ detail::Result<Swapchain> SwapchainBuilder::build() const {
swapchain.extent = extent; swapchain.extent = extent;
detail::vulkan_functions().get_device_proc_addr( detail::vulkan_functions().get_device_proc_addr(
info.device, swapchain.internal_table.fp_vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR"); info.device, swapchain.internal_table.fp_vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR");
detail::vulkan_functions().get_device_proc_addr( detail::vulkan_functions().get_device_proc_addr(info.device, swapchain.internal_table.fp_vkCreateImageView, "vkCreateImageView");
info.device, swapchain.internal_table.fp_vkCreateImageView, "vkCreateImageView"); detail::vulkan_functions().get_device_proc_addr(info.device, swapchain.internal_table.fp_vkDestroyImageView, "vkDestroyImageView");
detail::vulkan_functions().get_device_proc_addr(
info.device, swapchain.internal_table.fp_vkDestroyImageView, "vkDestroyImageView");
detail::vulkan_functions().get_device_proc_addr( detail::vulkan_functions().get_device_proc_addr(
info.device, swapchain.internal_table.fp_vkDestroySwapchainKHR, "vkDestroySwapchainKHR"); info.device, swapchain.internal_table.fp_vkDestroySwapchainKHR, "vkDestroySwapchainKHR");
auto images = swapchain.get_images(); auto images = swapchain.get_images();
@ -1844,8 +1812,8 @@ detail::Result<Swapchain> SwapchainBuilder::build() const {
detail::Result<std::vector<VkImage>> Swapchain::get_images() { detail::Result<std::vector<VkImage>> Swapchain::get_images() {
std::vector<VkImage> swapchain_images; std::vector<VkImage> swapchain_images;
auto swapchain_images_ret = detail::get_vector<VkImage>( auto swapchain_images_ret =
swapchain_images, internal_table.fp_vkGetSwapchainImagesKHR, device, swapchain); detail::get_vector<VkImage>(swapchain_images, internal_table.fp_vkGetSwapchainImagesKHR, device, swapchain);
if (swapchain_images_ret != VK_SUCCESS) { if (swapchain_images_ret != VK_SUCCESS) {
return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret }; return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret };
} }
@ -1875,10 +1843,8 @@ detail::Result<std::vector<VkImageView>> Swapchain::get_image_views() {
createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1; createInfo.subresourceRange.layerCount = 1;
VkResult res = VkResult res = internal_table.fp_vkCreateImageView(device, &createInfo, allocation_callbacks, &views[i]);
internal_table.fp_vkCreateImageView(device, &createInfo, allocation_callbacks, &views[i]); if (res != VK_SUCCESS) return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res };
if (res != VK_SUCCESS)
return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res };
} }
return views; return views;
} }

View File

@ -21,6 +21,7 @@
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include <string>
#include <system_error> #include <system_error>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
@ -28,25 +29,25 @@
#include "VkBootstrapDispatch.h" #include "VkBootstrapDispatch.h"
#ifdef VK_MAKE_API_VERSION #ifdef VK_MAKE_API_VERSION
#define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_API_VERSION(variant, major, minor, patch) #define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_API_VERSION(variant, major, minor, patch)
#elif defined(VK_MAKE_VERSION) #elif defined(VK_MAKE_VERSION)
#define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch) #define VKB_MAKE_VK_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch)
#endif #endif
#if defined(VK_API_VERSION_1_3) || defined(VK_VERSION_1_3) #if defined(VK_API_VERSION_1_3) || defined(VK_VERSION_1_3)
#define VKB_VK_API_VERSION_1_3 VKB_MAKE_VK_VERSION(0, 1, 3, 0) #define VKB_VK_API_VERSION_1_3 VKB_MAKE_VK_VERSION(0, 1, 3, 0)
#endif #endif
#if defined(VK_API_VERSION_1_2) || defined(VK_VERSION_1_2) #if defined(VK_API_VERSION_1_2) || defined(VK_VERSION_1_2)
#define VKB_VK_API_VERSION_1_2 VKB_MAKE_VK_VERSION(0, 1, 2, 0) #define VKB_VK_API_VERSION_1_2 VKB_MAKE_VK_VERSION(0, 1, 2, 0)
#endif #endif
#if defined(VK_API_VERSION_1_1) || defined(VK_VERSION_1_1) #if defined(VK_API_VERSION_1_1) || defined(VK_VERSION_1_1)
#define VKB_VK_API_VERSION_1_1 VKB_MAKE_VK_VERSION(0, 1, 1, 0) #define VKB_VK_API_VERSION_1_1 VKB_MAKE_VK_VERSION(0, 1, 1, 0)
#endif #endif
#if defined(VK_API_VERSION_1_0) || defined(VK_VERSION_1_0) #if defined(VK_API_VERSION_1_0) || defined(VK_VERSION_1_0)
#define VKB_VK_API_VERSION_1_0 VKB_MAKE_VK_VERSION(0, 1, 0, 0) #define VKB_VK_API_VERSION_1_0 VKB_MAKE_VK_VERSION(0, 1, 0, 0)
#endif #endif
namespace vkb { namespace vkb {
@ -65,8 +66,7 @@ template <typename T> class Result {
Result(Error error) : m_error{ error }, m_init{ false } {} Result(Error error) : m_error{ error }, m_init{ false } {}
Result(std::error_code error_code, VkResult result = VK_SUCCESS) Result(std::error_code error_code, VkResult result = VK_SUCCESS) : m_error{ error_code, result }, m_init{ false } {}
: m_error{ error_code, result }, m_init{ false } {}
~Result() { destroy(); } ~Result() { destroy(); }
Result(Result const& expected) : m_init(expected.m_init) { Result(Result const& expected) : m_init(expected.m_init) {
@ -423,8 +423,8 @@ class InstanceBuilder {
PFN_vkDebugUtilsMessengerCallbackEXT debug_callback = default_debug_callback; PFN_vkDebugUtilsMessengerCallbackEXT debug_callback = default_debug_callback;
VkDebugUtilsMessageSeverityFlagsEXT debug_message_severity = VkDebugUtilsMessageSeverityFlagsEXT debug_message_severity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
VkDebugUtilsMessageTypeFlagsEXT debug_message_type = VkDebugUtilsMessageTypeFlagsEXT debug_message_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
void* debug_user_data_pointer = nullptr; void* debug_user_data_pointer = nullptr;
@ -450,15 +450,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback(VkDebugUtilsMessageSeverit
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData); void* pUserData);
void destroy_debug_utils_messenger(VkInstance const instance, void destroy_debug_utils_messenger(
VkDebugUtilsMessengerEXT const messenger, VkInstance const instance, VkDebugUtilsMessengerEXT const messenger, VkAllocationCallbacks* allocation_callbacks = nullptr);
VkAllocationCallbacks* allocation_callbacks = nullptr);
// ---- Physical Device ---- // // ---- Physical Device ---- //
class PhysicalDeviceSelector; class PhysicalDeviceSelector;
class DeviceBuilder; class DeviceBuilder;
struct PhysicalDevice { struct PhysicalDevice {
std::string name;
VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE;
@ -492,28 +492,55 @@ struct PhysicalDevice {
std::vector<const char*> extensions_to_enable; std::vector<const char*> extensions_to_enable;
std::vector<VkQueueFamilyProperties> queue_families; std::vector<VkQueueFamilyProperties> queue_families;
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain; std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
#if defined(VKB_VK_API_VERSION_1_1)
VkPhysicalDeviceFeatures2 features2{};
#else
VkPhysicalDeviceFeatures2KHR features2{};
#endif
bool defer_surface_initialization = false; bool defer_surface_initialization = false;
enum class Suitable { yes, partial, no };
Suitable suitable = Suitable::yes;
friend class PhysicalDeviceSelector; friend class PhysicalDeviceSelector;
friend class DeviceBuilder; friend class DeviceBuilder;
}; };
enum class PreferredDeviceType { enum class PreferredDeviceType { other = 0, integrated = 1, discrete = 2, virtual_gpu = 3, cpu = 4 };
other = 0,
integrated = 1, enum class DeviceSelectionMode {
discrete = 2, // return all suitable and partially suitable devices
virtual_gpu = 3, partially_and_fully_suitable,
cpu = 4 // return only physical devices which are fully suitable
only_fully_suitable
}; };
// Enumerates the physical devices on the system, and based on the added criteria, returns a physical device or list of physical devies
// A device is considered suitable if it meets all the 'required' and 'desired' criteria.
// A device is considered partially suitable if it meets only the 'required' criteria.
class PhysicalDeviceSelector { class PhysicalDeviceSelector {
public: public:
// Requires a vkb::Instance to construct, needed to pass instance creation info. // Requires a vkb::Instance to construct, needed to pass instance creation info.
explicit PhysicalDeviceSelector(Instance const& instance); explicit PhysicalDeviceSelector(Instance const& instance);
// Requires a vkb::Instance to construct, needed to pass instance creation info, optionally specify the surface here
explicit PhysicalDeviceSelector(Instance const& instance, VkSurfaceKHR surface);
detail::Result<PhysicalDevice> select() const; // Return the first device which is suitable
// use the `selection` parameter to configure if partially
detail::Result<PhysicalDevice> select(DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const;
// Return all devices which are considered suitable - intended for applications which want to let the user pick the physical device
detail::Result<std::vector<PhysicalDevice>> select_devices(
DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const;
// Return the names of all devices which are considered suitable - intended for applications which want to let the user pick the physical device
detail::Result<std::vector<std::string>> select_device_names(
DeviceSelectionMode selection = DeviceSelectionMode::partially_and_fully_suitable) const;
// Set the surface in which the physical device should render to. // Set the surface in which the physical device should render to.
// Be sure to set it if swapchain functionality is to be used.
PhysicalDeviceSelector& set_surface(VkSurfaceKHR surface); PhysicalDeviceSelector& set_surface(VkSurfaceKHR surface);
// Set the name of the device to select.
PhysicalDeviceSelector& set_name(std::string const& name);
// Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete. // Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete.
PhysicalDeviceSelector& prefer_gpu_device_type(PreferredDeviceType type = PreferredDeviceType::discrete); PhysicalDeviceSelector& prefer_gpu_device_type(PreferredDeviceType type = PreferredDeviceType::discrete);
// Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true. // Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true.
@ -555,8 +582,7 @@ class PhysicalDeviceSelector {
// Require a physical device which supports a specific set of general/extension features. // Require a physical device which supports a specific set of general/extension features.
#if defined(VKB_VK_API_VERSION_1_1) #if defined(VKB_VK_API_VERSION_1_1)
template <typename T> template <typename T> PhysicalDeviceSelector& add_required_extension_features(T const& features) {
PhysicalDeviceSelector& add_required_extension_features(T const& features) {
criteria.extended_features_chain.push_back(features); criteria.extended_features_chain.push_back(features);
return *this; return *this;
} }
@ -614,10 +640,8 @@ class PhysicalDeviceSelector {
// We copy the extension features stored in the selector criteria under the prose of a // We copy the extension features stored in the selector criteria under the prose of a
// "template" to ensure that after fetching everything is compared 1:1 during a match. // "template" to ensure that after fetching everything is compared 1:1 during a match.
PhysicalDeviceDesc populate_device_details(VkPhysicalDevice phys_device,
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const;
struct SelectionCriteria { struct SelectionCriteria {
std::string name;
PreferredDeviceType preferred_type = PreferredDeviceType::discrete; PreferredDeviceType preferred_type = PreferredDeviceType::discrete;
bool allow_any_type = true; bool allow_any_type = true;
bool require_present = true; bool require_present = true;
@ -643,9 +667,12 @@ class PhysicalDeviceSelector {
bool use_first_gpu_unconditionally = false; bool use_first_gpu_unconditionally = false;
} criteria; } criteria;
enum class Suitable { yes, partial, no }; PhysicalDevice populate_device_details(VkPhysicalDevice phys_device,
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const;
Suitable is_device_suitable(PhysicalDeviceDesc phys_device) const; PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const;
detail::Result<std::vector<PhysicalDevice>> select_impl(DeviceSelectionMode selection) const;
}; };
// ---- Queue ---- // // ---- Queue ---- //

View File

@ -204,6 +204,31 @@ TEST_CASE("Device Configuration", "[VkBootstrap.bootstrap]") {
vkb::destroy_instance(instance); vkb::destroy_instance(instance);
} }
TEST_CASE("Select all Physical Devices", "[VkBootstrap.bootstrap]") {
auto window = create_window_glfw("Select all Physical Devices");
auto instance = get_instance(1);
auto surface = create_surface_glfw(instance.instance, window);
vkb::PhysicalDeviceSelector phys_device_selector(instance, surface);
auto phys_device_ret = phys_device_selector.select_devices();
REQUIRE(phys_device_ret.has_value());
auto phys_devices = phys_device_ret.value();
REQUIRE(phys_devices.at(0).name.size() > 0);
auto phys_device_names_ret = phys_device_selector.select_device_names();
REQUIRE(phys_device_names_ret.has_value());
REQUIRE(phys_device_names_ret.value().at(0).size() > 0);
vkb::DeviceBuilder device_builder(phys_devices.at(0));
auto device_ret = device_builder.build();
REQUIRE(device_ret.has_value());
auto dispatch_table = device_ret.value().make_table();
}
TEST_CASE("Loading Dispatch Table", "[VkBootstrap.bootstrap]") { TEST_CASE("Loading Dispatch Table", "[VkBootstrap.bootstrap]") {
auto instance = get_headless_instance(0); auto instance = get_headless_instance(0);
{ {