Correct me if I’m wrong, but when recording command buffer those commands are not executed immediately.
But rather when this command buffer is submited to the render queue. Only then driver process it and send it to the graphics card.
So not only " command buffers solve is being able to build sequences of commands asynchronously on multiple threads." But additional driver have more knowledge about “what to draw”.
… so what?
What does the driver really know about what you’re doing with a command buffer? It only knows the sequence of commands in that buffer. It doesn’t know:
1: What commands were executed before that buffer.
2: What commands will be executed after that buffer.
So what does it really know about what you’re doing?
You might wonder why it matters what commands were executed before or after. But that’s very important.
Consider a vital concept in Vulkan: image layouts. Every image exists in a particular layout, which potentially restricts what operations it can be used with. Well, the layout an image “currently” is in is completely unknown to Vulkan. You, the user, are expected to keep up with that. After all, commands are built asynchronously and executed in a generally undefined order.
So when you’re building a command buffer, and you use an image in a descriptor set… what layout is it in? Even if the image is in the wrong layout when you’re building that CB, Vulkan cannot know if you will execute a command buffer before this one which will transition the layout to an appropriate one.
Therefore, Vulkan requires that you specify the layout of an image when you use it. And if, by the time that command executes, the image isn’t actually in that layout, you’re boned.
OpenGL doesn’t have image layouts or anything even remotely like them. Why? Because OpenGL’s execution model is essentially synchronous. Stuff like image layouts and transitions are an implementation detail to OpenGL, something the driver can handle behind the scenes. All thanks to the synchronous execution model.
If you attach an image to an FBO, render to it, then detach it and bind it as a sampler, OpenGL can see that you’re doing all of those things in order. If the implementation needs to do layout transitions, it can do them as needed between those operations. This is possible because OpenGL’s execution model requires all commands to behave as if they were executed synchronously.
The only way to make something like NV_command_list work is to make the execution model asychronous (and add other features like image layouts). At which point, why bother using OpenGL at all? It’s not like you can slowly transition to using this. You have to rewrite far too much code to be able to handle asynchronous execution effectively and efficiently. You have to deal with things like image layouts and async memory transfer operations. And so forth.
Just look at NV_command_list as it is. To use it, you have to use non-core APIs for vertex specification, uniform buffer binding, SSBOs, and texture and image binding. By the time you’re finished with all of this, how many OpenGL API calls are you using that don’t have an NV or ARB suffix on them?
By the end of this process, all you will have is OpenGL-in-name-only. Better to just use Vulkan and get it over with.
There is no low-hanging fruit to be picked from the Vulkan tree. You can’t just pull parts of Vulkan over and expect the result to make sense. Direct3D11 tried that with deferred contexts. Notice how NVIDIA implemented that in their hardware.
Ever notice that AMD didn’t? I’d bet that image layout issues were a huge part of the reason why. NVIDIA has no such concept in their hardware; they ignore all the Vulkan layout stuff. AMD’s hardware really relies on it.
SPIR-V makes sense to be able to be consumed by OpenGL. Descriptor sets might make sense for OpenGL, in some respects. And maybe push-constants & input attachments. Anything else requires fundamental changes to the very execution model of OpenGL before they can actually work.
And if you change that much, why are you using OpenGL? Because you don’t like writing vk
in front of your function names?
As for more of the “what does the driver really know with CBs”, consider Vulkan’s complex render-pass system. Why does this system exist? To permit implementations to actually know something about how you’re going to render.
Pipelines are built based on being executed within a specific subpass of a render pass. At pipeline creation time, the implementation knows the numbers and formats of the images that are used as render targets. It knows when you’re going to do read/modify/write glTextureBarrier gymnastics. It knows this at compile time. It can therefore compile your shaders (particularly for tile-based architectures) into efficient forms to utilize this knowledge.
If command buffers were so useful for knowing something about what you’re rendering, why would render passes and subpasses exist? Surely, the command buffer building system could just read the current framebuffer attachments and make similar inferences based on them, right?
No. Because that happens at command submission time. Whereas the way Vulkan does things, it happens at pipeline creation time, a thing that is expected to be a heavyweight operation. Command submissions are expected to be lightweight operations, and Vulkan makes sure that implementations never have a reason to do things like recompile or reconfigure shaders based on various state.
So no, command buffers don’t exist to allow implementations to know more about what you’re trying to render. The thing Vulkan does to permit this is that it forces users to specify things up-front.