-
Notifications
You must be signed in to change notification settings - Fork 10
This is a new version of the tutorial using RAII and SLang #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Streamline formatting, clarify key setup steps, and consolidate redundant information in Vulkan development guide. Focus on improving readability while maintaining cross-platform support for Vulkan SDK, dependencies, and build tools like CMake and GLFW.
Refactor code to replace raw Vulkan handles with RAII wrappers from Vulkan-Hpp for better resource management and safety. Key changes include RAII usage for buffers, images, and device memory, along with modernized function calls and parameter handling. Code readability and alignment with updated Vulkan practices were also improved.
Migrated codebase to use `vk::` RAII wrappers, enhanced type safety, and updated Vulkan API conventions. Made textual corrections in documentation for grammatical consistency and clarity. Adjusted resource creation and rendering process to comply with Vulkan's multi-sampling requirements.
Corrected punctuation, grammar, and terminology for clarity and accuracy. Improved consistency in formatting, adopted RAII style for Vulkan handles, and refined examples by updating to modern Vulkan C++ bindings for better maintainability.
Fix typos and improve clarity in compute shader documentation Corrected grammatical mistakes and improved phrasing for better readability in compute shader documentation. Adjusted links to use `.adoc` extension and reformatted lines for consistency and clarity. No functional changes were made to the content. ```
Updated tutorial text to use inclusive language ("we" instead of "I") and improved readability. Modernized Vulkan instance creation using RAII and cleaner initialization code in compliance with up-to-date Vulkan best practices.
can click them to learn more. | ||
Vulkan is a very new API, so there may be some shortcomings in the | ||
specification itself. | ||
You are encouraged to submit feedback to https://github |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's not a good way to do that sadly :( The line width in the adoc pages didn't seem to work so I just grabbed the original markdown settings and used them.
We might need to check if Antora supports highlighting for slang. Afair it does glsl out of the box, but for hlsl I had to manually add in a syntax highlighter. |
Slang isn't in the list for our antora site. Here's the list of languages we have a highlight script for. We should probably add SLang at the very least. var hljs = require('highlight.js/lib/highlight') hljs.registerLanguage('asciidoc', require('highlight.js/lib/languages/asciidoc')) |
We might get away by duplicating the hlsl one, and adding in a few slang keywords ;) |
Yep, that's what I'm thinking. I'll make a tracking issue on the vulkan-site repo and begin a pass at making a highlight.js. It might be worth it to investigate how to contribute that back upstream to help promote use of Slang? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of questions...
|
||
VkInstance instance; | ||
vk::raii::Context context; | ||
std::unique_ptr<vk::raii::Instance> instance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to have the vk::raii
objects wrapped by a std::unique_ptr
. You could as well just use them as
vk::raii::Instance instance = nullptr;
Note that, as those classes don't have a default constructor, you would be forced to initialize them with nullptr
.
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
createInfo.pApplicationInfo = &appInfo; | ||
|
||
constexpr auto appInfo = vk::ApplicationInfo("Hello Triangle", 1, "No Engine", 1, vk::ApiVersion14); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's worth to use designated initializers with all the structs. Would make it a bit more verbose again, but maybe it's easier to digest then. Would look like
constexpr vk::ApplicationInfo applicationInfo{ .pApplicationName = "Hello Triangle",
.applicationVersion = VK_MAKE_VERSION( 1, 0, 0 ),
.pEngineName = "No Engine",
.engineVersion = VK_MAKE_VERSION( 1, 0, 0 ),
.apiVersion = VK_API_VERSION_1_4 };
You would need to set VULKAN_HPP_NO_STRUCT_CONSTRUCTORS to have that feature available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really good idea. I'll work on getting it in.
} | ||
auto glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | ||
vk::InstanceCreateInfo createInfo({}, &appInfo, {}, glfwExtensions); | ||
instance = std::make_unique<vk::raii::Instance>(context, createInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case you want to switch from std::unique_ptr<vk::raii::Instance>
to vk::raii::Instance
, you could encode it here as
instance = vk::raii::Instance( context, createInfo );
or
instance = context.createInstance( createInfo );
createInfo.queueCreateInfoCount = 1; | ||
// get the first index into queueFamiliyProperties which supports graphics | ||
auto graphicsQueueFamilyProperty = | ||
std::find_if( queueFamilyProperties.begin(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use std::ranges::find_if
here as well?
// first check if the graphicsIndex is good enough | ||
auto presentIndex = physicalDevice->getSurfaceSupportKHR( graphicsIndex, *surface ) | ||
? graphicsIndex | ||
: static_cast<uint32_t>( queueFamilyProperties.size() ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make the invalid case independent of the queueFamilyProperties
, like ~0
?
|
||
return availableFormats[0]; | ||
static vk::Format chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats) { | ||
return ( availableFormats[0].format == vk::Format::eUndefined ) ? vk::Format::eB8G8R8A8Unorm : availableFormats[0].format; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very different from the original.
return availablePresentMode; | ||
} | ||
} | ||
|
||
return VK_PRESENT_MODE_FIFO_KHR; | ||
return vk::PresentModeKHR::eFifo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
return std::ranges::contains( presentModes, vk::PresentModeKHR::eMailbox ) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo;
vk::DeviceQueueCreateInfo deviceQueueCreateInfo( {}, graphicsIndex, 1, &queuePriority ); | ||
vk::DeviceCreateInfo deviceCreateInfo( {}, deviceQueueCreateInfo ); | ||
deviceCreateInfo.enabledExtensionCount = deviceExtensions.size(); | ||
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
vk::DeviceCreateInfo deviceCreateInfo( {}, deviceQueueCreateInfo, {}, deviceExtensions );
or, with designated initializers
vk::DeviceCreateInfo deviceCreateInfo{ .queueCreateInfoCount = 1,
.pQueueCreateInfos = &deviceQueueCreateInfo,
.enabledExtensionCount = static_cast<uint32_t>( deviceExtensions.size() ),
.ppEnabledExtensionNames = deviceExtensions.data() };
} | ||
|
||
return indices; | ||
physicalDevice = std::make_unique<vk::raii::PhysicalDevice>(vk::raii::PhysicalDevices( *instance ).front()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No check for suitability of the device?
Remarks on the CMake build setup: First, kudos for providing a proper CMakeLists.txt. That has always been one of my main issues with the current tutorial 👍🏻 On Windows though it does not yet work out of the box, as you have to manually specify folders for e.g. glfw and that does not seem to work with the glfw package you can download from their site. For my own projects I moved to using CMake's FetchContent for such third party libraries, as that takes care of download, compiling and even installing these dependencies. Would it be possible to use FetchContent for the tutorial too? That would simplify project setup even further 😄 |
@@ -4,96 +4,177 @@ | |||
|
|||
== Attribution | |||
|
|||
The Khronos Vulkan^®^ Tutorial is based on the "link:https://vulkan-tutorial.com/[Vulkan Tutorial]" by Alexander Overvoorde licensed under link:https://creativecommons.org/licenses/by-sa/4.0/[CC BY-SA 4.0]. | |||
The Khronos Vulkan^®^ Tutorial is based on the "link:https://vulkan-tutorial .com/[Vulkan Tutorial]" by Alexander Overvoorde licensed under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link is broken due to a whitespace.
code structuring. | ||
We will use C{pp} features like classes and RAII to organize logic and | ||
resource lifetimes. | ||
There are also two alternative versions of this tutorial available for Rust |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could remove those links. One of those hasn't been updated in years, the other one is also lagging behind.
However, Microsoft has introduced a fantastic package management tool which | ||
does work cross-platform. VCPkg also includes setting up all required CMake | ||
settings. We recommend following the excellent documentation | ||
https://learn.microsoft.com/en-us/vcpkg/get_started/get-started?pivots=shell |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link is broken due to the line break.
|
||
=== Vulkan Packages | ||
Download the VulkanSDK tarball from [LunarG](https://vulkan.lunarg.com/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link is wrongly formatted (markdown style), which doesn't work for Asciidoc. Correct format would be url[caption], so https://vulkan.lunarg.com/[LunarG]
g++ $(CFLAGS) -o VulkanTest main.cpp $(LDFLAGS) | ||
---- | ||
I will assume that you already have some basic experience with CMake, like | ||
how variables and rules work. If not, you can get up to speed very quickly with [this tutorial](https://cmake.org/cmake/help/book/mastering-cmake/cmake/Help/guide/tutorial/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above. This link is using the markdown format, and needs to be changed to asciidoc link format.
The most important component you'll need for developing Vulkan applications is the SDK. | ||
It includes the headers, standard validation layers, debugging tools and a loader for the Vulkan functions. | ||
The loader looks up the functions in the driver at runtime, similarly to GLEW for OpenGL - if you're familiar with that. | ||
The SDK version for macOS internally uses link:https://github.com/KhronosGroup/MoltenVK[MoltenVK]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link works fine, the link: is superflous though and can be removed (sorry for nitpicking ;) )
glfwPollEvents(); | ||
} | ||
We recommend using CMake everywhere, and Apple is no different. An example | ||
of how to use CMake for Apple can be found [here](https://medium.com/practical-coding/migrating-to-cmake-in-c-and-getting-it-working-with-xcode-50b7bb80ae3d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link needs to be adjusted to the asciidoc link format too.
|
||
[,c++] | ||
---- | ||
vkInstance instance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nitpick: But I think we should remove indenation for standalone code blocks. Having snippets like these start every line with lots of empty space seems wasteful.
First replace the `#include <vulkan/vulkan.h>` line with | ||
Vulkan works perfectly fine without creating a window if you want to use it for | ||
off-screen rendering, but it's a lot more exciting to actually show something! | ||
First, let's add GLFW: NB: we will continue to use the GLFW_INCLUDE_VULKAN as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might sound odd to a native speaker, but what does "NB" stand for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, NB is latin, 'nota bene' or in English, "Note Well"
I'll change that to Note which basically means the same thing. There's subtle differences that don't really matter here.
We're using value initialization here to leave it as `nullptr`. | ||
While vk::ApiVersion10 or Vulkan 1.0 does exist, some functionality | ||
is older and doesn't work well with RAII, or as we'll link:../02_Graphics_pipeline_basics/01_Shader_modules.adoc[talk about later], | ||
the SLang language, so we're showing 1.4 as our baseline. If we were in the C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be Slang instead of SLang, at least accordig to Slang
That makes sense for essential extensions like the window system interface, but what if we want to check for optional functionality? | ||
== Encountered VK_ERROR_INCOMPATIBLE_DRIVER: | ||
If using macOS with the latest MoltenVK sdk, you may get `VK_ERROR_INCOMPATIBLE_DRIVER` | ||
returned from `vkCreateInstance`. According to the [Getting Start Notes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link needs to be converted to asciidoc format
|
||
Before continuing with the more complex steps after instance creation, it's time to evaluate our debugging options by checking out xref:./02_Validation_layers.adoc[validation layers]. | ||
Before continuing with the more complex steps after instance creation, it's time | ||
to evaluate our debugging options by checking out [validation layers](!en/Drawing_a_triangle/Setup/Validation_layers). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link needs to be converted to asciidoc format
* `VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT`: Informational message like the creation of a resource | ||
* `VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT`: Message about behavior that is not necessarily an error, but very likely a bug in your application | ||
* `VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT`: Message about behavior that is invalid and may cause crashes | ||
* `VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT`: Informational message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be updated to use the vulkan.hpp enums instead (e.g. vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning)
} | ||
---- | ||
|
||
The next section will introduce the first requirements that we'll check for in the `isDeviceSuitable` function. | ||
As we'll start using more Vulkan features in the later chapters we will also extend this function to include more checks. | ||
|
||
== Base device suitability checks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider rewriting or removing this chapter all along and instead replace it with explicit device selection. This "device suitability check" has caused lots of confusion judging from community feedback.
createInfo.pEnabledFeatures = &deviceFeatures; | ||
---- | ||
Previous implementations of Vulkan made a distinction between instance and | ||
device-specific validation layers, but this is [no longer the case] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link needs to be changed to asciidoc format
Note: At the time of writing SlangC will natively support SPIR-V 1.3 and above | ||
without needing to go through emitting GLSL to get to SPIR-V. While | ||
everything in this tutorial could work in SPIR-V 1.0, it would require us to | ||
break the SLang shaders up into multiple files which begs the question, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be Slang instead of SLang
) | ||
add_custom_command ( | ||
OUTPUT ${SHADERS_DIR}/slang.spv | ||
COMMAND ${SLANGC_EXECUTABLE} ${SHADER_SOURCES} -target spirv -profile spirv_1_4 -emit-spirv-directly -fvk-use-entrypoint-name ${ENTRY_POINTS} -o slang.spv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to lack information on how SLANGC_EXECUTABLE is actually set/discovered.
https://shader-slang.org/slang/user-guide/[SLANG], and | ||
https://en.wikipedia.org/wiki/High-Level_Shading_Language[HLSL]. | ||
This bytecode format is called https://www.khronos.org/spir[SPIR-V] and is designed | ||
to be used with both Vulkan and OpenCL (both Khronos APIs). It is a format that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe remove OpenCL. This is confusing esp. to beginners, as the SPIR generated for Vulkan is not directly compatible with the one for OpenCL.
compiler directly via `slangc`, we will be using `slangc` in our cmake build | ||
process instead. | ||
|
||
SLang is a shading language with a C-style syntax. Programs written in it have a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be Slang.
@@ -58,10 +53,10 @@ The `VkPipelineInputAssemblyStateCreateInfo` struct describes two things: what k | |||
The former is specified in the `topology` member and can have values like: | |||
|
|||
* `VK_PRIMITIVE_TOPOLOGY_POINT_LIST`: points from vertices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should/could be changed to use the vulkan.hpp enums instead.
---- | ||
|
||
If `rasterizerDiscardEnable` is set to `VK_TRUE`, then geometry never passes through the rasterizer stage. | ||
This basically disables any output to the framebuffer. | ||
|
||
[,c++] | ||
---- | ||
rasterizer.polygonMode = VK_POLYGON_MODE_FILL; | ||
vk::PipelineRasterizationStateCreateInfo rasterizer({}, vk::False, vk::False, vk::PolygonMode::eFill); | ||
---- | ||
|
||
The `polygonMode` determines how fragments are generated for geometry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list below should/could use vulkan.hpp enums instead
@@ -59,7 +64,7 @@ We have the following choices for `loadOp`: | |||
* `VK_ATTACHMENT_LOAD_OP_DONT_CARE`: Existing contents are undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should/could use the vulkan.hpp enums.
Textures and framebuffers in Vulkan are represented by `VkImage` objects with a certain pixel format, however the layout of the pixels in memory can change based on what you're trying to do with an image. | ||
Textures and framebuffers in Vulkan are represented by `VkImage` objects | ||
with a certain pixel format, however, the layout of the pixels in memory can | ||
change based on what you're trying to do with an image. | ||
|
||
Some of the most common layouts are: | ||
|
||
* `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL`: Images used as color attachment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should/could use the vulkan.hpp enums.
@gpx1000: One general issue I see is with initialization now. A lot of paragraphs explicitly refer to parameters by name, but the code now always passes parameters to constructurs. E.g. this: People have no way of connecting the parameters mentioned in the text with the code above. For my projects that use vulkan.hpp I prefer designated initializers like this: vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState{ .topology = vk::PrimitiveTopology::eTriangleList }; While it's a bit more code, it's also much easier to understand and follow. |
Yep, @asuessenbach said the same thing. I'm currently working on a new version that uses designated initializers. it's a big change will take a few days ;-). |
Thanks 👍🏻 Somehow missed Andrea's comment during my review marathon. |
---- | ||
|
||
Now we are ready to issue the draw command for the triangle: | ||
|
||
[,c++] | ||
---- | ||
vkCmdDraw(commandBuffer, 3, 1, 0, 0); | ||
commandBuffer->draw(3, 1, 0, 0); | ||
---- | ||
|
||
The actual `vkCmdDraw` function is a bit anticlimactic, but it's so simple because of all the information we specified in advance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the tutorial now using slang, the glsl specific built-ins like gl_vertexIndex
and gl_InstanceIndex
should be replaced by the Slang variants.
@@ -518,9 +436,17 @@ You'll see that the program now exits without problems when closing the window. | |||
|
|||
A little over 900 lines of code later, we've finally gotten to the stage of seeing something pop up on the screen! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LoC count might be revised, with vulkan.hpp i'ts far less than 900 ;)
@@ -11,7 +11,7 @@ The way to fix this is to allow multiple frames to be _in-flight_ at once, that | |||
How do we do this? | |||
Any resource that is accessed and modified during rendering must be duplicated. | |||
Thus, we need multiple command buffers, semaphores, and fences. | |||
In later chapters we will also add multiple instances of other resources, so we will see this concept reappear. | |||
In later chapters, we will also add multiple instances of other resources, so we will see this concept reappear. | |||
|
|||
Start by adding a constant at the top of the program that defines how many frames should be processed concurrently: | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next line could be updated to constexpr
Another general thing we might want to discuss is the use of C constants. They're still used in a lot of places, and now often no longer match the code snippets. I think we should use vulkan.hpp enums instead everywhere, even in text paragraphs to make things less confusing for readers. |
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; | ||
attributeDescriptions[1].offset = offsetof(Vertex, color); | ||
---- | ||
This is automatically calculated using the `offsetof` macro. However, it's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we should keep that paragraph. Feels odd to use offsetof
and yet warn people to maybe not use it.
---- | ||
|
||
For the view transformation I've decided to look at the geometry from above at a 45 degree angle. | ||
The `glm::lookAt` function takes the eye position, center position and up axis as parameters. | ||
|
||
[,c++] | ||
---- | ||
ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); | ||
ubo.proj = glm::perspective(glm::radians(45.0f), static_cast<float>(swapChainExtent.width) / static_cast<float>(swapChainExtent.height), 0.1f, 10.0f); | ||
---- | ||
|
||
I've chosen to use a perspective projection with a 45 degree vertical field-of-view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While not part of this PR we should revise this chapter. I don't think manually flipping projection (see line 344+) is required anymore or if it still is there is a GLM define to change that. If this is changed, the whole "orginally for OpenGL" paragraph can be removed.
createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); | ||
vk::raii::Image textureImageTemp({}); | ||
vk::raii::DeviceMemory textureImageMemoryTemp({}); | ||
createImage(texWidth, texHeight, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, textureImageTemp, textureImageMemoryTemp); | ||
} | ||
---- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This chapter lacks a proper introduction. There is no real mention as to what a layout transition is and why it's used. Maybe add some context.
@@ -5,7 +5,7 @@ | |||
== Introduction | |||
|
|||
We looked at descriptors for the first time in the uniform buffers part of the tutorial. | |||
In this chapter we will look at a new type of descriptor: _combined image sampler_. | |||
In this chapter, we will look at a new type of descriptor: _combined image sampler_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to rephrase that a bit. This sounds like you MUST use a combined image sampler to sample an image in a shader, which isn't the case.
---- | ||
struct VSInput { | ||
float2 inPosition; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth to unify naming. The C++ code above e.g. has "pos" instead of "position".
|
||
layout(location = 0) out vec4 outColor; | ||
Sampler2D texture; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though it may not be required, maybe explicitly add the binding slot, so people know it's something that might be required by Slang and Vulkan.
---- | ||
|
||
There are equivalent `sampler1D` and `sampler3D` types for other types of images. | ||
Make sure to use the correct binding here. | ||
|
||
[,glsl] | ||
[,slang] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code snippet here is still using glsl syntax and needs to be updated.
|
||
Many graphics API tutorials have the reader write their own OBJ loader in a chapter like this. | ||
The problem with this is that any remotely interesting 3D application will soon require features that are not supported by this file format, like skeletal animation. | ||
We _will_ load mesh data from an OBJ model in this chapter, but we'll focus more on integrating the mesh data with the program itself rather than the details of loading it from a file. | ||
|
||
== Library |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this was completely removed? I think we should at least keep the mention of tinyobj and where it's coming from.
// find the index of the first queue family that supports graphics | ||
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice->getQueueFamilyProperties(); | ||
|
||
// get the first index into queueFamiliyProperties which supports graphics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, the determination of those two indices could be formulated like this:
// look for a queueFamilyIndex that supports graphics and present
auto combinedIt = std::ranges::find_if( queueFamilyProperties,
[&physicalDevice, &surface]( vk::QueueFamilyProperties const & qfp )
{
static uint32_t index = 0;
return ( qfp.queueFlags & vk::QueueFlagBits::eGraphics ) && physicalDevice.getSurfaceSupportKHR( index++, surface );
} );
if ( combinedIt != queueFamilyProperties.end() )
{
uint32_t index = static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), combinedIt ) );
return { index, index }; // the first index that supports graphics and present
}
else
{
// there's no single index that supports both graphics and present -> look for separate ones
auto graphicsIt =
std::ranges::find_if( queueFamilyProperties, []( vk::QueueFamilyProperties const & qfp ) { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; } );
if ( graphicsIt != queueFamilyProperties.end() )
{
uint32_t graphicsIndex = static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), graphicsIt ) );
auto presentIt = std::ranges::find_if( queueFamilyProperties,
[&physicalDevice, &surface]( vk::QueueFamilyProperties const & )
{
static uint32_t index = 0;
return physicalDevice.getSurfaceSupportKHR( index++, surface );
} );
if ( presentIt != queueFamilyProperties.end() )
{
uint32_t presentIndex = static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), presentIt ) );
return { graphicsIndex, presentIndex };
}
else
{
throw std::runtime_error( "Could not find a queue family index that supports present -> terminating" );
}
}
else
{
throw std::runtime_error( "Could not find a queue family index that supports graphics -> terminating" );
}
}
} | ||
[[nodiscard]] vk::raii::ShaderModule createShaderModule(const std::vector<char>& code) const { | ||
vk::ShaderModuleCreateInfo createInfo({}, code.size(), reinterpret_cast<const uint32_t*>(code.data()) ); | ||
vk::raii::ShaderModule shaderModule(*device, createInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just
return device->createShaderModule( createInfo );
|
||
std::vector<vk::ExtensionProperties> props = context.enumerateInstanceExtensionProperties(); | ||
if (const auto propsIterator = std::ranges::find_if(props, []( vk::ExtensionProperties const & ep ) { return strcmp( ep.extensionName, vk::EXTDebugUtilsExtensionName ) == 0; } ); propsIterator == props.end() ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe perform this check only if enableValidationLayers
is true
?
for ( auto image : swapChainImages ) | ||
{ | ||
imageViewCreateInfo.image = image; | ||
swapChainImageViews.emplace_back( *device, imageViewCreateInfo ); | ||
} | ||
} | ||
|
||
void createGraphicsPipeline() { | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intended to have this empty function to create the graphics pipeline?
In the tutorial named 08_graphics_pipeline?
vkDestroyShaderModule(device, vertShaderModule, nullptr); | ||
vk::PipelineShaderStageCreateInfo vertShaderStageInfo({}, vk::ShaderStageFlagBits::eVertex, shaderModule, "vertMain"); | ||
vk::PipelineShaderStageCreateInfo fragShaderStageInfo({}, vk::ShaderStageFlagBits::eFragment, shaderModule, "fragMain"); | ||
vk::PipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createGraphicsPipeline just creates a ShaderModule?
|
||
if (result == VK_ERROR_OUT_OF_DATE_KHR) { | ||
if (result == vk::Result::eErrorOutOfDateKHR) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as you don't have VULKAN_HPP_NO_EXCEPTIONS
defined, vk::Result::eErrorOutOfDateKHR
would be thrown as an exception.
That is, you would need to try
acquireNextImage
and either catch
the specific vk::OutOfDateKHRError
, or the general vk::SystemError
.
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { | ||
const vk::PresentInfoKHR presentInfoKHR( **renderFinishedSemaphore[currentFrame], **swapChain, imageIndex ); | ||
result = presentQueue->presentKHR( presentInfoKHR ); | ||
if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above: you need to try
/catch
on vk::Result::eErrorOutOfDataKHR
.
} | ||
|
||
vkBindBufferMemory(device, buffer, bufferMemory, 0); | ||
void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Buffer& buffer, vk::raii::DeviceMemory& bufferMemory) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a slightly different signature:
std::pair<vk::raii::Buffer, vk::raii::DeviceMemory> createBuffer( vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties )
Would be called then like
vk::raii::Buffer stagingBuffer(nullptr);
vk::raii::DeviceMemory stagingBufferMemory(nullptr);
std::tie( stagingBuffer, stagingBufferMemory ) =
createBuffer( bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent );
or
auto[ stagingBuffer, stagingBufferMemory ] =
createBuffer( bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent );
memcpy(data, vertices.data(), (size_t) bufferSize); | ||
vkUnmapMemory(device, stagingBufferMemory); | ||
vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); | ||
vk::raii::Buffer stagingBuffer({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make sure, a vk::raii::Object
is generated empty, pass a nullptr
in:
vk::raii::Buffer stagingBuffer(nullptr);
|
||
copyBuffer(stagingBuffer, vertexBuffer, bufferSize); | ||
vk::raii::Buffer vertexBufferTemp({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you use some temporary objects here? Can't you directly use the actual vertexBuffer?
commandBuffers[currentFrame]->setViewport(0, vk::Viewport(0.0f, 0.0f, static_cast<float>(swapChainExtent.width), static_cast<float>(swapChainExtent.height), 0.0f, 1.0f)); | ||
commandBuffers[currentFrame]->setScissor( 0, vk::Rect2D( vk::Offset2D( 0, 0 ), swapChainExtent ) ); | ||
commandBuffers[currentFrame]->bindVertexBuffers(0, **vertexBuffer, {0}); | ||
commandBuffers[currentFrame]->bindIndexBuffer( *indexBuffer, 0, vk::IndexType::eUint16 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, instead of vk::IndexType::eUint16
:
vk::IndexTypeValue<decltype(indices)::value_type>::value
It's substantially harder to read, but it makes clear where that value comes from (indices
), and it stay correct in case the indices are changed to uint32_t
, or so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this file be named 22_descriptor_set_layout.cpp
?
createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); | ||
vk::raii::Image textureImageTemp({}); | ||
vk::raii::DeviceMemory textureImageMemoryTemp({}); | ||
createImage(texWidth, texHeight, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, textureImageTemp, textureImageMemoryTemp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createImage
takes uint32_t
for width
and height
, but texWidth
and texHeight
are int
. Maybe change them to uint32_t
?
vk::DeviceQueueCreateInfo deviceQueueCreateInfo( {}, graphicsIndex, 1, &queuePriority ); | ||
vk::DeviceCreateInfo deviceCreateInfo( {}, deviceQueueCreateInfo ); | ||
vk::PhysicalDeviceFeatures deviceFeatures; | ||
deviceFeatures.samplerAnisotropy = vk::True; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it checked somewhere if samplerAnisotropy
is supported?
The previous version checked it in pickPhysicalDevice
via isDeviceSuitable
@@ -711,354 +421,203 @@ class HelloTriangleApplication { | |||
throw std::runtime_error("failed to load texture image!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three lines above: VkDeviceSize
-> vk::DeviceSize
struct VSOutput | ||
{ | ||
float4 pos : SV_Position; | ||
float3 fragColor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fragColor
is not used.
vk::DescriptorSetLayoutBinding( 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment, nullptr) | ||
}; | ||
|
||
vk::DescriptorSetLayoutCreateInfo layoutInfo({}, bindings.size(), bindings.data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe static_cast<uint32_t>(bindings.size())
for (VkFormat format : candidates) { | ||
VkFormatProperties props; | ||
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); | ||
vk::Format findSupportedFormat(const std::vector<vk::Format>& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
auto formatIt = std::ranges::find_if(candidates, [&physicalDevice, &tiling, &features](auto const format){
vk::FormatProperties props = physicalDevice->getFormatProperties(format);
return (((tiling == vk::ImageTiling::eLinear) && ((props.linearTilingFeatures & features) == features)) ||
((tiling == vk::ImageTiling::eOptimal) && ((props.optimalTilingFeatures & features) == features)));
});
if ( formatIt == candidates.end())
{
throw std::runtime_error("failed to find supported format!");
}
return *formatIt;
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, | ||
VK_IMAGE_TILING_OPTIMAL, | ||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | ||
{vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about all the other depth formats (like eD16Unorm
, eX8D24UnormPack32
, eD16UnormS8Uint
,...)?
bool hasStencilComponent(VkFormat format) { | ||
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; | ||
bool hasStencilComponent(vk::Format format) { | ||
return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the other stencil formats (like eS8Uint
, eD16UnormS8Uint
, eS8Uint
,...)
|
||
return shaderModule; | ||
} | ||
|
||
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { | ||
static vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
auto formatIt = std::ranges::find(availableFormats, vk::SurfaceFormatKHR(vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear));
return (formatIt != availableFormats.end() ? *formatIt : availableFormats[0];
if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { | ||
throw std::runtime_error("failed to create image view!"); | ||
} | ||
[[nodiscard]] std::unique_ptr<vk::raii::ImageView> createImageView(const vk::raii::Image& image, vk::Format format, vk::ImageAspectFlags aspectFlags) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the const
s should be applied to the samples before as well.
@@ -1012,7 +601,7 @@ class HelloTriangleApplication { | |||
|
|||
vertex.color = {1.0f, 1.0f, 1.0f}; | |||
|
|||
if (uniqueVertices.count(vertex) == 0) { | |||
if (!uniqueVertices.contains(vertex)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead:
auto [vertexIt, inserted] = uniqueVertices.insert({vertex, static_cast<uint32_t>(vertices.size())});
if (inserted)
{
vertices.push_back(vertex);
}
indices.push_back(vertexIt->second);
vk::DeviceQueueCreateInfo deviceQueueCreateInfo( {}, graphicsIndex, 1, &queuePriority ); | ||
vk::PhysicalDeviceFeatures deviceFeatures; | ||
vk::DeviceCreateInfo deviceCreateInfo( {}, deviceQueueCreateInfo, {}, deviceExtensions, &deviceFeatures ); | ||
deviceFeatures.samplerAnisotropy = vk::True; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it checked anywhere that samplerAnisotropy
is supported?
deviceFeatures.samplerAnisotropy = vk::True; | ||
deviceCreateInfo.pEnabledFeatures = &deviceFeatures; | ||
deviceCreateInfo.enabledExtensionCount = deviceExtensions.size(); | ||
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it checked anywhere that the deviceExtensions
are supported?
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | ||
barrier.srcQueueFamilyIndex = vk::QueueFamilyIgnored; | ||
barrier.dstQueueFamilyIndex = vk::QueueFamilyIgnored; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those three members are already set in the constructor right above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean, that there are two directories, attachments
and code
, with nearly but not completely identical content?
queueFamilyProperties.end(), | ||
[]( vk::QueueFamilyProperties const & qfp ) { return (qfp.queueFlags & vk::QueueFlagBits::eGraphics && qfp.queueFlags & vk::QueueFlagBits::eCompute); } ); | ||
|
||
graphicsAndComputeIndex = static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), graphicsAndComputeQueueFamilyProperty ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there's no queue family supporting both, graphics and compute?
device = std::make_unique<vk::raii::Device>( *physicalDevice, deviceCreateInfo ); | ||
graphicsQueue = std::make_unique<vk::raii::Queue>( *device, graphicsAndComputeIndex, 0 ); | ||
computeQueue = std::make_unique<vk::raii::Queue>( *device, graphicsAndComputeIndex, 0 ); | ||
presentQueue = std::make_unique<vk::raii::Queue>( *device, presentIndex, 0 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While presentQueue
might be different from the graphicsQueue
, computeQueue
and graphicsQueue
are the same. Why do you use them as separate entities?
Maybe you should at least use different queue indices for those two?
|
||
vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal); | ||
vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics, {}, {colorAttachmentRef}); | ||
vk::SubpassDependency dependency(VK_SUBPASS_EXTERNAL, {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VK_SUBPASS_EXTERNAL
-> vk::SubpassExternal
?
VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; | ||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | ||
// vk::PipelineShaderStageCreateInfo vertShaderStageInfo({}, vk::ShaderStageFlagBits::eVertex, vertShaderModule, "main"); | ||
// vk::PipelineShaderStageCreateInfo fragShaderStageInfo({}, vk::ShaderStageFlagBits::eFragment, fragShaderModule, "main"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed to remove that out-commented code?
|
||
if (vkAllocateCommandBuffers(device, &allocInfo, computeCommandBuffers.data()) != VK_SUCCESS) { | ||
throw std::runtime_error("failed to allocate compute command buffers!"); | ||
vk::CommandBufferAllocateInfo allocInfo(*commandPool, vk::CommandBufferLevel::ePrimary, computeCommandBuffers.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big difference to createCommandBuffers
. Maybe replace them by a function handling both cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this file intentionally empty?
void createTextureImage() { | ||
int texWidth, texHeight, texChannels; | ||
stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); | ||
VkDeviceSize imageSize = texWidth * texHeight * 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VkDeviceSize
-> vk::DeviceSize
?
, vk::QueueFamilyIgnored, vk::QueueFamilyIgnored, image); | ||
barrier.image = image; | ||
barrier.srcQueueFamilyIndex = vk::QueueFamilyIgnored; | ||
barrier.dstQueueFamilyIndex = vk::QueueFamilyIgnored; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
image and the queue family indices are already set in the constructor above.
There should only be an "attachment" folder. That's what Antora requires, all files from code need to be moved there. |
The graphics driver will do a lot less hand holding, which means that you will have to do more work in your application to ensure correct behavior. | ||
This tutorial will teach you the basics of using the | ||
https://www.khronos.org/vulkan/[Vulkan] graphics and compute API. | ||
Vulkan is a new API by the https://www.khronos.org/[Khronos group] (known |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Vulkan still a "new" API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have you changed the background of this and all the following images?
[,c++] | ||
---- | ||
auto createInfo = vk::xxx(); | ||
object = vk::raii::XXX(context, createInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing auto
here?
This is a rewrite of the tutorial to use SLang and RAII. While it is complete and everything is working, this is a work in progress; PR is here to enable discussion and thought.