Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions en/00_Introduction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ Some other great computer graphics resources are:
* https://github.com/RayTracing/raytracing.github.io[Ray tracing in one weekend]
* https://www.pbr-book.org/[Physically Based Rendering book]

You can use C instead of C{pp} if you want, but you will have to use a
different linear algebra library, and you will be on your own in terms of
code structuring.
We will use C{pp} features like classes and RAII to organize logic and
resource lifetimes.
This tutorial exclusively uses C{pp} and the Vulkan-Hpp bindings; it does not
provide a parallel C track. We use C{pp} features like classes and RAII to
organize logic and resource lifetimes, so if you use the raw C Vulkan API
instead, the code shown here will not translate directly and you will be on
your own in terms of code structuring.

To make it easier to learn to work with Vulkan, we'll be using the newer
https://github.com/KhronosGroup/Vulkan-Hpp[Vulkan-Hpp] bindings that
Expand Down
34 changes: 17 additions & 17 deletions en/01_Overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,23 @@ This is just to give you a big picture to relate all the individual components t

=== Step 1 - Instance and physical device selection

A Vulkan application starts by setting up the Vulkan API through a `vk::Instance`.
A Vulkan application starts by setting up the Vulkan API through a `vk::raii::Instance`.
An instance is created by describing your application and any API extensions
you will be using. After creating the instance, you can query for Vulkan
supported hardware and select one or more ``vk::PhysicalDevice``s to use for
supported hardware and select one or more ``vk::raii::PhysicalDevice``s to use for
operations. You can query for properties like VRAM size and device
capabilities to select desired devices, for example, to prefer using
dedicated graphics cards.

=== Step 2 - Logical device and queue families

After selecting the right hardware device to use, you need to create a
`vk::Device` (logical device), where you describe more specifically which
`vk::raii::Device` (logical device), where you describe more specifically which
physical device features you will be using, like multi viewport rendering
and 64-bit floats.
You also need to specify which queue families you would like to use.
Most operations performed with Vulkan, like draw commands and memory
operations, are asynchronously executed by submitting them to a `vk::Queue`.
operations, are asynchronously executed by submitting them to a `vk::raii::Queue`.
Queues are allocated from queue families, where each queue family supports a
specific set of operations in its queues.
For example, there could be separate queue families for graphics, compute
Expand All @@ -100,7 +100,7 @@ We will be using GLFW in this tutorial, but more about that in the next
chapter.

We need two more parts to actually render to a window: a window surface
(`vk::SurfaceKHR`) and a swap chain (`vk::SwapchainKHR`).
(`vk::raii::SurfaceKHR`) and a swap chain (`vk::raii::SwapchainKHR`).
Note the `KHR` postfix, which means that these objects are part of a Vulkan
extension. The Vulkan API itself is completely platform-agnostic, which is
why we need to use the standardized WSI (Window System Interface) extension
Expand Down Expand Up @@ -133,7 +133,7 @@ could be used to implement your own window manager, for example.
=== Step 4 - Swap chain image views

To draw to an image acquired from the swap chain, we would typically wrap
it into a `vk::ImageView`. An image view references a specific
it into a `vk::raii::ImageView`. An image view references a specific
part of an image to be used for e.g. color writes.
Because there could be different images in the swap chain,
we would preemptively create an image view for each
Expand All @@ -144,18 +144,18 @@ of them and select the right one at draw time.
In earlier versions of Vulkan, a render pass defined how rendering operations
should occur with framebuffers, specifying the types of images used (e.g., color, depth)
and how their contents should be treated (e.g., cleared, loaded, or stored).
A `vk::RenderPass` would define subpasses and attachment usage, and a `vk::Framebuffer`
A `vk::raii::RenderPass` would define subpasses and attachment usage, and a `vk::raii::Framebuffer`
would bind specific image views to these attachments.

However, with dynamic rendering (introduced in Vulkan 1.3),
you no longer need to create a `vk::Framebuffer` at all.
you no longer need to create a `vk::raii::Framebuffer` at all.
Dynamic rendering eliminates the need for predefined render passes and framebuffers,
allowing you to specify rendering attachments directly during command recording.
This makes the API much simpler, as we can define the rendering targets on the
fly without worrying about the overhead of managing framebuffers.

With dynamic rendering, you no longer need to predefine `vk::RenderPass` or `vk::Framebuffer`.
Instead, you specify the rendering attachments at the start of command recording, using `vk::beginRendering`
With dynamic rendering, you no longer need to predefine `vk::raii::RenderPass` or `vk::raii::Framebuffer`.
Instead, you specify the rendering attachments at the start of command recording, using `vk::raii::CommandBuffer::beginRendering`
and structs like `vk::RenderingInfo` to provide all necessary attachment information dynamically.

In our initial triangle rendering application,
Expand All @@ -164,10 +164,10 @@ a single image as a color target and instruct Vulkan to clear it to a solid colo

=== Step 6 - Graphics pipeline

The graphics pipeline in Vulkan is set up by creating a `VkPipeline` object.
The graphics pipeline in Vulkan is set up by creating a `vk::raii::Pipeline` object.
It describes the configurable state of the graphics card, like the viewport
size and depth buffer operation and the programmable state using `vk::ShaderModule` objects.
The `vk::ShaderModule` objects are created from shader byte code.
size and depth buffer operation and the programmable state using `vk::raii::ShaderModule` objects.
The `vk::raii::ShaderModule` objects are created from shader byte code.
The driver also needs to know which render targets will be used in the
pipeline, which we specify by referencing the render pass.

Expand All @@ -194,9 +194,9 @@ are made very explicit.

As mentioned earlier, many of the operations in Vulkan that we want to
execute, like drawing operations, need to be submitted to a queue.
These operations first need to be recorded into a `vk::CommandBuffer` before
These operations first need to be recorded into a `vk::raii::CommandBuffer` before
they can be submitted.
These command buffers are allocated from a `vk::CommandPool` that is
These command buffers are allocated from a `vk::raii::CommandPool` that is
associated with a specific queue family.
To draw a triangle prior to dynamic rendering, we would need to record a command buffer with the
following operations:
Expand Down Expand Up @@ -257,7 +257,7 @@ So in short, to draw the first triangle, we need to:
* Select a supported graphics card (`PhysicalDevice`)
* Create a `Device` and `Queue` for drawing and presentation
* Create a window, window surface and swap chain
* Wrap the swap chain images into `VkImageView`
* Wrap the swap chain images into `vk::raii::ImageView`
* Set up dynamic rendering
* Set up the graphics pipeline
* Allocate and record a command buffer with the draw commands for every
Expand Down Expand Up @@ -300,7 +300,7 @@ Many structures in Vulkan require you to explicitly specify the type of
structure in the `sType` member.
The `pNext` member can point to an extension structure and will always be
`nullptr` in this tutorial.
Functions that create or destroy an object will have a `VkAllocationCallbacks`
Functions that create or destroy an object will have a `vk::AllocationCallbacks`
parameter that allows you to use a custom allocator for driver memory,
which will also be left `nullptr` in this tutorial.

Expand Down
2 changes: 1 addition & 1 deletion en/05_Uniform_buffers/01_Descriptor_pool_and_sets.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ vk::DescriptorPoolCreateInfo poolInfo{ .flags = vk::DescriptorPoolCreateFlagBits
Aside from the maximum number of individual descriptors that are available, we also need to specify the maximum number of descriptor sets that may be allocated.

The structure has an optional flag similar to command pools that determines if individual descriptor sets can be freed or not.
As the `vk::raii::DescriptorSets` destroy the underlying `VkDescriptorSet` on destruction we need to set it to `vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet` to allow that.
As the `vk::raii::DescriptorSets` destroy the underlying `vk::DescriptorSet` on destruction we need to set it to `vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet` to allow that.

[,c++]
----
Expand Down
2 changes: 1 addition & 1 deletion en/06_Texture_mapping/01_Image_view_and_sampler.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The first resource is one that we've already seen before while working with the
We've seen before, with the swap chain images and the framebuffer, that images are accessed through image views rather than directly.
We will also need to create such an image view for the texture image.

Add a class member to hold a `VkImageView` for the texture image and create a new function `createTextureImageView` where we'll create it:
Add a class member to hold a `vk::raii::ImageView` for the texture image and create a new function `createTextureImageView` where we'll create it:

[,c++]
----
Expand Down
2 changes: 1 addition & 1 deletion en/06_Texture_mapping/02_Combined_image_sampler.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ After that, we're going to add texture coordinates to `Vertex`, replacing the co

== Updating the descriptors

Browse to the `createDescriptorSetLayout` function and add a `VkDescriptorSetLayoutBinding` for a combined image sampler descriptor.
Browse to the `createDescriptorSetLayout` function and add a `vk::DescriptorSetLayoutBinding` for a combined image sampler descriptor.
We'll simply put it in the binding after the uniform buffer:

[,c++]
Expand Down
2 changes: 1 addition & 1 deletion en/11_Compute_Shader.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ work on a one-dimensional array, like we do in this chapter, you only have to sp

As an example: If we dispatch a work group count of [64, 1, 1] with a compute shader local size of [32, 32, 1], our compute shader will be invoked 64 x 32 x 32 = 65,536 times.

Note that the maximum count for work groups and local sizes differs from implementation to implementation, so you should always check the compute related `maxComputeWorkGroupCount`, `maxComputeWorkGroupInvocations` and `maxComputeWorkGroupSize` limits in `VkPhysicalDeviceLimits`.
Note that the maximum count for work groups and local sizes differs from implementation to implementation, so you should always check the compute related `maxComputeWorkGroupCount`, `maxComputeWorkGroupInvocations` and `maxComputeWorkGroupSize` limits in `vk::PhysicalDeviceLimits`.

== Compute shaders

Expand Down
2 changes: 1 addition & 1 deletion en/courses/18_Ray_tracing/01_Dynamic_rendering.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

*Objective*: Ensure the base project uses *dynamic rendering* and understand how to verify it using RenderDoc.

In dynamic rendering, we no longer create a VkRenderPass or VkFrameBuffer; instead we begin rendering with `vkCmdBeginRenderingKHR`, specifying attachments on-the-fly. This makes our code more flexible (no need to predeclare subpasses) and is now the "modern" way to render in Vulkan.
In dynamic rendering, we no longer create a `vk::raii::RenderPass` or `vk::raii::Framebuffer`; instead we begin rendering with `commandBuffer.beginRendering()`, specifying attachments on-the-fly. This makes our code more flexible (no need to predeclare subpasses) and is now the "modern" way to render in Vulkan.

== Task 1: Check the setup for dynamic rendering

Expand Down
12 changes: 6 additions & 6 deletions en/courses/18_Ray_tracing/02_Acceleration_structures.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ vk::AccelerationStructureBuildSizesInfoKHR blasBuildSizes =
);
----

This helper function uses `vkGetAccelerationStructureBuildSizesKHR()` and returns the memory sizes needed for the BLAS. We need to allocate:
This helper function uses `device.getAccelerationStructureBuildSizesKHR()` and returns the memory sizes needed for the BLAS. We need to allocate:

. A buffer for the BLAS itself.
. Another buffer for the scratch space used during the build process.

We can then create these buffers and store them in persistent arrays as they will be needed later.

We also need to create the BLAS handle itself, which is done with `vk::AccelerationStructureCreateInfoKHR` and this device function helper that uses `vkCreateAccelerationStructureKHR()`. The handle is stored in a vector for later use (remember that we need one for each submesh):
We also need to create the BLAS handle itself, which is done with `vk::AccelerationStructureCreateInfoKHR` and this device function helper that uses `device.createAccelerationStructureKHR()`. The handle is stored in a vector for later use (remember that we need one for each submesh):

[,c{pp}]
----
Expand All @@ -98,7 +98,7 @@ The following diagram summarizes all the structures and buffers we have created

image::../../../images/38_TASK02_blas_structures.png[]

To put it all together, we need to submit a command buffer to build the BLAS on the GPU. This is done with `vkCmdBuildAccelerationStructuresKHR()`, which takes the build info and a range. The range adds flexibility to build multiple geometries in one go, but here we only have one geometry per BLAS so it is kept simple:
To put it all together, we need to submit a command buffer to build the BLAS on the GPU. This is done with the command buffer's `buildAccelerationStructuresKHR()` method, which takes the build info and a range. The range adds flexibility to build multiple geometries in one go, but here we only have one geometry per BLAS so it is kept simple:

[,c{pp}]
----
Expand Down Expand Up @@ -148,7 +148,7 @@ vk::AccelerationStructureInstanceKHR instance{
instances.push_back(instance);
----

Note how we needed to get the device address of the BLAS using `vkGetAccelerationStructureDeviceAddressKHR()`. We also set the transform matrix as the identity matrix for now, we will revisit this later in the lab.
Note how we needed to get the device address of the BLAS using `device.getAccelerationStructureAddressKHR()`. We also set the transform matrix as the identity matrix for now, we will revisit this later in the lab.

Now that all instances are stored in a vector, we need to prepare the instance data for the TLAS. This involves creating a buffer that holds the instance data.

Expand Down Expand Up @@ -201,7 +201,7 @@ vk::AccelerationStructureBuildSizesInfoKHR tlasBuildSizes =

And again we create the necessary buffers.

To create the TLAS handle, we use `vkCreateAccelerationStructureKHR()` as before:
To create the TLAS handle, we use `device.createAccelerationStructureKHR()` as before:

[,c{pp}]
----
Expand Down Expand Up @@ -274,7 +274,7 @@ vk::WriteDescriptorSet asWrite{
};
----

And later on call `vkUpdateDescriptorSets()` with the TLAS included in the list:
And later on call `device.updateDescriptorSets()` with the TLAS included in the list:

[,c{pp}]
----
Expand Down
Loading