From d6d63121e930893480eb78dbe2f2bd1f72c0266c Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Thu, 27 May 2021 10:05:38 -0600 Subject: [PATCH] Add VulkanFeatureConfig, mechanism to select pre-made configs This makes it easy to enable a set of features, extensions, and a Vulkan Version that are pre-defined. --- src/VkBootstrap.cpp | 9 ++ src/VkBootstrap.h | 6 ++ src/VulkanFeatureConfig.h | 205 ++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 2 +- tests/common.h | 12 ++- tests/feature_config.cpp | 35 +++++++ 6 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 src/VulkanFeatureConfig.h create mode 100644 tests/feature_config.cpp diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 406c909..3a62a69 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -1227,6 +1227,15 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::allow_any_gpu_device_type(bool a criteria.allow_any_type = allow_any_type; return *this; } +PhysicalDeviceSelector& PhysicalDeviceSelector::set_vulkan_feature_config(VulkanFeatureConfig& feature_config) { + set_minimum_version(feature_config.api_major_version, feature_config.api_minor_version); + require_present(feature_config.require_presentation); + add_required_extensions(feature_config.required_extensions); + set_required_features(feature_config.features_1_0); + set_required_features_11(feature_config.features_1_1); + set_required_features_12(feature_config.features_1_2); + return *this; +} PhysicalDeviceSelector& PhysicalDeviceSelector::require_present(bool require) { criteria.require_present = require; return *this; diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index dc065bc..e95ff5f 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -25,6 +25,7 @@ #include #include "VkBootstrapDispatch.h" +#include "VulkanFeatureConfig.h" namespace vkb { @@ -444,6 +445,11 @@ class PhysicalDeviceSelector { // Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true. PhysicalDeviceSelector& allow_any_gpu_device_type(bool allow_any_type = true); + // Sets the "Vulkan Feature Config" to use when selecting a VkPhysicalDevice. + // A Feature Config is a pre defined collection of extensions, features, and properties that are + // required to be supported. For more information, go to `VulkanFeatureConfig.h` + PhysicalDeviceSelector& set_vulkan_feature_config(VulkanFeatureConfig& feature_config); + // Require that a physical device supports presentation. Defaults to true. PhysicalDeviceSelector& require_present(bool require = true); diff --git a/src/VulkanFeatureConfig.h b/src/VulkanFeatureConfig.h new file mode 100644 index 0000000..74903d1 --- /dev/null +++ b/src/VulkanFeatureConfig.h @@ -0,0 +1,205 @@ +#pragma once + +#include + +/* +The VulkanFeatureConfig mechanism of vk-bootstrap provides an easy way to select a VkPhysicalDevice based off of list of pre-made +configurations that specify the required version, features, extensions, and properties. +*/ + + +namespace vkb { + +namespace detail { + +struct StructureWrapper { + template StructureWrapper(T const& structure) noexcept { + T* ptr = new T(); + *ptr = structure; + pStructure = reinterpret_cast(ptr); + } + ~StructureWrapper() noexcept { delete pStructure; } + StructureWrapper(StructureWrapper const&) = delete; + StructureWrapper& operator=(StructureWrapper const&) = delete; + StructureWrapper(StructureWrapper&& other) noexcept : pStructure(other.pStructure) { + other.pStructure = nullptr; + } + StructureWrapper& operator=(StructureWrapper&& other) noexcept { + delete pStructure; + pStructure = other.pStructure; + other.pStructure = nullptr; + return *this; + } + VkBaseOutStructure* pStructure = nullptr; +}; + +inline VkPhysicalDeviceFeatures get_common_1_0_features() { + VkPhysicalDeviceFeatures features{}; + features.fullDrawIndexUint32 = true; + features.imageCubeArray = true; + features.independentBlend = true; + features.geometryShader = true; + features.tessellationShader = true; + features.sampleRateShading = true; + features.dualSrcBlend = true; + features.logicOp = true; + features.multiDrawIndirect = true; + features.drawIndirectFirstInstance = true; + features.depthClamp = true; + features.depthBiasClamp = true; + features.fillModeNonSolid = true; + features.wideLines = true; + features.largePoints = true; + features.multiViewport = true; + features.samplerAnisotropy = true; + features.pipelineStatisticsQuery = true; + features.textureCompressionBC = true; + features.occlusionQueryPrecise = true; + features.vertexPipelineStoresAndAtomics = true; + features.fragmentStoresAndAtomics = true; + features.shaderTessellationAndGeometryPointSize = true; + features.shaderImageGatherExtended = true; + features.shaderStorageImageExtendedFormats = true; + features.shaderStorageImageWriteWithoutFormat = true; + features.shaderUniformBufferArrayDynamicIndexing = true; + features.shaderSampledImageArrayDynamicIndexing = true; + features.shaderStorageBufferArrayDynamicIndexing = true; + features.shaderStorageImageArrayDynamicIndexing = true; + features.shaderClipDistance = true; + features.shaderCullDistance = true; + features.variableMultisampleRate = true; + return features; +} + +inline VkPhysicalDeviceVulkan11Features get_common_1_1_features() { + VkPhysicalDeviceVulkan11Features features; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + features.storageBuffer16BitAccess = true; + features.uniformAndStorageBuffer16BitAccess = true; + features.multiview = true; + features.multiviewTessellationShader = true; + features.variablePointersStorageBuffer = true; + features.variablePointers = true; + features.shaderDrawParameters = true; + return features; +} + +inline VkPhysicalDeviceVulkan12Features get_common_1_2_features() { + VkPhysicalDeviceVulkan12Features features{}; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features.samplerMirrorClampToEdge = true; + features.drawIndirectCount = true; + features.descriptorIndexing = true; + features.scalarBlockLayout = true; + features.imagelessFramebuffer = true; + features.uniformBufferStandardLayout = true; + features.shaderSubgroupExtendedTypes = true; + features.separateDepthStencilLayouts = true; + features.hostQueryReset = true; + features.timelineSemaphore = true; + features.bufferDeviceAddress = true; + features.subgroupBroadcastDynamicId = true; + return features; +} +} // namespace detail + +/* +Stores all the possible versions, features, extensions, and properties for a VulkanFeatureConfig +*/ +struct VulkanFeatureConfig { + VulkanFeatureConfig() { + features_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + } + uint32_t api_major_version = 1; + uint32_t api_minor_version = 0; + bool require_presentation = true; + + std::vector required_extensions; + + VkPhysicalDeviceFeatures features_1_0{}; + VkPhysicalDeviceVulkan11Features features_1_1{}; + VkPhysicalDeviceVulkan12Features features_1_2{}; + + VkPhysicalDeviceProperties properties_1_0{}; +}; + +/* +Configuration for commonly available features in Vulkan 1.0 devices +*/ + +static VulkanFeatureConfig& get_feature_config_vulkan_1_0_desktop() { + static VulkanFeatureConfig config{}; + config.features_1_0 = detail::get_common_1_0_features(); + return config; +} + +/* +Configuration for commonly available features in Vulkan 1.1 devices +*/ +static VulkanFeatureConfig& get_feature_config_vulkan_1_1_desktop() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 1; + config.features_1_0 = detail::get_common_1_0_features(); + config.features_1_1 = detail::get_common_1_1_features(); + return config; +} + +/* +Configuration for commonly available features in Vulkan 1.2 devices +*/ +static VulkanFeatureConfig& get_feature_config_vulkan_1_2_desktop() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 2; + config.features_1_0 = detail::get_common_1_0_features(); + config.features_1_1 = detail::get_common_1_1_features(); + config.features_1_2 = detail::get_common_1_2_features(); + return config; +} + +/* +Configuration that requests features available only found in the most recent hardware +*/ +static VulkanFeatureConfig& get_feature_config_bleeding_edge_desktop() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 2; + config.required_extensions.push_back("VK_KHR_deferred_host_operations"); + config.required_extensions.push_back("VK_KHR_acceleration_structure"); + config.required_extensions.push_back("VK_KHR_ray_query"); + config.required_extensions.push_back("VK_KHR_pipeline_library"); + config.required_extensions.push_back("VK_KHR_ray_tracing_pipeline"); + config.required_extensions.push_back("VK_KHR_fragment_shading_rate"); + config.features_1_0 = detail::get_common_1_0_features(); + config.features_1_1 = detail::get_common_1_1_features(); + config.features_1_2 = detail::get_common_1_2_features(); + return config; +} + +static VulkanFeatureConfig& get_feature_config_vulkan_1_0_mobile() { + static VulkanFeatureConfig config{}; + return config; +} + +static VulkanFeatureConfig& get_feature_config_vulkan_1_1_mobile() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 1; + return config; +} + +static VulkanFeatureConfig& get_feature_config_vulkan_1_2_mobile() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 2; + return config; +} + +static VulkanFeatureConfig& get_feature_config_virtual_reality_base() { + static VulkanFeatureConfig config{}; + config.api_minor_version = 1; + config.features_1_0 = detail::get_common_1_0_features(); + config.features_1_1 = detail::get_common_1_1_features(); + + + return config; +} + +} // namespace vkb \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5a58dbe..fd13912 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(vk-bootstrap-test main.cpp bootstrap_tests.cpp error_code_tests.cpp unit_tests.cpp) +add_executable(vk-bootstrap-test main.cpp bootstrap_tests.cpp error_code_tests.cpp unit_tests.cpp feature_config.cpp) target_link_libraries(vk-bootstrap-test PRIVATE vk-bootstrap diff --git a/tests/common.h b/tests/common.h index c8d0bdc..064cbf8 100644 --- a/tests/common.h +++ b/tests/common.h @@ -20,18 +20,18 @@ #include "../src/VkBootstrap.h" -GLFWwindow* create_window_glfw(const char* window_name = "", bool resize = true) { +inline GLFWwindow* create_window_glfw(const char* window_name = "", bool resize = true) { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); if (!resize) glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); return glfwCreateWindow(1024, 1024, window_name, NULL, NULL); } -void destroy_window_glfw(GLFWwindow* window) { +inline void destroy_window_glfw(GLFWwindow* window) { glfwDestroyWindow(window); glfwTerminate(); } -VkSurfaceKHR create_surface_glfw( +inline VkSurfaceKHR create_surface_glfw( VkInstance instance, GLFWwindow* window, VkAllocationCallbacks* allocator = nullptr) { VkSurfaceKHR surface = VK_NULL_HANDLE; VkResult err = glfwCreateWindowSurface(instance, window, allocator, &surface); @@ -48,6 +48,12 @@ VkSurfaceKHR create_surface_glfw( return surface; } +inline void destroy_surface(vkb::Instance& instance_ret, VkSurfaceKHR surface) { + PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = reinterpret_cast( + instance_ret.fp_vkGetInstanceProcAddr(instance_ret.instance, "vkDestroySurfaceKHR")); + fp_vkDestroySurfaceKHR(instance_ret.instance, surface, nullptr); +} + struct VulkanLibrary { #if defined(__linux__) || defined(__APPLE__) void* library; diff --git a/tests/feature_config.cpp b/tests/feature_config.cpp new file mode 100644 index 0000000..0c3e29d --- /dev/null +++ b/tests/feature_config.cpp @@ -0,0 +1,35 @@ +#include "common.h" + +#include + +TEST_CASE("TargetBaseVulkanDesktop", "[VkBootstrap.feature_config]") { + + auto window = create_window_glfw("Instance with surface"); + vkb::InstanceBuilder instance_builder; + auto instance_ret = instance_builder.use_default_debug_messenger().request_validation_layers().build(); + REQUIRE(instance_ret); + vkb::Instance instance = instance_ret.value(); + auto surface = create_surface_glfw(instance.instance, window); + GIVEN("A window and a vulkan instance") { + vkb::PhysicalDeviceSelector selector(instance); + auto phys_dev_ret = selector.set_surface(surface) + .set_vulkan_feature_config(vkb::get_feature_config_vulkan_1_0_desktop()) + .select(); + REQUIRE(phys_dev_ret); + + GIVEN("A physical device created with the TargetBaseVulkanDesktop") { + vkb::DeviceBuilder device_builder(phys_dev_ret.value()); + auto device_ret = device_builder.build(); + REQUIRE(device_ret); + vkb::Device device = device_ret.value(); + + // possible swapchain creation... + + vkb::destroy_device(device); + } + } + + destroy_surface(*instance_ret, surface); + vkb::destroy_instance(instance); + destroy_window_glfw(window); +} \ No newline at end of file