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
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:
* Instance Creation
* Physical Device Selection
* Picking a Physical Device
* Device Creation
* Getting Queues
* Swapchain Creation
@ -14,7 +14,7 @@ It also adds several conveniences for:
* enabling validation layers
* 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
@ -22,65 +22,74 @@ It also adds several conveniences for:
#include "VkBootstrap.h"
void device_init()
void init_vulkan()
{
vkb::InstanceBuilder builder;
builder.setup_validation_layers()
.set_app_name ("example")
builder.check_and_setup_validation_layers()
.set_app_name ("Example Vulkan Application")
.set_default_debug_messenger ();
auto inst_ret = builder.build();
if (!inst_ret.has_value()) {
// error
}
vkb::Instance inst = inst_ret.value();
vkb::Instance vkb_inst = inst_ret.value();
vkb::PhysicalDeviceSelector selector{ inst };
selector.set_surface (/* from user created window*/)
.set_minimum_version (1, 0)
.require_dedicated_transfer_queue();
.set_minimum_version (1, 1) //require a vulkan 1.1 capable device
.require_dedicated_transfer_queue(); //require a transfer queue
auto phys_ret = selector.select ();
if (!phys_ret.has_value()) {
// 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 ();
if (!dev_ret.has_value()){
// 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()){
//error
// error
}
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
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
add_subdirectory(vk-bootstrap)
target_link_libraries(your_application_name vk-bootstrap)
```
## Manually Building
### Manually Building
```bash
git clone https://github.com/charles-lunarg/vk-bootstrap
@ -90,17 +99,17 @@ cd build
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
git submodule update --init
```
to get the required dependencies for testing setup.
Then return to the build directory and enable tests with `VK_BOOTSTRAP_TEST`
In the build directory, enable tests by adding `-DVK_BOOTSTRAP_TEST` to the cmake command line arguments
```bash
cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON
@ -112,4 +121,3 @@ cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON
* More examples
* Testing
* Documenting API
* Fleshing out device configuration

View File

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

View File

@ -124,37 +124,61 @@ void destroy_instance (Instance instance); // release instance resources
class InstanceBuilder {
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);
// Sets the name of the engine. Defaults to "" if none is provided.
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);
// Sets the (major, minor, patch) version of the engine.
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& add_layer (const char* layer_name);
InstanceBuilder& add_extension (const char* extension_name);
bool check_and_add_layer (const char* layer_name);
bool check_and_add_extension (const char* extension_name);
// Loads the specified layer if it is available.
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);
// 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 ();
// Provide a user defined debug 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);
// Add a message severity to the list that triggers the callback.
InstanceBuilder& add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity);
// Set what message type triggers the callback.
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);
// Disable some validation checks.
// Checks: All, and Shaders
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);
private:
@ -192,10 +216,10 @@ class InstanceBuilder {
bool headless_context = false;
} info;
struct SystemInfo {
std::vector<VkLayerProperties> available_layers;
struct SystemInfo {
std::vector<VkLayerProperties> available_layers;
std::vector<VkExtensionProperties> available_extensions;
} system;
} system;
};
const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s);
@ -237,36 +261,53 @@ struct PhysicalDevice {
friend class DeviceBuilder;
};
enum class PreferredDeviceType { discrete, integrated, virtual_gpu, cpu, dont_care };
struct PhysicalDeviceSelector {
public:
// Requires a vkb::Instance to construct, needed to pass instance creation info.
PhysicalDeviceSelector (Instance const& instance);
detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> select ();
// Set the surface in which the physical device should render to.
PhysicalDeviceSelector& set_surface (VkSurfaceKHR instance);
enum PreferredDeviceType { discrete, integrated, virtual_gpu, cpu, dont_care };
// Set the desired physical device type to select. Defaults to 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_dedicated_transfer_queue ();
// Require a queue family that supports compute operations but not graphics nor transfer.
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);
// Prefer a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available.
PhysicalDeviceSelector& desired_device_memory_size (VkDeviceSize size);
// Require a physical device which supports a specific 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);
// Prefer a physical device which supports a specific 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);
// Prefer a physical device that supports a (major, minor) version of vulkan.
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);
// Require a physical device which supports the features in VkPhysicalDeviceFeatures.
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);
private:
@ -339,14 +380,19 @@ class DeviceBuilder {
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);
// Require a queue family that supports transfer operations but not graphics nor compute.
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);
// 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:
struct DeviceInfo {
VkDeviceCreateFlags flags = 0;

View File

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