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 14 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...


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.

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

* `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)

Copy link
Author

Choose a reason for hiding this comment

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

I left it with the C version in the tutorial text on purpose with the idea that we can link directly to the spec such that the reader will know to look for the spec's version instead of the C++ version. That might be confusing though, maybe we should change it to C++ everywhere?

}
----

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.

Copy link
Author

Choose a reason for hiding this comment

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

This might be good for after the merge to revisit. I agree, this needs to be revisited.


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.

Copy link
Author

Choose a reason for hiding this comment

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

Let's come back to this after the merge. I'd like to do it across all of the tutorial instead of this one instance so we commit to one method as the "best practice" for teaching it.

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

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?

Copy link
Author

Choose a reason for hiding this comment

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

Let's do that after the merge. The changes are already getting a bit too large for one PR and a move in git commit seems like a quick way to make it worse.

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?

Copy link
Author

Choose a reason for hiding this comment

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

stbi_load takes them as ints. It might be worthwhile to make the type specifics clear but maybe after the merge this is worth doing?

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

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,...)?

Copy link
Author

Choose a reason for hiding this comment

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

Let's add that after the merge.

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

Copy link
Author

Choose a reason for hiding this comment

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

same here, I want to test all the code in as many places as possible and this PR already has too much scope.

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.

Copy link
Author

Choose a reason for hiding this comment

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

agreed, but this is already getting to be a lot of changes, I'll do another PR to cleanup things like this and ensure it's the same in all the files after this one is merged.

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


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?

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

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?

Copy link
Author

Choose a reason for hiding this comment

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

I didn't even know that I made that change. I didn't touch the images.

gpx1000 added 2 commits June 18, 2025 12:52
… attachments directory and only there.

* switch to using VULKAN_HPP_NO_STRUCT_CONSTRUCTORS so we get slightly more verbosity in our example code.
* use Vulkan 1.4 modern techniques
** show removal of framebuffer and renderpasses
** show timeline semaphores in the compute example.
** show synch2 practices.
Simplified examples by updating to RAII patterns for Vulkan-Hpp bindings. Added dynamic rendering to replace render pass and framebuffer usage, aligning with Vulkan 1.3+. Improved code readability with structured initialization and fixed typos in documentation.
@gpx1000
Copy link
Author

gpx1000 commented Jun 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 😄

I'm going to come back to this request after it's merged. It's definitely a good idea. Don't know if it's better/worse than submodules, but it's a good idea.

Updated code and documentation to align with modern Vulkan best practices:
- Adopted RAII wrappers for better resource management and type safety.
- Introduced structured initialization for clarity and consistency.
- Improved tutorial content with grammatical fixes, terminology updates, and modernization of examples.
- Rephrased sections for better readability and alignment with Vulkan 1.3+ features.
@gpx1000
Copy link
Author

gpx1000 commented Jun 20, 2025

If possible, everyone take another look. There's several comments here that I have left open and haven't yet addressed. However, this PR is getting pretty complicated to work with and I'd like to get it merged so we can address any short comings in their own PR. So unless you see anything drastically as a bug, let's go ahead and merge this and then circle back for improvements.

@SaschaWillems
Copy link
Collaborator

One thing that I might've missed initially: Base requirement is now Vulkan1.4.

Not sure if that's what we want. Android is still far behind, and I fear we would leave too many people behind?

@gpx1000
Copy link
Author

gpx1000 commented Jun 20, 2025 via email

Introduce `install_dependencies_windows.bat` and `install_dependencies_linux.sh` to simplify setup for Vulkan projects. Updated documentation with instructions for using the scripts and clarified Vulkan SDK installation steps.
@gpx1000
Copy link
Author

gpx1000 commented Jun 20, 2025

I went ahead and made two install scripts one for Linux and one for Windows. The Windows one is using vcpkg. While I might not use vcpkg anywhere, enough people have asked for it, and it does make writing the windows install script easier.
I wanted to avoid fetchcontent and submodules because both of those solutions would only capture the dependencies at a specific version and we'd have to update over time. This way, it's a system wide install that should work everywhere in all platforms. Try it out and lemme know if it works for you (I only have my Linux machine up so I haven't tested the Windows install).

@SaschaWillems
Copy link
Collaborator

Awesome 👍🏻 That simplifies things a lot. CMake is now almost working out of the box. I'm still seeing these errors:

CMake Error at CMakeLists.txt:123 (file):
file COPY cannot find
"V:/Vulkan-Docs-Site/Vulkan-Tutorial-review/attachments/../resources/viking_room.obj":
No error.
Call Stack (most recent call first):
CMakeLists.txt:219 (add_chapter)

(multiple similar errors)

and

CMake Error at V:/github/vcpkg/scripts/buildsystems/vcpkg.cmake:598 (_add_executable):
Cannot find source file:

11_render_passes.cpp

Call Stack (most recent call first):
CMakeLists.txt:99 (add_executable)
CMakeLists.txt:154 (add_chapter)

(miltuple simial errors)

The first one can be fixed by adjusting the path for the resources (they're now located right besides the CMake file)

The second one seems to be caused by files still being included that are no longer present (due to using dynamic rendering).

Here is a patch file for that (not sure if it works outside of windows):
patch_file.patch

@SaschaWillems
Copy link
Collaborator

A few chapters won't compile for me (Windows 11, Visual Studio 2022) due to syntax errors:

06_swap_chain_creation.cpp(263,29): error C2039: 'contains': is not a member of 'std::ranges'e formal list?)
06_swap_chain_creation.cpp(263,39): error C2065: 'presentModes': undeclared identifier
06_swap_chain_creation.cpp(263,29): error C3861: 'contains': identifier not found

05_window_surface.cpp(176,49): error C2187: syntax error: ')' was unexpected here
05_window_surface.cpp(176,49): error C2059: syntax error: ')'

07_image_views.cpp(234,14): error C2039: 'enabledExtensionCount': is not a member of 'vk::DeviceQueueCreateInfo'
07_image_views.cpp(234,61): error C2059: syntax error: ';'
07_image_views.cpp(235,13): error C2059: syntax error: '.'
07_image_views.cpp(238,9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
07_image_views.cpp(238,36): error C2065: 'physicalDevice': undeclared identifier
07_image_views.cpp(238,52): error C2065: 'deviceCreateInfo': undeclared identifier
07_image_views.cpp(239,9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
07_image_views.cpp(239,50): error C2065: 'graphicsIndex': undeclared identifier
07_image_views.cpp(240,9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
07_image_views.cpp(240,49): error C2065: 'presentIndex': undeclared identifier
07_image_views.cpp(241,5): error C2059: syntax error: '}'
07_image_views.cpp(241,5): error C2143: syntax error: missing ';' before '}'
07_image_views.cpp(243,28): error C2143: syntax error: missing ';' before '{'
07_image_views.cpp(243,28): error C2447: '{': missing function header (old-style formal list?)

27_depth_buffering.cpp(1002,16): error C1075: '(': no matching token found

It also looks like MSVC is having some serious issues with the Vulkan module, most samples look like this:

image

They do compile, but code highlighting is completely broken. Matches my former experience with modules and MSVC :(

@SaschaWillems
Copy link
Collaborator

So I got at least some of the chapters working. Did test e.g. the compute shader (since I wrote that one), but it didn't work as expected.

First, it looks very different:

image
image

Top is what it should look like, bottom is what it now looks like. Should be easy to fix.

But the sample now also crashes when trying to resize the window:

vk::Queue::presentKHR: ErrorOutOfDateKHR

gpx1000 added 2 commits June 21, 2025 11:25
…ctory didn't fully make it over. This should compile everything again, I'm still working on compute_shader.
Updated swap chain format to use `vk::SurfaceFormatKHR` for better type clarity and consistency. Refined format and color space references throughout the code. Improved `chooseSwapSurfaceFormat` logic for desirable format selection and adjusted pipeline blending constants for correctness. Removed unused shader modules in the graphics pipeline setup.
@gpx1000
Copy link
Author

gpx1000 commented Jun 21, 2025

I'll have to look at the color blending again when there's more time. The cause of that change is escaping me but I'm not getting a crash when I resize. I'll have to wait until after the move when I can setup my windows machine again to test if there's something I'm missing there that might cause the resize issue. Thanks for testing @SaschaWillems

@SaschaWillems
Copy link
Collaborator

Thanks for fixing.

I do get a compilation error in the base code project, but that looks totally bogus and I think it's a bug with MSVC and not the actual code.

All other chapters now compile fine 👍🏻

@SaschaWillems
Copy link
Collaborator

Did a build of the docs site with this PR and read through all chapters. I'm happy with the changes, and I only noticed a few minotr things like some chapters still linking to the GLSL shaders or some broken links.

But those can be easily fixed after the merge.

Maybe we can discuss this PR on the next docs call and decide on how to move forward.

[]( vk::QueueFamilyProperties const & qfp ) { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; } );
// get the first index into queueFamilyProperties which supports graphics
auto graphicsQueueFamilyProperty = std::ranges::find_if( queueFamilyProperties, []( auto const & qfp )
{ return (qfp.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlags>(0); } );

Choose a reason for hiding this comment

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

Maybe:
return (qfp.queueFlags & vk::QueueFlagBits::eGraphics);

Copy link
Author

Choose a reason for hiding this comment

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

Compiler error complaining that the type is not a bool. I could cast it, but I bet the compiler would give the same asm output from casting as simply comparing against 0.

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.

Some more comments...

std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
std::vector dynamicStates = {

Choose a reason for hiding this comment

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

What a std::vector is this?

Copy link
Author

Choose a reason for hiding this comment

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

clang lint has started suggesting when the compiler can figure out what the types are automatically. This is an instance of trying that suggestion out. If you feel this loses clarity to the tutorial, we can certainly be more verbose.

Choose a reason for hiding this comment

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

Why is this file deleted?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Afaikr because the tutorial now uses dynamic rendering, so no more need for render passes.

Copy link
Author

Choose a reason for hiding this comment

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

Yep, same for the framebuffers. Those go away. Once we merge, I'll renumber the chapters. I also am trying to hold back a new chapter on Mobile and using Vulkan prior to 1.4 and determining when that's needed. But we're already pretty complicated PR as it stands so I don't want to add until it can be an atomic add.

Choose a reason for hiding this comment

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

Why is this file deleted?

Refactor device extension handling and physical device selection logic

Unified device extension references to `requiredDeviceExtension` for clarity and consistency. Improved physical device selection by incorporating checks for Vulkan 1.3 features and necessary dynamic rendering capabilities. Enhanced code readability with structured initialization and modern Vulkan C++ practices.
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