vk-bootstrap

repository·main·Indexed 23 days ago

https://github.com/charles-lunarg/vk-bootstrap

A C++ utility library designed to simplify and accelerate the initialization of Vulkan. It provides a builder pattern via vkb::InstanceBuilder, vkb::PhysicalDeviceSelector, and vkb::DeviceBuilder to handle complex tasks such as instance creation with validation layers, physical device selection based on specific criteria, logical device creation, and queue management.

Tokens
6.4K
Snippets
19
Records
21
Agent score
29%

What's inside vk-bootstrap

  1. Understand the vk-bootstrap build pattern

    main

    vk-bootstrap uses a builder pattern to construct Vulkan objects. You configure the desired settings using various set_* methods and then call .build() to finalize the creation. The result is typically returned as a vkb::Result<T>, which you must check for errors before accessing the underlying wrapper or Vulkan handle.

    vkb::Result<vkb::Wrapper> result = vkb::WrapperBuilder()
                                                .set_thing()
                                                .more_things()
                                                .build();
    
    if (!result) { /* handle error */ }
    
    // The result also holds the vk-bootstrap wrapper
    vkb::Wrapper wrapper = result.value();
    
    // The underlying Vulkan handle can easily be acquired
    VkObject object = wrapper.object;
  2. Select a physical device with PhysicalDeviceSelector

    main

    After creating an instance, use vkb::PhysicalDeviceSelector to find a suitable GPU. The selector prefers discrete GPUs by default. You can specify requirements that must be met for a device to be selected:

    • add_required_extension(name): The device MUST support this extension.
    • add_desired_extension(name): The device should support this, but it's not a hard requirement.
    • set_required_features(features): Sets required VkPhysicalDeviceFeatures.
    • set_required_features_11/12/etc(features): Sets required features for newer Vulkan versions.
    • add_required_extension_features(features): Sets features that are only available via specific extensions.

    Requirements set in the selector automatically propagate to the vkb::DeviceBuilder used later.

    vkb::PhysicalDeviceSelector phys_device_selector(vkb_instance);
    
    // select() grabs a PhysicalDevice
    auto physical_device_selector_return = phys_device_selector.require_things().select();
    if (!physical_device_selector_return) {
        if (physical_device_selector_return.error() == vkb::PhysicalDeviceError::no_suitable_device) {
            const auto& detailed_reasons = physical_device_selector_return.detailed_failure_reasons();
            // ... handle reasons
        }
    }
    
    // Example requirements
    phys_device_selector.add_required_extension("VK_KHR_timeline_semaphore");
    phys_device_selector.add_desired_extension("VK_KHR_imageless_framebuffer");
    
    VkPhysicalDeviceFeatures required_features{};
    required_features.multiViewport = true;
    phys_device_selector.set_required_features(required_features);
    
    VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features{};
    descriptor_indexing_features.<features_used> = true;
    phys_device_selector.add_required_extension_features(descriptor_indexing_features);
  3. Create a Vulkan Surface with GLFW or SDL2

    main

    While vk-bootstrap automatically enables the necessary windowing extensions during VkInstance creation, it does not create the VkSurfaceKHR handle itself, as this is the responsibility of your windowing library. You must use your library's specific functions (like glfwCreateWindowSurface or SDL_Vulkan_CreateSurface) after creating the vkb::Instance.

    If your application needs to present but cannot create a VkSurfaceKHR handle before physical device selection, call defer_surface_initialization() on your instance builder to prevent the no_surface_provided error.

    // 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 */ }
  4. Clean up Vulkan resources

    main

    To properly shut down your application, destroy resources in the reverse order of their creation. Use the following vkb utility functions:

    1. vkb::destroy_swapchain(swapchain)
    2. vkb::destroy_device(device)
    3. vkb::destroy_surface(instance, surface)
    4. vkb::destroy_instance(instance)
    vkb::Instance instance;
    VkSurfaceKHR surface;
    vkb::Device device;
    vkb::Swapchain swapchain;
    
    // ... renderer logic ...
    
    vkb::destroy_swapchain(swapchain);
    vkb::destroy_device(device);
    vkb::destroy_surface(instance, surface);
    vkb::destroy_instance(instance);
  5. Initialize Vulkan with vk-bootstrap

    main

    Use the vkb::InstanceBuilder, vkb::PhysicalDeviceSelector, and vkb::DeviceBuilder classes to simplify the Vulkan initialization lifecycle. This includes creating an instance with validation layers, selecting a physical device based on specific criteria (like surface support or queue requirements), and creating a logical device with access to specific queues.

    #include "VkBootstrap.h"
    
    void init_vulkan () {
        vkb::InstanceBuilder builder;
        auto inst_ret = builder.set_app_name ("Example Vulkan Application")
                            .request_validation_layers ()
                            .use_default_debug_messenger ()
                            .build ();
        if (!inst_ret) { /* report */ }
        vkb::Instance vkb_inst = inst_ret.value ();
    
        vkb::PhysicalDeviceSelector selector{ vkb_inst };
        auto phys_ret = selector.set_surface (surface)
                            .set_minimum_version (1, 1)
                            .require_dedicated_transfer_queue ()
                            .select ();
        if (!phys_ret) { /* report */ }
    
        vkb::DeviceBuilder device_builder{ phys_ret.value () };
        auto dev_ret = device_builder.build ();
        if (!dev_ret) { /* report */ }
        vkb::Device vkb_device = dev_ret.value ();
    
        auto graphics_queue_ret = vkb_device.get_queue (vkb::QueueType::graphics);
        if (!graphics_queue_ret)  { /* report */ }
        VkQueue graphics_queue = graphics_queue_ret.value ();
    }
  6. Install vk-bootstrap via git-submodule and CMake

    main

    Add the repository as a git submodule and use add_subdirectory in your CMake configuration.

    1. Add the submodule:
    git submodule add https://github.com/charles-lunarg/vk-bootstrap
    1. Configure CMake:
    add_subdirectory(vk-bootstrap)
    target_link_libraries(your_application_name vk-bootstrap::vk-bootstrap)
  7. Install vk-bootstrap via Copy-Paste

    main

    For a simple integration, copy the following files directly into your project and include them in your build process. Note that vk-bootstrap is not a header-only library.

    Required files:

    • src/VkBootstrap.h
    • src/VkBootstrapDispatch.h
    • src/VkBootstrap.cpp

    Linux Specific Requirement: On Unix platforms, the library loads symbols at runtime. You must link against the system dynamic linker. If using CMake, link with ${CMAKE_DL_LIBS}.

  8. Configure custom queues

    main

    For fine-grained control, you can provide a list of vkb::CustomQueueDescription objects to the device builder. You can obtain queue family information from the vkb::PhysicalDevice using get_queue_families().

    std::vector<vkb::CustomQueueDescription> queue_descriptions;
    auto queue_families = phys_device.get_queue_families ();
    for (uint32_t i = 0; i < static_cast<uint32_t>(queue_families.size ()); i++) {
        if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            // Find the first queue family with graphics operations supported
            queue_descriptions.push_back (vkb::CustomQueueDescription (
                i, std::vector<float> (queue_families[i].queueCount, 1.0f)));
        }
    }
    // These descriptions would then be used in the DeviceBuilder
  9. Enable instance-level extensions and layers

    main

    Use vkb::SystemInfo to query available capabilities before enabling them in the InstanceBuilder.

    • Use system_info.is_layer_available(name) to check for specific layers.
    • Use system_info.is_extension_available(name) to check for extensions.
    • Use instance_builder.enable_validation_layers() combined with .use_default_debug_messenger() for standard validation setup.
    vkb::InstanceBuilder instance_builder;
    
    auto system_info_ret = vkb::SystemInfo::get_system_info();
    if (!system_info_ret) { /* report error */ }
    auto system_info = system_info_ret.value();
    
    // check for a layer
    if (system_info.is_layer_available("VK_LAYER_LUNARG_api_dump")) {
        instance_builder.enable_layer("VK_LAYER_LUNARG_api_dump");
    }
    
    // validation layers
    if (system_info.validation_layers_available){
        instance_builder.enable_validation_layers()
                        .use_default_debug_messenger();
    }
    
    // instance level extension
    if (system_info.is_extension_available("VK_KHR_get_physical_device_properties2")) {
        instance_builder.enable_extension("VK_KHR_get_physical_device_properties2");
    }
  10. Install vk-bootstrap via CMake FetchContent

    main

    If using CMake 3.12 or higher, you can use FetchContent to automatically download and build the library.

    include(FetchContent)
    FetchContent_Declare(
        fetch_vk_bootstrap
        GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap
        GIT_TAG        BRANCH_OR_TAG # Use a specific tag to ensure stability
    )
    FetchContent_MakeAvailable(fetch_vk_bootstrap)
    target_link_libraries(your_application_name vk-bootstrap::vk-bootstrap)
    include(FetchContent)
    FetchContent_Declare(
        fetch_vk_bootstrap
        GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap
        GIT_TAG        BRANCH_OR_TAG #suggest using a tag so the library doesn't update whenever new commits are pushed to a branch
    )
    FetchContent_MakeAvailable(fetch_vk_bootstrap)
    target_link_libraries(your_application_name vk-bootstrap::vk-bootstrap)
  11. Set a custom Debug Callback for Validation Layers

    main

    By default, calling .use_default_debug_messenger() on the InstanceBuilder provides a standard callback. To control how validation layer messages are logged (e.g., to redirect to a file or a custom UI), use .set_debug_callback() with a lambda or function matching the Vulkan debug callback signature.

    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;
            }
        );