DX12 capture: document why it's pricier than DX11/GL (measured breakdown)
Investigated the DX12 present-thread overhead (~0.38 ms vs DX11 ~0.05 / GL ~0.09). Per-stage timing of the D3D11On12 path showed the cost is NOT where you'd assume: fence 0.005 + wrap(CreateWrappedResource+Acquire) 0.012 + copy 0.133 + flush 0.057 ms CreateWrappedResource is cheap. The cost is the CopyResource issued on the 11On12 immediate context plus the mandatory Flush to make the shared copy visible to the host -- both inherent to the bridge and not paid by the native-D3D11 path. The per-frame GetDevice can't be skipped either (it's how device recreation is detected). Documented this in the capture path. Improving it means a native-D3D12 copy-queue path into a D3D12-shared texture, but the host consumes the shared surface via IDXGIKeyedMutex (a D3D11 concept), so that also requires switching the DX12 producer<->host sync to a shared ID3D12Fence -- a cross-API rewrite. Deferred: the overhead is ~5% of a 144 Hz frame and correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -424,6 +424,16 @@ void capture_backbuffer_d3d12(IDXGISwapChain* sc)
|
||||
std::scoped_lock lock(g_tex_mutex);
|
||||
if (ensure_on12_locked(dev))
|
||||
{
|
||||
// DX12 capture is pricier than DX11/OpenGL (measured ~0.38 ms vs ~0.05/0.09 ms of
|
||||
// present-thread overhead) because it goes through the D3D11On12 bridge. The cost is NOT
|
||||
// CreateWrappedResource (measured ~0.012 ms) -- it's the CopyResource issued on the 11On12
|
||||
// immediate context (~0.13 ms) plus the mandatory Flush to make the shared copy visible to
|
||||
// the host (~0.06 ms), neither of which the native-D3D11 path pays. Eliminating it needs a
|
||||
// native-D3D12 copy-queue path into a D3D12-shared texture, but the host consumes the
|
||||
// shared surface via an IDXGIKeyedMutex (a D3D11 concept), so that also means switching the
|
||||
// DX12 producer<->host sync to a shared ID3D12Fence -- a cross-API rewrite. Deferred: the
|
||||
// overhead is ~5% of a 144 Hz frame and the capture is correct; the bridge stays for now.
|
||||
//
|
||||
// Order our copy after the game's frame without burdening the game's queue: the
|
||||
// game queue signals the fence (cheap), our copy queue waits on it. Skipped if the
|
||||
// queue isn't captured yet or the fence is missing (one possibly-early frame).
|
||||
|
||||
Reference in New Issue
Block a user