From df53490ede014699efbc538a64b33ef14dc22735 Mon Sep 17 00:00:00 2001 From: Lesley Lai Date: Sun, 17 May 2020 07:31:02 -0600 Subject: [PATCH] Error handling with std::error_code --- docs/getting_started.md | 4 +- example/triangle.cpp | 12 +- src/VkBootstrap.cpp | 275 ++++++++++++------- src/VkBootstrap.h | 131 +++++---- tests/CMakeLists.txt | 2 +- tests/{run_tests.cpp => bootstrap_tests.cpp} | 14 +- tests/error_code_tests.cpp | 98 +++++++ 7 files changed, 367 insertions(+), 169 deletions(-) rename tests/{run_tests.cpp => bootstrap_tests.cpp} (96%) create mode 100644 tests/error_code_tests.cpp diff --git a/docs/getting_started.md b/docs/getting_started.md index 5f8fe6f..a073223 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -11,7 +11,7 @@ Simply create a builder variable and call the `build()` member function. vkb::InstanceBuilder instance_builder; auto instance_builder_return = instance_builder.build(); ``` -Because creating an instance may fail, the builder returns an 'Expected' type. This contains either a valid `vkb::Instance` struct, which includes a `VkInstance` handle, or contains an `vkb::InstanceError`. +Because creating an instance may fail, the builder returns an 'Result' type. This contains either a valid `vkb::Instance` struct, which includes a `VkInstance` handle, or contains an `vkb::InstanceError`. ```cpp if (!instance_builder_return) { printf("Failed to create Vulkan instance. Cause %s\n", @@ -19,7 +19,7 @@ if (!instance_builder_return) { return -1; } ``` -Once any possible errors have been dealt with, we can pull the `vkb::Instance` struct out of the `Expected`. +Once any possible errors have been dealt with, we can pull the `vkb::Instance` struct out of the `Result`. ```cpp vkb::Instance vkb_instance = instance_builder_return.value(); ``` diff --git a/example/triangle.cpp b/example/triangle.cpp index 69405fa..17971f3 100644 --- a/example/triangle.cpp +++ b/example/triangle.cpp @@ -44,7 +44,7 @@ int device_initialization (Init& init) { vkb::InstanceBuilder instance_builder; auto instance_ret = instance_builder.use_default_debug_messenger ().request_validation_layers ().build (); if (!instance_ret) { - std::cout << vkb::to_string (instance_ret.error ().type) << "\n"; + std::cout << instance_ret.error () << "\n"; return -1; } init.instance = instance_ret.value (); @@ -54,7 +54,7 @@ int device_initialization (Init& init) { vkb::PhysicalDeviceSelector phys_device_selector (init.instance); auto phys_device_ret = phys_device_selector.set_surface (init.surface).select (); if (!phys_device_ret) { - std::cout << vkb::to_string (phys_device_ret.error ().type) << "\n"; + std::cout << phys_device_ret.error () << "\n"; return -1; } vkb::PhysicalDevice physical_device = phys_device_ret.value (); @@ -62,7 +62,7 @@ int device_initialization (Init& init) { vkb::DeviceBuilder device_builder{ physical_device }; auto device_ret = device_builder.build (); if (!device_ret) { - std::cout << vkb::to_string (device_ret.error ().type) << "\n"; + std::cout << device_ret.error () << "\n"; return -1; } init.device = device_ret.value (); @@ -71,7 +71,7 @@ int device_initialization (Init& init) { auto swap_ret = swapchain_builder.use_default_format_selection ().use_default_present_mode_selection ().build (); if (!swap_ret) { - std::cout << vkb::to_string (swap_ret.error ().type) << "\n"; + std::cout << swap_ret.error () << "\n"; return -1; } init.swapchain = swap_ret.value (); @@ -81,14 +81,14 @@ int device_initialization (Init& init) { int get_queues (Init& init, RenderData& data) { auto gq = init.device.get_queue (vkb::QueueType::graphics); if (!gq.has_value ()) { - std::cout << "failed to get graphics queue: " << vkb::to_string (gq.error ().type) << "\n"; + std::cout << "failed to get graphics queue: " << gq.error () << "\n"; return -1; } data.graphics_queue = gq.value (); auto pq = init.device.get_queue (vkb::QueueType::present); if (!pq.has_value ()) { - std::cout << "failed to get present queue: " << vkb::to_string (gq.error ().type) << "\n"; + std::cout << "failed to get present queue: " << pq.error () << "\n"; return -1; } data.present_queue = pq.value (); diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 8e08a45..bb13b40 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -15,22 +15,18 @@ void get_inst_proc_addr ( // Helper for robustly executing the two-call pattern template -auto get_vector (F&& f, Ts&&... ts) -> Expected, VkResult> { +auto get_vector (std::vector& out, F&& f, Ts&&... ts) -> VkResult { uint32_t count = 0; - std::vector results; VkResult err; do { err = f (ts..., &count, nullptr); if (err) { return err; }; - results.resize (count); - err = f (ts..., &count, results.data ()); + out.resize (count); + err = f (ts..., &count, out.data ()); } while (err == VK_INCOMPLETE); - if (err != VK_SUCCESS) { - return err; - }; - return results; + return err; } template @@ -167,8 +163,64 @@ void setup_pNext_chain (T& structure, std::vector const& st } 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 (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 (err)); + } +}; +const PhysicalDeviceErrorCategory physical_device_error_category; + +struct QueueErrorCategory : std::error_category { + const char* name () const noexcept override { return "vkb_queue"; } + std::string message (int err) const override { + return to_string (static_cast (err)); + } +}; +const QueueErrorCategory queue_error_category; + +struct DeviceErrorCategory : std::error_category { + const char* name () const noexcept override { return "vkb_device"; } + std::string message (int err) const override { + return to_string (static_cast (err)); + } +}; +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 (err)); + } +}; +const SwapchainErrorCategory swapchain_error_category; + } // namespace detail +std::error_code make_error_code (InstanceError instance_error) { + return { static_cast (instance_error), detail::instance_error_category }; +} +std::error_code make_error_code (PhysicalDeviceError physical_device_error) { + return { static_cast (physical_device_error), detail::physical_device_error_category }; +} +std::error_code make_error_code (QueueError queue_error) { + return { static_cast (queue_error), detail::queue_error_category }; +} +std::error_code make_error_code (DeviceError device_error) { + return { static_cast (device_error), detail::device_error_category }; +} +std::error_code make_error_code (SwapchainError swapchain_error) { + return { static_cast (swapchain_error), detail::swapchain_error_category }; +} + const char* to_string (InstanceError err) { switch (err) { case InstanceError::vulkan_unavailable: @@ -249,19 +301,22 @@ const char* to_string (SwapchainError err) { } SystemInfo::SystemInfo () { - auto available_extensions_ret = - detail::get_vector (vkEnumerateInstanceExtensionProperties, nullptr); - if (available_extensions_ret.has_value ()) { - this->available_extensions = available_extensions_ret.value (); + auto available_extensions_ret = detail::get_vector ( + this->available_extensions, vkEnumerateInstanceExtensionProperties, nullptr); + if (available_extensions_ret != VK_SUCCESS) { + this->available_extensions.clear (); } + for (auto& ext : this->available_extensions) if (strcmp (ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) debug_messenger_available = true; - auto available_layers_ret = detail::get_vector (vkEnumerateInstanceLayerProperties); - if (available_layers_ret.has_value ()) { - this->available_layers = available_layers_ret.value (); + auto available_layers_ret = + detail::get_vector (this->available_layers, vkEnumerateInstanceLayerProperties); + if (available_layers_ret != VK_SUCCESS) { + this->available_layers.clear (); } + for (auto& layer : this->available_layers) if (strcmp (layer.layerName, detail::validation_layer_name) == 0) validation_layers_available = true; @@ -285,7 +340,7 @@ void destroy_instance (Instance instance) { SystemInfo InstanceBuilder::get_system_info () const { return system; } -detail::Expected> InstanceBuilder::build () const { +detail::Result InstanceBuilder::build () const { uint32_t api_version = VK_MAKE_VERSION (1, 0, 0); @@ -300,15 +355,15 @@ detail::Expected> InstanceBuilder::build VkResult res = pfn_vkEnumerateInstanceVersion (&queried_api_version); // Should always return VK_SUCCESS if (res != VK_SUCCESS && info.required_api_version > 0) - return detail::Error{ InstanceError::vulkan_version_unavailable }; + return make_error_code (InstanceError::vulkan_version_unavailable); } if (pfn_vkEnumerateInstanceVersion == nullptr || queried_api_version < info.required_api_version) { if (VK_VERSION_MINOR (info.required_api_version) == 2) - return detail::Error{ InstanceError::vulkan_version_1_2_unavailable }; + return make_error_code (InstanceError::vulkan_version_1_2_unavailable); else if (VK_VERSION_MINOR (info.required_api_version)) - return detail::Error{ InstanceError::vulkan_version_1_1_unavailable }; + return make_error_code (InstanceError::vulkan_version_1_1_unavailable); else - return detail::Error{ InstanceError::vulkan_version_unavailable }; + return make_error_code (InstanceError::vulkan_version_unavailable); } if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0)) { api_version = info.required_api_version; @@ -354,7 +409,7 @@ detail::Expected> InstanceBuilder::build } bool all_extensions_supported = detail::check_extensions_supported (system.available_extensions, extensions); if (!all_extensions_supported) { - return detail::Error{ InstanceError::requested_extensions_not_present }; + return make_error_code (InstanceError::requested_extensions_not_present); } std::vector layers; @@ -366,7 +421,7 @@ detail::Expected> InstanceBuilder::build } bool all_layers_supported = detail::check_layers_supported (system.available_layers, layers); if (!all_layers_supported) { - return detail::Error{ InstanceError::requested_layers_not_present }; + return make_error_code (InstanceError::requested_layers_not_present); } std::vector pNext_chain; @@ -417,7 +472,7 @@ detail::Expected> InstanceBuilder::build Instance instance; VkResult res = vkCreateInstance (&instance_create_info, info.allocation_callbacks, &instance.instance); if (res != VK_SUCCESS) - return detail::Error{ InstanceError::failed_create_instance, res }; + return detail::Result (InstanceError::failed_create_instance, res); if (info.use_debug_messenger) { res = create_debug_utils_messenger (instance.instance, @@ -427,7 +482,7 @@ detail::Expected> InstanceBuilder::build &instance.debug_messenger, info.allocation_callbacks); if (res != VK_SUCCESS) { - return detail::Error{ InstanceError::failed_create_debug_messenger, res }; + return detail::Result (InstanceError::failed_create_debug_messenger, res); } } @@ -538,12 +593,13 @@ namespace detail { std::vector check_device_extension_support ( VkPhysicalDevice device, std::vector desired_extensions) { - auto available_extensions = - detail::get_vector (vkEnumerateDeviceExtensionProperties, device, nullptr); - if (!available_extensions.has_value ()) return {}; + std::vector available_extensions; + auto available_extensions_ret = detail::get_vector ( + available_extensions, vkEnumerateDeviceExtensionProperties, device, nullptr); + if (available_extensions_ret != VK_SUCCESS) return {}; std::vector extensions_to_enable; - for (const auto& extension : available_extensions.value ()) { + for (const auto& extension : available_extensions) { for (auto& req_ext : desired_extensions) { if (strcmp (req_ext, extension.extensionName) == 0) { extensions_to_enable.push_back (req_ext); @@ -742,14 +798,16 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (Phy if (criteria.defer_surface_initialization) { swapChainAdequate = true; } else if (!system_info.headless) { + std::vector formats; + std::vector present_modes; - auto formats = detail::get_vector ( - vkGetPhysicalDeviceSurfaceFormatsKHR, pd.phys_device, system_info.surface); - auto present_modes = detail::get_vector ( - vkGetPhysicalDeviceSurfacePresentModesKHR, pd.phys_device, system_info.surface); + 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); - if (formats.has_value () && present_modes.has_value ()) { - swapChainAdequate = !formats.value ().empty () && !present_modes.value ().empty (); + if (formats_ret == VK_SUCCESS && present_modes_ret == VK_SUCCESS) { + swapChainAdequate = !formats.empty () && !present_modes.empty (); } } if (criteria.require_present && !swapChainAdequate) return Suitable::no; @@ -791,24 +849,27 @@ PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) { criteria.desired_version = instance.instance_version; } -detail::Expected> PhysicalDeviceSelector::select () const { +detail::Result PhysicalDeviceSelector::select () const { if (!system_info.headless && !criteria.defer_surface_initialization) { if (system_info.surface == nullptr) - return detail::Error{ PhysicalDeviceError::no_surface_provided }; + return detail::Result{ PhysicalDeviceError::no_surface_provided }; } - auto physical_devices = - detail::get_vector (vkEnumeratePhysicalDevices, system_info.instance); - if (!physical_devices.has_value ()) { - return detail::Error{ PhysicalDeviceError::failed_enumerate_physical_devices, - physical_devices.error () }; + + std::vector physical_devices; + + auto physical_devices_ret = detail::get_vector ( + physical_devices, vkEnumeratePhysicalDevices, system_info.instance); + if (physical_devices_ret != VK_SUCCESS) { + return detail::Result{ PhysicalDeviceError::failed_enumerate_physical_devices, + physical_devices_ret }; } - if (physical_devices.value ().size () == 0) { - return detail::Error{ PhysicalDeviceError::no_physical_devices_found }; + if (physical_devices.size () == 0) { + return detail::Result{ PhysicalDeviceError::no_physical_devices_found }; } std::vector phys_device_descriptions; - for (auto& phys_device : physical_devices.value ()) { + for (auto& phys_device : physical_devices) { phys_device_descriptions.push_back (populate_device_details (phys_device)); } @@ -829,7 +890,7 @@ detail::Expected> PhysicalDev } if (selected_device.phys_device == VK_NULL_HANDLE) { - return detail::Error{ PhysicalDeviceError::no_suitable_device }; + return detail::Result{ PhysicalDeviceError::no_suitable_device }; } PhysicalDevice out_device{}; out_device.physical_device = selected_device.phys_device; @@ -949,43 +1010,43 @@ std::vector PhysicalDevice::get_queue_families () const // ---- Queues ---- // -detail::Expected> Device::get_queue_index (QueueType type) const { +detail::Result Device::get_queue_index (QueueType type) const { int index = -1; switch (type) { case QueueType::present: index = detail::get_present_queue_index (physical_device.physical_device, surface, queue_families); - if (index < 0) return detail::Error{ QueueError::present_unavailable }; + if (index < 0) return detail::Result{ QueueError::present_unavailable }; break; case QueueType::graphics: index = detail::get_graphics_queue_index (queue_families); - if (index < 0) return detail::Error{ QueueError::graphics_unavailable }; + if (index < 0) return detail::Result{ QueueError::graphics_unavailable }; break; case QueueType::compute: index = detail::get_separate_compute_queue_index (queue_families); - if (index < 0) return detail::Error{ QueueError::compute_unavailable }; + if (index < 0) return detail::Result{ QueueError::compute_unavailable }; break; case QueueType::transfer: index = detail::get_separate_transfer_queue_index (queue_families); - if (index < 0) return detail::Error{ QueueError::transfer_unavailable }; + if (index < 0) return detail::Result{ QueueError::transfer_unavailable }; break; default: - return detail::Error{ QueueError::invalid_queue_family_index }; + return detail::Result{ QueueError::invalid_queue_family_index }; } return static_cast (index); } -detail::Expected> Device::get_dedicated_queue_index (QueueType type) const { +detail::Result Device::get_dedicated_queue_index (QueueType type) const { int index = -1; switch (type) { case QueueType::compute: index = detail::get_dedicated_compute_queue_index (queue_families); - if (index < 0) return detail::Error{ QueueError::compute_unavailable }; + if (index < 0) return detail::Result{ QueueError::compute_unavailable }; break; case QueueType::transfer: index = detail::get_dedicated_transfer_queue_index (queue_families); - if (index < 0) return detail::Error{ QueueError::transfer_unavailable }; + if (index < 0) return detail::Result{ QueueError::transfer_unavailable }; break; default: - return detail::Error{ QueueError::invalid_queue_family_index }; + return detail::Result{ QueueError::invalid_queue_family_index }; } return static_cast (index); } @@ -996,14 +1057,14 @@ VkQueue get_queue (VkDevice device, uint32_t family) { return out_queue; } } // namespace detail -detail::Expected> Device::get_queue (QueueType type) const { +detail::Result Device::get_queue (QueueType type) const { auto index = get_queue_index (type); - if (!index.has_value ()) return index.error (); + if (!index.has_value ()) return { index.error () }; return detail::get_queue (device, index.value ()); } -detail::Expected> Device::get_dedicated_queue (QueueType type) const { +detail::Result Device::get_dedicated_queue (QueueType type) const { auto index = get_dedicated_queue_index (type); - if (!index.has_value ()) return index.error (); + if (!index.has_value ()) return { index.error () }; return detail::get_queue (device, index.value ()); } @@ -1027,7 +1088,7 @@ DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) { info.defer_surface_initialization = phys_device.defer_surface_initialization; } -detail::Expected> DeviceBuilder::build () const { +detail::Result DeviceBuilder::build () const { std::vector queue_descriptions; queue_descriptions.insert ( @@ -1079,7 +1140,7 @@ detail::Expected> DeviceBuilder::build () con info.allocation_callbacks, &device.device); if (res != VK_SUCCESS) { - return detail::Error{ DeviceError::failed_create_device, res }; + return { DeviceError::failed_create_device, res }; } device.physical_device = info.physical_device; device.surface = info.surface; @@ -1112,27 +1173,51 @@ enum class SurfaceSupportError { failed_enumerate_present_modes }; -Expected> query_surface_support_details ( - VkPhysicalDevice phys_device, VkSurfaceKHR surface) { +struct SurfaceSupportErrorCategory : std::error_category { + const char* name () const noexcept override { return "vbk_surface_support"; } + std::string message (int err) const override { + switch (static_cast (err)) { + case SurfaceSupportError::surface_handle_null: + return "surface_handle_null"; + case SurfaceSupportError::failed_get_surface_capabilities: + return "failed_get_surface_capabilities"; + case SurfaceSupportError::failed_enumerate_surface_formats: + return "failed_enumerate_surface_formats"; + case SurfaceSupportError::failed_enumerate_present_modes: + return "failed_enumerate_present_modes"; + default: + return ""; + } + } +}; +const SurfaceSupportErrorCategory surface_support_error_category; + +std::error_code make_error_code (SurfaceSupportError surface_support_error) { + return { static_cast (surface_support_error), detail::surface_support_error_category }; +} + +Result query_surface_support_details (VkPhysicalDevice phys_device, VkSurfaceKHR surface) { if (surface == VK_NULL_HANDLE) - return detail::Error{ SurfaceSupportError::surface_handle_null }; + return make_error_code (SurfaceSupportError::surface_handle_null); VkSurfaceCapabilitiesKHR capabilities; VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR (phys_device, surface, &capabilities); if (res != VK_SUCCESS) { - return detail::Error{ SurfaceSupportError::failed_get_surface_capabilities, res }; + return { make_error_code (SurfaceSupportError::failed_get_surface_capabilities), res }; } - auto formats = detail::get_vector ( - vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface); - if (!formats.has_value ()) - return detail::Error{ SurfaceSupportError::failed_enumerate_surface_formats, - formats.error () }; - auto present_modes = detail::get_vector ( - vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface); - if (!present_modes.has_value ()) - return detail::Error{ SurfaceSupportError::failed_enumerate_present_modes, - formats.error () }; - return SurfaceSupportDetails{ capabilities, formats.value (), present_modes.value () }; + + std::vector formats; + std::vector present_modes; + + auto formats_ret = detail::get_vector ( + formats, 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); + if (present_modes_ret != VK_SUCCESS) + return { make_error_code (SurfaceSupportError::failed_enumerate_present_modes), present_modes_ret }; + return SurfaceSupportDetails{ capabilities, formats, present_modes }; } VkSurfaceFormatKHR find_surface_format (std::vector const& available_formats, @@ -1220,13 +1305,10 @@ SwapchainBuilder::SwapchainBuilder ( info.graphics_queue_index = static_cast (graphics_queue_index); info.present_queue_index = static_cast (present_queue_index); } -detail::Expected> SwapchainBuilder::build () const { - return build (VK_NULL_HANDLE); -} -detail::Expected> SwapchainBuilder::build ( - VkSwapchainKHR old_swapchain) const { +detail::Result SwapchainBuilder::build () const { return build (VK_NULL_HANDLE); } +detail::Result SwapchainBuilder::build (VkSwapchainKHR old_swapchain) const { if (info.surface == VK_NULL_HANDLE) { - return detail::Error{ SwapchainError::surface_handle_not_provided }; + return detail::Error{ SwapchainError::surface_handle_not_provided }; } auto desired_formats = info.desired_formats; @@ -1236,8 +1318,8 @@ detail::Expected> SwapchainBuilder::bui auto surface_support = detail::query_surface_support_details (info.physical_device, info.surface); if (!surface_support.has_value ()) - return detail::Error{ SwapchainError::failed_query_surface_support_details, - surface_support.error ().vk_result }; + return detail::Error{ SwapchainError::failed_query_surface_support_details, + surface_support.vk_result () }; VkSurfaceFormatKHR surface_format = detail::find_surface_format (surface_support.value ().formats, desired_formats); VkPresentModeKHR present_mode = @@ -1281,32 +1363,33 @@ detail::Expected> SwapchainBuilder::bui VkResult res = vkCreateSwapchainKHR ( info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain); if (res != VK_SUCCESS) { - return detail::Error{ SwapchainError::failed_create_swapchain, res }; + return detail::Error{ SwapchainError::failed_create_swapchain, res }; } swapchain.device = info.device; swapchain.image_format = surface_format.format; swapchain.extent = extent; auto images = swapchain.get_images (); if (!images) { - return detail::Error{ SwapchainError::failed_get_swapchain_images }; + return detail::Error{ SwapchainError::failed_get_swapchain_images }; } swapchain.image_count = static_cast (images.value ().size ()); swapchain.allocation_callbacks = info.allocation_callbacks; return swapchain; } -detail::Expected> SwapchainBuilder::recreate ( - Swapchain const& swapchain) const { +detail::Result SwapchainBuilder::recreate (Swapchain const& swapchain) const { return build (swapchain.swapchain); } -detail::Expected, detail::Error> Swapchain::get_images () { - auto swapchain_images = detail::get_vector (vkGetSwapchainImagesKHR, device, swapchain); - if (!swapchain_images) { - return detail::Error{ SwapchainError::failed_get_swapchain_images, - swapchain_images.error () }; +detail::Result> Swapchain::get_images () { + std::vector swapchain_images; + + auto swapchain_images_ret = + detail::get_vector (swapchain_images, vkGetSwapchainImagesKHR, device, swapchain); + if (swapchain_images_ret != VK_SUCCESS) { + return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret }; } - return swapchain_images.value (); + return swapchain_images; } -detail::Expected, detail::Error> Swapchain::get_image_views () { +detail::Result> Swapchain::get_image_views () { auto swapchain_images_ret = get_images (); if (!swapchain_images_ret) return swapchain_images_ret.error (); @@ -1332,7 +1415,7 @@ detail::Expected, detail::Error> Swapch VkResult res = vkCreateImageView (device, &createInfo, allocation_callbacks, &views[i]); if (res != VK_SUCCESS) - return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; + return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; } return views; } diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 8e61502..3218379 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -3,93 +3,94 @@ #include #include +#include #include namespace vkb { namespace detail { -template struct Error { - explicit Error (ErrorType type, VkResult result = VK_SUCCESS) - : type (type), vk_result (result) {} - ErrorType type; - VkResult vk_result; // optional error value if a vulkan call failed +struct Error { + std::error_code type; + VkResult vk_result = VK_SUCCESS; // optional error value if a vulkan call failed }; -template class Expected { +template class Result { public: - Expected (const E& expect) : m_expect{ expect }, m_init{ true } {} - Expected (E&& expect) : m_expect{ std::move (expect) }, m_init{ true } {} - Expected (const U& error) : m_error{ error }, m_init{ false } {} - Expected (U&& error) : m_error{ std::move (error) }, m_init{ false } {} - ~Expected () { destroy (); } - Expected (Expected const& expected) : m_init (expected.m_init) { + Result (const T& value) : m_value{ value }, m_init{ true } {} + Result (T&& value) : m_value{ std::move (value) }, m_init{ true } {} + + 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 () { destroy (); } + Result (Result const& expected) : m_init (expected.m_init) { if (m_init) - new (&m_expect) E{ expected.m_expect }; + new (&m_value) T{ expected.m_value }; else - new (&m_error) U{ expected.m_error }; + m_error = expected.m_error; } - Expected (Expected&& expected) : m_init (expected.m_init) { + Result (Result&& expected) : m_init (expected.m_init) { if (m_init) - new (&m_expect) E{ std::move (expected.m_expect) }; + new (&m_value) T{ std::move (expected.m_value) }; else - new (&m_error) U{ std::move (expected.m_error) }; + m_error = std::move (expected.m_error); expected.destroy (); } - Expected& operator= (const E& expect) { + Result& operator= (const T& expect) { destroy (); m_init = true; - new (&m_expect) E{ expect }; + new (&m_value) T{ expect }; return *this; } - Expected& operator= (E&& expect) { + Result& operator= (T&& expect) { destroy (); m_init = true; - new (&m_expect) E{ std::move (expect) }; + new (&m_value) T{ std::move (expect) }; return *this; } - Expected& operator= (const U& error) { + Result& operator= (const Error& error) { destroy (); m_init = false; - new (&m_error) U{ error }; + m_error = error; return *this; } - Expected& operator= (U&& error) { + Result& operator= (Error&& error) { destroy (); m_init = false; - new (&m_error) U{ std::move (error) }; + m_error = error; return *this; } // clang-format off - const E* operator-> () const { assert (m_init); return &m_expect; } - E* operator-> () { assert (m_init); return &m_expect; } - const E& operator* () const& { assert (m_init); return m_expect; } - E& operator* () & { assert (m_init); return m_expect; } - E&& operator* () && { assert (m_init); return std::move (m_expect); } - const E& value () const& { assert (m_init); return m_expect; } - E& value () & { assert (m_init); return m_expect; } - const E&& value () const&& { assert (m_init); return std::move (m_expect); } - E&& value () && { assert (m_init); return std::move (m_expect); } - const U& error () const& { assert (!m_init); return m_error; } - U& error () & { assert (!m_init); return m_error; } - const U&& error () const&& { assert (!m_init); return std::move (m_error); } - U&& error () && { assert (!m_init); return std::move (m_error); } + const T* operator-> () const { assert (m_init); return &m_value; } + T* operator-> () { assert (m_init); return &m_value; } + const T& operator* () const& { assert (m_init); return m_value; } + T& operator* () & { assert (m_init); return m_value; } + T&& operator* () && { assert (m_init); return std::move (m_value); } + const T& value () const& { assert (m_init); return m_value; } + T& value () & { assert (m_init); return m_value; } + const T&& value () const&& { assert (m_init); return std::move (m_value); } + T&& value () && { assert (m_init); return std::move (m_value); } + + std::error_code error() const { assert (!m_init); return m_error.type; } + VkResult vk_result() const { assert (!m_init); return m_error.vk_result; } // clang-format on + + bool has_value () const { return m_init; } explicit operator bool () const { return m_init; } private: void destroy () { - if (m_init) - m_expect.~E (); - else - m_error.~U (); + if (m_init) m_value.~T (); } union { - E m_expect; - U m_error; + T m_value; + Error m_error; }; bool m_init; }; @@ -131,6 +132,13 @@ enum class SwapchainError { failed_get_swapchain_images, failed_create_swapchain_image_views, }; + +std::error_code make_error_code (InstanceError instance_error); +std::error_code make_error_code (PhysicalDeviceError physical_device_error); +std::error_code make_error_code (QueueError queue_error); +std::error_code make_error_code (DeviceError device_error); +std::error_code make_error_code (SwapchainError swapchain_error); + const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s); const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s); @@ -178,7 +186,7 @@ class InstanceBuilder { SystemInfo get_system_info () const; // Create a VkInstance. Return an error if it failed. - detail::Expected> build () const; + detail::Result build () const; // Sets the name of the application. Defaults to "" if none is provided. InstanceBuilder& set_app_name (const char* app_name); @@ -336,7 +344,7 @@ class PhysicalDeviceSelector { // Requires a vkb::Instance to construct, needed to pass instance creation info. PhysicalDeviceSelector (Instance const& instance); - detail::Expected> select () const; + detail::Result select () const; // Set the surface in which the physical device should render to. PhysicalDeviceSelector& set_surface (VkSurfaceKHR surface); @@ -446,13 +454,13 @@ struct Device { std::vector queue_families; VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; - detail::Expected> get_queue_index (QueueType type) const; + detail::Result get_queue_index (QueueType type) const; // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue index - detail::Expected> get_dedicated_queue_index (QueueType type) const; + detail::Result get_dedicated_queue_index (QueueType type) const; - detail::Expected> get_queue (QueueType type) const; + detail::Result get_queue (QueueType type) const; // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue - detail::Expected> get_dedicated_queue (QueueType type) const; + detail::Result get_dedicated_queue (QueueType type) const; }; // For advanced device queue setup @@ -470,7 +478,7 @@ class DeviceBuilder { // Any features and extensions that are requested/required in PhysicalDeviceSelector are automatically enabled. DeviceBuilder (PhysicalDevice physical_device); - detail::Expected> build () const; + detail::Result build () const; // For Advanced Users: specify the exact list of VkDeviceQueueCreateInfo's needed for the application. // If a custom queue setup is provided, getting the queues and queue indexes is up to the application. @@ -511,11 +519,11 @@ struct Swapchain { VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; // Returns a vector of VkImage handles to the swapchain - detail::Expected, detail::Error> get_images (); + detail::Result> get_images (); // Returns a vector of VkImageView's to the VkImage's of the swapchain // VkImageViews must be destroyed - detail::Expected, detail::Error> get_image_views (); + detail::Result> get_image_views (); void destroy_image_views (std::vector const& image_views); }; @@ -527,8 +535,8 @@ class SwapchainBuilder { SwapchainBuilder (Device const& device, VkSurfaceKHR const surface); SwapchainBuilder (VkPhysicalDevice const physical_device, VkDevice const device, VkSurfaceKHR const surface); - detail::Expected> build () const; - detail::Expected> recreate (Swapchain const& swapchain) const; + detail::Result build () const; + detail::Result recreate (Swapchain const& swapchain) const; SwapchainBuilder& set_desired_extent (uint32_t width, uint32_t height); @@ -547,7 +555,7 @@ class SwapchainBuilder { void add_desired_formats (std::vector& formats) const; void add_desired_present_modes (std::vector& modes) const; // for use in swapchain recreation - detail::Expected> build (VkSwapchainKHR old_swapchain) const; + detail::Result build (VkSwapchainKHR old_swapchain) const; struct SwapchainInfo { VkPhysicalDevice physical_device = VK_NULL_HANDLE; @@ -565,4 +573,13 @@ class SwapchainBuilder { } info; }; -} // namespace vkb \ No newline at end of file +} // namespace vkb + + +namespace std { +template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; +} // namespace std \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ac876a..f1b790d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(vk-bootstrap-test main.cpp run_tests.cpp) +add_executable(vk-bootstrap-test main.cpp bootstrap_tests.cpp error_code_tests.cpp) target_link_libraries(vk-bootstrap-test PRIVATE vk-bootstrap diff --git a/tests/run_tests.cpp b/tests/bootstrap_tests.cpp similarity index 96% rename from tests/run_tests.cpp rename to tests/bootstrap_tests.cpp index 66e2879..38a3ee0 100644 --- a/tests/run_tests.cpp +++ b/tests/bootstrap_tests.cpp @@ -8,7 +8,7 @@ // changing present modes and/or image formats -TEST_CASE ("Instance with surface") { +TEST_CASE ("Instance with surface", "[VkBootstrap.bootstrap]") { GIVEN ("A window and a vulkan instance") { auto window = create_window_glfw (); @@ -54,7 +54,7 @@ TEST_CASE ("Instance with surface") { } } -TEST_CASE ("instance configuration") { +TEST_CASE ("instance configuration", "[VkBootstrap.bootstrap]") { SECTION ("custom debug callback") { vkb::InstanceBuilder builder; @@ -97,7 +97,7 @@ TEST_CASE ("instance configuration") { } } -TEST_CASE ("Headless Vulkan") { +TEST_CASE ("Headless Vulkan", "[VkBootstrap.bootstrap]") { vkb::InstanceBuilder builder; auto instance_ret = builder.request_validation_layers ().set_headless ().build (); @@ -116,7 +116,7 @@ TEST_CASE ("Headless Vulkan") { vkb::destroy_instance (instance_ret.value ()); } -TEST_CASE ("Device Configuration") { +TEST_CASE ("Device Configuration", "[VkBootstrap.bootstrap]") { auto window = create_window_glfw (); vkb::InstanceBuilder builder; @@ -180,7 +180,7 @@ TEST_CASE ("Device Configuration") { vkb::destroy_instance (instance_ret.value ()); } -TEST_CASE ("Swapchain") { +TEST_CASE ("Swapchain", "[VkBootstrap.bootstrap]") { GIVEN ("A working instance, window, surface, and device") { auto window = create_window_glfw (); vkb::InstanceBuilder builder; @@ -268,7 +268,7 @@ void* VKAPI_PTR shim_vkReallocationFunction ( void VKAPI_PTR shim_vkFreeFunction (void* /*pUserData*/, void* pMemory) { return free (pMemory); } -TEST_CASE ("Allocation Callbacks") { +TEST_CASE ("Allocation Callbacks", "[VkBootstrap.bootstrap]") { VkAllocationCallbacks allocation_callbacks{}; allocation_callbacks.pfnAllocation = &shim_vkAllocationFunction; allocation_callbacks.pfnReallocation = &shim_vkReallocationFunction; @@ -296,7 +296,7 @@ TEST_CASE ("Allocation Callbacks") { vkb::SwapchainBuilder swapchain_builder (device); auto swapchain_ret = swapchain_builder.set_allocation_callbacks (&allocation_callbacks).build (); REQUIRE (swapchain_ret.has_value ()); - auto swapchain = swapchain_ret.value (); + // auto swapchain = swapchain_ret.value (); vkb::destroy_swapchain (swapchain_ret.value ()); vkb::destroy_device (device_ret.value ()); diff --git a/tests/error_code_tests.cpp b/tests/error_code_tests.cpp new file mode 100644 index 0000000..a3f7a84 --- /dev/null +++ b/tests/error_code_tests.cpp @@ -0,0 +1,98 @@ +#include + +#include "VkBootstrap.h" + +TEST_CASE ("is_error_code_enum", "[VkBootstrap.error_code]") { + STATIC_REQUIRE (std::is_error_code_enum::value); + STATIC_REQUIRE (std::is_error_code_enum::value); + STATIC_REQUIRE (std::is_error_code_enum::value); + STATIC_REQUIRE (std::is_error_code_enum::value); + STATIC_REQUIRE (std::is_error_code_enum::value); +} + +TEST_CASE ("make_error_code", "[VkBootstrap.error_code]") { + GIVEN ("An InstanceError") { + const auto error = vkb::InstanceError::vulkan_unavailable; + + WHEN ("Creating an error code from it") { + std::error_code ec = make_error_code (error); + THEN ("The error code is equal to the original error") { REQUIRE (ec == error); } + + THEN ("The error code is not equal to an unrelated error") { + REQUIRE (ec != vkb::InstanceError::failed_create_instance); + } + + THEN ("We can get the error message") { + REQUIRE (ec.message () == "vulkan_unavailable"); + } + } + } + + GIVEN ("A PhysicalDeviceError") { + const auto error = vkb::PhysicalDeviceError::no_physical_devices_found; + + WHEN ("Creating an error code from it") { + std::error_code ec = make_error_code (error); + THEN ("The error code is equal to the original error") { REQUIRE (ec == error); } + + THEN ("The error code is not equal to an unrelated error") { + REQUIRE (ec != vkb::InstanceError::failed_create_instance); + } + + THEN ("We can get the error message") { + REQUIRE (ec.message () == "no_physical_devices_found"); + } + } + } + + GIVEN ("A QueueError") { + const auto error = vkb::QueueError::invalid_queue_family_index; + + WHEN ("Creating an error code from it") { + std::error_code ec = make_error_code (error); + THEN ("The error code is equal to the original error") { REQUIRE (ec == error); } + + THEN ("The error code is not equal to an unrelated error") { + REQUIRE (ec != vkb::InstanceError::failed_create_instance); + } + + THEN ("We can get the error message") { + REQUIRE (ec.message () == "invalid_queue_family_index"); + } + } + } + + GIVEN ("A DeviceError") { + const auto error = vkb::DeviceError::failed_create_device; + + WHEN ("Creating an error code from it") { + std::error_code ec = make_error_code (error); + THEN ("The error code is equal to the original error") { REQUIRE (ec == error); } + + THEN ("The error code is not equal to an unrelated error") { + REQUIRE (ec != vkb::InstanceError::failed_create_instance); + } + + THEN ("We can get the error message") { + REQUIRE (ec.message () == "failed_create_device"); + } + } + } + + GIVEN ("A SwapchainError") { + const auto error = vkb::SwapchainError::failed_create_swapchain; + + WHEN ("Creating an error code from it") { + std::error_code ec = make_error_code (error); + THEN ("The error code is equal to the original error") { REQUIRE (ec == error); } + + THEN ("The error code is not equal to an unrelated error") { + REQUIRE (ec != vkb::InstanceError::failed_create_instance); + } + + THEN ("We can get the error message") { + REQUIRE (ec.message () == "failed_create_swapchain"); + } + } + } +}