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
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 4

View File

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

View File

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

View File

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