difference between availability and visibility


I’m reading the specification, and can’t understand the difference between the meaning of “memory writes are available” and “memory writes are visible”.

it says ,

Once written values are made visible to a particular type of memory access, they can be read or written by that type of memory access.

in “6.1. Execution and Memory Dependencies”.
then what does it mean that written values are available ?
Does it mean just that the queue operation writing those values has been completed ( but they cannot be read or written ) ?

Thank you.

Two paragraphs before that, the standard says:

Two additional types of operation are used to control memory access. Availability operations cause the values generated by specified memory write accesses to become available for future access. Any available value remains available until a subsequent write to the same memory location occurs (whether it is made available or not) or the memory is freed. Visibility operations cause any available values to become visible to specified memory accesses.

Emphasis added. The point is that availability is a necessary part of visibility, but availability alone is not sufficient. You can do things that might have caused visibility, but because the write was not available, they don’t actually make the write visible.

Here’s an example. Let’s say you write to an image and then read from it in a separate command. Now, lets say the write is in the fragment stage and the read is in the vertex shader. You need a dependency in the middle, typically a pipeline barrier.

Now, you could make a pipeline barrier without a memory barrier. That would fail to ensure visibility, since memory barriers are visibility operations. Without them, you wouldn’t have visibility of the memory.

Let’s say you make a pipeline barrier that has a memory barrier. But that alone isn’t enough, because the execution barrier has to be correct. You could have the destination stage of the execution barrier be “top of the pipe”. But the reading stage is the vertex shader, so you have failed to ensure the availability of the memory.

And since the write is not available, it cannot be visible either.

I kind of got it.
In order to make some writes visible, they must be available, and memory barriers ensure the visibility and correct execution barriers ensure the availability. So basically, the completion of the writing command roughly implies the availability of written values, right ?

Incidentally, how is the visibility controlled under the hood ?

So basically, the completion of the writing command roughly implies the availability of written values, right ?

That’s the wrong way to think of it. Yes, the execution barrier ensures the completion of it, but the source/destination pipeline stages and access pathways are what handles availability.

Incidentally, how is the visibility controlled under the hood ?

It’s implementation-specific. The pure-visibility parts typically involve forcing lines out of caches and/or invalidating them. But some kinds of visibility may not require even that.

Ah, i see.
In my intuitive understanding, making writes available is preparing pathways to the results of them , and making writes visible is like cache flushing.
Is this right ?

Generally what is the pathways at implementation level ?

What does it matter? It’s a thing you have to do in Vulkan. How it works isn’t particularly important, and the way it works will depend on various implementation-specific stuff.

For example, if a vertex shader has to wait on a fragment shader side effect, on a tile-based renderer, that operation could be incredibly expensive. TBRs frequently execute the graphics pipeline extremely asynchronously, with the vertex processing part running almost entirely independently from the fragment processing (the latter only running when the former is finished). As such, an FS->VS dependency could involve a huge amount of stuff.

By contrast, on most desktop hardware, this process wouldn’t be too terrible. The VS simply has to wait on the FS of previous commands. And while that certainly puts a small bubble in the pipeline, it’s not a huge deal.

I just wanted to precisely know how it works, and now i think i can understand the logical difference between them and the fact their actual behavior is highly implementation-specific.
Thanks for your answering.