Skip to content

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

gpx1000
Copy link

@gpx1000 gpx1000 commented Mar 18, 2025

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.

gpx1000 added 7 commits March 17, 2025 12:46
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.
@SaschaWillems SaschaWillems self-requested a review March 19, 2025 20:48
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the line width changed for all adoc files. This makes it kinda hard to compare and in some cases also breaks links:

image

Is there a way to revert the line width/break changes?

Copy link
Author

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.

@SaschaWillems
Copy link
Collaborator

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.

@gpx1000
Copy link
Author

gpx1000 commented Mar 19, 2025

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'))
hljs.registerLanguage('bash', require('highlight.js/lib/languages/bash'))
hljs.registerLanguage('clojure', require('highlight.js/lib/languages/clojure'))
hljs.registerLanguage('cpp', require('highlight.js/lib/languages/cpp'))
hljs.registerLanguage('cs', require('highlight.js/lib/languages/cs'))
hljs.registerLanguage('css', require('highlight.js/lib/languages/css'))
hljs.registerLanguage('diff', require('highlight.js/lib/languages/diff'))
hljs.registerLanguage('dockerfile', require('highlight.js/lib/languages/dockerfile'))
hljs.registerLanguage('elixir', require('highlight.js/lib/languages/elixir'))
hljs.registerLanguage('glsl', require('highlight.js/lib/languages/glsl'))
hljs.registerLanguage('go', require('highlight.js/lib/languages/go'))
hljs.registerLanguage('groovy', require('highlight.js/lib/languages/groovy'))
hljs.registerLanguage('haskell', require('highlight.js/lib/languages/haskell'))
hljs.registerLanguage('hlsl', require('./hlsl.js'))
hljs.registerLanguage('java', require('highlight.js/lib/languages/java'))
hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript'))
hljs.registerLanguage('json', require('highlight.js/lib/languages/json'))
hljs.registerLanguage('julia', require('highlight.js/lib/languages/julia'))
hljs.registerLanguage('kotlin', require('highlight.js/lib/languages/kotlin'))
hljs.registerLanguage('lua', require('highlight.js/lib/languages/lua'))
hljs.registerLanguage('markdown', require('highlight.js/lib/languages/markdown'))
hljs.registerLanguage('nix', require('highlight.js/lib/languages/nix'))
hljs.registerLanguage('none', require('highlight.js/lib/languages/plaintext'))
hljs.registerLanguage('objectivec', require('highlight.js/lib/languages/objectivec'))
hljs.registerLanguage('perl', require('highlight.js/lib/languages/perl'))
hljs.registerLanguage('php', require('highlight.js/lib/languages/php'))
hljs.registerLanguage('properties', require('highlight.js/lib/languages/properties'))
hljs.registerLanguage('puppet', require('highlight.js/lib/languages/puppet'))
hljs.registerLanguage('python', require('highlight.js/lib/languages/python'))
hljs.registerLanguage('ruby', require('highlight.js/lib/languages/ruby'))
hljs.registerLanguage('rust', require('highlight.js/lib/languages/rust'))
hljs.registerLanguage('scala', require('highlight.js/lib/languages/scala'))
hljs.registerLanguage('shell', require('highlight.js/lib/languages/shell'))
hljs.registerLanguage('sql', require('highlight.js/lib/languages/sql'))
hljs.registerLanguage('swift', require('highlight.js/lib/languages/swift'))
hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'))
hljs.registerLanguage('yaml', require('highlight.js/lib/languages/yaml'))

@SaschaWillems
Copy link
Collaborator

We might get away by duplicating the hlsl one, and adding in a few slang keywords ;)

@gpx1000
Copy link
Author

gpx1000 commented Mar 19, 2025

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?

Copy link

@asuessenbach asuessenbach left a 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;

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);

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.

Copy link
Author

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);

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(),

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() );

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;

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;

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();

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());

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?

@SaschaWillems
Copy link
Collaborator

SaschaWillems commented Mar 20, 2025

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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/).
Copy link
Collaborator

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/).
Copy link
Collaborator

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].
Copy link
Collaborator

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)
Copy link
Collaborator

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;
Copy link
Collaborator

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
Copy link
Collaborator

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?

Copy link
Author

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
Copy link
Collaborator

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]
Copy link
Collaborator

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).
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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]
Copy link
Collaborator

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,
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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.
Copy link
Collaborator

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;
Copy link
Collaborator

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
Copy link
Collaborator

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.

@SaschaWillems
Copy link
Collaborator

SaschaWillems commented Mar 21, 2025

@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:

image

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.

@gpx1000
Copy link
Author

gpx1000 commented Mar 21, 2025

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 ;-).

@SaschaWillems
Copy link
Collaborator

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.
Copy link
Collaborator

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!
Copy link
Collaborator

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:

Copy link
Collaborator

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

@SaschaWillems
Copy link
Collaborator

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
Copy link
Collaborator

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.
Copy link
Collaborator

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);
}
----

Copy link
Collaborator

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_.
Copy link
Collaborator

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;
Copy link
Collaborator

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;
Copy link
Collaborator

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]
Copy link
Collaborator

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
Copy link
Collaborator

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

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);

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() )

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() {

}

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};

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) {

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) {

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) {

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({});

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({});

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 );

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.

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);

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;

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!");

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;

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());

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) {

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},

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;

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) {

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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the consts 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)) {

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;

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();

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;

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.

Copy link

@asuessenbach asuessenbach left a 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 ) );

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 );

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, {},

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");

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());

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?

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;

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;

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.

@SaschaWillems
Copy link
Collaborator

What does it mean, that there are two directories, attachments and code, with nearly but not completely identical content?

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

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?

Copy link

@asuessenbach asuessenbach Apr 24, 2025

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing auto here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants