Add CommandBuffer abstraction with backend-specific downcasting#1033
Add CommandBuffer abstraction with backend-specific downcasting#1033
Conversation
378ec7a to
97e6af6
Compare
lib/API/DX/Device.cpp
Outdated
| if (!CBOrErr) | ||
| return CBOrErr.takeError(); | ||
| auto CBOwner = std::move(*CBOrErr); | ||
| State.CB = &CBOwner->as<DXCommandBuffer>(); |
There was a problem hiding this comment.
Seems like it would be better if the Invocation state owned the command buffer rather than a temporary object in the function here. If we really should have two copies of it we should probably make it a shared_ptr rather than a unique_ptr.
There was a problem hiding this comment.
I don't think using a shared_ptr for a command buffer is a good idea. The usage of shared_ptr in the rendering backend API is there for resources that can be accessed across multiple threads. However, command buffers should be access from one thread at a time.
I am open for either storing it as a unique_ptr in the InvocationState or just passing it as an argument each time.
There was a problem hiding this comment.
Good points, the InvocationState also lives on the stack here and owns all members, except for the backend-specific-typed command buffer which is merely a pointer reference to another stack member (which becomes dangling when CBOwner is dropped from the stack before InvocationState).
I also vouch for keeping it uniquely owned, that way command buffers can only be passed by reference and not accidentally held and modified in multiple places (trying very hard to forget Rust's natural ownership semantics and page in C++'s nonsensical ones 🙃).
There was a problem hiding this comment.
The only downside of that is needing the backend-specific pointer easily accessible. Either the field is stored twice, or this backend-specific executeProgram() call uses the specific XxxCommandBuffer::create() call to immediately have the right type until every internal field access is replaced with a generic abstraction.
8803d26 to
dadf52d
Compare
| ComPtr<ID3D12Fence> Fence; | ||
| #ifdef _WIN32 | ||
| HANDLE Event = nullptr; | ||
| #else // WSL | ||
| int Event = -1; | ||
| #endif |
There was a problem hiding this comment.
Can we get rid of this by using the abstract Fence type introduced in #1007?
There was a problem hiding this comment.
Yeah will do once that PR is merged. Although at this point I'm weary of the fact that the fence is stored in the CommandBuffer object in the first place, despite it being single-use anyway.
dadf52d to
4efc0b0
Compare
5a2952f to
b3ddfc2
Compare
Command buffer creation and management was previously spread across each backend's executeProgram() with no shared interface, making it impossible to manage command buffers from backend-agnostic code. This introduces a CommandBuffer base class on Device so that higher-level code can create and pass around command buffers without knowing the backend. Per-object allocator/pool ownership also prepares for future async execution where multiple command buffers may be in-flight with independent lifetimes. - DX: DXCommandBuffer owns Allocator, CmdList, Fence, Event - VK: VKCommandBuffer owns CmdPool, CmdBuffer; each submission creates a new CommandBuffer for independent lifetime management - MTL: MTLCommandBuffer wraps MTL::CommandBuffer Device::createCommandBuffer() returns Expected<unique_ptr<CommandBuffer>> with a default "not supported" implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b3ddfc2 to
095fc23
Compare
Command buffer creation and management was previously spread across each backend's
executeProgram()with no shared interface, making it impossible to manage command buffers from backend-agnostic code. This introduces aCommandBufferbase class onDeviceso that higher-level code can create and pass around command buffers without knowing the backend. Per-object allocator/pool ownership also prepares for future async execution where multiple command buffers may be in-flight with independent lifetimes.DXCommandBufferowns Allocator, CmdList, Fence, EventVKCommandBufferowns CmdPool, CmdBuffer; each submission creates a new CommandBuffer for independent lifetime managementMTLCommandBufferwrapsMTL::CommandBufferDevice::createCommandBuffer()returnsExpected<unique_ptr<CommandBuffer>>with a default "not supported" implementation.Test plan
🤖 Generated with Claude Code