vk-bootstrap/docs/getting_started.md
2020-04-19 00:42:57 -06:00

5.4 KiB

Getting Started

vk-bootstrap reduces the complexity of setting up a vulkan application into 3 main steps; instance creation, Physical device selection, and device creation.

Instance Creation

Creating an instance with vk-bootstrap uses the vkb::InstanceBuilder class.

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 which can be either a valid vkb::Instance struct, containing the VkInstance handle, or the builder returns an error.

if (!instance_builder_return) {
    printf("Failed to create Vulkan instance. Cause %s\n", 
        vkb::to_string(instance_builder_return.error().type));
    return -1;
} 

Now that any possible errors have been handled,

vkb::Instance vkb_instance = instance_builder_return.value();

This is enough to create a usable VkInstance but most use cases will want to customize it a bit. To do so, simply call the member functions on the vkb::InstanceBuilder object before build() is called.

The most common customization to instance creation is enabling the "Validation Layers", an invaluable tool for any vulkan application developer.

instance_builder.request_validation_layers ();

The other common customization point is setting up the Debug Messenger Callback, the mechanism in which an application can control what and where the "Validation Layers" log its output.

instance_builder.set_debug_callback (
    [] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
	    VkDebugUtilsMessageTypeFlagsEXT messageType,
	    const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
	    void *pUserData) 
        -> VkBool32 {
			auto severity = vkb::to_string_message_severity (messageSeverity);
			auto type = vkb::to_string_message_type (messageType);
			printf ("[%s: %s] %s\n", severity, type, pCallbackData->pMessage);
			return VK_FALSE;
		}
    );

Alternatively, vk-bootstrap provides a 'default debug messenger' that prints to standard output.

instance_builder.use_default_debug_messenger();

Configuration can be chained together and done inline with building, like so.

auto inst_builder_ret = instance_builder 
        .set_app_name ("Awesome Vulkan Application")
        .set_engine_name("Excellent Game Engine")
        .require_api_version(1,0,0)
        .build();

The vkb::Instance struct is meant to hold all the necessary instance level data to enable proper Physical Device selection. It also is meant for easy destructuring into custom classes if so desired.

struct CustomVulkanWrapper {
    VkInstance instance;
    //...
};
CustomVulkanWrapper custom_vk_class;
custom_vk_class.instance = vkb_instance.instance;

When the application is finished with the vulkan, call vkb::destroy_instance() to dispose of the instance and associated data.

// cleanup 
vkb::destroy_instance(vkb_instance);

Instance Creation Summary

vkb::InstanceBuilder instance_builder;
auto instance_builder_return = instance_builder
        // Instance creation configuration
        .request_validation_layers()
        .use_default_debug_messenger()
        .build ();
if (!instance_builder_return) {
    // Handle error
} 
vkb::Instance vkb_instance = instance_builder_return.value ();

// at program end
vkb::destroy_instance(vkb_instance);

Surface Creation

Vulkan requires manually creating a surface, called VkSurfaceKHR. Creating a surface is the responsibility of the windowing system, thus is out of scope for vk-bootstrap. However, it does try to make the process as painless as possible by automatically enabling the correct windowing extensions in VkInstance creation.

Windowing libraries which support Vulkan usually provide a way of getting the VkSurfaceKHR handle for the window. These methods require a valid Vulkan instance, thus must be done after instance creation.

Examples for GLFW and SDL2 are listed below.

vkb::Instance vkb_instance; //valid vkb::Instance
VkSurfaceKHR surface = nullptr;
// window is a valid library specific Window handle

// GLFW
VkResult err = glfwCreateWindowSurface (vkb_instance.instance, window, NULL, &surface);
if (err != VK_SUCCESS) { /* handle error */ }

// SDL2
SDL_bool err = SDL_Vulkan_CreateSurface(window, vkb_instance.instance, &surface);
if (!err){ /* handle error */ }

Physical Device Selection

Once a Vulkan instance has been created, the next step is to find a suitable GPU for the application to use. vk-bootstrap provide the vkb::PhysicalDeviceSelector class to streamline this process.

Creating a vkb::PhysicalDeviceSelector requires a valid vkb::Instance to construct.

It follows the same pattern laid out by vkb::InstanceBuilder.

vkb::PhysicalDeviceSelector phys_device_selector (vkb_instance); 
auto physical_device_selector_return = phys_device_selector
        .set_surface(surface_handle)
        .select ();
if (!physical_device_selector_return) {
    // Handle error
}
auto phys_device = phys_device_ret.value ();

To select a physical device, call select() on the vkb::PhysicalDeviceSelector object. By default, this will prefer a discrete GPU.

No cleanup is required for vkb::PhysicalDevice.

// TODO -- configuring selection, querying phys device details, explaining why a surface is needed

Device Creation

// TODO

Swapchain

// TODO

Error Handling

// TODO