diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/CMakeLists.txt b/CMakeLists.txt index f050e10..b48d576 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,6 @@ target_compile_options(vk-bootstrap-compiler-warnings target_include_directories(vk-bootstrap PUBLIC src) target_include_directories(vk-bootstrap PUBLIC ${Vulkan_INCLUDE_DIR}) target_link_libraries(vk-bootstrap - PUBLIC ${Vulkan_LIBRARY} PRIVATE vk-bootstrap-compiler-warnings) diff --git a/docs/getting_started.md b/docs/getting_started.md index a0c8563..21cac58 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -57,10 +57,15 @@ auto inst_builder_ret = instance_builder .build(); ``` -To query the available layers and extensions, use the `get_system_info()` function of `vkb::InstanceBuilder` to get a `SystemInfo` struct. It contains a `is_layer_available()` and `is_extension_available()` function to check for a layer or extensions before enabling it. It also has booleans for if the validation layers are present and if the VK_EXT_debug_utils extension is available. +To query the available layers and extensions, get a `SystemInfo` struct from `SystemInfo::get_system_info()`. It contains a `is_layer_available()` and `is_extension_available()` function to check for a layer or extensions before enabling it. It also has booleans for if the validation layers are present and if the VK_EXT_debug_utils extension is available. ```cpp -auto system_info = instance_builder.get_system_info(); +auto system_info_ret = vkb::SystemInfo.get_system_info(); +if (!system_info_ret) { + printf("%s\n", system_info_ret.error().message()); + return -1; +} +auto system_info = system_info_ret.value(); if (system_info.is_layer_available("VK_LAYER_LUNARG_api_dump")) { instance_builder.enable_layer("VK_LAYER_LUNARG_api_dump"); } diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index fe8e433..193f4d5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -3,7 +3,8 @@ target_link_libraries(vk-bootstrap-triangle PRIVATE glfw vk-bootstrap - vk-bootstrap-compiler-warnings) + vk-bootstrap-compiler-warnings + ${Vulkan_LIBRARY}) add_custom_command( TARGET vk-bootstrap-triangle diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 73e3b6f..a5cda71 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -16,18 +16,185 @@ #include "VkBootstrap.h" -#include -#include +#include +#include + +#if defined(_WIN32) +#include +#define NOMINMAX +#include +#endif // _WIN32 + +#if defined(__linux__) || defined(__APPLE__) +#include +#endif + +#include namespace vkb { namespace detail { -template -void get_inst_proc_addr ( - T& out_ptr, const char* func_name, VkInstance instance, PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr) { - out_ptr = reinterpret_cast (ptr_vkGetInstanceProcAddr (instance, func_name)); -} +class VulkanFunctions { + private: + std::mutex init_mutex; + struct VulkanLibrary { +#if defined(__linux__) || defined(__APPLE__) + void* library; +#elif defined(_WIN32) + HMODULE library; +#endif + PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = VK_NULL_HANDLE; + + VulkanLibrary () { +#if defined(__linux__) + library = dlopen ("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); + if (!library) library = dlopen ("libvulkan.so", RTLD_NOW | RTLD_LOCAL); +#elif defined(__APPLE__) + library = dlopen ("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); + if (!library) library = dlopen ("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); +#elif defined(_WIN32) + library = LoadLibrary (TEXT ("vulkan-1.dll")); +#else + assert (false && "Unsupported platform"); +#endif + if (!library) return; + load_func (ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr"); + } + + template void load_func (T& func_dest, const char* func_name) { +#if defined(__linux__) || defined(__APPLE__) + func_dest = reinterpret_cast (dlsym (library, func_name)); +#elif defined(_WIN32) + func_dest = reinterpret_cast (GetProcAddress (library, func_name)); +#endif + } + void close () { +#if defined(__linux__) || defined(__APPLE__) + dlclose (library); +#elif defined(_WIN32) + FreeLibrary (library); +#endif + library = 0; + } + }; + VulkanLibrary& get_vulkan_library () { + static VulkanLibrary lib; + return lib; + } + + bool load_vulkan (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr) { + if (fp_vkGetInstanceProcAddr != nullptr) { + ptr_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr; + return true; + } else { + auto& lib = get_vulkan_library (); + ptr_vkGetInstanceProcAddr = lib.ptr_vkGetInstanceProcAddr; + return lib.library != nullptr && lib.ptr_vkGetInstanceProcAddr != VK_NULL_HANDLE; + } + } + + template void get_proc_addr (T& out_ptr, const char* func_name) { + out_ptr = reinterpret_cast (ptr_vkGetInstanceProcAddr (instance, func_name)); + } + + void init_pre_instance_funcs () { + get_proc_addr (fp_vkEnumerateInstanceExtensionProperties, "vkEnumerateInstanceExtensionProperties"); + get_proc_addr (fp_vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties"); + get_proc_addr (fp_vkEnumerateInstanceVersion, "vkEnumerateInstanceVersion"); + get_proc_addr (fp_vkCreateInstance, "vkCreateInstance"); + } + + public: + PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = nullptr; + VkInstance instance = nullptr; + + PFN_vkEnumerateInstanceExtensionProperties fp_vkEnumerateInstanceExtensionProperties = nullptr; + PFN_vkEnumerateInstanceLayerProperties fp_vkEnumerateInstanceLayerProperties = nullptr; + PFN_vkEnumerateInstanceVersion fp_vkEnumerateInstanceVersion = nullptr; + PFN_vkCreateInstance fp_vkCreateInstance = nullptr; + PFN_vkDestroyInstance fp_vkDestroyInstance = nullptr; + + PFN_vkEnumeratePhysicalDevices fp_vkEnumeratePhysicalDevices = nullptr; + PFN_vkGetPhysicalDeviceFeatures fp_vkGetPhysicalDeviceFeatures = nullptr; + PFN_vkGetPhysicalDeviceFeatures2 fp_vkGetPhysicalDeviceFeatures2 = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties fp_vkGetPhysicalDeviceFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties fp_vkGetPhysicalDeviceImageFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceProperties fp_vkGetPhysicalDeviceProperties = nullptr; + PFN_vkGetPhysicalDeviceProperties2 fp_vkGetPhysicalDeviceProperties2 = nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties fp_vkGetPhysicalDeviceQueueFamilyProperties = nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fp_vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties fp_vkGetPhysicalDeviceMemoryProperties = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties2 fp_vkGetPhysicalDeviceFormatProperties2 = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties2 fp_vkGetPhysicalDeviceMemoryProperties2 = nullptr; + + PFN_vkCreateDevice fp_vkCreateDevice = nullptr; + PFN_vkDestroyDevice fp_vkDestroyDevice = nullptr; + PFN_vkEnumerateDeviceExtensionProperties fp_vkEnumerateDeviceExtensionProperties = nullptr; + PFN_vkGetDeviceQueue fp_vkGetDeviceQueue = nullptr; + + PFN_vkCreateImageView fp_vkCreateImageView = nullptr; + PFN_vkDestroyImageView fp_vkDestroyImageView = nullptr; + + PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fp_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fp_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkCreateSwapchainKHR fp_vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR fp_vkDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR fp_vkGetSwapchainImagesKHR = nullptr; + + bool init_vulkan_funcs (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { + std::lock_guard lg (init_mutex); + if (!load_vulkan (fp_vkGetInstanceProcAddr)) return false; + init_pre_instance_funcs (); + return true; + } + + template void get_inst_proc_addr (T& out_ptr, const char* func_name) { + std::lock_guard lg (init_mutex); + get_proc_addr (out_ptr, func_name); + } + + void init_instance_funcs (VkInstance inst) { + std::lock_guard lg (init_mutex); + + instance = inst; + get_proc_addr (fp_vkDestroyInstance, "vkDestroyInstance"); + get_proc_addr (fp_vkEnumeratePhysicalDevices, "vkEnumeratePhysicalDevices"); + get_proc_addr (fp_vkGetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures"); + get_proc_addr (fp_vkGetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2"); + get_proc_addr (fp_vkGetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties"); + get_proc_addr (fp_vkGetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties"); + get_proc_addr (fp_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); + get_proc_addr (fp_vkGetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2"); + get_proc_addr (fp_vkGetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties"); + get_proc_addr (fp_vkGetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2"); + get_proc_addr (fp_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); + get_proc_addr (fp_vkGetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2"); + get_proc_addr (fp_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2"); + + get_proc_addr (fp_vkCreateDevice, "vkCreateDevice"); + get_proc_addr (fp_vkDestroyDevice, "vkDestroyDevice"); + get_proc_addr (fp_vkEnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties"); + get_proc_addr (fp_vkGetDeviceQueue, "vkGetDeviceQueue"); + + get_proc_addr (fp_vkCreateImageView, "vkCreateImageView"); + get_proc_addr (fp_vkDestroyImageView, "vkDestroyImageView"); + + get_proc_addr (fp_vkDestroySurfaceKHR, "vkDestroySurfaceKHR"); + get_proc_addr (fp_vkGetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR"); + get_proc_addr (fp_vkGetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR"); + get_proc_addr (fp_vkGetPhysicalDeviceSurfacePresentModesKHR, "vkGetPhysicalDeviceSurfacePresentModesKHR"); + get_proc_addr (fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + get_proc_addr (fp_vkCreateSwapchainKHR, "vkCreateSwapchainKHR"); + get_proc_addr (fp_vkDestroySwapchainKHR, "vkDestroySwapchainKHR"); + get_proc_addr (fp_vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR"); + } +}; + +VulkanFunctions vk_functions; // Helper for robustly executing the two-call pattern template @@ -41,6 +208,7 @@ auto get_vector (std::vector& out, F&& f, Ts&&... ts) -> VkResult { }; out.resize (count); err = f (ts..., &count, out.data ()); + out.resize (count); } while (err == VK_INCOMPLETE); return err; } @@ -52,6 +220,7 @@ auto get_vector_noerror (F&& f, Ts&&... ts) -> std::vector { f (ts..., &count, nullptr); results.resize (count); f (ts..., &count, results.data ()); + results.resize (count); return results; } } // namespace detail @@ -97,8 +266,7 @@ VkResult create_debug_utils_messenger (VkInstance instance, messengerCreateInfo.pfnUserCallback = debug_callback; PFN_vkCreateDebugUtilsMessengerEXT createMessengerFunc; - detail::get_inst_proc_addr ( - createMessengerFunc, "vkCreateDebugUtilsMessengerEXT", instance, vkGetInstanceProcAddr); + detail::vk_functions.get_inst_proc_addr (createMessengerFunc, "vkCreateDebugUtilsMessengerEXT"); if (createMessengerFunc != nullptr) { return createMessengerFunc (instance, &messengerCreateInfo, allocation_callbacks, pDebugMessenger); @@ -109,9 +277,10 @@ VkResult create_debug_utils_messenger (VkInstance instance, void destroy_debug_utils_messenger ( VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, VkAllocationCallbacks* allocation_callbacks) { + PFN_vkDestroyDebugUtilsMessengerEXT deleteMessengerFunc; - detail::get_inst_proc_addr ( - deleteMessengerFunc, "vkDestroyDebugUtilsMessengerEXT", instance, vkGetInstanceProcAddr); + detail::vk_functions.get_inst_proc_addr (deleteMessengerFunc, "vkDestroyDebugUtilsMessengerEXT"); + if (deleteMessengerFunc != nullptr) { deleteMessengerFunc (instance, debugMessenger, allocation_callbacks); } @@ -320,9 +489,22 @@ const char* to_string (SwapchainError err) { } } +detail::Result SystemInfo::get_system_info () { + if (!detail::vk_functions.init_vulkan_funcs (nullptr)) { + return make_error_code (InstanceError::vulkan_unavailable); + } + return SystemInfo (); +} + +detail::Result SystemInfo::get_system_info (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { + // Using externally provided function pointers, assume the loader is available + detail::vk_functions.init_vulkan_funcs (fp_vkGetInstanceProcAddr); + return SystemInfo (); +} + SystemInfo::SystemInfo () { - auto available_layers_ret = - detail::get_vector (this->available_layers, vkEnumerateInstanceLayerProperties); + auto available_layers_ret = detail::get_vector ( + this->available_layers, detail::vk_functions.fp_vkEnumerateInstanceLayerProperties); if (available_layers_ret != VK_SUCCESS) { this->available_layers.clear (); } @@ -332,7 +514,7 @@ SystemInfo::SystemInfo () { validation_layers_available = true; auto available_extensions_ret = detail::get_vector ( - this->available_extensions, vkEnumerateInstanceExtensionProperties, nullptr); + this->available_extensions, detail::vk_functions.fp_vkEnumerateInstanceExtensionProperties, nullptr); if (available_extensions_ret != VK_SUCCESS) { this->available_extensions.clear (); } @@ -344,7 +526,7 @@ SystemInfo::SystemInfo () { for (auto& layer : this->available_layers) { std::vector layer_extensions; auto layer_extensions_ret = detail::get_vector ( - layer_extensions, vkEnumerateInstanceExtensionProperties, layer.layerName); + layer_extensions, detail::vk_functions.fp_vkEnumerateInstanceExtensionProperties, layer.layerName); if (layer_extensions_ret != VK_SUCCESS) { for (auto& ext : layer_extensions) if (strcmp (ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) @@ -365,21 +547,27 @@ void destroy_instance (Instance instance) { if (instance.instance != VK_NULL_HANDLE) { if (instance.debug_messenger != nullptr) destroy_debug_utils_messenger (instance.instance, instance.debug_messenger, instance.allocation_callbacks); - vkDestroyInstance (instance.instance, instance.allocation_callbacks); + detail::vk_functions.fp_vkDestroyInstance (instance.instance, instance.allocation_callbacks); } } -SystemInfo InstanceBuilder::get_system_info () const { return system; } +InstanceBuilder::InstanceBuilder (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { + info.fp_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr; +} +InstanceBuilder::InstanceBuilder () {} detail::Result InstanceBuilder::build () const { + auto sys_info_ret = SystemInfo::get_system_info (); + if (!sys_info_ret) return sys_info_ret.error (); + auto system = sys_info_ret.value (); + uint32_t api_version = VK_MAKE_VERSION (1, 0, 0); if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0) || info.desired_api_version > VK_MAKE_VERSION (1, 0, 0)) { - PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion; - detail::get_inst_proc_addr ( - pfn_vkEnumerateInstanceVersion, "vkEnumerateInstanceVersion", nullptr, vkGetInstanceProcAddr); + PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = + detail::vk_functions.fp_vkEnumerateInstanceVersion; uint32_t queried_api_version = VK_MAKE_VERSION (1, 0, 0); if (pfn_vkEnumerateInstanceVersion != nullptr) { @@ -455,7 +643,7 @@ detail::Result InstanceBuilder::build () const { for (auto& layer : info.layers) layers.push_back (layer); - if (info.enable_validation_layers) { + if (info.enable_validation_layers || (info.request_validation_layers && system.validation_layers_available)) { layers.push_back (detail::validation_layer_name); } bool all_layers_supported = detail::check_layers_supported (system.available_layers, layers); @@ -509,10 +697,13 @@ detail::Result InstanceBuilder::build () const { instance_create_info.ppEnabledLayerNames = layers.data (); Instance instance; - VkResult res = vkCreateInstance (&instance_create_info, info.allocation_callbacks, &instance.instance); + VkResult res = detail::vk_functions.fp_vkCreateInstance ( + &instance_create_info, info.allocation_callbacks, &instance.instance); if (res != VK_SUCCESS) return detail::Result (InstanceError::failed_create_instance, res); + detail::vk_functions.init_instance_funcs (instance.instance); + if (info.use_debug_messenger) { res = create_debug_utils_messenger (instance.instance, info.debug_callback, @@ -530,6 +721,7 @@ detail::Result InstanceBuilder::build () const { } instance.allocation_callbacks = info.allocation_callbacks; instance.instance_version = api_version; + instance.fp_vkGetInstanceProcAddr = detail::vk_functions.ptr_vkGetInstanceProcAddr; return instance; } @@ -574,9 +766,7 @@ InstanceBuilder& InstanceBuilder::enable_validation_layers (bool enable_validati return *this; } InstanceBuilder& InstanceBuilder::request_validation_layers (bool enable_validation) { - info.enable_validation_layers = - enable_validation && - detail::check_layer_supported (system.available_layers, detail::validation_layer_name); + info.request_validation_layers = enable_validation; return *this; } InstanceBuilder& InstanceBuilder::use_default_debug_messenger () { @@ -634,7 +824,7 @@ std::vector check_device_extension_support ( VkPhysicalDevice device, std::vector desired_extensions) { std::vector available_extensions; auto available_extensions_ret = detail::get_vector ( - available_extensions, vkEnumerateDeviceExtensionProperties, device, nullptr); + available_extensions, detail::vk_functions.fp_vkEnumerateDeviceExtensionProperties, device, nullptr); if (available_extensions_ret != VK_SUCCESS) return {}; std::vector extensions_to_enable; @@ -776,7 +966,7 @@ int get_present_queue_index (VkPhysicalDevice const phys_device, for (size_t i = 0; i < families.size (); i++) { VkBool32 presentSupport = false; if (surface != VK_NULL_HANDLE) { - VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR ( + VkResult res = detail::vk_functions.fp_vkGetPhysicalDeviceSurfaceSupportKHR ( phys_device, static_cast (i), surface, &presentSupport); if (res != VK_SUCCESS) return -1; // TODO: determine if this should fail another way } @@ -792,12 +982,12 @@ PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_devi PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; desc.phys_device = phys_device; auto queue_families = detail::get_vector_noerror ( - vkGetPhysicalDeviceQueueFamilyProperties, phys_device); + detail::vk_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties, phys_device); desc.queue_families = queue_families; - vkGetPhysicalDeviceProperties (phys_device, &desc.device_properties); - vkGetPhysicalDeviceFeatures (phys_device, &desc.device_features); - vkGetPhysicalDeviceMemoryProperties (phys_device, &desc.mem_properties); + detail::vk_functions.fp_vkGetPhysicalDeviceProperties (phys_device, &desc.device_properties); + detail::vk_functions.fp_vkGetPhysicalDeviceFeatures (phys_device, &desc.device_features); + detail::vk_functions.fp_vkGetPhysicalDeviceMemoryProperties (phys_device, &desc.mem_properties); return desc; } @@ -840,10 +1030,14 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (Phy std::vector formats; std::vector present_modes; - auto formats_ret = detail::get_vector ( - formats, vkGetPhysicalDeviceSurfaceFormatsKHR, pd.phys_device, system_info.surface); - auto present_modes_ret = detail::get_vector ( - present_modes, vkGetPhysicalDeviceSurfacePresentModesKHR, pd.phys_device, system_info.surface); + auto formats_ret = detail::get_vector (formats, + detail::vk_functions.fp_vkGetPhysicalDeviceSurfaceFormatsKHR, + pd.phys_device, + system_info.surface); + auto present_modes_ret = detail::get_vector (present_modes, + detail::vk_functions.fp_vkGetPhysicalDeviceSurfacePresentModesKHR, + pd.phys_device, + system_info.surface); if (formats_ret == VK_SUCCESS && present_modes_ret == VK_SUCCESS) { swapChainAdequate = !formats.empty () && !present_modes.empty (); @@ -898,7 +1092,7 @@ detail::Result PhysicalDeviceSelector::select () const { std::vector physical_devices; auto physical_devices_ret = detail::get_vector ( - physical_devices, vkEnumeratePhysicalDevices, system_info.instance); + physical_devices, detail::vk_functions.fp_vkEnumeratePhysicalDevices, system_info.instance); if (physical_devices_ret != VK_SUCCESS) { return detail::Result{ PhysicalDeviceError::failed_enumerate_physical_devices, physical_devices_ret }; @@ -1092,7 +1286,7 @@ detail::Result Device::get_dedicated_queue_index (QueueType type) cons namespace detail { VkQueue get_queue (VkDevice device, uint32_t family) { VkQueue out_queue; - vkGetDeviceQueue (device, family, 0, &out_queue); + detail::vk_functions.fp_vkGetDeviceQueue (device, family, 0, &out_queue); return out_queue; } } // namespace detail @@ -1115,7 +1309,7 @@ CustomQueueDescription::CustomQueueDescription (uint32_t index, uint32_t count, } void destroy_device (Device device) { - vkDestroyDevice (device.device, device.allocation_callbacks); + detail::vk_functions.fp_vkDestroyDevice (device.device, device.allocation_callbacks); } DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) { @@ -1135,7 +1329,7 @@ detail::Result DeviceBuilder::build () const { if (queue_descriptions.size () == 0) { for (uint32_t i = 0; i < info.queue_families.size (); i++) { - queue_descriptions.push_back ({ i, 1, std::vector{ 1.0f } }); + queue_descriptions.push_back (CustomQueueDescription{ i, 1, std::vector{ 1.0f } }); } } @@ -1174,7 +1368,7 @@ detail::Result DeviceBuilder::build () const { } Device device; - VkResult res = vkCreateDevice (info.physical_device.physical_device, + VkResult res = detail::vk_functions.fp_vkCreateDevice (info.physical_device.physical_device, &device_create_info, info.allocation_callbacks, &device.device); @@ -1240,7 +1434,8 @@ Result query_surface_support_details (VkPhysicalDevice ph return make_error_code (SurfaceSupportError::surface_handle_null); VkSurfaceCapabilitiesKHR capabilities; - VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR (phys_device, surface, &capabilities); + VkResult res = detail::vk_functions.fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR ( + phys_device, surface, &capabilities); if (res != VK_SUCCESS) { return { make_error_code (SurfaceSupportError::failed_get_surface_capabilities), res }; } @@ -1249,11 +1444,11 @@ Result query_surface_support_details (VkPhysicalDevice ph std::vector present_modes; auto formats_ret = detail::get_vector ( - formats, vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface); + formats, detail::vk_functions.fp_vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface); if (formats_ret != VK_SUCCESS) return { make_error_code (SurfaceSupportError::failed_enumerate_surface_formats), formats_ret }; auto present_modes_ret = detail::get_vector ( - present_modes, vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface); + present_modes, detail::vk_functions.fp_vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface); if (present_modes_ret != VK_SUCCESS) return { make_error_code (SurfaceSupportError::failed_enumerate_present_modes), present_modes_ret }; @@ -1310,8 +1505,8 @@ VkExtent2D find_extent ( void destroy_swapchain (Swapchain const& swapchain) { if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE) { - vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks); - + detail::vk_functions.fp_vkDestroySwapchainKHR ( + swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks); } } @@ -1404,7 +1599,7 @@ detail::Result SwapchainBuilder::build () const { swapchain_create_info.clipped = info.clipped; swapchain_create_info.oldSwapchain = info.old_swapchain; Swapchain swapchain{}; - VkResult res = vkCreateSwapchainKHR ( + VkResult res = detail::vk_functions.fp_vkCreateSwapchainKHR ( info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain); if (res != VK_SUCCESS) { return detail::Error{ SwapchainError::failed_create_swapchain, res }; @@ -1423,8 +1618,8 @@ detail::Result SwapchainBuilder::build () const { detail::Result> Swapchain::get_images () { std::vector swapchain_images; - auto swapchain_images_ret = - detail::get_vector (swapchain_images, vkGetSwapchainImagesKHR, device, swapchain); + auto swapchain_images_ret = detail::get_vector ( + swapchain_images, detail::vk_functions.fp_vkGetSwapchainImagesKHR, device, swapchain); if (swapchain_images_ret != VK_SUCCESS) { return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret }; } @@ -1454,7 +1649,8 @@ detail::Result> Swapchain::get_image_views () { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - VkResult res = vkCreateImageView (device, &createInfo, allocation_callbacks, &views[i]); + VkResult res = detail::vk_functions.fp_vkCreateImageView ( + device, &createInfo, allocation_callbacks, &views[i]); if (res != VK_SUCCESS) return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; } @@ -1462,7 +1658,7 @@ detail::Result> Swapchain::get_image_views () { } void Swapchain::destroy_image_views (std::vector const& image_views) { for (auto& image_view : image_views) { - vkDestroyImageView (device, image_view, allocation_callbacks); + detail::vk_functions.fp_vkDestroyImageView (device, image_view, allocation_callbacks); } } SwapchainBuilder& SwapchainBuilder::set_old_swapchain (VkSwapchainKHR old_swapchain) { diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index b5b1b8b..f2f5875 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -23,6 +23,7 @@ #include + namespace vkb { namespace detail { @@ -111,7 +112,6 @@ template class Result { bool m_init; }; -/* TODO implement operator == and operator != as friend or global */ } // namespace detail enum class InstanceError { @@ -165,9 +165,18 @@ const char* to_string (QueueError err); const char* to_string (DeviceError err); const char* to_string (SwapchainError err); - +// Gathers useful information about the available vulkan capabilities, like layers and instance extensions. +// Use this for enabling features conditionally, ie if you would like an extension but can use a fallback if +// it isn't supported but need to know if support is available first. struct SystemInfo { + private: SystemInfo (); + + public: + // Use get_system_info to create a SystemInfo struct. This is because loading vulkan could fail. + static detail::Result get_system_info (); + static detail::Result get_system_info (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); + // Returns true if a layer is available bool is_layer_available (const char* layer_name) const; // Returns true if an extension is available @@ -179,6 +188,7 @@ struct SystemInfo { bool debug_utils_available = false; }; + class InstanceBuilder; class PhysicalDeviceSelector; @@ -187,6 +197,8 @@ struct Instance { VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE; VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; + PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; + private: bool headless = false; uint32_t instance_version = VK_MAKE_VERSION (1, 0, 0); @@ -199,8 +211,10 @@ void destroy_instance (Instance instance); // release instance resources class InstanceBuilder { public: - // contains useful information about the available vulkan capabilities, like layers and instance extensions. - SystemInfo get_system_info () const; + // Default constructor, will load vulkan. + explicit InstanceBuilder (); + // Optional: Can use your own PFN_vkGetInstanceProcAddr + explicit InstanceBuilder (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); // Create a VkInstance. Return an error if it failed. detail::Result build () const; @@ -291,25 +305,15 @@ class InstanceBuilder { // Custom allocator VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; + bool request_validation_layers = false; bool enable_validation_layers = false; bool use_debug_messenger = false; bool headless_context = false; + + PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; } info; - - SystemInfo system; }; -VkResult create_debug_utils_messenger (VkInstance instance, - PFN_vkDebugUtilsMessengerCallbackEXT debug_callback, - VkDebugUtilsMessageSeverityFlagsEXT severity, - VkDebugUtilsMessageTypeFlagsEXT type, - VkDebugUtilsMessengerEXT* pDebugMessenger, - VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE); - -void destroy_debug_utils_messenger (VkInstance instance, - VkDebugUtilsMessengerEXT debugMessenger, - VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE); - VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, @@ -359,7 +363,7 @@ enum class PreferredDeviceType { class PhysicalDeviceSelector { public: // Requires a vkb::Instance to construct, needed to pass instance creation info. - PhysicalDeviceSelector (Instance const& instance); + explicit PhysicalDeviceSelector (Instance const& instance); detail::Result select () const; @@ -482,7 +486,7 @@ struct Device { // For advanced device queue setup struct CustomQueueDescription { - CustomQueueDescription (uint32_t index, uint32_t count, std::vector priorities); + explicit CustomQueueDescription (uint32_t index, uint32_t count, std::vector priorities); uint32_t index = 0; uint32_t count = 0; std::vector priorities; @@ -493,7 +497,7 @@ void destroy_device (Device device); class DeviceBuilder { public: // Any features and extensions that are requested/required in PhysicalDeviceSelector are automatically enabled. - DeviceBuilder (PhysicalDevice physical_device); + explicit DeviceBuilder (PhysicalDevice physical_device); detail::Result build () const; @@ -548,8 +552,8 @@ void destroy_swapchain (Swapchain const& swapchain); class SwapchainBuilder { public: - SwapchainBuilder (Device const& device); - SwapchainBuilder (Device const& device, VkSurfaceKHR const surface); + explicit SwapchainBuilder (Device const& device); + explicit SwapchainBuilder (Device const& device, VkSurfaceKHR const surface); detail::Result build () const; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1b790d..f546f6b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,8 @@ add_executable(vk-bootstrap-test main.cpp bootstrap_tests.cpp error_code_tests.cpp) target_link_libraries(vk-bootstrap-test - PRIVATE - vk-bootstrap - vk-bootstrap-compiler-warnings - glfw - Catch2 - ) \ No newline at end of file + PRIVATE + vk-bootstrap + vk-bootstrap-compiler-warnings + glfw + Catch2 +) \ No newline at end of file diff --git a/tests/bootstrap_tests.cpp b/tests/bootstrap_tests.cpp index 1bcbdbf..ab09a01 100644 --- a/tests/bootstrap_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -3,16 +3,22 @@ #include // TODO -// Getting queues -// get dedicated vs distinct compute queues - // changing present modes and/or image formats +void destroy_surface (vkb::detail::Result 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); +} + TEST_CASE ("Instance with surface", "[VkBootstrap.bootstrap]") { GIVEN ("A window and a vulkan instance") { auto window = create_window_glfw ("Instance with surface"); + auto sys_info_ret = vkb::SystemInfo::get_system_info (); + REQUIRE (sys_info_ret); + vkb::InstanceBuilder instance_builder; auto instance_ret = instance_builder.use_default_debug_messenger ().build (); REQUIRE (instance_ret); @@ -48,7 +54,7 @@ TEST_CASE ("Instance with surface", "[VkBootstrap.bootstrap]") { REQUIRE (phys_dev_ret.has_value ()); } - destroy_surface (instance.instance, surface); + destroy_surface (instance_ret, surface); vkb::destroy_instance (instance); destroy_window_glfw (window); } @@ -176,7 +182,7 @@ TEST_CASE ("Device Configuration", "[VkBootstrap.bootstrap]") { vkb::destroy_device (device_ret.value ()); } - destroy_surface (instance_ret.value ().instance, surface); + destroy_surface (instance_ret, surface); vkb::destroy_instance (instance_ret.value ()); } @@ -258,7 +264,7 @@ TEST_CASE ("Swapchain", "[VkBootstrap.bootstrap]") { } vkb::destroy_device (device_ret.value ()); - destroy_surface (instance_ret.value ().instance, surface); + destroy_surface (instance_ret, surface); vkb::destroy_instance (instance_ret.value ()); } } @@ -307,6 +313,70 @@ TEST_CASE ("Allocation Callbacks", "[VkBootstrap.bootstrap]") { vkb::destroy_swapchain (swapchain_ret.value ()); vkb::destroy_device (device_ret.value ()); - destroy_surface (instance_ret.value ().instance, surface); + destroy_surface (instance_ret, surface); vkb::destroy_instance (instance_ret.value ()); } + +TEST_CASE ("SystemInfo Loading Vulkan Automatically", "[VkBootstrap.loading]") { + auto info_ret = vkb::SystemInfo::get_system_info (); + REQUIRE (info_ret); + vkb::InstanceBuilder builder; + auto ret = builder.build (); + REQUIRE (ret); +} + +TEST_CASE ("SystemInfo Loading Vulkan Manually", "[VkBootstrap.loading]") { + VulkanLibrary vk_lib; + REQUIRE (vk_lib.ptr_vkGetInstanceProcAddr != NULL); + auto info_ret = vkb::SystemInfo::get_system_info (vk_lib.ptr_vkGetInstanceProcAddr); + REQUIRE (info_ret); + vkb::InstanceBuilder builder; + auto ret = builder.build (); + REQUIRE (ret); + vk_lib.close (); +} + +TEST_CASE ("InstanceBuilder Loading Vulkan Automatically", "[VkBootstrap.loading]") { + vkb::InstanceBuilder builder; + auto ret = builder.build (); + REQUIRE (ret); +} + +TEST_CASE ("InstanceBuilder Loading Vulkan Manually", "[VkBootstrap.loading]") { + VulkanLibrary vk_lib; + REQUIRE (vk_lib.ptr_vkGetInstanceProcAddr != NULL); + vkb::InstanceBuilder builder{ vk_lib.ptr_vkGetInstanceProcAddr }; + auto ret = builder.build (); + vk_lib.close (); +} +TEST_CASE ("ReLoading Vulkan Automatically", "[VkBootstrap.loading]") { + { + vkb::InstanceBuilder builder; + auto ret = builder.build (); + REQUIRE(ret); + } + { + vkb::InstanceBuilder builder; + auto ret = builder.build (); + REQUIRE(ret); + } +} + +TEST_CASE ("ReLoading Vulkan Manually", "[VkBootstrap.loading]") { + { + VulkanLibrary vk_lib; + REQUIRE (vk_lib.ptr_vkGetInstanceProcAddr != NULL); + vkb::InstanceBuilder builder{ vk_lib.ptr_vkGetInstanceProcAddr }; + auto ret = builder.build (); + REQUIRE(ret); + vk_lib.close (); + } + { + VulkanLibrary vk_lib; + REQUIRE (vk_lib.ptr_vkGetInstanceProcAddr != NULL); + vkb::InstanceBuilder builder{ vk_lib.ptr_vkGetInstanceProcAddr }; + auto ret = builder.build (); + REQUIRE(ret); + vk_lib.close (); + } +} \ No newline at end of file diff --git a/tests/common.h b/tests/common.h index ead0448..4f5df46 100644 --- a/tests/common.h +++ b/tests/common.h @@ -5,17 +5,27 @@ #include #include +#if defined(_WIN32) +#include +#define NOMINMAX +#include +#endif // _WIN32 + +#if defined(__linux__) || defined(__APPLE__) +#include +#endif + #define GLFW_INCLUDE_VULKAN #include "GLFW/glfw3.h" #include "../src/VkBootstrap.h" -GLFWwindow* create_window_glfw (const char * window_name = "", bool resize = true) { +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); + return glfwCreateWindow (1024, 1024, window_name, NULL, NULL); } void destroy_window_glfw (GLFWwindow* window) { glfwDestroyWindow (window); @@ -36,6 +46,44 @@ VkSurfaceKHR create_surface_glfw (VkInstance instance, GLFWwindow* window) { } return surface; } -void destroy_surface (VkInstance instance, VkSurfaceKHR surface) { - vkDestroySurfaceKHR (instance, surface, nullptr); -} \ No newline at end of file + +struct VulkanLibrary { +#if defined(__linux__) || defined(__APPLE__) + void* library; +#elif defined(_WIN32) + + HMODULE library; +#endif + PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = VK_NULL_HANDLE; + + VulkanLibrary () { +#if defined(__linux__) + library = dlopen ("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); + if (!library) library = dlopen ("libvulkan.so", RTLD_NOW | RTLD_LOCAL); +#elif defined(__APPLE__) + library = dlopen ("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); + if (!library) library = dlopen ("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); +#elif defined(_WIN32) + library = LoadLibrary (TEXT ("vulkan-1.dll")); +#else + assert (false && "Unsupported platform"); +#endif + if (!library) return; +#if defined(__linux__) || defined(__APPLE__) + ptr_vkGetInstanceProcAddr = + reinterpret_cast (dlsym (library, "vkGetInstanceProcAddr")); +#elif defined(_WIN32) + ptr_vkGetInstanceProcAddr = reinterpret_cast ( + GetProcAddress (library, "vkGetInstanceProcAddr")); +#endif + } + + void close () { +#if defined(__linux__) || defined(__APPLE__) + dlclose (library); +#elif defined(_WIN32) + FreeLibrary (library); +#endif + library = 0; + } +};