mirror of
https://github.com/KhronosGroup/Vulkan-Hpp.git
synced 2024-10-14 16:32:17 +00:00
docs: refactor code blocks to use ```cpp
This format can be understood by doxygen for syntax highlighting.
This commit is contained in:
parent
e22f73df33
commit
c8ffcb4154
62
README.md
62
README.md
@ -100,7 +100,7 @@ The scoped enum feature adds type safety to the flags, but also prevents using t
|
|||||||
|
|
||||||
As solution Vulkan-Hpp provides a template class `vk::Flags` which brings the standard operations like `&=`, `|=`, `&` and `|` to our scoped enums. Except for the initialization with 0 this class behaves exactly like a normal bitmask with the improvement that it is impossible to set bits not specified by the corresponding enum by accident. Here are a few examples for the bitmask handling:
|
As solution Vulkan-Hpp provides a template class `vk::Flags` which brings the standard operations like `&=`, `|=`, `&` and `|` to our scoped enums. Except for the initialization with 0 this class behaves exactly like a normal bitmask with the improvement that it is impossible to set bits not specified by the corresponding enum by accident. Here are a few examples for the bitmask handling:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::ImageUsageFlags iu1; // initialize a bitmask with no bit set
|
vk::ImageUsageFlags iu1; // initialize a bitmask with no bit set
|
||||||
vk::ImageUsageFlags iu2 = {}; // initialize a bitmask with no bit set
|
vk::ImageUsageFlags iu2 = {}; // initialize a bitmask with no bit set
|
||||||
vk::ImageUsageFlags iu3 = vk::ImageUsageFlagBits::eColorAttachment; // initialize with a single value
|
vk::ImageUsageFlags iu3 = vk::ImageUsageFlagBits::eColorAttachment; // initialize with a single value
|
||||||
@ -112,7 +112,7 @@ PipelineShaderStageCreateInfo ci( {} /* pass a flag without any bits set */, ...
|
|||||||
|
|
||||||
When constructing a handle in Vulkan one usually has to create some `CreateInfo` struct which describes the new handle. This can result in quite lengthy code as can be seen in the following Vulkan C example:
|
When constructing a handle in Vulkan one usually has to create some `CreateInfo` struct which describes the new handle. This can result in quite lengthy code as can be seen in the following Vulkan C example:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
VkImageCreateInfo ci;
|
VkImageCreateInfo ci;
|
||||||
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
ci.pNext = nullptr;
|
ci.pNext = nullptr;
|
||||||
@ -140,7 +140,7 @@ Especially the first one is hard to detect.
|
|||||||
|
|
||||||
Vulkan-Hpp provides constructors for all CreateInfo objects which accept one parameter for each member variable. This way the compiler throws a compiler error if a value has been forgotten. In addition to this `sType` is automatically filled with the correct value and `pNext` set to a `nullptr` by default. Here's how the same code looks with a constructor:
|
Vulkan-Hpp provides constructors for all CreateInfo objects which accept one parameter for each member variable. This way the compiler throws a compiler error if a value has been forgotten. In addition to this `sType` is automatically filled with the correct value and `pNext` set to a `nullptr` by default. Here's how the same code looks with a constructor:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::ImageCreateInfo ci({}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
|
vk::ImageCreateInfo ci({}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
|
||||||
{ width, height, 1 },
|
{ width, height, 1 },
|
||||||
1, 1, vk::SampleCountFlagBits::e1,
|
1, 1, vk::SampleCountFlagBits::e1,
|
||||||
@ -151,7 +151,7 @@ vk::Image image = device.createImage(ci);
|
|||||||
|
|
||||||
With constructors for CreateInfo structures one can also pass temporaries to Vulkan functions like this:
|
With constructors for CreateInfo structures one can also pass temporaries to Vulkan functions like this:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::Image image = device.createImage({{}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
|
vk::Image image = device.createImage({{}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
|
||||||
{ width, height, 1 },
|
{ width, height, 1 },
|
||||||
1, 1, vk::SampleCountFlagBits::e1,
|
1, 1, vk::SampleCountFlagBits::e1,
|
||||||
@ -162,7 +162,7 @@ vk::Image image = device.createImage({{}, vk::ImageType::e2D, vk::Format::eR8G8B
|
|||||||
### Designated Initializers
|
### Designated Initializers
|
||||||
|
|
||||||
Beginning with C++20, C++ supports designated initializers. As that feature requires to not have any user-declared or inherited constructors, you have to `#define VULKAN_HPP_NO_CONSTRUCTORS`, which removes all the structure and union constructors from vulkan.hpp. Instead you can then use aggregate initialization. The first few vk-lines in your source might then look like
|
Beginning with C++20, C++ supports designated initializers. As that feature requires to not have any user-declared or inherited constructors, you have to `#define VULKAN_HPP_NO_CONSTRUCTORS`, which removes all the structure and union constructors from vulkan.hpp. Instead you can then use aggregate initialization. The first few vk-lines in your source might then look like
|
||||||
```c++
|
```cpp
|
||||||
// initialize the vk::ApplicationInfo structure
|
// initialize the vk::ApplicationInfo structure
|
||||||
vk::ApplicationInfo applicationInfo{ .pApplicationName = AppName,
|
vk::ApplicationInfo applicationInfo{ .pApplicationName = AppName,
|
||||||
.applicationVersion = 1,
|
.applicationVersion = 1,
|
||||||
@ -174,7 +174,7 @@ vk::ApplicationInfo applicationInfo{ .pApplicationName = AppName,
|
|||||||
vk::InstanceCreateInfo instanceCreateInfo{ .pApplicationInfo = & applicationInfo };
|
vk::InstanceCreateInfo instanceCreateInfo{ .pApplicationInfo = & applicationInfo };
|
||||||
```
|
```
|
||||||
instead of
|
instead of
|
||||||
```c++
|
```cpp
|
||||||
// initialize the vk::ApplicationInfo structure
|
// initialize the vk::ApplicationInfo structure
|
||||||
vk::ApplicationInfo applicationInfo( AppName, 1, EngineName, 1, VK_API_VERSION_1_1 );
|
vk::ApplicationInfo applicationInfo( AppName, 1, EngineName, 1, VK_API_VERSION_1_1 );
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ The Vulkan API has several places where which require (count,pointer) as two fun
|
|||||||
|
|
||||||
Here are some code samples on how to use the ArrayProxy:
|
Here are some code samples on how to use the ArrayProxy:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::CommandBuffer c;
|
vk::CommandBuffer c;
|
||||||
|
|
||||||
// pass an empty array
|
// pass an empty array
|
||||||
@ -233,7 +233,7 @@ c.setScissor(0, vec);
|
|||||||
|
|
||||||
Vulkan-Hpp generates references for pointers to structs. This conversion allows passing temporary structs to functions which can result in shorter code. In case the input is optional and thus accepting a null pointer the parameter type will be a `vk::Optional<T> const&` type. This type accepts either a reference to `T` or nullptr as input and thus allows optional temporary structs.
|
Vulkan-Hpp generates references for pointers to structs. This conversion allows passing temporary structs to functions which can result in shorter code. In case the input is optional and thus accepting a null pointer the parameter type will be a `vk::Optional<T> const&` type. This type accepts either a reference to `T` or nullptr as input and thus allows optional temporary structs.
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// C
|
// C
|
||||||
VkImageSubresource subResource;
|
VkImageSubresource subResource;
|
||||||
subResource.aspectMask = 0;
|
subResource.aspectMask = 0;
|
||||||
@ -250,7 +250,7 @@ auto layout = device.getImageSubresourceLayout(image, { {} /* flags*/, 0 /* mipl
|
|||||||
|
|
||||||
Vulkan allows chaining of structures through the pNext pointer. Vulkan-Hpp has a variadic template class which allows constructing of such structure chains with minimal efforts. In addition to this it checks at compile time if the spec allows the construction of such a `pNext` chain.
|
Vulkan allows chaining of structures through the pNext pointer. Vulkan-Hpp has a variadic template class which allows constructing of such structure chains with minimal efforts. In addition to this it checks at compile time if the spec allows the construction of such a `pNext` chain.
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// This will compile successfully.
|
// This will compile successfully.
|
||||||
vk::StructureChain<vk::MemoryAllocateInfo, vk::ImportMemoryFdInfoKHR> c;
|
vk::StructureChain<vk::MemoryAllocateInfo, vk::ImportMemoryFdInfoKHR> c;
|
||||||
vk::MemoryAllocateInfo &allocInfo = c.get<vk::MemoryAllocateInfo>();
|
vk::MemoryAllocateInfo &allocInfo = c.get<vk::MemoryAllocateInfo>();
|
||||||
@ -264,7 +264,7 @@ vk::ImportMemoryFdInfoKHR &fdInfo = c.get<vk::ImportMemoryFdInfoKHR>();
|
|||||||
|
|
||||||
Vulkan-Hpp provides a constructor for these chains similar to the CreateInfo objects which accepts a list of all structures part of the chain. The `pNext` field is automatically set to the correct value:
|
Vulkan-Hpp provides a constructor for these chains similar to the CreateInfo objects which accepts a list of all structures part of the chain. The `pNext` field is automatically set to the correct value:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
|
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
|
||||||
vk::MemoryAllocateInfo(size, type),
|
vk::MemoryAllocateInfo(size, type),
|
||||||
vk::MemoryDedicatedAllocateInfo(image)
|
vk::MemoryDedicatedAllocateInfo(image)
|
||||||
@ -276,7 +276,7 @@ In case that very same structure has to be re-added to the StructureChain again,
|
|||||||
|
|
||||||
Sometimes the user has to pass a preallocated structure chain to query information. For those cases there are two corresponding getter functions. One with a variadic template generating a structure chain of at least two elements to construct the return value:
|
Sometimes the user has to pass a preallocated structure chain to query information. For those cases there are two corresponding getter functions. One with a variadic template generating a structure chain of at least two elements to construct the return value:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// Query vk::MemoryRequirements2HR and vk::MemoryDedicatedRequirementsKHR when calling Device::getBufferMemoryRequirements2KHR:
|
// Query vk::MemoryRequirements2HR and vk::MemoryDedicatedRequirementsKHR when calling Device::getBufferMemoryRequirements2KHR:
|
||||||
auto result = device.getBufferMemoryRequirements2KHR<vk::MemoryRequirements2KHR, vk::MemoryDedicatedRequirementsKHR>({});
|
auto result = device.getBufferMemoryRequirements2KHR<vk::MemoryRequirements2KHR, vk::MemoryDedicatedRequirementsKHR>({});
|
||||||
vk::MemoryRequirements2KHR &memReqs = result.get<vk::MemoryRequirements2KHR>();
|
vk::MemoryRequirements2KHR &memReqs = result.get<vk::MemoryRequirements2KHR>();
|
||||||
@ -296,13 +296,13 @@ By default Vulkan-Hpp has exceptions enabled. This means that Vulkan-Hpp checks
|
|||||||
|
|
||||||
To create a device you can now just write:
|
To create a device you can now just write:
|
||||||
|
|
||||||
```C++
|
```cpp
|
||||||
vk::Device device = physicalDevice.createDevice(createInfo);
|
vk::Device device = physicalDevice.createDevice(createInfo);
|
||||||
```
|
```
|
||||||
|
|
||||||
Some functions allow more than just `vk::Result::eSuccess` to be considered as a success code. For those functions, we always return a `ResultValue<SomeType>`. An example is `acquireNextImage2KHR`, that can be used like this:
|
Some functions allow more than just `vk::Result::eSuccess` to be considered as a success code. For those functions, we always return a `ResultValue<SomeType>`. An example is `acquireNextImage2KHR`, that can be used like this:
|
||||||
|
|
||||||
```C++
|
```cpp
|
||||||
vk::ResultValue<uint32_t> result = device->acquireNextImage2KHR(acquireNextImageInfo);
|
vk::ResultValue<uint32_t> result = device->acquireNextImage2KHR(acquireNextImageInfo);
|
||||||
switch (result.result)
|
switch (result.result)
|
||||||
{
|
{
|
||||||
@ -328,7 +328,7 @@ In case you don’t want to use the `vk::ArrayProxy` and return value transforma
|
|||||||
|
|
||||||
The first snippet shows how to use the API without exceptions and the return value transformation:
|
The first snippet shows how to use the API without exceptions and the return value transformation:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// No exceptions, no return value transformation
|
// No exceptions, no return value transformation
|
||||||
ShaderModuleCreateInfo createInfo(...);
|
ShaderModuleCreateInfo createInfo(...);
|
||||||
ShaderModule shader1;
|
ShaderModule shader1;
|
||||||
@ -352,7 +352,7 @@ if (result != VK_SUCCESS)
|
|||||||
|
|
||||||
The second snippet shows how to use the API using return value transformation, but without exceptions. It’s already a little bit shorter than the original code:
|
The second snippet shows how to use the API using return value transformation, but without exceptions. It’s already a little bit shorter than the original code:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
ResultValue<ShaderModule> shaderResult1 = device.createShaderModule({...} /* createInfo temporary */);
|
ResultValue<ShaderModule> shaderResult1 = device.createShaderModule({...} /* createInfo temporary */);
|
||||||
if (shaderResult1.result != VK_SUCCESS)
|
if (shaderResult1.result != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
@ -375,13 +375,13 @@ if (result != VK_SUCCESS)
|
|||||||
|
|
||||||
A nicer way to unpack the result is provided by the structured bindings of C++17. They will allow us to get the result with a single line of code:
|
A nicer way to unpack the result is provided by the structured bindings of C++17. They will allow us to get the result with a single line of code:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */);
|
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */);
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, the last code example is using exceptions and return value transformation. This is the default mode of the API.
|
Finally, the last code example is using exceptions and return value transformation. This is the default mode of the API.
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
ShaderModule shader1;
|
ShaderModule shader1;
|
||||||
ShaderModule shader2;
|
ShaderModule shader2;
|
||||||
try {
|
try {
|
||||||
@ -402,7 +402,7 @@ With C++17 and above, some functions are attributed with [[nodiscard]], resultin
|
|||||||
|
|
||||||
For the return value transformation, there's one special class of return values which require special handling: Enumerations. For enumerations you usually have to write code like this:
|
For the return value transformation, there's one special class of return values which require special handling: Enumerations. For enumerations you usually have to write code like this:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
std::vector<LayerProperties,Allocator> properties;
|
std::vector<LayerProperties,Allocator> properties;
|
||||||
uint32_t propertyCount;
|
uint32_t propertyCount;
|
||||||
Result result;
|
Result result;
|
||||||
@ -424,7 +424,7 @@ properties.resize(propertyCount);
|
|||||||
|
|
||||||
Since writing this loop over and over again is tedious and error prone the C++ binding takes care of the enumeration so that you can just write:
|
Since writing this loop over and over again is tedious and error prone the C++ binding takes care of the enumeration so that you can just write:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties();
|
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties();
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ This mechanism ensures correct destruction order even if the parent SharedHandle
|
|||||||
|
|
||||||
There are no functions which return a `vk::SharedHandle` directly yet. Instead, you can construct a `vk::SharedHandle` from a `vk::Handle`:
|
There are no functions which return a `vk::SharedHandle` directly yet. Instead, you can construct a `vk::SharedHandle` from a `vk::Handle`:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
|
|
||||||
vk::Buffer buffer = device.createBuffer(...);
|
vk::Buffer buffer = device.createBuffer(...);
|
||||||
vk::SharedBuffer sharedBuffer(buffer, device); // sharedBuffer now owns the buffer
|
vk::SharedBuffer sharedBuffer(buffer, device); // sharedBuffer now owns the buffer
|
||||||
@ -454,14 +454,14 @@ vk::SharedBuffer sharedBuffer(buffer, device); // sharedBuffer now owns the buff
|
|||||||
|
|
||||||
There are several specializations of `vk::SharedHandle` for different handle types. For example, `vk::SharedImage` may take an additional argument to specify if the image is owned by swapchain:
|
There are several specializations of `vk::SharedHandle` for different handle types. For example, `vk::SharedImage` may take an additional argument to specify if the image is owned by swapchain:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::Image image = swapchain.getImages(...)[0]; // get the first image from the swapchain
|
vk::Image image = swapchain.getImages(...)[0]; // get the first image from the swapchain
|
||||||
vk::SharedImage sharedImage(image, device, SwapChainOwns::yes); // sharedImage now owns the image, but won't destroy it
|
vk::SharedImage sharedImage(image, device, SwapChainOwns::yes); // sharedImage now owns the image, but won't destroy it
|
||||||
```
|
```
|
||||||
|
|
||||||
There is also a specialization for `vk::SwapchainKHR` which takes an additional argument to specify a surface:
|
There is also a specialization for `vk::SwapchainKHR` which takes an additional argument to specify a surface:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
|
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
|
||||||
vk::SharedSwapchainKHR sharedSwapchain(swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface
|
vk::SharedSwapchainKHR sharedSwapchain(swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface
|
||||||
```
|
```
|
||||||
@ -473,7 +473,7 @@ You can create a `vk::SharedHandle` overload for your own handle type or own sha
|
|||||||
|
|
||||||
With this, provide a custom static destruction function `internalDestroy`, that takes in a parent handle and a handle to destroy. Don't forget to add a friend declaration for the base class.
|
With this, provide a custom static destruction function `internalDestroy`, that takes in a parent handle and a handle to destroy. Don't forget to add a friend declaration for the base class.
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// Example of a custom shared device, that takes in an instance as a parent
|
// Example of a custom shared device, that takes in an instance as a parent
|
||||||
class shared_handle<VkDevice> : public vk::SharedHandleBase<VkDevice, vk::SharedInstance, shared_handle<VkDevice>>
|
class shared_handle<VkDevice> : public vk::SharedHandleBase<VkDevice, vk::SharedInstance, shared_handle<VkDevice>>
|
||||||
{
|
{
|
||||||
@ -504,13 +504,13 @@ The API will be extended to provide creation functions in the future.
|
|||||||
|
|
||||||
Sometimes it is required to use `std::vector` with custom allocators. Vulkan-Hpp supports vectors with custom allocators as input for `vk::ArrayProxy` and for functions which do return a vector. For the latter case, add your favorite custom allocator as template argument to the function call like this:
|
Sometimes it is required to use `std::vector` with custom allocators. Vulkan-Hpp supports vectors with custom allocators as input for `vk::ArrayProxy` and for functions which do return a vector. For the latter case, add your favorite custom allocator as template argument to the function call like this:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>();
|
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>();
|
||||||
```
|
```
|
||||||
|
|
||||||
You can as well use a stateful custom allocator by providing it as an argument to those functions. Unfortunately, to make the compilers happy, you also need to explicitly set the Dispatch argument. To get the default there, a simple `{}` would suffice:
|
You can as well use a stateful custom allocator by providing it as an argument to those functions. Unfortunately, to make the compilers happy, you also need to explicitly set the Dispatch argument. To get the default there, a simple `{}` would suffice:
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
MyStatefulCustomAllocator allocator;
|
MyStatefulCustomAllocator allocator;
|
||||||
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties( allocator, {} );
|
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties( allocator, {} );
|
||||||
```
|
```
|
||||||
@ -529,7 +529,7 @@ There are a couple of static assertions for each handle class and each struct in
|
|||||||
|
|
||||||
The Vulkan loader exposes only the Vulkan core functions and a limited number of extensions. To use Vulkan-Hpp with extensions it's required to have either a library which provides stubs to all used Vulkan functions or to tell Vulkan-Hpp to dispatch those functions pointers. Vulkan-Hpp provides a per-function dispatch mechanism by accepting a dispatch class as last parameter in each function call. The dispatch class must provide a callable type for each used Vulkan function. Vulkan-Hpp provides one implementation, ```DispatchLoaderDynamic```, which fetches all function pointers known to the library.
|
The Vulkan loader exposes only the Vulkan core functions and a limited number of extensions. To use Vulkan-Hpp with extensions it's required to have either a library which provides stubs to all used Vulkan functions or to tell Vulkan-Hpp to dispatch those functions pointers. Vulkan-Hpp provides a per-function dispatch mechanism by accepting a dispatch class as last parameter in each function call. The dispatch class must provide a callable type for each used Vulkan function. Vulkan-Hpp provides one implementation, ```DispatchLoaderDynamic```, which fetches all function pointers known to the library.
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// Providing a function pointer resolving vkGetInstanceProcAddr, just the few functions not depending an an instance or a device are fetched
|
// Providing a function pointer resolving vkGetInstanceProcAddr, just the few functions not depending an an instance or a device are fetched
|
||||||
vk::DispatchLoaderDynamic dld( getInstanceProcAddr );
|
vk::DispatchLoaderDynamic dld( getInstanceProcAddr );
|
||||||
|
|
||||||
@ -547,27 +547,27 @@ To use the `vk::DispatchLoaderDynamic` as the default dispatcher (means: you don
|
|||||||
Creating a full featured `vk::DispatchLoaderDynamic` is a two- to three-step process, where you have three choices for the first step:
|
Creating a full featured `vk::DispatchLoaderDynamic` is a two- to three-step process, where you have three choices for the first step:
|
||||||
1. Before any call into a vk-function you need to initialize the dynamic dispatcher by one of three methods
|
1. Before any call into a vk-function you need to initialize the dynamic dispatcher by one of three methods
|
||||||
- Let Vulkan-Hpp do all the work by internally using a little helper class `vk::DynamicLoader`:
|
- Let Vulkan-Hpp do all the work by internally using a little helper class `vk::DynamicLoader`:
|
||||||
```c++
|
```cpp
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init();
|
VULKAN_HPP_DEFAULT_DISPATCHER.init();
|
||||||
```
|
```
|
||||||
- Use your own dynamic loader, which just needs to provide a templated function `getProcAddress` (compare with `vk::DynamicLoader` in vulkan.hpp):
|
- Use your own dynamic loader, which just needs to provide a templated function `getProcAddress` (compare with `vk::DynamicLoader` in vulkan.hpp):
|
||||||
```c++
|
```cpp
|
||||||
YourDynamicLoader ydl;
|
YourDynamicLoader ydl;
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);
|
||||||
```
|
```
|
||||||
Note that you need to keep that dynamic loader object alive until after the last call to a vulkan function in your program. For example by making it static, or storing it somewhere globally.
|
Note that you need to keep that dynamic loader object alive until after the last call to a vulkan function in your program. For example by making it static, or storing it somewhere globally.
|
||||||
- Use your own initial function pointer of type PFN_vkGetInstanceProcAddr:
|
- Use your own initial function pointer of type PFN_vkGetInstanceProcAddr:
|
||||||
```c++
|
```cpp
|
||||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||||
```
|
```
|
||||||
2. initialize it with a vk::Instance to get all the other function pointers:
|
2. initialize it with a vk::Instance to get all the other function pointers:
|
||||||
```c++
|
```cpp
|
||||||
vk::Instance instance = vk::createInstance({}, nullptr);
|
vk::Instance instance = vk::createInstance({}, nullptr);
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||||
```
|
```
|
||||||
3. optionally initialize it with a vk::Device to get device-specific function pointers
|
3. optionally initialize it with a vk::Device to get device-specific function pointers
|
||||||
```c++
|
```cpp
|
||||||
std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
|
std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
|
||||||
assert(!physicalDevices.empty());
|
assert(!physicalDevices.empty());
|
||||||
vk::Device device = physicalDevices[0].createDevice({}, nullptr);
|
vk::Device device = physicalDevices[0].createDevice({}, nullptr);
|
||||||
|
@ -10,28 +10,37 @@ vulkan_raii.hpp is a C++ layer on top of vulkan.hpp that follows the RAII-princi
|
|||||||
|
|
||||||
As a simple example, instead of creating a `vk::Device`
|
As a simple example, instead of creating a `vk::Device`
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::Device, given a vk::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a vk::Device, given a vk::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
vk::Device device = physicalDevice.createDevice( deviceCreateInfo );
|
vk::Device device = physicalDevice.createDevice( deviceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
and destroying it at some point
|
and destroying it at some point
|
||||||
|
|
||||||
|
```cpp
|
||||||
// destroy a vk::Device
|
// destroy a vk::Device
|
||||||
device.destroy();
|
device.destroy();
|
||||||
|
```
|
||||||
|
|
||||||
you would create a `vk::raii::Device`
|
you would create a `vk::raii::Device`
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
vk::raii::Device device( physicalDevice, deviceCreateInfo );
|
vk::raii::Device device( physicalDevice, deviceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
That `vk::raii::Device` is automatically destroyed, when its scope is left.
|
That `vk::raii::Device` is automatically destroyed, when its scope is left.
|
||||||
|
|
||||||
Alternatively, you can use a creation function to create a `vk::raii::Device`:
|
Alternatively, you can use a creation function to create a `vk::raii::Device`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
vk::raii::Device device = physicalDevice.createDevice( deviceCreateInfo );
|
vk::raii::Device device = physicalDevice.createDevice( deviceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
Finally, if you have defined `VULKAN_HPP_NO_EXCPETIONS` and compile for at least C++23, the constructors as described above are not available (they would potentially throw an exception which is not allowed then) but you have to use the construction functions. Those functions then do not return the created object, but a `std::expected<vk::raii::Object, vk::Result>`:
|
Finally, if you have defined `VULKAN_HPP_NO_EXCPETIONS` and compile for at least C++23, the constructors as described above are not available (they would potentially throw an exception which is not allowed then) but you have to use the construction functions. Those functions then do not return the created object, but a `std::expected<vk::raii::Object, vk::Result>`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
// when VULKAN_HPP_NO_EXCPETIONS is defined and your using at least C++23
|
// when VULKAN_HPP_NO_EXCPETIONS is defined and your using at least C++23
|
||||||
auto deviceExpected = physicalDevice.createDevice( deviceCreateInfo );
|
auto deviceExpected = physicalDevice.createDevice( deviceCreateInfo );
|
||||||
@ -39,15 +48,18 @@ Finally, if you have defined `VULKAN_HPP_NO_EXCPETIONS` and compile for at least
|
|||||||
{
|
{
|
||||||
device = std::move( *deviceExpected );
|
device = std::move( *deviceExpected );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In the code snippets in this text, I will consistently use the constructor-approach.
|
In the code snippets in this text, I will consistently use the constructor-approach.
|
||||||
|
|
||||||
|
|
||||||
Other than the `vk::Device`, you can assign the `vk::raii::Device` to a smart pointer:
|
Other than the `vk::Device`, you can assign the `vk::raii::Device` to a smart pointer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a smart-pointer to a vk::raii::Device, given a smart-pointer to a vk::raii::PhysicalDevice pPhysicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a smart-pointer to a vk::raii::Device, given a smart-pointer to a vk::raii::PhysicalDevice pPhysicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
std::unique_ptr<vk::raii::Device> pDevice;
|
std::unique_ptr<vk::raii::Device> pDevice;
|
||||||
pDevice = std::make_unique<vk::raii::Device>( *pPhysicalDevice, deviceCreateInfo );
|
pDevice = std::make_unique<vk::raii::Device>( *pPhysicalDevice, deviceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
Note that the vk::raii objects own the actual Vulkan resource. Therefore, all vk::raii objects that own destructable resources are just movable, but not copyable. Therefore, a few vk::raii objects, like vk::raii::PhysicalDevice are copyable as well.
|
Note that the vk::raii objects own the actual Vulkan resource. Therefore, all vk::raii objects that own destructable resources are just movable, but not copyable. Therefore, a few vk::raii objects, like vk::raii::PhysicalDevice are copyable as well.
|
||||||
|
|
||||||
@ -57,21 +69,27 @@ Similar to a `vk::Device`, a `vk::raii::Device` provides the functions related t
|
|||||||
|
|
||||||
That is, calling a device-related function is identical for both cases:
|
That is, calling a device-related function is identical for both cases:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// call waitIdle from a vk::Device
|
// call waitIdle from a vk::Device
|
||||||
device.waitIdle();
|
device.waitIdle();
|
||||||
|
|
||||||
// call waitIdle from a vk::raii::Device
|
// call waitIdle from a vk::raii::Device
|
||||||
device.waitIdle();
|
device.waitIdle();
|
||||||
|
```
|
||||||
|
|
||||||
vk::raii goes one step further. In the vk namespace, most of the functions are members of `vk::Device`. In the vk::raii namespace functions strongly related to a non-dispatchable handle are members of the corresponding vi::raii object. For example, to bind memory to a buffer, in vk namespace you write
|
vk::raii goes one step further. In the vk namespace, most of the functions are members of `vk::Device`. In the vk::raii namespace functions strongly related to a non-dispatchable handle are members of the corresponding vi::raii object. For example, to bind memory to a buffer, in vk namespace you write
|
||||||
|
|
||||||
|
```cpp
|
||||||
// bind vk::DeviceMemory memory to a vk::Buffer buffer, given vk::DeviceSize memoryOffset
|
// bind vk::DeviceMemory memory to a vk::Buffer buffer, given vk::DeviceSize memoryOffset
|
||||||
device.bindBufferMemory( buffer, memory, memoryOffset );
|
device.bindBufferMemory( buffer, memory, memoryOffset );
|
||||||
|
```
|
||||||
|
|
||||||
In vk::raii namespace you write
|
In vk::raii namespace you write
|
||||||
|
|
||||||
|
```cpp
|
||||||
// bind vk::raii::DeviceMemory memory to a vk::raii::Buffer buffer, given vk::DeviceSize memoryOffset
|
// bind vk::raii::DeviceMemory memory to a vk::raii::Buffer buffer, given vk::DeviceSize memoryOffset
|
||||||
buffer.bindMemory( *memory, memoryOffset );
|
buffer.bindMemory( *memory, memoryOffset );
|
||||||
|
```
|
||||||
|
|
||||||
Note that `vk::raii::Buffer::bindMemory()`takes a `vk::DeviceMemory` as its first argument, not a `vk::raii::DeviceMemory`. From a vk::raii object you get to the corresponding vk object by just dereferencing the vk::raii object.
|
Note that `vk::raii::Buffer::bindMemory()`takes a `vk::DeviceMemory` as its first argument, not a `vk::raii::DeviceMemory`. From a vk::raii object you get to the corresponding vk object by just dereferencing the vk::raii object.
|
||||||
|
|
||||||
@ -81,42 +99,56 @@ Note that `vk::raii::Buffer::bindMemory()`takes a `vk::DeviceMemory` as its firs
|
|||||||
|
|
||||||
The very first step when using classes from the vk::raii namespace is to instantiate a `vk::raii::Context`. This class has no counterpart in either the vk namespace or the pure C-API of Vulkan. It is the handle to the few functions that are not bound to a `VkInstance` or a `VkDevice`:
|
The very first step when using classes from the vk::raii namespace is to instantiate a `vk::raii::Context`. This class has no counterpart in either the vk namespace or the pure C-API of Vulkan. It is the handle to the few functions that are not bound to a `VkInstance` or a `VkDevice`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// instantiate a vk::raii::Context
|
// instantiate a vk::raii::Context
|
||||||
vk::raii::Context context;
|
vk::raii::Context context;
|
||||||
|
```
|
||||||
|
|
||||||
To use any of those "global" functions, your code would look like that:
|
To use any of those "global" functions, your code would look like that:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the API version, using that context
|
// get the API version, using that context
|
||||||
uint32_t apiVersion = context.enumerateInstanceVersion();
|
uint32_t apiVersion = context.enumerateInstanceVersion();
|
||||||
|
```
|
||||||
|
|
||||||
### 01 Create a vk::raii::Instance
|
### 01 Create a vk::raii::Instance
|
||||||
|
|
||||||
To pass that information on to a `vk::raii::Instance`, its constructor gets a reference to that `vk::raii::Context`:
|
To pass that information on to a `vk::raii::Instance`, its constructor gets a reference to that `vk::raii::Context`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// instantiate a vk::raii::Instance, given a vk::raii::Context context and a vk::InstanceCreateInfo instanceCreateInfo
|
// instantiate a vk::raii::Instance, given a vk::raii::Context context and a vk::InstanceCreateInfo instanceCreateInfo
|
||||||
vk::raii::Instance instance( context, instanceCreateInfo );
|
vk::raii::Instance instance( context, instanceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
The `vk::raii::Instance` now holds all the instance-related functions. For example, to get all the `vk::PhysicalDeviceGroupProperties` for an instance, your call would look like this:
|
The `vk::raii::Instance` now holds all the instance-related functions. For example, to get all the `vk::PhysicalDeviceGroupProperties` for an instance, your call would look like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get all vk::PhysicalDeviceGroupProperties from a vk::raii::Instance instance
|
// get all vk::PhysicalDeviceGroupProperties from a vk::raii::Instance instance
|
||||||
std::vector<vk::PhysicalDeviceGroupProperties> physicalDeviceGroupProperties = instance.enumeratePhysicalDeviceGroups();
|
std::vector<vk::PhysicalDeviceGroupProperties> physicalDeviceGroupProperties = instance.enumeratePhysicalDeviceGroups();
|
||||||
|
```
|
||||||
|
|
||||||
### 02 Enumerate the vk::raii::PhysicalDevices
|
### 02 Enumerate the vk::raii::PhysicalDevices
|
||||||
|
|
||||||
Enumerating the physical devices of an instance is slightly different in vk::raii namespace as you might be used to from the vk-namespace or the pure C-API. As there might be multiple physical devices attached, you would instantiate a `vk::raii::PhysicalDevices` (note the trailing 's' here!), which essentially is a `std::vector` of `vk::raii::PhysicalDevice`s (note the trailing 's' here!):
|
Enumerating the physical devices of an instance is slightly different in vk::raii namespace as you might be used to from the vk-namespace or the pure C-API. As there might be multiple physical devices attached, you would instantiate a `vk::raii::PhysicalDevices` (note the trailing 's' here!), which essentially is a `std::vector` of `vk::raii::PhysicalDevice`s (note the trailing 's' here!):
|
||||||
|
|
||||||
|
```cpp
|
||||||
// enumerate the vk::raii::PhysicalDevices, given a vk::raii::Instance instance
|
// enumerate the vk::raii::PhysicalDevices, given a vk::raii::Instance instance
|
||||||
vk::raii::PhysicalDevices physicalDevices( instance );
|
vk::raii::PhysicalDevices physicalDevices( instance );
|
||||||
|
```
|
||||||
|
|
||||||
As vk::raii::PhysicalDevices is just a `std::vector<vk::raii::PhysicalDevice>`, you can access any specific `vk::raii:PhysicalDevice` by indexing into that `std::vector`:
|
As vk::raii::PhysicalDevices is just a `std::vector<vk::raii::PhysicalDevice>`, you can access any specific `vk::raii:PhysicalDevice` by indexing into that `std::vector`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the vk::LayerProperties of the vk::raii::PhysicalDevice with index physicalDeviceIndex, given a vk::raii::PhysicalDevices physicalDevices
|
// get the vk::LayerProperties of the vk::raii::PhysicalDevice with index physicalDeviceIndex, given a vk::raii::PhysicalDevices physicalDevices
|
||||||
std::vector<vk::LayerProperties> layerProperties = physicalDevices[physicalDeviceIndex].enumerateDeviceLayerProperties();
|
std::vector<vk::LayerProperties> layerProperties = physicalDevices[physicalDeviceIndex].enumerateDeviceLayerProperties();
|
||||||
|
```
|
||||||
|
|
||||||
You can as well get one `vk::raii::PhysicalDevice` out of a `vk::raii::PhysicalDevices` like this:
|
You can as well get one `vk::raii::PhysicalDevice` out of a `vk::raii::PhysicalDevices` like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the vk::raii::PhysicalDevice with index physicalDeviceIndex, given a vk::raii::PhysicalDevices physicalDevices object:
|
// get the vk::raii::PhysicalDevice with index physicalDeviceIndex, given a vk::raii::PhysicalDevices physicalDevices object:
|
||||||
vk::raii::PhysicalDevice physicalDevice( std::move( physicalDevices[physicalDeviceIndex] ) );
|
vk::raii::PhysicalDevice physicalDevice( std::move( physicalDevices[physicalDeviceIndex] ) );
|
||||||
|
```
|
||||||
|
|
||||||
Note, that even though the actual `VkPhysicalDevice` owned by a `vk::raii::PhysicalDevice` is not a destructible resource, for consistency reasons a `vk::raii::PhysicalDevice` is a movable but not copyable object just like all the other vk::raii objects.
|
Note, that even though the actual `VkPhysicalDevice` owned by a `vk::raii::PhysicalDevice` is not a destructible resource, for consistency reasons a `vk::raii::PhysicalDevice` is a movable but not copyable object just like all the other vk::raii objects.
|
||||||
|
|
||||||
@ -124,47 +156,61 @@ Note, that even though the actual `VkPhysicalDevice` owned by a `vk::raii::Physi
|
|||||||
|
|
||||||
To create a `vk::raii::Device`, you just instantiate an object of that class:
|
To create a `vk::raii::Device`, you just instantiate an object of that class:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
// create a vk::raii::Device, given a vk::raii::PhysicalDevice physicalDevice and a vk::DeviceCreateInfo deviceCreateInfo
|
||||||
vk::raii::Device device( physicalDevice, deviceCreateInfo );
|
vk::raii::Device device( physicalDevice, deviceCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
For each instantiated `vk::raii::Device`, the device-specific Vulkan function pointers are resolved. That is, for multi-device programs, you automatically use the correct device-specific function pointers, and organizing a multi-device program is simplified:
|
For each instantiated `vk::raii::Device`, the device-specific Vulkan function pointers are resolved. That is, for multi-device programs, you automatically use the correct device-specific function pointers, and organizing a multi-device program is simplified:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Device per vk::raii::PhysicalDevice, given a vk::raii::PhysicalDevices physicalDevices, and a corresponding array of vk::DeviceCreateInfo deviceCreateInfos
|
// create a vk::raii::Device per vk::raii::PhysicalDevice, given a vk::raii::PhysicalDevices physicalDevices, and a corresponding array of vk::DeviceCreateInfo deviceCreateInfos
|
||||||
std::vector<vk::raii::Device> devices;
|
std::vector<vk::raii::Device> devices;
|
||||||
for ( size_t i = 0; i < physicalDevices.size(); i++ )
|
for ( size_t i = 0; i < physicalDevices.size(); i++ )
|
||||||
{
|
{
|
||||||
devices.push_back( vk::raii::Device( physicalDevices[i], deviceCreateInfos[i] ) );
|
devices.push_back( vk::raii::Device( physicalDevices[i], deviceCreateInfos[i] ) );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 04 Create a vk::raii::CommandPool and vk::raii::CommandBuffers
|
### 04 Create a vk::raii::CommandPool and vk::raii::CommandBuffers
|
||||||
|
|
||||||
Creating a `vk::raii::CommandPool` is simply done by instantiating such an object:
|
Creating a `vk::raii::CommandPool` is simply done by instantiating such an object:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::CommandPool, given a vk::raii::Device device and a vk::CommandPoolCreateInfo commandPoolCreateInfo
|
// create a vk::raii::CommandPool, given a vk::raii::Device device and a vk::CommandPoolCreateInfo commandPoolCreateInfo
|
||||||
vk::raii::CommandPool commandPool( device, commandPoolCreateInfo );
|
vk::raii::CommandPool commandPool( device, commandPoolCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
As the number of `vk::raii::CommandBuffer`s to allocate from a `vk::raii::CommandPool` is given by the member `commandBufferCount` of a `vk::CommandBufferAllocateInfo` structure, it can't be instantiated as a single object. Instead you get a `vk::raii::CommandBuffers` (note the trailing 's' here!), which essentially is a `std::vector` of `vk::raii::CommandBuffer`s (note the trailing 's' here!).
|
As the number of `vk::raii::CommandBuffer`s to allocate from a `vk::raii::CommandPool` is given by the member `commandBufferCount` of a `vk::CommandBufferAllocateInfo` structure, it can't be instantiated as a single object. Instead you get a `vk::raii::CommandBuffers` (note the trailing 's' here!), which essentially is a `std::vector` of `vk::raii::CommandBuffer`s (note the trailing 's' here!).
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::CommandBuffers, given a vk::raii::Device device and a vk::CommandBufferAllocateInfo commandBufferAllocateInfo
|
// create a vk::raii::CommandBuffers, given a vk::raii::Device device and a vk::CommandBufferAllocateInfo commandBufferAllocateInfo
|
||||||
vk::raii::CommandBuffers commandBuffers( device, commandBufferAllocateInfo );
|
vk::raii::CommandBuffers commandBuffers( device, commandBufferAllocateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
Note, that the `vk::CommandBufferAllocateInfo` holds a `vk::CommandPool` member `commandPool`. To assign that from a `vk::raii::CommandPool` you can use the `operator*()`:
|
Note, that the `vk::CommandBufferAllocateInfo` holds a `vk::CommandPool` member `commandPool`. To assign that from a `vk::raii::CommandPool` you can use the `operator*()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// assign vk::CommandBufferAllocateInfo::commandPool, given a vk::raii::CommandPool commandPool
|
// assign vk::CommandBufferAllocateInfo::commandPool, given a vk::raii::CommandPool commandPool
|
||||||
commandBufferAllocateInfo.commandPool = *commandPool;
|
commandBufferAllocateInfo.commandPool = *commandPool;
|
||||||
|
```
|
||||||
|
|
||||||
As a `vk::raii::CommandBuffers` is just a `std::vector<vk::raii::CommandBuffer>`, you can access any specific `vk::raii:CommandBuffer` by indexing into that `std::vector`:
|
As a `vk::raii::CommandBuffers` is just a `std::vector<vk::raii::CommandBuffer>`, you can access any specific `vk::raii:CommandBuffer` by indexing into that `std::vector`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// start recording of the vk::raii::CommandBuffer with index commandBufferIndex, given a vk::raii::CommandBuffers commandBuffers
|
// start recording of the vk::raii::CommandBuffer with index commandBufferIndex, given a vk::raii::CommandBuffers commandBuffers
|
||||||
commandBuffers[commandBufferIndex].begin();
|
commandBuffers[commandBufferIndex].begin();
|
||||||
|
```
|
||||||
|
|
||||||
You can as well get one `vk::raii::CommandBuffer` out of a `vk::raii::CommandBuffers` like this:
|
You can as well get one `vk::raii::CommandBuffer` out of a `vk::raii::CommandBuffers` like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the vk::raii::CommandBuffer with index commandBufferIndex, given a vk::raii::CommandBuffers commandBuffers
|
// get the vk::raii::CommandBuffer with index commandBufferIndex, given a vk::raii::CommandBuffers commandBuffers
|
||||||
vk::raii::CommandBuffer commandBuffer( std::move( commandBuffers[commandBufferIndex] ) );
|
vk::raii::CommandBuffer commandBuffer( std::move( commandBuffers[commandBufferIndex] ) );
|
||||||
|
|
||||||
// start recording
|
// start recording
|
||||||
commandBuffer.begin();
|
commandBuffer.begin();
|
||||||
|
```
|
||||||
|
|
||||||
There is one important thing to note, regarding command pool and command buffer handling. When you destroy a `VkCommandPool`, all `VkCommandBuffer`s allocated from that pool are implicitly freed. That automatism does not work well with the raii-approach. As the `vk::raii::CommandBuffers` are independent objects, they are not automatically destroyed when the `vk::raii::CommandPool` they are created from is destroyed. Instead, their destructor would try to use an invalid `vk::raii::CommandPool`, which obviously is an error.
|
There is one important thing to note, regarding command pool and command buffer handling. When you destroy a `VkCommandPool`, all `VkCommandBuffer`s allocated from that pool are implicitly freed. That automatism does not work well with the raii-approach. As the `vk::raii::CommandBuffers` are independent objects, they are not automatically destroyed when the `vk::raii::CommandPool` they are created from is destroyed. Instead, their destructor would try to use an invalid `vk::raii::CommandPool`, which obviously is an error.
|
||||||
|
|
||||||
@ -174,18 +220,23 @@ To handle that correctly, you have to make sure, that all `vk::raii::CommandBuff
|
|||||||
|
|
||||||
To initialize a swap chain, you first instantiate a `vk::raii::SwapchainKHR`:
|
To initialize a swap chain, you first instantiate a `vk::raii::SwapchainKHR`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::SwapchainKHR, given a vk::raii::Device device and a vk::SwapchainCreateInfoKHR swapChainCreateInfo
|
// create a vk::raii::SwapchainKHR, given a vk::raii::Device device and a vk::SwapchainCreateInfoKHR swapChainCreateInfo
|
||||||
vk::raii::SwapchainKHR swapchain( device, swapChainCreateInfo );
|
vk::raii::SwapchainKHR swapchain( device, swapChainCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
You can get an array of presentable images associated with that swap chain:
|
You can get an array of presentable images associated with that swap chain:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get presentable images associated with vk::raii::SwapchainKHR swapchain
|
// get presentable images associated with vk::raii::SwapchainKHR swapchain
|
||||||
std::vector<VkImage> images = swapchain.getImages();
|
std::vector<VkImage> images = swapchain.getImages();
|
||||||
|
```
|
||||||
|
|
||||||
Note, that you don't get `vk::raii::Image`s here, but plain `VkImage`s. They are controlled by the swap chain, and you should not destroy them.
|
Note, that you don't get `vk::raii::Image`s here, but plain `VkImage`s. They are controlled by the swap chain, and you should not destroy them.
|
||||||
|
|
||||||
But you can create `vk::raii::ImageView`s out of them:
|
But you can create `vk::raii::ImageView`s out of them:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::ImageView per VkImage, given a vk::raii::Device sevice, a vector of VkImages images and a vk::ImageViewCreateInfo imageViewCreateInfo
|
// create a vk::raii::ImageView per VkImage, given a vk::raii::Device sevice, a vector of VkImages images and a vk::ImageViewCreateInfo imageViewCreateInfo
|
||||||
std::vector<vk::raii::ImageView> imageViews;
|
std::vector<vk::raii::ImageView> imageViews;
|
||||||
for ( auto image : images )
|
for ( auto image : images )
|
||||||
@ -193,17 +244,21 @@ But you can create `vk::raii::ImageView`s out of them:
|
|||||||
imageViewCreatInfo.image = image;
|
imageViewCreatInfo.image = image;
|
||||||
imageViews.push_back( vk::raii::ImageView( device, imageViewCreateInfo ) );
|
imageViews.push_back( vk::raii::ImageView( device, imageViewCreateInfo ) );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 06 Create a Depth Buffer
|
### 06 Create a Depth Buffer
|
||||||
|
|
||||||
For a depth buffer, you need an image and some device memory and bind the memory to that image. That is, you first create a vk::raii::Image
|
For a depth buffer, you need an image and some device memory and bind the memory to that image. That is, you first create a vk::raii::Image
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Image image, given a vk::raii::Device device and a vk::ImageCreateInfo imageCreateInfo
|
// create a vk::raii::Image image, given a vk::raii::Device device and a vk::ImageCreateInfo imageCreateInfo
|
||||||
// imageCreateInfo.usage should hold vk::ImageUsageFlagBits::eDepthStencilAttachment
|
// imageCreateInfo.usage should hold vk::ImageUsageFlagBits::eDepthStencilAttachment
|
||||||
vk::raii::Image depthImage( device, imageCreateInfo );
|
vk::raii::Image depthImage( device, imageCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
To create the corresponding vk::raii::DeviceMemory, you should determine appropriate values for the vk::MemoryAllocateInfo. That is, get the memory requirements from the pDepthImage, and determine some memoryTypeIndex from the pPhysicalDevice's memory properties, requiring vk::MemoryPropertyFlagBits::eDeviceLocal.
|
To create the corresponding vk::raii::DeviceMemory, you should determine appropriate values for the vk::MemoryAllocateInfo. That is, get the memory requirements from the pDepthImage, and determine some memoryTypeIndex from the pPhysicalDevice's memory properties, requiring vk::MemoryPropertyFlagBits::eDeviceLocal.
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the vk::MemoryRequirements of the pDepthImage
|
// get the vk::MemoryRequirements of the pDepthImage
|
||||||
vk::MemoryRequirements memoryRequirements = depthImage.getMemoryRequirements();
|
vk::MemoryRequirements memoryRequirements = depthImage.getMemoryRequirements();
|
||||||
|
|
||||||
@ -214,22 +269,28 @@ To create the corresponding vk::raii::DeviceMemory, you should determine appropr
|
|||||||
// create a vk::raii::DeviceMemory depthDeviceMemory for the depth buffer
|
// create a vk::raii::DeviceMemory depthDeviceMemory for the depth buffer
|
||||||
vk::MemoryAllocateInfo memoryAllocateInfo( memoryRequirements.size, memoryTypeIndex );
|
vk::MemoryAllocateInfo memoryAllocateInfo( memoryRequirements.size, memoryTypeIndex );
|
||||||
vk::raii::DeviceMemory depthDeviceMemory( device, memoryAllocateInfo );
|
vk::raii::DeviceMemory depthDeviceMemory( device, memoryAllocateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
Then you can bind the depth memory to the depth image
|
Then you can bind the depth memory to the depth image
|
||||||
|
|
||||||
|
```cpp
|
||||||
// bind the pDepthMemory to the pDepthImage
|
// bind the pDepthMemory to the pDepthImage
|
||||||
depthImage.bindMemory( *depthDeviceMemory, 0 );
|
depthImage.bindMemory( *depthDeviceMemory, 0 );
|
||||||
|
```
|
||||||
|
|
||||||
Finally, you can create an image view on that depth buffer image
|
Finally, you can create an image view on that depth buffer image
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::ImageView depthView, given a vk::ImageViewCreateInfo imageViewCreateInfo
|
// create a vk::raii::ImageView depthView, given a vk::ImageViewCreateInfo imageViewCreateInfo
|
||||||
imageViewCreateInfo.image = *depthImage;
|
imageViewCreateInfo.image = *depthImage;
|
||||||
vk::raii::ImageView depthImageView( device, imageViewCreateInfo );
|
vk::raii::ImageView depthImageView( device, imageViewCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
### 07 Create a Uniform Buffer
|
### 07 Create a Uniform Buffer
|
||||||
|
|
||||||
Initializing a uniform buffer is very similar to initializing a depth buffer as described above. You just instantiate a `vk::raii::Buffer` instead of a `vk::raii::Image`, and a `vk::raii::DeviceMemory`, and bind the memory to the buffer:
|
Initializing a uniform buffer is very similar to initializing a depth buffer as described above. You just instantiate a `vk::raii::Buffer` instead of a `vk::raii::Image`, and a `vk::raii::DeviceMemory`, and bind the memory to the buffer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Buffer, given a vk::raii::Device device and a vk::BufferCreateInfo bufferCreateInfo
|
// create a vk::raii::Buffer, given a vk::raii::Device device and a vk::BufferCreateInfo bufferCreateInfo
|
||||||
vk::raii::Buffer uniformBuffer( device, bufferCreateInfo );
|
vk::raii::Buffer uniformBuffer( device, bufferCreateInfo );
|
||||||
|
|
||||||
@ -246,17 +307,20 @@ Initializing a uniform buffer is very similar to initializing a depth buffer as
|
|||||||
|
|
||||||
// bind the vk::raii::DeviceMemory uniformDeviceMemory to the vk::raii::Buffer uniformBuffer
|
// bind the vk::raii::DeviceMemory uniformDeviceMemory to the vk::raii::Buffer uniformBuffer
|
||||||
uniformBuffer.bindMemory( *uniformDeviceMemory, 0 );
|
uniformBuffer.bindMemory( *uniformDeviceMemory, 0 );
|
||||||
|
```
|
||||||
|
|
||||||
### 08 Create a vk::raii::PipelineLayout
|
### 08 Create a vk::raii::PipelineLayout
|
||||||
|
|
||||||
To initialize a Pipeline Layout you just have to instantiate a `vk::raii::DescriptorSetLayout` and a `vk::raii::PipelineLayout` using that `vk::raii::DescriptorSetLayout`:
|
To initialize a Pipeline Layout you just have to instantiate a `vk::raii::DescriptorSetLayout` and a `vk::raii::PipelineLayout` using that `vk::raii::DescriptorSetLayout`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::DescriptorSetLayout, given a vk::raii::Device device and a vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo
|
// create a vk::raii::DescriptorSetLayout, given a vk::raii::Device device and a vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo
|
||||||
vk::raii::DescriptorSetLayout descriptorSetLayout( device, descriptorSetLayoutCreateInfo );
|
vk::raii::DescriptorSetLayout descriptorSetLayout( device, descriptorSetLayoutCreateInfo );
|
||||||
|
|
||||||
// create a vk::raii::PipelineLayout, given a vk::raii::Device device and a vk::raii::DescriptorSetLayout
|
// create a vk::raii::PipelineLayout, given a vk::raii::Device device and a vk::raii::DescriptorSetLayout
|
||||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( {}, *descriptorSetLayout );
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( {}, *descriptorSetLayout );
|
||||||
vk::raii::PipelineLayout pipelineLayout( device, pipelineLayoutCreateInfo );
|
vk::raii::PipelineLayout pipelineLayout( device, pipelineLayoutCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
### 09 Create a vk::raii::DescriptorPool and vk::raii::DescriptorSets
|
### 09 Create a vk::raii::DescriptorPool and vk::raii::DescriptorSets
|
||||||
|
|
||||||
@ -266,9 +330,11 @@ As a `vk::raii::DescriptorSet` object destroys itself in the destructor, you hav
|
|||||||
|
|
||||||
That is, an instantiation of a `vk::raii::DescriptorPool` would look like this:
|
That is, an instantiation of a `vk::raii::DescriptorPool` would look like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::DescriptorPool, given a vk::raii::Device device and a vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo
|
// create a vk::raii::DescriptorPool, given a vk::raii::Device device and a vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo
|
||||||
assert( descriptorPoolCreateInfo.flags & vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet );
|
assert( descriptorPoolCreateInfo.flags & vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet );
|
||||||
vk::raii::DescriptorPool descriptorPool( device, descriptorPoolCreateInfo );
|
vk::raii::DescriptorPool descriptorPool( device, descriptorPoolCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
To actually instantiate a `vk::raii::DescriptorSet`, you need a `vk::raii::DescriptorPool`, as just described, and a `vk::raii::DescriptorSetLayout`, similar to the one described in the previous section.
|
To actually instantiate a `vk::raii::DescriptorSet`, you need a `vk::raii::DescriptorPool`, as just described, and a `vk::raii::DescriptorSetLayout`, similar to the one described in the previous section.
|
||||||
|
|
||||||
@ -276,33 +342,42 @@ Moreover, as the number of `vk::raii::DescriptorSet`s to allocate from a `vk::ra
|
|||||||
|
|
||||||
When you want to create just one `vk::raii::DescriptorSet`, using just one `vk::raii::DescriptorSetLayout`, your code might look like this:
|
When you want to create just one `vk::raii::DescriptorSet`, using just one `vk::raii::DescriptorSetLayout`, your code might look like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::DescriptorSets, holding a single vk::raii::DescriptorSet, given a vk::raii::Device device, a vk::raii::DescriptorPool descriptorPool, and a single vk::raii::DescriptorSetLayout descriptorSetLayout
|
// create a vk::raii::DescriptorSets, holding a single vk::raii::DescriptorSet, given a vk::raii::Device device, a vk::raii::DescriptorPool descriptorPool, and a single vk::raii::DescriptorSetLayout descriptorSetLayout
|
||||||
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo( *descriptorPool, *descriptorSetLayout );
|
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo( *descriptorPool, *descriptorSetLayout );
|
||||||
vk::raii::DescriptorSets pDescriptorSets( device, descriptorSetAllocateInfo );
|
vk::raii::DescriptorSets pDescriptorSets( device, descriptorSetAllocateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
And, again similar to the vk::raii::CommandBuffers handling described above, you can get one `vk::raii::DescriptorSet` out of a `vk::raii::DescriptorSets` like this:
|
And, again similar to the vk::raii::CommandBuffers handling described above, you can get one `vk::raii::DescriptorSet` out of a `vk::raii::DescriptorSets` like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// get the vk::raii::DescriptorSet with index descriptorSetIndex, given a vk::raii::DescriptorSets descriptorSets
|
// get the vk::raii::DescriptorSet with index descriptorSetIndex, given a vk::raii::DescriptorSets descriptorSets
|
||||||
vk::raii::DescriptorSet descriptorSet( std::move( descriptorSets[descriptorSetIndex] ) );
|
vk::raii::DescriptorSet descriptorSet( std::move( descriptorSets[descriptorSetIndex] ) );
|
||||||
|
```
|
||||||
|
|
||||||
### 10 Create a vk::raii::RenderPass
|
### 10 Create a vk::raii::RenderPass
|
||||||
|
|
||||||
Creating a `vk::raii::RenderPass` is pretty simple, given you already have a meaningful `vk::RenderPassCreateInfo`:
|
Creating a `vk::raii::RenderPass` is pretty simple, given you already have a meaningful `vk::RenderPassCreateInfo`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::RenderPass, given a vk::raii::Device device and a vk::RenderPassCreateInfo renderPassCreateInfo
|
// create a vk::raii::RenderPass, given a vk::raii::Device device and a vk::RenderPassCreateInfo renderPassCreateInfo
|
||||||
vk::raii::RenderPass renderPass( device, renderPassCreateInfo );
|
vk::raii::RenderPass renderPass( device, renderPassCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
### 11 Create a vk::raii::ShaderModule
|
### 11 Create a vk::raii::ShaderModule
|
||||||
|
|
||||||
Again, creating a `vk::raii::ShaderModule` is simple, given a `vk::ShaderModuleCreateInfo` with some meaningful code:
|
Again, creating a `vk::raii::ShaderModule` is simple, given a `vk::ShaderModuleCreateInfo` with some meaningful code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::ShaderModule, given a vk::raii::Device device and a vk::ShaderModuleCreateInfo shaderModuleCreateInfo
|
// create a vk::raii::ShaderModule, given a vk::raii::Device device and a vk::ShaderModuleCreateInfo shaderModuleCreateInfo
|
||||||
vk::raii::ShaderModule shaderModule( device, shaderModuleCreateInfo );
|
vk::raii::ShaderModule shaderModule( device, shaderModuleCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
### 12 Create vk::raii::Framebuffers
|
### 12 Create vk::raii::Framebuffers
|
||||||
|
|
||||||
If you have a `std::vector<vk::raii::ImageView>` as described in chapter 05 above, with one view per `VkImage` that you got from a `vk::raii::SwapchainKHR`; and one `vk::raii::ImageView` as described in chapter 06 above, which is a view on a `vk::raii::Image`, that is supposed to be a depth buffer, you can create a `vk::raii::Framebuffer` per swapchain image.
|
If you have a `std::vector<vk::raii::ImageView>` as described in chapter 05 above, with one view per `VkImage` that you got from a `vk::raii::SwapchainKHR`; and one `vk::raii::ImageView` as described in chapter 06 above, which is a view on a `vk::raii::Image`, that is supposed to be a depth buffer, you can create a `vk::raii::Framebuffer` per swapchain image.
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vector of vk::raii::Framebuffer, given a vk::raii::ImageView depthImageView, a vector of vk::raii::ImageView swapchainImageViews, a vk::raii::RenderPass renderPass, a vk::raii::Devie device, and some width and height
|
// create a vector of vk::raii::Framebuffer, given a vk::raii::ImageView depthImageView, a vector of vk::raii::ImageView swapchainImageViews, a vk::raii::RenderPass renderPass, a vk::raii::Devie device, and some width and height
|
||||||
// use the depth image view as the second attachment for each vk::raii::Framebuffer
|
// use the depth image view as the second attachment for each vk::raii::Framebuffer
|
||||||
std::array<vk::ImageView, 2> attachments;
|
std::array<vk::ImageView, 2> attachments;
|
||||||
@ -315,11 +390,13 @@ If you have a `std::vector<vk::raii::ImageView>` as described in chapter 05 abov
|
|||||||
vk::FramebufferCreateInfo framebufferCreateInfo( {}, *renderPass, attachments, width, height, 1 );
|
vk::FramebufferCreateInfo framebufferCreateInfo( {}, *renderPass, attachments, width, height, 1 );
|
||||||
framebuffers.push_back( vk::raii::Framebuffer( device, framebufferCreateInfo ) );
|
framebuffers.push_back( vk::raii::Framebuffer( device, framebufferCreateInfo ) );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 13 Initialize a Vertex Buffer
|
### 13 Initialize a Vertex Buffer
|
||||||
|
|
||||||
To initialize a vertex buffer, you essentially have to combine some of the pieces described in the chapters before. First, you need to create a `vk::raii::Buffer` and a `vk::raii::DeviceMemory` and bind them:
|
To initialize a vertex buffer, you essentially have to combine some of the pieces described in the chapters before. First, you need to create a `vk::raii::Buffer` and a `vk::raii::DeviceMemory` and bind them:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Buffer vertexBuffer, given a vk::raii::Device device and some vertexData in host memory
|
// create a vk::raii::Buffer vertexBuffer, given a vk::raii::Device device and some vertexData in host memory
|
||||||
vk::BufferCreateInfo bufferCreateInfo( {}, sizeof( vertexData ), vk::BufferUsageFlagBits::eVertexBuffer );
|
vk::BufferCreateInfo bufferCreateInfo( {}, sizeof( vertexData ), vk::BufferUsageFlagBits::eVertexBuffer );
|
||||||
vk::raii::Buffer vertexBuffer( device, bufferCreateInfo );
|
vk::raii::Buffer vertexBuffer( device, bufferCreateInfo );
|
||||||
@ -334,21 +411,27 @@ To initialize a vertex buffer, you essentially have to combine some of the piece
|
|||||||
|
|
||||||
// copy the vertex data into the vertexDeviceMemory
|
// copy the vertex data into the vertexDeviceMemory
|
||||||
...
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Later on, you can bind that vertex buffer to a command buffer:
|
Later on, you can bind that vertex buffer to a command buffer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// bind a complete single vk::raii::Buffer vertexBuffer as a vertex buffer, given a vk::raii::CommandBuffer commandBuffer
|
// bind a complete single vk::raii::Buffer vertexBuffer as a vertex buffer, given a vk::raii::CommandBuffer commandBuffer
|
||||||
commandBuffer.bindVertexBuffer( 0, { *vertexBuffer }, { 0 } );
|
commandBuffer.bindVertexBuffer( 0, { *vertexBuffer }, { 0 } );
|
||||||
|
```
|
||||||
|
|
||||||
### 14 Initialize a Graphics Pipeline
|
### 14 Initialize a Graphics Pipeline
|
||||||
|
|
||||||
Initializing a graphics pipeline is not very raii-specific. Just instantiate it, provided you have a valid vk::GraphicsPipelineCreateInfo:
|
Initializing a graphics pipeline is not very raii-specific. Just instantiate it, provided you have a valid vk::GraphicsPipelineCreateInfo:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Pipeline, given a vk::raii::Device device and a vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
// create a vk::raii::Pipeline, given a vk::raii::Device device and a vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||||
vk::raii::Pipeline graphicsPipeline( device, graphicsPipelineCreateInfo );
|
vk::raii::Pipeline graphicsPipeline( device, graphicsPipelineCreateInfo );
|
||||||
|
```
|
||||||
|
|
||||||
The only thing to keep in mind here is the dereferencing of raii handles, like `pipelineLayout` or `renderPass` in the `vk::GraphicsPipelineCreateInfo`:
|
The only thing to keep in mind here is the dereferencing of raii handles, like `pipelineLayout` or `renderPass` in the `vk::GraphicsPipelineCreateInfo`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo(
|
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo(
|
||||||
{}, // flags
|
{}, // flags
|
||||||
pipelineShaderStageCreateInfos, // stages
|
pipelineShaderStageCreateInfos, // stages
|
||||||
@ -364,6 +447,7 @@ The only thing to keep in mind here is the dereferencing of raii handles, like `
|
|||||||
*pipelineLayout, // layout
|
*pipelineLayout, // layout
|
||||||
*renderPass // renderPass
|
*renderPass // renderPass
|
||||||
);
|
);
|
||||||
|
```
|
||||||
|
|
||||||
### 15 Drawing a Cube
|
### 15 Drawing a Cube
|
||||||
|
|
||||||
@ -371,14 +455,18 @@ Finally, we get all those pieces together and draw a cube.
|
|||||||
|
|
||||||
To do so, you need a `vk::raii::Semaphore`:
|
To do so, you need a `vk::raii::Semaphore`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Semaphore, given a vk::raii::Device
|
// create a vk::raii::Semaphore, given a vk::raii::Device
|
||||||
vk::raii::Semaphore imageAcquiredSemphore( device, vk::SemaphoreCreateInfo() );
|
vk::raii::Semaphore imageAcquiredSemphore( device, vk::SemaphoreCreateInfo() );
|
||||||
|
```
|
||||||
|
|
||||||
That semaphore can be used, to acquire the next imageIndex from the `vk::raii::SwapchainKHR` swapchain:
|
That semaphore can be used, to acquire the next imageIndex from the `vk::raii::SwapchainKHR` swapchain:
|
||||||
|
|
||||||
|
```cpp
|
||||||
vk::Result result;
|
vk::Result result;
|
||||||
uint32_t imageIndex;
|
uint32_t imageIndex;
|
||||||
std::tie( result, imageIndex ) = swapchain.acquireNextImage( timeout, *imageAcquiredSemaphore );
|
std::tie( result, imageIndex ) = swapchain.acquireNextImage( timeout, *imageAcquiredSemaphore );
|
||||||
|
```
|
||||||
|
|
||||||
Note, `vk::raii::SwapchainKHR::acquireNextImage` returns a `std::pair<vk::Result, uint32_t>`, that can nicely be assigned onto two separate values using std::tie().
|
Note, `vk::raii::SwapchainKHR::acquireNextImage` returns a `std::pair<vk::Result, uint32_t>`, that can nicely be assigned onto two separate values using std::tie().
|
||||||
|
|
||||||
@ -386,6 +474,7 @@ And also note, the returned `vk::Result` can not only be `vk::Result::eSuccess`,
|
|||||||
|
|
||||||
Next, you can record some commands into a `vk::raii::CommandBuffer`:
|
Next, you can record some commands into a `vk::raii::CommandBuffer`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
// open the commandBuffer for recording
|
// open the commandBuffer for recording
|
||||||
commandBuffer.begin( {} );
|
commandBuffer.begin( {} );
|
||||||
|
|
||||||
@ -414,27 +503,36 @@ Next, you can record some commands into a `vk::raii::CommandBuffer`:
|
|||||||
// end the render pass and stop recording
|
// end the render pass and stop recording
|
||||||
commandBuffer.endRenderPass();
|
commandBuffer.endRenderPass();
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
|
```
|
||||||
|
|
||||||
To submit that command buffer to a `vk::raii::Queue` graphicsQueue you might want to use a `vk::raii::Fence`
|
To submit that command buffer to a `vk::raii::Queue` graphicsQueue you might want to use a `vk::raii::Fence`
|
||||||
|
|
||||||
|
```cpp
|
||||||
// create a vk::raii::Fence, given a vk::raii::Device device
|
// create a vk::raii::Fence, given a vk::raii::Device device
|
||||||
vk::raii::Fence fence( device, vk::FenceCreateInfo() );
|
vk::raii::Fence fence( device, vk::FenceCreateInfo() );
|
||||||
|
```
|
||||||
|
|
||||||
With that, you can fill a `vk::SubmitInfo` and submit the command buffer
|
With that, you can fill a `vk::SubmitInfo` and submit the command buffer
|
||||||
|
|
||||||
|
```cpp
|
||||||
vk::PipelineStageFlags waitDestinationStageMask( vk::PipelineStageFlagBits::eColorAttachmentOutput );
|
vk::PipelineStageFlags waitDestinationStageMask( vk::PipelineStageFlagBits::eColorAttachmentOutput );
|
||||||
vk::SubmitInfo submitInfo( *imageAcquiredSemaphore, waitDestinationStageMask, *commandBuffer );
|
vk::SubmitInfo submitInfo( *imageAcquiredSemaphore, waitDestinationStageMask, *commandBuffer );
|
||||||
graphicsQueue.submit( submitInfo, *fence );
|
graphicsQueue.submit( submitInfo, *fence );
|
||||||
|
```
|
||||||
|
|
||||||
At some later point, you can wait for that submit being ready by waiting for the fence
|
At some later point, you can wait for that submit being ready by waiting for the fence
|
||||||
|
|
||||||
|
```cpp
|
||||||
while ( vk::Result::eTimeout == device.waitForFences( { *fence }, VK_TRUE, timeout ) )
|
while ( vk::Result::eTimeout == device.waitForFences( { *fence }, VK_TRUE, timeout ) )
|
||||||
;
|
;
|
||||||
|
```
|
||||||
|
|
||||||
And finally, you can use the `vk::raii::Queue` presentQueue to, well, present that image
|
And finally, you can use the `vk::raii::Queue` presentQueue to, well, present that image
|
||||||
|
|
||||||
|
```cpp
|
||||||
vk::PresentInfoKHR presentInfoKHR( nullptr, *swapChain, imageIndex );
|
vk::PresentInfoKHR presentInfoKHR( nullptr, *swapChain, imageIndex );
|
||||||
result = presentQueue.presentKHR( presentInfoKHR );
|
result = presentQueue.presentKHR( presentInfoKHR );
|
||||||
|
```
|
||||||
|
|
||||||
Note here, again, that `result` can not only be `vk::Result::eSuccess`, but also `vk::Result::eSuboptimalKHR`, which should be handled accordingly.
|
Note here, again, that `result` can not only be `vk::Result::eSuccess`, but also `vk::Result::eSuboptimalKHR`, which should be handled accordingly.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user