Error handling with std::error_code

This commit is contained in:
Lesley Lai 2020-05-17 07:31:02 -06:00
parent 120a162264
commit df53490ede
7 changed files with 367 additions and 169 deletions

View File

@ -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();
```

View File

@ -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 ();

View File

@ -15,22 +15,18 @@ void get_inst_proc_addr (
// Helper for robustly executing the two-call pattern
template <typename T, typename F, typename... Ts>
auto get_vector (F&& f, Ts&&... ts) -> Expected<std::vector<T>, VkResult> {
auto get_vector (std::vector<T>& out, F&& f, Ts&&... ts) -> VkResult {
uint32_t count = 0;
std::vector<T> 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;
}
template <typename T, typename F, typename... Ts>
@ -167,8 +163,64 @@ void setup_pNext_chain (T& structure, std::vector<VkBaseOutStructure*> 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<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));
}
};
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<QueueError> (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<DeviceError> (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<SwapchainError> (err));
}
};
const SwapchainErrorCategory swapchain_error_category;
} // namespace detail
std::error_code make_error_code (InstanceError instance_error) {
return { static_cast<int> (instance_error), detail::instance_error_category };
}
std::error_code make_error_code (PhysicalDeviceError physical_device_error) {
return { static_cast<int> (physical_device_error), detail::physical_device_error_category };
}
std::error_code make_error_code (QueueError queue_error) {
return { static_cast<int> (queue_error), detail::queue_error_category };
}
std::error_code make_error_code (DeviceError device_error) {
return { static_cast<int> (device_error), detail::device_error_category };
}
std::error_code make_error_code (SwapchainError swapchain_error) {
return { static_cast<int> (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<VkExtensionProperties> (vkEnumerateInstanceExtensionProperties, nullptr);
if (available_extensions_ret.has_value ()) {
this->available_extensions = available_extensions_ret.value ();
auto available_extensions_ret = detail::get_vector<VkExtensionProperties> (
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<VkLayerProperties> (vkEnumerateInstanceLayerProperties);
if (available_layers_ret.has_value ()) {
this->available_layers = available_layers_ret.value ();
auto available_layers_ret =
detail::get_vector<VkLayerProperties> (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<Instance, detail::Error<InstanceError>> InstanceBuilder::build () const {
detail::Result<Instance> InstanceBuilder::build () const {
uint32_t api_version = VK_MAKE_VERSION (1, 0, 0);
@ -300,15 +355,15 @@ detail::Expected<Instance, detail::Error<InstanceError>> 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>{ 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>{ 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>{ InstanceError::vulkan_version_1_1_unavailable };
return make_error_code (InstanceError::vulkan_version_1_1_unavailable);
else
return detail::Error<InstanceError>{ 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<Instance, detail::Error<InstanceError>> InstanceBuilder::build
}
bool all_extensions_supported = detail::check_extensions_supported (system.available_extensions, extensions);
if (!all_extensions_supported) {
return detail::Error<InstanceError>{ InstanceError::requested_extensions_not_present };
return make_error_code (InstanceError::requested_extensions_not_present);
}
std::vector<const char*> layers;
@ -366,7 +421,7 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
}
bool all_layers_supported = detail::check_layers_supported (system.available_layers, layers);
if (!all_layers_supported) {
return detail::Error<InstanceError>{ InstanceError::requested_layers_not_present };
return make_error_code (InstanceError::requested_layers_not_present);
}
std::vector<VkBaseOutStructure*> pNext_chain;
@ -417,7 +472,7 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
Instance instance;
VkResult res = vkCreateInstance (&instance_create_info, info.allocation_callbacks, &instance.instance);
if (res != VK_SUCCESS)
return detail::Error<InstanceError>{ InstanceError::failed_create_instance, res };
return detail::Result<Instance> (InstanceError::failed_create_instance, res);
if (info.use_debug_messenger) {
res = create_debug_utils_messenger (instance.instance,
@ -427,7 +482,7 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
&instance.debug_messenger,
info.allocation_callbacks);
if (res != VK_SUCCESS) {
return detail::Error<InstanceError>{ InstanceError::failed_create_debug_messenger, res };
return detail::Result<Instance> (InstanceError::failed_create_debug_messenger, res);
}
}
@ -538,12 +593,13 @@ namespace detail {
std::vector<const char*> check_device_extension_support (
VkPhysicalDevice device, std::vector<const char*> desired_extensions) {
auto available_extensions =
detail::get_vector<VkExtensionProperties> (vkEnumerateDeviceExtensionProperties, device, nullptr);
if (!available_extensions.has_value ()) return {};
std::vector<VkExtensionProperties> available_extensions;
auto available_extensions_ret = detail::get_vector<VkExtensionProperties> (
available_extensions, vkEnumerateDeviceExtensionProperties, device, nullptr);
if (available_extensions_ret != VK_SUCCESS) return {};
std::vector<const char*> 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<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> present_modes;
auto formats = detail::get_vector<VkSurfaceFormatKHR> (
vkGetPhysicalDeviceSurfaceFormatsKHR, pd.phys_device, system_info.surface);
auto present_modes = detail::get_vector<VkPresentModeKHR> (
vkGetPhysicalDeviceSurfacePresentModesKHR, pd.phys_device, system_info.surface);
auto formats_ret = detail::get_vector<VkSurfaceFormatKHR> (
formats, vkGetPhysicalDeviceSurfaceFormatsKHR, pd.phys_device, system_info.surface);
auto present_modes_ret = detail::get_vector<VkPresentModeKHR> (
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<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDeviceSelector::select () const {
detail::Result<PhysicalDevice> PhysicalDeviceSelector::select () const {
if (!system_info.headless && !criteria.defer_surface_initialization) {
if (system_info.surface == nullptr)
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_surface_provided };
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_surface_provided };
}
auto physical_devices =
detail::get_vector<VkPhysicalDevice> (vkEnumeratePhysicalDevices, system_info.instance);
if (!physical_devices.has_value ()) {
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::failed_enumerate_physical_devices,
physical_devices.error () };
std::vector<VkPhysicalDevice> physical_devices;
auto physical_devices_ret = detail::get_vector<VkPhysicalDevice> (
physical_devices, vkEnumeratePhysicalDevices, system_info.instance);
if (physical_devices_ret != VK_SUCCESS) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::failed_enumerate_physical_devices,
physical_devices_ret };
}
if (physical_devices.value ().size () == 0) {
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_physical_devices_found };
if (physical_devices.size () == 0) {
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_physical_devices_found };
}
std::vector<PhysicalDeviceDesc> 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<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDev
}
if (selected_device.phys_device == VK_NULL_HANDLE) {
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_suitable_device };
return detail::Result<PhysicalDevice>{ PhysicalDeviceError::no_suitable_device };
}
PhysicalDevice out_device{};
out_device.physical_device = selected_device.phys_device;
@ -949,43 +1010,43 @@ std::vector<VkQueueFamilyProperties> PhysicalDevice::get_queue_families () const
// ---- Queues ---- //
detail::Expected<uint32_t, detail::Error<QueueError>> Device::get_queue_index (QueueType type) const {
detail::Result<uint32_t> 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>{ QueueError::present_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::present_unavailable };
break;
case QueueType::graphics:
index = detail::get_graphics_queue_index (queue_families);
if (index < 0) return detail::Error<QueueError>{ QueueError::graphics_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::graphics_unavailable };
break;
case QueueType::compute:
index = detail::get_separate_compute_queue_index (queue_families);
if (index < 0) return detail::Error<QueueError>{ QueueError::compute_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::compute_unavailable };
break;
case QueueType::transfer:
index = detail::get_separate_transfer_queue_index (queue_families);
if (index < 0) return detail::Error<QueueError>{ QueueError::transfer_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::transfer_unavailable };
break;
default:
return detail::Error<QueueError>{ QueueError::invalid_queue_family_index };
return detail::Result<uint32_t>{ QueueError::invalid_queue_family_index };
}
return static_cast<uint32_t> (index);
}
detail::Expected<uint32_t, detail::Error<QueueError>> Device::get_dedicated_queue_index (QueueType type) const {
detail::Result<uint32_t> 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>{ QueueError::compute_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::compute_unavailable };
break;
case QueueType::transfer:
index = detail::get_dedicated_transfer_queue_index (queue_families);
if (index < 0) return detail::Error<QueueError>{ QueueError::transfer_unavailable };
if (index < 0) return detail::Result<uint32_t>{ QueueError::transfer_unavailable };
break;
default:
return detail::Error<QueueError>{ QueueError::invalid_queue_family_index };
return detail::Result<uint32_t>{ QueueError::invalid_queue_family_index };
}
return static_cast<uint32_t> (index);
}
@ -996,14 +1057,14 @@ VkQueue get_queue (VkDevice device, uint32_t family) {
return out_queue;
}
} // namespace detail
detail::Expected<VkQueue, detail::Error<QueueError>> Device::get_queue (QueueType type) const {
detail::Result<VkQueue> 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<VkQueue, detail::Error<QueueError>> Device::get_dedicated_queue (QueueType type) const {
detail::Result<VkQueue> 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<Device, detail::Error<DeviceError>> DeviceBuilder::build () const {
detail::Result<Device> DeviceBuilder::build () const {
std::vector<CustomQueueDescription> queue_descriptions;
queue_descriptions.insert (
@ -1079,7 +1140,7 @@ detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () con
info.allocation_callbacks,
&device.device);
if (res != VK_SUCCESS) {
return detail::Error<DeviceError>{ 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<SurfaceSupportDetails, detail::Error<SurfaceSupportError>> 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<SurfaceSupportError> (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<int> (surface_support_error), detail::surface_support_error_category };
}
Result<SurfaceSupportDetails> query_surface_support_details (VkPhysicalDevice phys_device, VkSurfaceKHR surface) {
if (surface == VK_NULL_HANDLE)
return detail::Error<SurfaceSupportError>{ 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>{ SurfaceSupportError::failed_get_surface_capabilities, res };
return { make_error_code (SurfaceSupportError::failed_get_surface_capabilities), res };
}
auto formats = detail::get_vector<VkSurfaceFormatKHR> (
vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface);
if (!formats.has_value ())
return detail::Error<SurfaceSupportError>{ SurfaceSupportError::failed_enumerate_surface_formats,
formats.error () };
auto present_modes = detail::get_vector<VkPresentModeKHR> (
vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface);
if (!present_modes.has_value ())
return detail::Error<SurfaceSupportError>{ SurfaceSupportError::failed_enumerate_present_modes,
formats.error () };
return SurfaceSupportDetails{ capabilities, formats.value (), present_modes.value () };
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> present_modes;
auto formats_ret = detail::get_vector<VkSurfaceFormatKHR> (
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<VkPresentModeKHR> (
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<VkSurfaceFormatKHR> const& available_formats,
@ -1220,13 +1305,10 @@ SwapchainBuilder::SwapchainBuilder (
info.graphics_queue_index = static_cast<uint32_t> (graphics_queue_index);
info.present_queue_index = static_cast<uint32_t> (present_queue_index);
}
detail::Expected<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::build () const {
return build (VK_NULL_HANDLE);
}
detail::Expected<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::build (
VkSwapchainKHR old_swapchain) const {
detail::Result<Swapchain> SwapchainBuilder::build () const { return build (VK_NULL_HANDLE); }
detail::Result<Swapchain> SwapchainBuilder::build (VkSwapchainKHR old_swapchain) const {
if (info.surface == VK_NULL_HANDLE) {
return detail::Error<SwapchainError>{ 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<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::bui
auto surface_support = detail::query_surface_support_details (info.physical_device, info.surface);
if (!surface_support.has_value ())
return detail::Error<SwapchainError>{ 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<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::bui
VkResult res = vkCreateSwapchainKHR (
info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain);
if (res != VK_SUCCESS) {
return detail::Error<SwapchainError>{ 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>{ SwapchainError::failed_get_swapchain_images };
return detail::Error{ SwapchainError::failed_get_swapchain_images };
}
swapchain.image_count = static_cast<uint32_t> (images.value ().size ());
swapchain.allocation_callbacks = info.allocation_callbacks;
return swapchain;
}
detail::Expected<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::recreate (
Swapchain const& swapchain) const {
detail::Result<Swapchain> SwapchainBuilder::recreate (Swapchain const& swapchain) const {
return build (swapchain.swapchain);
}
detail::Expected<std::vector<VkImage>, detail::Error<SwapchainError>> Swapchain::get_images () {
auto swapchain_images = detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, device, swapchain);
if (!swapchain_images) {
return detail::Error<SwapchainError>{ SwapchainError::failed_get_swapchain_images,
swapchain_images.error () };
detail::Result<std::vector<VkImage>> Swapchain::get_images () {
std::vector<VkImage> swapchain_images;
auto swapchain_images_ret =
detail::get_vector<VkImage> (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<std::vector<VkImageView>, detail::Error<SwapchainError>> Swapchain::get_image_views () {
detail::Result<std::vector<VkImageView>> 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<std::vector<VkImageView>, detail::Error<SwapchainError>> Swapch
VkResult res = vkCreateImageView (device, &createInfo, allocation_callbacks, &views[i]);
if (res != VK_SUCCESS)
return detail::Error<SwapchainError>{ SwapchainError::failed_create_swapchain_image_views, res };
return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res };
}
return views;
}

View File

@ -3,93 +3,94 @@
#include <cassert>
#include <vector>
#include <system_error>
#include <vulkan/vulkan.h>
namespace vkb {
namespace detail {
template <typename ErrorType> 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 <typename E, typename U> class Expected {
template <typename T> 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<Instance, detail::Error<InstanceError>> build () const;
detail::Result<Instance> 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<PhysicalDevice, detail::Error<PhysicalDeviceError>> select () const;
detail::Result<PhysicalDevice> 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<VkQueueFamilyProperties> queue_families;
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE;
detail::Expected<uint32_t, detail::Error<QueueError>> get_queue_index (QueueType type) const;
detail::Result<uint32_t> 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<uint32_t, detail::Error<QueueError>> get_dedicated_queue_index (QueueType type) const;
detail::Result<uint32_t> get_dedicated_queue_index (QueueType type) const;
detail::Expected<VkQueue, detail::Error<QueueError>> get_queue (QueueType type) const;
detail::Result<VkQueue> 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<VkQueue, detail::Error<QueueError>> get_dedicated_queue (QueueType type) const;
detail::Result<VkQueue> 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<Device, detail::Error<DeviceError>> build () const;
detail::Result<Device> 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<std::vector<VkImage>, detail::Error<SwapchainError>> get_images ();
detail::Result<std::vector<VkImage>> get_images ();
// Returns a vector of VkImageView's to the VkImage's of the swapchain
// VkImageViews must be destroyed
detail::Expected<std::vector<VkImageView>, detail::Error<SwapchainError>> get_image_views ();
detail::Result<std::vector<VkImageView>> get_image_views ();
void destroy_image_views (std::vector<VkImageView> 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<Swapchain, detail::Error<SwapchainError>> build () const;
detail::Expected<Swapchain, detail::Error<SwapchainError>> recreate (Swapchain const& swapchain) const;
detail::Result<Swapchain> build () const;
detail::Result<Swapchain> 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<VkSurfaceFormatKHR>& formats) const;
void add_desired_present_modes (std::vector<VkPresentModeKHR>& modes) const;
// for use in swapchain recreation
detail::Expected<Swapchain, detail::Error<SwapchainError>> build (VkSwapchainKHR old_swapchain) const;
detail::Result<Swapchain> build (VkSwapchainKHR old_swapchain) const;
struct SwapchainInfo {
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
@ -566,3 +574,12 @@ class SwapchainBuilder {
};
} // namespace vkb
namespace std {
template <> struct is_error_code_enum<vkb::InstanceError> : true_type {};
template <> struct is_error_code_enum<vkb::PhysicalDeviceError> : true_type {};
template <> struct is_error_code_enum<vkb::QueueError> : true_type {};
template <> struct is_error_code_enum<vkb::DeviceError> : true_type {};
template <> struct is_error_code_enum<vkb::SwapchainError> : true_type {};
} // namespace std

View File

@ -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

View File

@ -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 ());

View File

@ -0,0 +1,98 @@
#include <catch2/catch.hpp>
#include "VkBootstrap.h"
TEST_CASE ("is_error_code_enum", "[VkBootstrap.error_code]") {
STATIC_REQUIRE (std::is_error_code_enum<vkb::InstanceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::PhysicalDeviceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::QueueError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::DeviceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::SwapchainError>::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");
}
}
}
}