The documentation on VK_SUBPASS_EXTERNAL seems to indicate that it can be used to create implicit barriers between draw commands in different renderpasses.
The fact that you had to create it yourself means that it’s an explicit barrier, not an implicit one.
You pretty much never want to introduce race conditions which means you pretty much always want to wait on earlier writes to an image before using it, or reads before writing to it.
OK, but how does the API know that you have issued a write command to an image when it sees that you’re trying to read from it? The write command could be in another command buffer. Therefore the only system that could possibly know when such an event has taken place is the queue, where you execute all of the commands in a well-defined order.
And the GPU queue doesn’t have the ability to detect such things. Which means that executing such a queue would require CPU intervention to create those barriers. And thus, you lose a big part of the advantages of command-buffer-style APIs, since the CPU is basically having to do a lot of the work for something that could otherwise be done entirely by the GPU.
Explicit synchronization is not optional if you’re want an API where you can have multiple threads can create commands, where at command creation time, the API is completely blind to what has happened before.
Plus, since you know your workload better than the GPU, you can do specific things like partially clear a region of memory, instead of assuming that the entire region of memory may have been written to. For example, you can write to part of an image. But the API cannot see that’s what you’re doing; only you know that. So if the API had to insert barriers itself, it would have no choice but to insert a full image barrier. Whereas you have the ability to insert a memory barrier that only covers part of the image rather than the whole thing.
Not to mention that you could read from a different part of the image which hasn’t been written to. The API can’t detect that either. So in this case, you don’t need a barrier at all. But the system cannot possibly detect that.
Subpasses don’t have to be dependent on each other. And if there is no dependency between two subpasses, then the GPU should be free to execute them in the most optimal order, or even interleave their execution, should sufficient resources exist.
The driver/GPU tracks all the necessary image-use info anyway (it has to so that external subpass dependencies know what to wait for)
No, you tell the API what to wait for. Subpass dependencies are no different from any other manual barrier in that regard. If an image has changed and you want to make that change visible to the subpass, then you have to add an explicit memory barrier for that.
The only images where that isn’t the case is dependencies for render targets. And those are treated specially by subpasses anyway. Each subpass says what it is writing and to where, so the system has the information to build those memory dependencies explicitly. For more general dependencies involving arbitrary memory, that is not the case.
Also, render targets during a renderpass are not considered to exist in normal memory anyway.
IMHO it’d be easier to just assume default subpass dependencies for everything and allow the developer to explicitly remove them, perhaps via a list of "VkSubpassNonDependency"s.
Vulkan doesn’t exist to make things “easier”. It exists to give you explicit and direct control over as many aspects of GPU behavior as is practical. And control over synchronization is a big part of that.