D3D12 has 4 separate kinds of command lists: direct, bundle, compute, and copy.
Vulkan has similar concepts, but they happen in a different way. Command buffers are allocated from command pools. And command pools are directly associated with a queue family. And command buffers allocated from a pool can only be submitted to the queue family for which the pool was built.
Queue families specify the kinds of operations that the queue can take: graphics, compute, or memory copying operations. D3D12's command queues have a similar concept, but D3D12's command list API has you to specify the list type. Vulkan's gets this information from the queue family the pool is meant for.
The D3D12 "bundle" command list type seems similar on the surface to Vulkan secondary command buffers. However, they are quite different.
The principle difference is this: bundles inherit all state from their executing direct command list, except for the bound PSO itself. This includes resource descriptor bindings.
Vulkan secondary command buffers inherit no state from their primary command buffer execution environment, except for secondary CBs that execute in a subpass of a renderpass instance (and queries). And those only inherit the current subpass state (and queries).
This means you do different things with them, compared to D3D bundles.
Bundles are sometimes used to modify descriptor tables and render stuff, on the assumption that the direct command list they're executed within will have set those tables up. So bundles are kind of like light-weight OpenGL display lists, only without all of the bad things those do. So the intent with bundles is that you build them once and keep them around. They're supposed to be small things.
Vulkan secondary CBs are essential for threaded building of commands intended for a single render pass instance. This is because a render pass instance can only be created within a primary CB, so for optimal use of threads, there needs to be a way to create commands meant to execute in the same subpass in different threads. That's one of the main use-cases of secondary CBs. So the intent is that you'll probably build secondary CBs each frame (though you can reuse them if you want).
So in the end, bundles and secondary CBs are intended to solve separate problems. Bundles are generally dependent on the executing environment, while secondary CBs are more stand-alone.
At the same time, Vulkan secondary CBs can do something bundles cannot: they can execute on compute/copy-only queues. Since Vulkan makes a distinction between the primary/secondary level of the command buffer and the queues where that CB can be sent, it is possible in Vulkan to have secondary command buffers that execute on compute or copy-only queues.
Direct3D 12 can't do that with bundles. The ExecuteBundle
function can only be called on a direct command list. So a copy-only command list cannot execute bundles.
Granted, because Vulkan doesn't inherit anything between secondary CBs except for subpass state, and compute/copy operations don't use render passes, there isn't much to be gained from putting such commands in a secondary CB rather than a primary one.
D3D12 has the same separation between compute pipelines and graphics pipelines that Vulkan does. However, when issuing commands, D3D12 has only one pipeline binding point, to which you can bind any kind of pipeline. By contrast, Vulkan has separate binding points for compute and graphics pipelines. Of course, Vulkan doesn't have different descriptor binding points for them, so the two pipelines can interfere with one another. But if you design their resource usage carefully, it is possible to invoke a dispatch operation without disturbing the needs of the graphics pipeline.
So overall, there's no real difference in pipeline architecture here.