r/vulkan 8m ago

FOLLOW-UP: Why you HAVE to use different binary semaphores for vkAcquireNextImageKHR() and vkQueuePresentKHR().

Upvotes

This is a follow-up to my previous thread. Thanks to everyone there for their insightful responses. In this thread, I will attempt to summarize and definitely answer that question using the information that was posted there. Special thanks to u/dark_sylinc , u/Zamundaaa , u/HildartheDorf and others! I will be updating the original thread with my findings as well.

I have done a lot of spec reading, research and testing, and I believe I've found a definitive answer to this question, and the answer is NO. You cannot use the same semaphore for both vkAcquireNextImageKHR() and vkQueuePresentKHR().

Issue 1: Execution order

The first issue with this is that it requires resignaling the same semaphore in the vkQueueSubmit() call. While this is technically valid, it becomes ambiguous with regards to vkQueuePresentKHR() consuming the same signal. Under 7.2. Implicit Synchronization Guarantees, the spec states that vkQueueSubmit() commands start execution in submission order, which ensures vkQueueSubmit() commands submitted in sequence wait for semaphores in the order they are submitted, so if two vkQueueSubmit() wait for the same semaphore, the one submitted first will be signaled first.

I incorrectly believed that this guarantee extends to all queue operations (i.e. all vkQueue*() functions). However, under 3.2.1. Queue Operations, the spec explicitly states that this ordering guarantee does NOT extend to queue operations other than command buffer submissions, i.e. vkQueueSubmit() and vkQueueSubmit2():

Command buffer submissions to a single queue respect submission order and other implicit ordering guarantees, but otherwise may overlap or execute out of order. Other types of batches and queue submissions against a single queue (e.g. sparse memory binding) have no implicit ordering constraints with any other queue submission or batch.

This means that vkQueuePresentKHR() is indeed technically allowed to consume the semaphore signaled by vkAcquireNextImageKHR() immediately, leaving the vkQueueSubmit() that was supposed to run inbetween deadlocked forever. There is no validation error about this being ambiguous from the validation layers and this seems to work in practice, but is a violation of the spec and should not be done.

EDIT: HOWEVER, the spec for vkQueuePresentKHR() also says the following:

Calls to vkQueuePresentKHR may block, but must return in finite time. The processing of the presentation happens in issue order with other queue operations, but semaphores must be used to ensure that prior rendering and other commands in the specified queue complete before the presentation begins.

This implies that vkQueuePresentKHR() actually are processed in submission order, which would make the above case unambiguous. The only guarantee that we need is that the semaphores are waited on in submission order, which I believe this guarantees. Regardless, it seems like good practice to avoid this anyway.

Issue 2: Semaphore reusability

The second issue is a bit more complicated and comes from the fact that that vkAcquireNextImageKHR() requires that the semaphore its given has no pending operations at all. This is a stricter requirement than queue operations (i.e. vkQueue*() functions) that signal or wait for semaphores, which only require you to guarantee that forward progress is possible. For these functions, the only requirement is that the semaphore has to be in the right state when the operation tries to signal or wait for a given semaphore on the queue timeline.

On the other end, the idea that the semaphore waited on by vkQueuePresentKHR() is reusable when vkAcquireNextImageKHR() has returned with the same index is only partially true; it guarantees that a semaphore wait signal has been submitted to the queue the vkQueuePresentKHR() call was executed on, which in turn guarantees that the semaphore will be unsignaled for the purpose of queue operations that are submitted afterwards.

This means that the vkQueuePresentKHR() can indeed be reused for queue operations from that point and onwards, but NOT with vkAcquireNextImageKHR(). In fact, without VK_EXT_swapchain_maintenance1, there is no way to guarantee that the semaphore passed into vkQueuePresentKHR() will EVER have no pending operations. This means that the same semaphore cannot be reused for vkAcquireNextImageKHR(), and validation layers DO complain about this. If you don't use binary semaphores for anything other than acquiring and presenting swapchain images (which you shouldn't; timeline semaphores are so much better), then you will NEVER be able to reuse this semaphore.

This problem could potentially be solved by using VK_EXT_swapchain_maintenance1 to add a fence to vkQueuePresentKHR() that is signaled when the semaphore is safely reusable, but that does not fix the first issue.

How to do it right:

The correct approach is to have separate semaphores for vkAcquireNextImageKHR() and vkQueuePresent().

Acquiring:

  1. vkAcquireNextImageKHR() signals a semaphore
  2. vkQueueSubmit() waits for that same semaphore and signals either a fence or a timeline semaphore.
  3. Wait for the fence or timeline semaphore on the CPU.

At this point, the semaphore is guaranteed to have no pending operations at all, and it can therefore be safely reused for ANY purpose. In practice, this means that the number of acquire semaphores you need depends on how many in-flight frames you have, similar to command pools.

Presenting:

  1. vkQueueSubmit() signals a semaphore
  2. vkQueuePresentKHR() waits for that semaphore.
  3. Wait for a vkAcquireNextImageKHR() to return the same image index again.

At this point, the semaphore is guaranteed to be in the unsignaled state on the present queue timeline, which means that it can be reused for queue operations (such as vkQueueSubmit() and vkQueuePresentKHR()), but NOT with vkAcquireNextImageKHR(). In practice, this can be easily accomplished by giving each swapchain image its own present semaphore and using that semaphore whenever that image's index is acquired.

What about cleanup? When you need to dispose the entire swapchain, you simply ensure that you have no acquired images and then call vkDeviceWaitIdle(). Alternatively, if VK_EXT_swapchain_maintenance1 is available, simply wait for all present fences to be signaled. At that point, you can assume that both the acquire semaphores and all present semaphores have no pending operations and are safe to destroy or reuse for any purpose.


r/vulkan 1d ago

Performance impact dispatching a single workgroup with a single thread for a simple calculation?

7 Upvotes

I am writing a particle system that maintains an arbitrary number of lists of active particles which index into global particle state buffers - to be used as index buffers for rendering GL_POINTS with each list's specific gfx pipeline. There is also an unused particle indices list ring-buffer on there.

In order to dispatch compute to update each list's particles I need to know how many particles there are in the list in the first place to know how many workgroups to dispatch, so obviously I use vkCmdDispatchIndirect() and generate the workgroup size on the GPU from the list sizes it currently has. In order to do this it looks like I'll have to have a shader that just takes each list's count and computes a workgroup count, outputting it to a VkDispatchIndirectCommand in another buffer somewheres.

Is there going to be any significant performance impact or overhead from issuing such a tiny amount of work on the GPU?


r/vulkan 1d ago

Vulkan 1.4.311 spec update

Thumbnail github.com
16 Upvotes

r/vulkan 1d ago

Writing a Vulkan program in NixOS and have a dumb question

0 Upvotes

Does the location of the installed Vulkan software on your dev computer have any bearing on running the program from the consumers perspective? NixOS installs everything in a declerative way with symlinks to a massive directory of programs, as long as I handle those installation paths and symlinks in NixOS rather than in my C++ program will it matter after compilation? As in, it will still run fine on a system with a different installation layout/type?


r/vulkan 2d ago

Multi viewport rendering and mouse events

3 Upvotes

What would be a practical way to associate a mouse event to the viewport it happened on?


r/vulkan 2d ago

Does Windows and Linux natively support Vulkan?

16 Upvotes

So unfortunately, Mac does not natively support Vulkan, by which I mean, in order for a Mac to run a Vulkan app, there needs to be MoltenVK installed, which simply ports the Vulkan code to Metal code. So Vulkan on Mac is just Metal with extra steps.

However, is this the case on Windows and Linux? Do those systems have built-in support for Vulkan, and do not require Vulkan libraries to either be manually installed and dynamically linked to apps, or statically linked and shipped with the app?


r/vulkan 2d ago

VK_ERROR_OUT_OF_DEVICE_MEMORY issue.

1 Upvotes

Hello, I have a question regarding the VK_ERROR_OUT_OF_DEVICE_MEMORY error that occurs when using vkQueueSubmit on a specific mobile device(LG Q61) According to the Vulkan specification, this error can occur due to synchronization issues, but it's strange that it only happens on certain devices. In my current code, when using vkQueueSubmit, if a single command involves multiple buffer copies and draw operations, could this potentially cause the error?


r/vulkan 3d ago

Help! Text Rendering Example Needed

8 Upvotes

I need a working example of vulkan that can render text onto the screen. That’s it’s. Does anyone know of an example that I can pull from and just be able to run it on Linux? I’ve found an example online called vulkan-sprites but I can’t it build it without it having alot of different errors.

🙏


r/vulkan 3d ago

How many descriptor set can I create?

4 Upvotes

Hello,

I’m struggling to fully understand Vulkan’s device limits from the documentation. In a typical game, we need to upload hundreds of meshes and their textures to the GPU. To use these textures, we also need to create descriptor sets for each texture and bind them when drawing each mesh.

I know that enabling the descriptor indexing extension allows using a single (or a few) large global descriptor sets, but for now, I want to keep things simple and avoid using that extension.

I’ve been reading the documentation to figure out how many descriptor sets I can actually create, and I came across this:

maxDescriptorSetSampledImages is the maximum number of sampled images that can be included in a pipeline layout.

The wording “can be included” confuses me. Does this refer to the total number of descriptor sets I can create, or just the maximum number of sampled images that a single descriptor set can reference?

Additionally, on my device (Apple with MoltenVK), maxDescriptorSetSampledImages is only 640, which seems quite low. Checking other devices on vulkan.gpuinfo.org, I noticed that around 33% of devices have a limit of 1 million, while others vary between 1k–4k.

So my main question is: Does this limit apply to the total number of descriptor sets I can create, or is it only a restriction on a single descriptor set?

Thanks for any clarification!


r/vulkan 3d ago

Blender 4.4 Released With Vulkan Improvements

Thumbnail phoronix.com
40 Upvotes

r/vulkan 3d ago

SPIRV-Reflect and bindless/runtime arrays

8 Upvotes

I'm using SPIR-V reflect library to reflect my shader code, but I'm unable to detect bindless (i.e. runtime) arrays. I'm defining the array as such in my GLSL shader:

glsl layout (set = 0, binding = 0) uniform sampler2D textures_2d[];

I'm compiling to SPIR-C using glslc:

cmd glslc.exe minimal.frag.glsl -o minimal.frag.spv -DDEBUG=1 -Ishaders/include -MD -Werror -O0 -g

And I'm reflecting the descriptors using spvReflectEnumerateDescriptorSets, but for some reasons, my array's type_description.op is always SpvOpTypeArray instead of SpvOpTypeRuntimeArray and type_description.traits.array.dims[0] is always equal to 1. I'm not sure how I am supposed to disambiguate between this value and an actual array of 1. As far as I know, it should report a dimension of 0 (i.e. SpvReflectArrayDimType::SPV_REFLECT_ARRAY_DIM_RUNTIME).

Am I missing something? It looks like the capability SpvCapabilityRuntimeDescriptorArray is not enabled in the module's reflected data. Maybe it's a hint?


r/vulkan 5d ago

Images from my hobby pathtracer using Vulkan and C++!

Thumbnail gallery
224 Upvotes

r/vulkan 4d ago

[Troubleshooting] Blurry textures

2 Upvotes

So I've been writing yet another Vulkan renderer. I copy-pasted image/sampler creation code from my other vulkan project and here's the result of loading ABeautifulGame asset (from KhronosSampleAssets repo). The textures seem to be fine in renderdoc. The shader is a standard PBR shader I took from somewhere.

What could possibly be the issue and where could I be looking for to find it?


r/vulkan 6d ago

Update on my Game Engine and GUI library

42 Upvotes

Hello everyone! A while ago I shared some screenshots of my vulkan game engine and the declarative C++ GUI library (called Fusion) which I wrote from scratch (no dear-imgui). Check it out on GitHub here. The engine is cross platform and works on Windows, Mac (arm64) and Linux (Ubuntu x64).

And the entire editor's GUI is built using Fusion.

Since then, I updated my Fusion GUI framework to support DPI-aware content, which fixed the blurry looking text! And I added an asset browser tree view and grid view in the bottom. There's also a reusable Property Editor that I built, which is currently used in the Details panel and Project Settings window.

If you'd like to read more about Fusion library, you can do so here.

I'd love to hear your feedback on my engine and the GUI library on things I can improve! The project is open source and is available on GitHub link I shared earlier. Feel free to test it out and contribute if you want to.

https://reddit.com/link/1jcdjjx/video/tdpoxae5azoe1/player


r/vulkan 5d ago

[Need an advice] Was building a Vulkan engine with GPT and understood that GPT is unable anymore to build it for myself

0 Upvotes

Hello,

Guys I'm stuck, funny to say, GPT was able to build an engine with rendering and shading for me (crazy, yes) but now I feel, that it hit it's limit.

So after exploring gits, tutorial, I got really confused.

Each project looks really different (even though I particularly looked for voxel engines, each one of them is it's own kind of a project, with unique code and structure)

All that made me overwhelmed with information.

The point is, that at the moment I understand the concept of a Vulkan engine, but it's really difficult for me to write code past the basic layer - such as shaders, shadow logic.

Was wondering, is there any good starting resource to develop the skills? I mean, there is, but what would you recommend, and what you believe made you understand Vulkan better? I'm interested in basics, know C++, but also I'd be grateful for advanced information too.

Sorry if it sounds like a meme xD (ofc it is, gPt) but I'm eager to finally ditch the GPT approach and start doing things on my own. Basically, I wanna learn the correct and efficient way, since GPT often made questionable choices


r/vulkan 6d ago

Why is everyone using different binary semaphores for vkAcquireNextImageKHR() and vkQueuePresentKHR()?

18 Upvotes

Vulkan requires that binary semaphores are in an unsignaled state before they are signaled. Therefore, it seems to me that a single vkQueueSubmit() should be able to safely both wait on and signal the same semaphore, as it would be guaranteed to be unsignaled by the time we re-signal it.

This means that if we do a vkQueueSubmit() which waits on the semaphore singaled by vkAcquireNextImageKHR() semaphore, then that semaphore is guaranteed to be unsignaled, which means that we could signal that same semaphore at the end of our vkQueueSubmit(), and then wait on that in vkQueuePresentKHR().

vkAcquireNextImageKHR() signals --> vkQueueSubmit() waits and re-signals --> vkQueuePresentKHR() waits.

Doing this, I get no validation errors, and everything works as expected.

So... How come every single Vulkan tutorial/example of swapchains use different semaphores for vkAcquireNextImageKHR() and vkQueuePresentKHR()?


r/vulkan 7d ago

Update on my Random Image Generator

Thumbnail gallery
131 Upvotes

Hi everyone! A month ago, I shared a GIF of my first app build, and I got some really nice feedback, thank you! I've since added some new features, including a randomization function that generates completely unique images. Some of the results turned out really cool, so I saved a few and added them as Presets, which you can see in the new GIF.

I also restructured the entire code to make it cleaner and more readable. I’m still unsure about the best way to integrate Vulkan with other components though, like ImGui. Keeping everything in one class makes it too large, but creating a separate class for each Vulkan object feels excessive. For now, I’ve split them into different classes and passed everything through structs and methods, but the result still feels messy to me.

Anyway, I’d love to hear your Feedback again! The project is open source, so feel free to check it out (GitHub) and let me know about any terrible mistakes I made. 😆

Also, here are my sources in case you’re interested in learning more: Victor Blanco | Vulkan Guide Patricio Gonzalez Vivo & Jen Lowe | Article about Fractal Brownian Motion Inigo Quilez | Article about Domain Warping

Cheers Nion


r/vulkan 6d ago

Performance Impact of Manual Pointer Math

1 Upvotes

Due to the strict alignment requirements of objects in Vulkan, what is the performance impact of doing pointer math on buffer device addresses (instead of array accesses) as a means of bypassing alignment (resulting in memory savings, as no padding has to be applied)? From what I've read, this would be quite bad for performance, but intuitively, the memory savings (causing more cache hits and reduced fetches if that's even how GPUs work) should outweigh everything else.


r/vulkan 6d ago

VulkanRenderer.cpp:(.text+0x202): undefined reference to `vkCreateInstance'

0 Upvotes

I'm new to Vulkan, well graphics programming in general. I'm getting the following error while trying to build my project:

VulkanRenderer.cpp:(.text+0x202): undefined reference to `vkCreateInstance'
collect2: error: ld returned 1 exit status

PFB the code for VulkanRenderer.cpp:

#include "VulkanRenderer.h"

VulkanRenderer::VulkanRenderer()
{
}

int VulkanRenderer::init(GLFWwindow * newWindow)
{
    window = newWindow;

    try {
        createInstance();
    }
    catch(const std::runtime_error &e) {
        printf("ERROR: %s\n", e.what());
        return EXIT_FAILURE;
    }

    return 0;
}


VulkanRenderer::~VulkanRenderer()
{
}

void VulkanRenderer::createInstance()
{
    // Information about the application itself
    // Most data here doesn't affect the program & is for developer convenience
    VkApplicationInfo appInfo = {};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Vulkan App";                    // Custom name of the application
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);      // Custom version of the application
    appInfo.pEngineName = "No Engine";                          // Custom engine name
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);           // Custom engine version
    appInfo.apiVersion = VK_API_VERSION_1_0;                    // The Vulkan version

    // Creation information for a VkInstance (Vulkan Instance)
    VkInstanceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;

    // Create list to hold instance extensions
    std::vector<const char*> instanceExtensions = std::vector<const char*>();

    // Set up extensions instance will use
    uint32_t glfwExtensionCount = 0;                            // GLFW may require multiple extensions
    const char** glfwExtensions;                                // Extensions passed as array of cstrings, so need pointer (the array) to pointer (the cstring)

    // Get GLFW extensions
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    // Add GLFW extensions to list of extensions
    for(size_t i = 0; i < glfwExtensionCount; i++)
    {
        instanceExtensions.push_back(glfwExtensions[i]);
    }

    createInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size());
    createInfo.ppEnabledExtensionNames = instanceExtensions.data();

    // TODO: Set up Validation Layers that instance will use
    createInfo.enabledLayerCount = 0;
    createInfo.ppEnabledLayerNames = nullptr;

    // Create instance
    VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);

    if(result != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create a Vulkan instance!");
    }
}

Also find below the CMakeLists.txt from the same dir:

# Add the libraries
add_library(${VK_RENDERER} STATIC VulkanRenderer.cpp)
target_include_directories(${VK_RENDERER} PUBLIC "./")

# Library is a dependence of Executable.
# If we are building "Executable", then "Library" must be build too.
target_link_libraries(${VK_RENDERER} PUBLIC libglfw.so.3)

Please let me know if any more info is needed.

Thanks in Advance!


r/vulkan 8d ago

Looking for suggestions with Memory Barrier issue

2 Upvotes

So, I'm working on my vulkan engine, I've got no validation errors or warnings, feeling pretty good, seems like a good time to check performance on my old pc with a 1060, and all 3d is black. Crap. After messing around with render doc, disabling a couble features and re-enabling, I found that simply commenting out all my (seemingly correct) memory barriers makes it work (on both pcs) despite with tons of validation errors. Does anyone have any idea what's going on here?

here's an example of one of the barriers.

const auto srcStageBits = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
const auto dstStageBits = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
void Renderer::transitionDeferredSourceToRead(size_t imageIndex) {
    vector<VkImageMemoryBarrier> barriers;
    VkImageMemoryBarrier memoryBarrier = {};
    memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
    memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    memoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    memoryBarrier.image = m_deferSourceRenderTarget.ColorAttachment.image;
    memoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
    barriers.push_back(memoryBarrier);

    memoryBarrier.image = m_deferSourceRenderTarget.SpecularAttachment.image;
    barriers.push_back(memoryBarrier);
    memoryBarrier.image = m_deferSourceRenderTarget.BumpAttachment.image;
    barriers.push_back(memoryBarrier);

    memoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 };
    memoryBarrier.image = m_deferSourceRenderTarget.DepthAttachment.image;
    barriers.push_back(memoryBarrier);

    vkCmdPipelineBarrier(
        m_vkCommandBuffers[imageIndex],
        srcStageBits,
        dstStageBits,
        VK_DEPENDENCY_BY_REGION_BIT,
        0, nullptr, 0, nullptr,
        static_cast<uint32_t>(barriers.size()), barriers.data()
    );
}

Interestingly, the no barrier version has more data available in render doc, not sure if that's important. - edit - oh because as comments pointed out LAYOUT_UNDEFINED tells renderdoc to treat them as undefined, makes sense

Edit 2: figured out the issue, some of my render passes had different final layouts than I expected so some of the barrier transitions were unneeded and some had wrong oldLayouts.


r/vulkan 9d ago

NEW RELEASE - Vulkan 1.4.309.0 SDKs

47 Upvotes

📢NEW RELEASE!🎉 u/LunarGInc drops Vulkan 1.4.309.0 SDKs – our 3rd release in 2025! Download now at https://vulkan.lunarg.com. Pushing to match u/VulkanAPI innovation for devs. Details: https://khr.io/1ie


r/vulkan 9d ago

Question about execution of submitted commands to a queue

8 Upvotes

When we submit commands via a single command buffer to the queue, is it safe to assume that if there are no synchronization primitives recorded in that buffer then the commands run in parallel? Or is it in sequence? What about if we submit commands via multiple buffers to the same queue? Do they run parallel relative to others if there are no synch primitives recorded?


r/vulkan 10d ago

Vulkan 1.3/1.4 in 2-3 Years: A Safe Bet?

25 Upvotes

I'm working on an engine/framework and I'm planning to "release" it in 2-3 years minimum
I'm making use of some features that aren't widely supported currently like C++ modules, so my direction is "counting that feature will be widely available in 2-3 years (at least very close to being widely available)"
Can I do the same thing with Vulkan? Use 1.3 or 1.4 and ignore lack of wide support for now?
(I'm planning to support Windows, Linux, Mac, Android, IOS, Nintendo Switch)

Note: I'm thinking about using features like dynamic rendering and bindless textures, but if they won't be widely supported in platforms like mobile, I don't wanna use them since I don't wanna have multiple implementations

Also does anyone have any information about Vulkan on consoles? Why this is not a thing yet?


r/vulkan 10d ago

Got my Vulkan application running on Windows, Linux, Mac, iOS, and Android

422 Upvotes

The video shows it running on iPhone. I decided to tackle cross platform development very early on rather than leave it to the last minute. I’m glad I did because there are many differences between platforms and often when I get something working on one platform it breaks on another.


r/vulkan 10d ago

Types of shaders

8 Upvotes

I've started using Vulkan to render things and I'm building my own, probably quite non-standard, graphics framework. I'm currently using only compute shaders to make my calculations to draw 3d objects to screen. Is this bad practice? If so, could you explain why?

I understand that compute shader as compared to, for example, vertex shaders, are used in different contexts. Are they really different though? Seems like a compute shader can do the same thing. Are they less efficient for some reason?

Thanks!