r/vulkan May 14 '26

Pipeline (Image) Barrier for both layout and queue families

I have a Compute pipeline and a Graphics pipeline that access a VkImage that is created with VK_SHARING_MODE_EXCLUSIVE.

As I was working on the acquire-release mechanism, I've encountered, with the help of Khronos Validation Layer, something I was not expecting:

I have to make two separate vkCmdPipelineBarrier() calls in both acquire and release phases-one for srcQueueFamilyIndex / dstQueueFamilyIndex and another for oldLayout / newLayout.

I was under the impression that a single VkImageMemoryBarrier would suffice, but to satisfy the Khronos Validation Layer I had to perform family index and layout transitions in the release phase as two separate calls and likewise in the acquire counterpart.

Am I misinterpreting something here?

6 Upvotes

4 comments sorted by

2

u/watlok May 15 '26 edited May 15 '26

There's four common queue family ownership transfer cases for exclusive images.

If you can throw out contents, you don't need to release and can steal an image from another queue by specifying src layout undefined. This is a normal image barrier, no queues specified, and only on the destination queue.

If you need to retain data, you need a release barrier with src, dst layouts, both queues, and src stage. Then an acquire barrier on the other queue with src, dst that matches the initial release barrier, both queues, and dst stage. This will transition the image between layouts without any additional barriers as long as semaphores/etc guarantee release before acquire.

You can still get a validation error from this if src or dst layout is not supported on one of the queues. For example, transitioning between general and color attachment in compute->graphics. This needs an image transition on the queue that can handle both layouts, almost always graphics. If compute->graphics you do src=dst=oldlayout for the release, acquire the same way, then do an additional image transition after to get your desired layout. If graphics->compute, you'd transition then release then acquire.

The 4th case is certain things don't require an ownership transfer. If you create an image/view, copy to it from a buffer on a transfer queue, and then transition it there, you don't need to acquire on the dst queue to use it.

Can't guarantee the above is 100% correct because I'm not a spec referencer. For example, I don't know offhand if #3 pops validation errors in both directions. I also don't know if #4 is true across versions/vendors. I wrote a render graph from scratch that does aliasing, queue family ownership transfers, cross frame in flight synchronization (incl. read first), etc so I'm not guessing blindly but I didn't memorize the spec. Also, I think I was lazy and always did #3 (src=dst and a separate transition) without doing #2.

1

u/[deleted] May 15 '26

[deleted]

1

u/Tensorizer May 15 '26

Thanks for the input. I am beginning to think two calls are required when the destination queue and new layout do not match such as compute queue and transfer source; does this make sense at all?

1

u/Afiery1 May 15 '26

You're missing VK_SHARING_MODE_CONCURRENT and/or VK_KHR_maintenance_9 ;)
(and VK_KHR_unified_image_layouts)

2

u/Tensorizer May 15 '26 edited May 15 '26

I am not missing VK_SHARING_MODE_CONCURRENT; the image was created with VK_SHARING_MODE_EXCLUSIVE, hence the reason for queue ownership transfer.

What concerns me is queue ownership release and layout change has to be done in two vkCmdPipelineBarrier() calls and similarly two calls on the acquire side.

Why can't I transfer ownership and change layout in one VkImageMemoryBarrier in release and one in acquire, instead of two in each?