A few days ago Casey described his ideal graphics api as follows:
- minimal number of calls, this should be limited to getting special memory and dispatching work (though practically, there should also be something to signal that work is done, like an IOCompletionQueue)
- all data formats are explicitly documented, this includes command buffer protocol, texture layouts (which layout the cache friendly packing should use), and that having the available layouts queryable by enum and having the user code use a massive switch to use them is not a bad thing. In practice someone will create a lib that will do it for you.
- errors should only occur when dispatching the big block of work and say exactly where you went wrong.
He didn't describe how synchronization between commands should look like even though that's one of the more complex parts of the API.
If you look at Vulkan with that mindset you can see those elements in there.
- First there is the initialization and capability queries.
- Then there is allocating vram and cpu ram (including mapping when needed) for data (vertices, uniforms and textures).
- Command pools manage the memory for the command buffer,
- Each vkCmd* command appends a command to the command buffer. It is append-only so you cannot tweak the values of a command after you have submitted it. This is one of my bigger issues with vulkan, you cannot have draws be appended *and* update the copy before the renderpass to account for the extra uniform data that needs to be DRM'ed from a system RAM ringbuffer to VRAM. Neither can you update offsets and pointers so you can reuse the entire command buffer when only the render targets and buffer offsets change.
- There are also a set of object that can be created (vkBuffer, vkImage, vkGraphicsPipeline, ...) that can be prebuilt for use in the command buffer. Main purpose here is to prevalidate those objects and in the case of the pipelines compile the shaders and various "fixed function" knobs into a single program blob that is known-good by the time you use it to draw.
- vkQueueSubmit does the actual submit of the command buffer.
- Then the vkFence is to signal that the work is done, there are extensions to bind them to Kernel objects but they never explicitly say that you can then use them in epoll or WaitForSingleObject.
- external validation layers to be used in debug mode to catch any errors you make.
Basically vulkan 1.* looks like an abstraction library made to hide all the specific data formats and still try and provide as low level access to the simple API Casey described.
Did it work? Not really, several idiosyncrasies exist in the API as is, for example how input attachments are bound and specified, you need to both add it to the framebuffer and as part of the descriptors as a special texture but all IHVs will only use one or the other, none of them look at both even though the translation from the framebuffer to descriptorset in the backend is fairly trivial. I haven't looked that closely to Metal and D3D12 but I believe both suffer from the type of issues because of that abstraction they make over the backend.
However I'm hopeful that vulkan 2.0 will be able to eliminate a lot of those idiosyncrasies as understanding of the commandbuffer paradigm increases. I've heard that some IHV's have already removed some of the limitations that forced some of the design decisions in vulkan. Though I don't recall the specifics on that.