Added documentation to InstanBuilder and PhysicalDevice Selector

Updated readme's example code and included better build instructions
This commit is contained in:
Charles Giessen 2020-02-18 19:12:31 -07:00
parent 7de79b81b1
commit 37656d311a
5 changed files with 126 additions and 71 deletions

View File

@ -1,11 +1,11 @@
# Vk-Bootstrap # Vk-Bootstrap
A Vulkan utility library meant to jump start a Vulkan Application A Vulkan utility library meant to jump start any Vulkan Application
This library simplifies the tedious process of: This library simplifies the tedious process of:
* Instance Creation * Instance Creation
* Physical Device Selection * Picking a Physical Device
* Device Creation * Device Creation
* Getting Queues * Getting Queues
* Swapchain Creation * Swapchain Creation
@ -14,7 +14,7 @@ It also adds several conveniences for:
* enabling validation layers * enabling validation layers
* setting up a debug callback * setting up a debug callback
* selecting a gpu based on a small set of common criteria * select a gpu based on a set of common criteria like features, extensions, memory, etc.
## Example ## Example
@ -22,65 +22,74 @@ It also adds several conveniences for:
#include "VkBootstrap.h" #include "VkBootstrap.h"
void device_init() void init_vulkan()
{ {
vkb::InstanceBuilder builder; vkb::InstanceBuilder builder;
builder.setup_validation_layers() builder.check_and_setup_validation_layers()
.set_app_name ("example") .set_app_name ("Example Vulkan Application")
.set_default_debug_messenger (); .set_default_debug_messenger ();
auto inst_ret = builder.build(); auto inst_ret = builder.build();
if (!inst_ret.has_value()) { if (!inst_ret.has_value()) {
// error // error
} }
vkb::Instance inst = inst_ret.value(); vkb::Instance vkb_inst = inst_ret.value();
vkb::PhysicalDeviceSelector selector{ inst }; vkb::PhysicalDeviceSelector selector{ inst };
selector.set_surface (/* from user created window*/) selector.set_surface (/* from user created window*/)
.set_minimum_version (1, 0) .set_minimum_version (1, 1) //require a vulkan 1.1 capable device
.require_dedicated_transfer_queue(); .require_dedicated_transfer_queue(); //require a transfer queue
auto phys_ret = selector.select (); auto phys_ret = selector.select ();
if (!phys_ret.has_value()) { if (!phys_ret.has_value()) {
// error // error
} }
vkb::PhysicalDevice physical_device = phys_ret.value();
vkb::DeviceBuilder device_builder{ physical_device }; vkb::DeviceBuilder device_builder{ phys_ret.value() };
auto dev_ret = device_builder.build (); auto dev_ret = device_builder.build ();
if (!dev_ret.has_value()){ if (!dev_ret.has_value()){
// error // error
} }
vkb::Device device = dev_ret.value();
auto graphics_queue_ret = vkb::get_graphics_queue(device); // Get the VkDevice handle used in the rest of a vulkan application
VkDevice device = dev_ret.value().device;
// Get the graphics queue with a helper function
auto graphics_queue_ret = vkb::get_graphics_queue(dev_ret.value());
if (!graphics_queue_ret.has_value()){ if (!graphics_queue_ret.has_value()){
//error // error
} }
VkQueue graphics_queue = graphics_queue_ret.value(); VkQueue graphics_queue = graphics_queue_ret.value();
// Reduced 400-500 lines of boilerplate to a less than fifty.
} }
``` ```
## Using this library See `example/triangle.cpp` for an example that renders a triangle to the screen.
#### Simple ## Setting up vk-bootstrap
Copy the `src/VkBootstrap.h` and `src/VkBootstrap.cpp` into your project ### Simple
#### With git-submodule + CMake This library has no external dependencies.
Simply copy the `src/VkBootstrap.h` and `src/VkBootstrap.cpp` files into your project and compile them as you normally would
add this project as a git-submodule ### With git-submodule + CMake
Add this project as a git-submodule
```bash ```bash
git submodule add https://github.com/charles-lunarg/vk-bootstrap git submodule add https://github.com/charles-lunarg/vk-bootstrap
``` ```
Then add the project with cmake With CMake, add the project as a subdirectory
```cmake ```cmake
add_subdirectory(vk-bootstrap) add_subdirectory(vk-bootstrap)
target_link_libraries(your_application_name vk-bootstrap)
``` ```
## Manually Building ### Manually Building
```bash ```bash
git clone https://github.com/charles-lunarg/vk-bootstrap git clone https://github.com/charles-lunarg/vk-bootstrap
@ -90,17 +99,17 @@ cd build
cmake .. cmake ..
``` ```
To test, glfw and Catch2 are needed and automatically included using git submodules. ## Testing
In the project directory, run To test, glfw and Catch2 are automatically included using git submodules.
In the project directory, run the following to get the required dependencies to test.
```bash ```bash
git submodule update --init git submodule update --init
``` ```
to get the required dependencies for testing setup. In the build directory, enable tests by adding `-DVK_BOOTSTRAP_TEST` to the cmake command line arguments
Then return to the build directory and enable tests with `VK_BOOTSTRAP_TEST`
```bash ```bash
cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON
@ -112,4 +121,3 @@ cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON
* More examples * More examples
* Testing * Testing
* Documenting API * Documenting API
* Fleshing out device configuration

View File

@ -42,7 +42,8 @@ int device_initialization (Init& init) {
init.window = create_window_glfw (false); init.window = create_window_glfw (false);
vkb::InstanceBuilder instance_builder; vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.set_default_debug_messenger ().setup_validation_layers ().build (); auto instance_ret =
instance_builder.set_default_debug_messenger ().check_and_setup_validation_layers ().build ();
if (!instance_ret) { if (!instance_ret) {
std::cout << static_cast<uint32_t> (instance_ret.error ().type) << "\n"; std::cout << static_cast<uint32_t> (instance_ret.error ().type) << "\n";
} }

View File

@ -312,37 +312,37 @@ InstanceBuilder& InstanceBuilder::set_api_version (uint32_t major, uint32_t mino
info.api_version = VK_MAKE_VERSION (major, minor, patch); info.api_version = VK_MAKE_VERSION (major, minor, patch);
return *this; return *this;
} }
InstanceBuilder& InstanceBuilder::add_layer (const char* layer_name) { InstanceBuilder& InstanceBuilder::must_enable_layer (const char* layer_name) {
if (!layer_name) return *this; if (!layer_name) return *this;
info.layers.push_back (layer_name); info.layers.push_back (layer_name);
return *this; return *this;
} }
InstanceBuilder& InstanceBuilder::add_extension (const char* extension_name) { InstanceBuilder& InstanceBuilder::must_enable_extension (const char* extension_name) {
if (!extension_name) return *this; if (!extension_name) return *this;
info.extensions.push_back (extension_name); info.extensions.push_back (extension_name);
return *this; return *this;
} }
bool InstanceBuilder::check_and_add_layer (const char* layer_name) { InstanceBuilder& InstanceBuilder::check_and_add_layer (const char* layer_name) {
if (!layer_name) return false; if (!layer_name) return *this;
bool available = detail::check_layer_supported (system.available_layers, layer_name); if (detail::check_layer_supported (system.available_layers, layer_name))
if (available) info.layers.push_back (layer_name); info.layers.push_back (layer_name);
return available; return *this;
} }
bool InstanceBuilder::check_and_add_extension (const char* extension_name) { InstanceBuilder& InstanceBuilder::check_and_add_extension (const char* extension_name) {
if (!extension_name) return false; if (!extension_name) return *this;
bool available = detail::check_extension_supported (system.available_extensions, extension_name); if (detail::check_extension_supported (system.available_extensions, extension_name))
if (available) info.extensions.push_back (extension_name); info.extensions.push_back (extension_name);
return available; return *this;
} }
InstanceBuilder& InstanceBuilder::setup_validation_layers (bool enable_validation) { InstanceBuilder& InstanceBuilder::must_enable_validation_layers (bool enable_validation) {
info.enable_validation_layers = enable_validation; info.enable_validation_layers = enable_validation;
return *this; return *this;
} }
bool InstanceBuilder::check_and_setup_validation_layers (bool enable_validation) { InstanceBuilder& InstanceBuilder::check_and_setup_validation_layers (bool enable_validation) {
bool available = bool available =
detail::check_extension_supported (system.available_extensions, detail::validation_layer_name); detail::check_extension_supported (system.available_extensions, detail::validation_layer_name);
setup_validation_layers (available); info.enable_validation_layers = available;
return available; return *this;
} }
InstanceBuilder& InstanceBuilder::set_default_debug_messenger () { InstanceBuilder& InstanceBuilder::set_default_debug_messenger () {
@ -513,8 +513,8 @@ int get_graphics_queue_index (std::vector<VkQueueFamilyProperties> const& famili
} }
return -1; return -1;
} }
// finds a compute queue which is distinct from the graphics queue and tries to find one without transfer support // finds a compute queue which is distinct from the graphics queue and tries to find one without
// returns -1 if none is found // transfer support returns -1 if none is found
int get_distinct_compute_queue_index (std::vector<VkQueueFamilyProperties> const& families) { int get_distinct_compute_queue_index (std::vector<VkQueueFamilyProperties> const& families) {
int compute = -1; int compute = -1;
for (int i = 0; i < families.size (); i++) { for (int i = 0; i < families.size (); i++) {
@ -529,8 +529,8 @@ int get_distinct_compute_queue_index (std::vector<VkQueueFamilyProperties> const
} }
return compute; return compute;
} }
// finds a transfer queue which is distinct from the graphics queue and tries to find one without compute support // finds a transfer queue which is distinct from the graphics queue and tries to find one without
// returns -1 if none is found // compute support returns -1 if none is found
int get_distinct_transfer_queue_index (std::vector<VkQueueFamilyProperties> const& families) { int get_distinct_transfer_queue_index (std::vector<VkQueueFamilyProperties> const& families) {
int transfer = -1; int transfer = -1;
for (int i = 0; i < families.size (); i++) { for (int i = 0; i < families.size (); i++) {

View File

@ -124,37 +124,61 @@ void destroy_instance (Instance instance); // release instance resources
class InstanceBuilder { class InstanceBuilder {
public: public:
InstanceBuilder(); //automatically gets available layers and extensions InstanceBuilder (); // automatically gets available layers and extensions.
detail::Expected<Instance, detail::Error<InstanceError>> build (); // use builder pattern detail::Expected<Instance, detail::Error<InstanceError>> build ();
// Sets the name of the application. Defaults to "" if none is provided.
InstanceBuilder& set_app_name (const char* app_name); InstanceBuilder& set_app_name (const char* app_name);
// Sets the name of the engine. Defaults to "" if none is provided.
InstanceBuilder& set_engine_name (const char* engine_name); InstanceBuilder& set_engine_name (const char* engine_name);
// Sets the (major, minor, patch) version of the application.
InstanceBuilder& set_app_version (uint32_t major, uint32_t minor, uint32_t patch); InstanceBuilder& set_app_version (uint32_t major, uint32_t minor, uint32_t patch);
// Sets the (major, minor, patch) version of the engine.
InstanceBuilder& set_engine_version (uint32_t major, uint32_t minor, uint32_t patch); InstanceBuilder& set_engine_version (uint32_t major, uint32_t minor, uint32_t patch);
// Sets the vulkan API version to use.
InstanceBuilder& set_api_version (uint32_t major, uint32_t minor, uint32_t patch); InstanceBuilder& set_api_version (uint32_t major, uint32_t minor, uint32_t patch);
InstanceBuilder& add_layer (const char* layer_name);
InstanceBuilder& add_extension (const char* extension_name);
bool check_and_add_layer (const char* layer_name); // Loads the specified layer if it is available.
bool check_and_add_extension (const char* extension_name); InstanceBuilder& check_and_add_layer (const char* layer_name);
// Adds a layer to be enabled. Will fail to create an instance if the layer isn't available.
InstanceBuilder& must_enable_layer (const char* layer_name);
// Enables the specified extension if it is avaiable.
InstanceBuilder& check_and_add_extension (const char* extension_name);
// Adds an extension to be enabled. Will fail to create an instance if the extension isn't available.
InstanceBuilder& must_enable_extension (const char* extension_name);
InstanceBuilder& setup_validation_layers (bool enable_validation = true); // Headless Mode does not load the required extensions for presentation. Defaults to false.
InstanceBuilder& set_headless (bool headless = false); InstanceBuilder& set_headless (bool headless = false);
// Checks if the validation layers are available and loads them if they are.
InstanceBuilder& check_and_setup_validation_layers (bool enable_validation = true);
// Enables the validation layers. Will fail to create an instance if the validation layers aren't available.
InstanceBuilder& must_enable_validation_layers (bool enable_validation = true);
bool check_and_setup_validation_layers (bool enable_validation = true); // Use a default debug callback that prints to standard out.
InstanceBuilder& set_default_debug_messenger (); InstanceBuilder& set_default_debug_messenger ();
// Provide a user defined debug callback.
InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback); InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback);
// Set what message severity is needed to trigger the callback.
InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity);
// Add a message severity to the list that triggers the callback.
InstanceBuilder& add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); InstanceBuilder& add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity);
// Set what message type triggers the callback.
InstanceBuilder& set_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); InstanceBuilder& set_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type);
// Add a message type to the list of that triggers the callback.
InstanceBuilder& add_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); InstanceBuilder& add_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type);
// Disable some validation checks.
// Checks: All, and Shaders
InstanceBuilder& add_validation_disable (VkValidationCheckEXT check); InstanceBuilder& add_validation_disable (VkValidationCheckEXT check);
InstanceBuilder& add_validation_feature_enable (VkValidationFeatureEnableEXT enable);
// Enables optional parts of the validation layers.
// Parts: best practices, gpu assisted, and gpu assisted reserve binding slot.
InstanceBuilder& add_validation_feature_enable (VkValidationFeatureEnableEXT enable);
// Disables sections of the validation layers.
// Options: All, shaders, thread safety, api parameters, object lifetimes, core checks, and unique handles.
InstanceBuilder& add_validation_feature_disable (VkValidationFeatureDisableEXT disable); InstanceBuilder& add_validation_feature_disable (VkValidationFeatureDisableEXT disable);
private: private:
@ -192,10 +216,10 @@ class InstanceBuilder {
bool headless_context = false; bool headless_context = false;
} info; } info;
struct SystemInfo { struct SystemInfo {
std::vector<VkLayerProperties> available_layers; std::vector<VkLayerProperties> available_layers;
std::vector<VkExtensionProperties> available_extensions; std::vector<VkExtensionProperties> available_extensions;
} system; } system;
}; };
const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s); const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s);
@ -237,36 +261,53 @@ struct PhysicalDevice {
friend class DeviceBuilder; friend class DeviceBuilder;
}; };
enum class PreferredDeviceType { discrete, integrated, virtual_gpu, cpu, dont_care };
struct PhysicalDeviceSelector { struct PhysicalDeviceSelector {
public: public:
// Requires a vkb::Instance to construct, needed to pass instance creation info.
PhysicalDeviceSelector (Instance const& instance); PhysicalDeviceSelector (Instance const& instance);
detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> select (); detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> select ();
// Set the surface in which the physical device should render to.
PhysicalDeviceSelector& set_surface (VkSurfaceKHR instance); PhysicalDeviceSelector& set_surface (VkSurfaceKHR instance);
// Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete.
enum PreferredDeviceType { discrete, integrated, virtual_gpu, cpu, dont_care };
PhysicalDeviceSelector& prefer_gpu_device_type (PreferredDeviceType type = PreferredDeviceType::discrete); PhysicalDeviceSelector& prefer_gpu_device_type (PreferredDeviceType type = PreferredDeviceType::discrete);
PhysicalDeviceSelector& allow_fallback_gpu (bool fallback = true); // Allow fallback to a device type that isn't the preferred physical device type. Defaults to true.
PhysicalDeviceSelector& allow_fallback_gpu (bool fallback = true);
// Require that a physical device supports presentation. Defaults to true.
PhysicalDeviceSelector& require_present (bool require = true); PhysicalDeviceSelector& require_present (bool require = true);
PhysicalDeviceSelector& require_dedicated_transfer_queue (); // Require a queue family that supports compute operations but not graphics nor transfer.
PhysicalDeviceSelector& require_dedicated_compute_queue (); PhysicalDeviceSelector& require_dedicated_compute_queue ();
// Require a queue family that supports transfer operations but not graphics nor compute.
PhysicalDeviceSelector& require_dedicated_transfer_queue ();
// Require a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available.
PhysicalDeviceSelector& required_device_memory_size (VkDeviceSize size); PhysicalDeviceSelector& required_device_memory_size (VkDeviceSize size);
// Prefer a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available.
PhysicalDeviceSelector& desired_device_memory_size (VkDeviceSize size); PhysicalDeviceSelector& desired_device_memory_size (VkDeviceSize size);
// Require a physical device which supports a specific extension.
PhysicalDeviceSelector& add_required_extension (const char* extension); PhysicalDeviceSelector& add_required_extension (const char* extension);
// Require a physical device which supports a set of extensions.
PhysicalDeviceSelector& add_required_extensions (std::vector<const char*> extensions); PhysicalDeviceSelector& add_required_extensions (std::vector<const char*> extensions);
// Prefer a physical device which supports a specific extension.
PhysicalDeviceSelector& add_desired_extension (const char* extension); PhysicalDeviceSelector& add_desired_extension (const char* extension);
// Prefer a physical device which supports a set of extensions.
PhysicalDeviceSelector& add_desired_extensions (std::vector<const char*> extensions); PhysicalDeviceSelector& add_desired_extensions (std::vector<const char*> extensions);
// Prefer a physical device that supports a (major, minor) version of vulkan.
PhysicalDeviceSelector& set_desired_version (uint32_t major, uint32_t minor); PhysicalDeviceSelector& set_desired_version (uint32_t major, uint32_t minor);
// Require a physical device that supports a (major, minor) version of vulkan. Default is Vulkan 1.0.
PhysicalDeviceSelector& set_minimum_version (uint32_t major = 1, uint32_t minor = 0); PhysicalDeviceSelector& set_minimum_version (uint32_t major = 1, uint32_t minor = 0);
// Require a physical device which supports the features in VkPhysicalDeviceFeatures.
PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features); PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features);
// Ignore all criteria and choose the first physical device that is available.
// Only use when: The first gpu in the list may be set by global user preferences and an application may wish to respect it.
PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true);
private: private:
@ -339,14 +380,19 @@ class DeviceBuilder {
detail::Expected<Device, detail::Error<DeviceError>> build (); detail::Expected<Device, detail::Error<DeviceError>> build ();
template <typename T> DeviceBuilder& add_pNext (T* structure); // Require a queue family that supports compute operations but not graphics nor transfer.
DeviceBuilder& request_dedicated_compute_queue (bool compute = true); DeviceBuilder& request_dedicated_compute_queue (bool compute = true);
// Require a queue family that supports transfer operations but not graphics nor compute.
DeviceBuilder& request_dedicated_transfer_queue (bool transfer = true); DeviceBuilder& request_dedicated_transfer_queue (bool transfer = true);
/* For advanced users */ // 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 applicatoin.
DeviceBuilder& custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions); DeviceBuilder& custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions);
// For Advanced Users: Add a structure to the pNext chain of VkDeviceCreateInfo.
// The structure must be valid when DeviceBuilder::build() is called.
template <typename T> DeviceBuilder& add_pNext (T* structure);
private: private:
struct DeviceInfo { struct DeviceInfo {
VkDeviceCreateFlags flags = 0; VkDeviceCreateFlags flags = 0;

View File

@ -44,7 +44,7 @@ int test_instance_basic () {
vkb::InstanceBuilder builder; vkb::InstanceBuilder builder;
auto instance_ret = auto instance_ret =
builder.setup_validation_layers () builder.check_and_setup_validation_layers ()
.set_app_name ("test") .set_app_name ("test")
.set_debug_callback ([] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, .set_debug_callback ([] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessageTypeFlagsEXT messageType,
@ -71,7 +71,7 @@ int test_instance_headless () {
vkb::InstanceBuilder builder; vkb::InstanceBuilder builder;
auto instance_ret = auto instance_ret =
builder.setup_validation_layers () builder.check_and_setup_validation_layers ()
.set_headless () .set_headless ()
.set_app_version (4, 5, 6) .set_app_version (4, 5, 6)
.set_app_name ("headless") .set_app_name ("headless")