Issue moving exclusive image ownership between queue families and synchronization

Hello community,

this is my scenario:

  1. graphics queue: draw world-space positions into an image (gbuffer)
  2. move ownership of image to compute queue to read the image data
  3. compute queue: process data (write into SSBO)

I get this callback error message:
Cannot submit cmd buffer using image (0xa) [sub-resource: aspectMask 0x1 array layer 0, mip level 0], with layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL when first use is VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.

Now some details, first approach:
The gbuffer image is created with the VK_SHARING_MODE_EXCLUSIVE flag. Its supposed first time usage is in a render pass transitioning from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL for rendering and stays in the latter layout after the pass.
At the end of the command buffer using that render pass I use an image memory barrier with the following pipeline barrier command:


VkImageMemoryBarrier image_to_compute_barrier = {
    srcAccessMask       : VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    dstAccessMask       : VK_ACCESS_SHADER_READ_BIT,
    oldLayout           : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    newLayout           : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    srcQueueFamilyIndex : graphics_queue_family_index,
    dstQueueFamilyIndex : compute_queue_family_index,
    image               : gbuffer_image,
    subresourceRange    : gbuffer_subresource_ange,
};

vkCmdPipelineBarrier( graphics_cmd_buffer,
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    0, 0, nullptr, 0 nullptr, 1, &image_to_compute_barrier );

The same barrier and command is used at the beginning of the compute command buffer later.

In the submit info I use a semaphore to signal the completion of the graphics command buffer:


VkSubmitInfo graphics_submit_info = {
    waitSemaphoreCount      : 0,      // this submit shall happen first
    pWaitSemaphores         : nullptr,
    pWaitDstStageMask       : nullptr,
    commandBufferCount      : 1,
    pCommandBuffers         : &graphics_cmd_buffer,
    signalSemaphoreCount    : 1,
    pSignalSemaphores       : &gbuffer_semaphore,
};

As mentioned already, and as per spec for exclusive resource ownership transition, the same image memory barrier as above is used at the beginning of the compute command buffer. Next is binding of a compute pipeline, descriptors and the dispatch command. After that command the image is not used until the frame fence. In the compute submit info I wait on the gbuffer semaphore:


VkPipelineStageFlags gbuffer_wait_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo compute_submit_info = {
    waitSemaphoreCount      : 1,
    pWaitSemaphores         : &gbuffer_semaphore,
    pWaitDstStageMask       : &gbuffer_wait_stage_mask,
    commandBufferCount      : 1,
    pCommandBuffers         : &compute_cmd_buffer,
    signalSemaphoreCount    : 1,
    pSignalSemaphores       : &compute_done_semaphore,     // some additional work is supposed to happen afterwards
};

The command buffers are recorded once and submitted multiple times to the corresponding queues, the order here should be irrelevant:


vkQueueSubmit( graphics_queue, 1, &graphics_submit_info, VK_NULL_HANDLE );
vkQueueSubmit(  compute_queue, 1,  &compute_submit_info, VK_NULL_HANDLE );

Stepping into the code with a debugger the error message happens right after the second submit command. My guess is that the compute queue does not wait on the gbuffer image transition and ownership release from the graphics queue, so (hope I gave enough info) what’s wrong with my setup?

My second approach was to transit the image to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL in the render pass. That got me a different error message which I don’t understand fully:
Source AccessMask 256 [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT] must contain at least one of access bits 48 [VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT] when layout is VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, unless the app has previously added a barrier for this transition.

This error message comes only twice, once per pipeline barrier command. Why shouldn’t the transition from access VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT to access VK_ACCESS_SHADER_READ_BIT be possible?

When I add VK_ACCESS_SHADER_READ_BIT to the image memory barrier:


VkImageMemoryBarrier image_to_compute_barriere = {
    srcAccessMask       : VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT,
    dstAccessMask       : VK_ACCESS_SHADER_READ_BIT,
    oldLayout           : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    newLayout           : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    ...
};

I get this message which I understand even less:
Additional bits in Source accessMask 0x120 [VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT] are specified when layout is VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.

Hello citizen, :slight_smile:

The first error shows if the Image’s actual layout differs from initialLayout you provided.

The second shows when you make transition from/to some specific layout and forget to add the appropriate Access flags. E.g. from LAYOUT_COLOR_ATTACHMENT and forgeting ACCESS_COLOR_ATTACHMENT flag.
You probably already are in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and that’s why the layer wants you to use the two access flags it says.

The third is probably because you use oldLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL with redundant VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, which does not match the layout.

[QUOTE=krOoze;40765]Hello citizen, :slight_smile:

The first error shows if the Image’s actual layout differs from initialLayout you provided.
[/QUOTE]

Hello :slight_smile: and yes, but it shouldn’t give me that error. Graphics queue, supposed to run before compute queue, transits the image to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL via barrier. Than compute queue should run on the image and expecting VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.
The error happens when submitting to the compute queue and the message tells me that the image is NOT in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL but in VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. That’s why I guessed that compute queue does not wait on the graphics queue, which is also nonsense btw. as the initial layout is undefined, but transitioned into color attachment by the graphics queue, so graphics queue must run before compute queue.

The second shows when you make transition from/to some specific layout and forget to add the appropriate Access flags. E.g. from LAYOUT_COLOR_ATTACHMENT and forgeting ACCESS_COLOR_ATTACHMENT flag.
You probably already are in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and that’s why the layer wants you to use the two access flags it says.

The third is probably because you use oldLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL with redundant VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, which does not match the layout.

Note to not confuse readers, we are talking about the second approach now.
Here you are right, removing VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT from the source access mask also got rid of the error, but I don’t understand this. My last access to the image was writing into it via render pass attachment, however, transitioned into VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL at the end of the pass. So setting the access source access mask to VK_ACCESS_SHADER_READ_BIT is … well … lying to vulkan! Besides, I can’t imagine that we are supposed to always match source access mask to corresponding source layout (as in this situation), then it wouldn’t make sense to have them separate parameters in the first place.

The second approach seems to work now, need further testing. But I don’t understand why the first approach should not. Is there something in the spec that a pipline barrier can be used only for layout transition or ownership transition, but not both at once?

Ok, I am less sleepy this time – let’s see:

Any reason not to use GRAPHICS+COMPUTE family (without transfer)?

Make sure the resource is SHARING_EXCLUSIVE (not CONCURENT) for the barrier way of transfer to work.

vkCmdPipelineBarrier( graphics_cmd_buffer,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr, 0 nullptr, 1, &image_to_compute_barrier );

You have ACCESS flag where a STAGE flag is expected. I think you want VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT instead.

Don’t forget it needs to be transfered back if you do that each frame (it can be stealed though, I think, if you sacrifice the data).

The transfer mechanic can potentionaly confuse the validation layers (because of the duplicated barrier and thus layout transition). It should be reported, if you get it working properly otherwise…

Renderpasses/subpasses changes layout (and overall acts as a barrier). Make sure they don’t confuse us and the situation.

The semafor wait stage seems to be wrong. It should probably be VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT.

Source AccessMask 256 [blah blah]

Are you sure this does not boil down to point 6) – forgeting the passes do barriers themselves?

srcAccessMask       : VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT,
dstAccessMask       : VK_ACCESS_SHADER_READ_BIT,
oldLayout           : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
newLayout           : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,

This really looks wrong though. How can you color attachment write to Image being shader read – doesn’t make sense. The last error in your first post says exactly that.

You said it is a transition from LAYOUT_COLOR but I see src=LAYOUT_SHADER_READ there. Which probably is the problem.

Besides, I can’t imagine that we are supposed to always match source access mask to corresponding source layout […]

Well, I can’t imagine otherwise. As I said in 8), what would it even mean if they didn’t match? Either you already did that barrier with that access mask before(when you changed the layout to the LAYOUT_SHADER_READ), which would make it nonsense here again OR if you didn’t your code is probably badly synchronized.

[…] then it wouldn’t make sense to have them separate parameters in the first place.

You can use LAYOUT_GENERAL or STAGE_ALL_COMMAND, having nothing for the driver to infer it, so it still makes some sense.

Is there something in the spec that a pipline barrier can be used only for layout transition or ownership transition, but not both at once?

I don’t think so.
For my taste though it is not perfectly indubitably clear where the layout transition happens. Or if the two barriers are sort of the same single one or two separate ones (which would mean it would attempt two layout transitions, if provided the exactly same barrier).

Sorry, I didn’t expect any further answer after some time. Thank you for your effort and time, I don’t take this as granted.
First I need to mention that approach 2 is working and I am using it. Further arguments are for (hopefully) my enlightenment.

[QUOTE=krOoze;40771]Ok, I am less sleepy this time – let’s see:

Any reason not to use GRAPHICS+COMPUTE family (without transfer)?
[/QUOTE]

Yes, self-educational.

Make sure the resource is SHARING_EXCLUSIVE (not CONCURENT) for the barrier way of transfer to work.

It was/is.

You have ACCESS flag where a STAGE flag is expected. I think you want VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT instead.

You are right, fixed, but this didn’t remove the error message.

Don’t forget it needs to be transfered back if you do that each frame (it can be stealed though, I think, if you sacrifice the data).

Was there already, I just didn’t mention it, as the error(s) happened before the first reverse-transfer

The transfer mechanic can potentionaly confuse the validation layers (because of the duplicated barrier and thus layout transition). It should be reported, if you get it working properly otherwise…

That might be the case, I will put something small together and investigate, possibly report. Currently all is D code and I doubt somebody at Khronos/LunarG would want to look into this.

Renderpasses/subpasses changes layout (and overall acts as a barrier). Make sure they don’t confuse us and the situation.

The Idea was to transit the image INTO VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL via render pass and keep this layout leaving the pass. I would need a memory barrier for the queue transfer anyways, so I could transit from VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT there.

The semafor wait stage seems to be wrong. It should probably be VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT.

Why so? The semaphore is supposed to be signaled when the graphics pipeline has accessed the image at the color attachment output stage. So the compute submit should wait for VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT and the corresponding semaphore signal I would say.

Are you sure this does not boil down to point 6) – forgeting the passes do barriers themselves?

Na, I triple checked now. Here are the two ways for the layout transitions I experimented with:

1.) render pass: VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
transfer barrier: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL -> VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
Not working!

2.) render pass: VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL -> VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
transfer barrier: No layout transition, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL -> VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
Working!

This really looks wrong though. How can you color attachment write to Image being shader read – doesn’t make sense. The last error in your first post says exactly that.

You said it is a transition from LAYOUT_COLOR but I see src=LAYOUT_SHADER_READ there. Which probably is the problem.

Yes, you are right :slight_smile: that IS probably the problem and also the reason why I am asking here. I don’t see the source of the error message. According to my understanding I write into the image in the VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, transition it into VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while moving into another queue family for reading. The first error message told me that this transition somehow didn’t happen before using it for shader reading. Or, as you suggest, the validation layers get confused due to the double layout transition.

Well, I can’t imagine otherwise. As I said in 8), what would it even mean if they didn’t match? Either you already did that barrier with that access mask before(when you changed the layout to the LAYOUT_SHADER_READ), which would make it nonsense here again OR if you didn’t your code is probably badly synchronized.

My case (approach 2) is a good example i’d say, explained in my first reply. I wrote into the image via render pass color access, so the SRC access mask in the image barrier should be VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT. Also, I transitioned the image layout into shader read optimal at the end of the render pass, but I did not access it in such a way. So the OLD image layout in the image barrier should be VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. These two are also not matching, but properly describe what I have done.

However I was getting an error message which went away with setting the SRC access mask to VK_ACCESS_SHADER_READ_BIT.

You can use LAYOUT_GENERAL or STAGE_ALL_COMMAND, having nothing for the driver to infer it, so it still makes some sense.

Good point, have not considered those.

I don’t think so.
For my taste though it is not perfectly indubitably clear where the layout transition happens. Or if the two barriers are sort of the same single one or two separate ones (which would mean it would attempt two layout transitions, if provided the exactly same barrier).

This is how the spec explains resource transfer between queue families, as far as I understand I need to use two similar image barriers. The case using image layout transitions in such a barrier (which is used twice) is not mentioned. I see your point that if both of them specify the same layout transition it might confuse layers or driver. Actually that was the reason for asking if a memory barrier could be used for either layout transition or queue family resource transfer but not both. In the end it works if both the barriers have identical src/dst access masks and old/new layouts.

[QUOTE]7)
The semafor wait stage seems to be wrong. It should probably be VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT.

Why so? The semaphore is supposed to be signaled when the graphics pipeline has accessed the image at the color attachment output stage.
[/QUOTE]

Because it says where the semaphor will be waited on (NOT where it will be signalled).

1.) render pass: VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
transfer barrier: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL -> VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
Not working!

Should work. (Assuming there are proper dependencies between the transitions). I can’t quadruple check after you :P, because you did not show us the subpass dependencies.

If found to be correct on your part, then great too, and belong here: https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues and we all benefit.

My case (approach 2) is a good example i’d say, explained in my first reply. I wrote into the image via render pass color access, so the SRC access mask in the image barrier should be VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT. Also, I transitioned the image layout into shader read optimal at the end of the render pass, but I did not access it in such a way. So the OLD image layout in the image barrier should be VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. These two are also not matching, but properly describe what I have done.

Oh, but you did access it. With the layout transition itself at the end of the subpass. That transition with dependency to SUBPASS_EXTERNAL should have handled the VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT and the next Pipeline barrier should therfore not do so.

If I understood your situation correctly, then the last subpass and the subsequent Pipeline Barrier should have an execution dependency through the pipeline stage flag. The layouts should be same (as you have them) and the source access flag of the barrier should be 0. You should only get a layer warning (saying something like “warning , unless… exactly this case”)

This is how the spec explains resource transfer between queue families, as far as I understand I need to use two similar image barriers.

It is kind of counter-intuitive to provide the layout transition info twice, but ignore my doubts – that’s just about the spec text being only slightly less than 100 % clear.

Hello again,

first things first, cutting your answer apart :slight_smile:

Done here!

Because it says where the semaphor will be waited on (NOT where it will be signalled).

Right, my bad, misunderstood the spec.

Should work. (Assuming there are proper dependencies between the transitions). I can’t quadruple check after you :P, because you did not show us the subpass dependencies.

For the sake of completeness here they are, but I have no idea which information you might get from that (<- begging for enlightenment!). The gbuffer is written in a render pass with one subpass:


VkSubpassDependency[2] dependencies;
dependencies[0].srcSubpass	= VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass	= 0;
dependencies[0].srcStageMask	= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask	= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask	= VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask	= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags	= VK_DEPENDENCY_BY_REGION_BIT;

dependencies[1].srcSubpass	= 0;
dependencies[1].dstSubpass	= VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask	= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask	= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask	= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask	= VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags	= VK_DEPENDENCY_BY_REGION_BIT;

I took this directly from API without Secrets and didn’t put much additional thoughts into it, so feel free to destroy.

Oh, but you did access it. With the layout transition itself at the end of the subpass. That transition with dependency to SUBPASS_EXTERNAL should have handled the VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT and the next Pipeline barrier should therfore not do so.

If I understood your situation correctly, then the last subpass and the subsequent Pipeline Barrier should have an execution dependency through the pipeline stage flag. The layouts should be same (as you have them) and the source access flag of the barrier should be 0. You should only get a layer warning (saying something like “warning , unless… exactly this case”)

But wouldn’t this imply that I am allowed to inform Vulkan about the last access of o memory resource only ONCE? In fact in my case I am informing Vulkan TWICE, once via subpass dependency and once via pipeline barrier about that access, but as I have not accessed the image in between other than layout transitioning, why should this be wrong? I can’t just pick some arbitrary stage access which I just assume happens after VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, can I? I am not getting any warning if I don’t transition the layout in the explicit barriers btw.

but I have no idea which information you might get from that (<- begging for enlightenment!)

I tended recently to do some debug service here and on SO for fun.
Simply, if wrong synchronization is among the suspects, I need to see all the synchronization. It forms sort of a chain (well, generally a… whatsitcalled… polytree?). If I see the chain broken, I say “your code is wrong” :P.

I took this directly from API without Secrets and didn’t put much additional thoughts into it, so feel free to destroy.

Tutorial writers are people too.
The thing is, lot of the wrong code just works (on current drivers), so nobody complains until it doesn’t (usually after some slight modification). This is probably the hardest thing to catch in a layer (so there’s no guarantee of warning/error message either).

The in dependency does look weird. It is neither paranoid (which would be src==ALL_COMMANDS and MEMORY_READ + WRITE) nor optimal for acquire hand-off (which would be srcStage==dstStage==graphics_submit_info.waitStage). Using BOTTOM in this context is dangerous (spec Note warns about that).

The out dependency should be different for our case (or perhaps even omitted in favor of that transfer barrier). Next access is the transfer, so dstAccess=0 is perhaps more appropriate (again should give only the warning “blah blah, unless you handle it in the next barrier”(which we would) ).

With that said, the following transfer barrier should then match this ( srcAccess=dep[1].dstAccess=0 and srcStage=dep[1].dstStage) and then in turn match the next one (dstStage=compute_submit_info.waitStage=COMPUTE or ALL_COMMANDS).

But wouldn’t this imply that I am allowed to inform Vulkan about the last access of o memory resource only ONCE? In fact in my case I am informing Vulkan TWICE, once via subpass dependency and once via pipeline barrier about that access, but as I have not accessed the image in between other than layout transitioning, why should this be wrong? I can’t just pick some arbitrary stage access which I just assume happens after VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, can I? I am not getting any warning if I don’t transition the layout in the explicit barriers btw.

You are not informing nor hinting. You are telling Vulkan what to do.

The stage+access forms a Memory Dependency. The src pair makes thing “available” and the dst “visible”. Usually both are specified in the same barrier.

But you can spread it across two barriers (as you want with your “I don’t access it in between”). It would be done with the barr1.dstAccess=0 and barr2.srcAccess=0 as shown above.

The stage would not be chosen arbitralily. You want your barriers as late as possible. So to make appropriate Execution Dependency between them, it would be barr2.dstStage=whatever_you_need_next and barr2.srcStage=barr2.dstStage(the same stage). And the previous barrier would be chained to this one(barr1.dstStage=barr2.srcStage.).

I too was surprised it works this way… I still think it’s weird (sometimes one would want not to chain them).

Okay, here’s my take on Resource Sharing Mode:
https://github.com/krOoze/Hello_Triangle/tree/queue_transfer

It indeed shows validation errors with my interpretation of the spec too.
Our guess, that it may confuse layers, were right (or the spec text is not to be taken too literally with the Memory Barrier sameness).