Constantin Constantin - 3 years ago 170
C++ Question


This question is regarding exception handling in Vulkan-Hpp (official Vulkan C++ bindings).

I wrote a small application using Vulkan-Hpp without

defined (and with exception handlers). But after coming across this stackoverflow question (Are Exceptions in C++ really slow) I started worrying about the penalty for using exceptions there. Then I found out about the define
, but it changes the syntax completely for all calls which could throw an exception (because of different return values): that means, one has to decide before starting implementation to either use
or not (i.e. they can't be enabled for the "Debug" Configuration and disabled for the "Release" Configuration easily).

If exception handling is disabled by defining

is a struct which contains the return
value and the error code in the fields result and value.



surface = instance.createWin32SurfaceKHR(surfaceCreateInfo);


vk::ResultValue<vk::SurfaceKHR> surfaceResult = instance.createWin32SurfaceKHR(surfaceCreateInfo);
if (surfaceResult.result == vk::Result::eSuccess) {
surface = surfaceResult.value;

So given that it is not trivial to change the strategy regarding
at a later stage in development, I wonder in which situations I should use
for my project and in which situations I shouldn't?

I assume there must be some technical rationale behind it, other than just personal taste/opinion.

Answer Source

The principle reason exceptions can be disabled is because many game developers for various platforms turn off exception handling at the compiler level. On some platforms, exception handling is flat out not supported. Those platforms still need a reasonable means to deal with errors, and that requires a different API.

Exceptions have been a hotly debated subject in C++ and likely always will be. While C++ programmers will agree that exceptions should only be used in exceptional circumstances, the line between "exceptional circumstances" and "expected behavior" is ultimately in the eye of the beholder.

Personally, I would consider Vulkan errors to be "exceptional circumstances". Device lost and OOM errors are not things you frequently expect to happen. Plus, your response to them will likely be decidedly non-local; code higher up in the call stack will be what actually deals with it.

Furthermore, many of the functions that error are not the functions commonly encountered in performance critical Vulkan code (vkCmd*, and such). After all, usage errors are supposed to be handled by validation layers and should be impossible at runtime. Errors are usually given for object creation/destruction, and allocations, which are not things you do in the middle of building command buffers.

The erroring function most likely to be found in performance-critical code is vkAllocateDescriptorSets. And while it can error out, can only do so for memory fragmentation reasons. The standard actually requires this:

Any returned error other than VK_ERROR_OUT_OF_POOL_MEMORY_KHR or VK_ERROR_FRAGMENTED_POOL does not imply its usual meaning: applications should assume that the allocation failed due to fragmentation, and create a new descriptor pool.

Fragmentation is something that you can usually prevent, if you have firm control over your input data. Given such control, you can ensure that you never get errors when allocating from descriptor pools.

vkBegin/EndCommandBuffer can error, but only for OOM reasons. Which typically means that there's little you can do to recover, so performance is irrelevant.

The commands that give you serious runtime errors that require actions are typically device commands. And you don't issue such commands in the middle of rendering; vkQueueSubmit is the one exception, and that's at the end of rendering (or beginning; however you want to see it).

This is probably why throwing in VK_HPP is the default.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download