From 37656d311a844cb83984b183664d1099ebffa216 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Tue, 18 Feb 2020 19:12:31 -0700 Subject: [PATCH] Added documentation to InstanBuilder and PhysicalDevice Selector Updated readme's example code and included better build instructions --- README.md | 62 +++++++++++++++++-------------- example/triangle.cpp | 3 +- src/VkBootstrap.cpp | 40 ++++++++++---------- src/VkBootstrap.h | 88 +++++++++++++++++++++++++++++++++----------- tests/run_tests.cpp | 4 +- 5 files changed, 126 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 76b730f..4d98def 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/example/triangle.cpp b/example/triangle.cpp index f9758bd..b38a4c0 100644 --- a/example/triangle.cpp +++ b/example/triangle.cpp @@ -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 (instance_ret.error ().type) << "\n"; } diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index fa540f1..210dcdb 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -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 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 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 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 const& families) { int transfer = -1; for (int i = 0; i < families.size (); i++) { diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 233199f..baede1c 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -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> build (); // use builder pattern + detail::Expected> 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 available_layers; + struct SystemInfo { + std::vector available_layers; std::vector 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> 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 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 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> build (); - template 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 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 DeviceBuilder& add_pNext (T* structure); + private: struct DeviceInfo { VkDeviceCreateFlags flags = 0; diff --git a/tests/run_tests.cpp b/tests/run_tests.cpp index e73816b..c79cb66 100644 --- a/tests/run_tests.cpp +++ b/tests/run_tests.cpp @@ -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")