镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

D3D12: Fix repeated fence waits and release DXGI frame-latency handles - #817

Open
hzqst wants to merge 2 commits into
DiligentGraphics:masterfrom
hzqst:fix/d3d12-fence-wait-816
Open

hzqst wants to merge 2 commits into
DiligentGraphics:masterfrom
hzqst:fix/d3d12-fence-wait-816

Conversation

@hzqst

@hzqst hzqst commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #816.

FenceD3D12Impl::Wait() armed one shared manual-reset event with ID3D12Fence::SetEventOnCompletion() and never reset it. After the first wait that actually blocked, the event stayed signaled, so every later Wait(Value) returned immediately while GetCompletedValue() < Value. Besides application code like the one in #816, this also breaks the frames-in-flight limit in OffScreenSwapChain::Present(), which relies on IFence::Wait().

Changes

This PR has two commits.

D3D12: fix IFence::Wait() returning before the fence reaches the value

  • Pass a null event to SetEventOnCompletion() and remove the shared event. The documentation states: "If hEvent is a null handle, then this API will not return until the specified fence value(s) have been reached." and "This method can be safely called from multiple threads at one time."
  • If the call fails, log the error and poll until the value is reached, rather than returning early or throwing from a public API method that is also exposed through the C interface.
  • Fix the wait-for-GPU event check in CommandQueueD3D12Impl: CreateEvent() returns NULL on failure, not INVALID_HANDLE_VALUE.
  • Add FenceTest.RepeatedCPUWait: for four consecutive values, a second thread signals the fence on the CPU 20 ms after the main thread starts waiting, and the test checks that each Wait() returns only after its value is reached. It requires NativeFence, so it is skipped on D3D11, OpenGL and WebGPU.

D3D11/D3D12: close DXGI frame-latency waitable object handles

  • The handle returned by IDXGISwapChain2::GetFrameLatencyWaitableObject() must be closed with CloseHandle() (documentation). It was overwritten every time the swap chain was recreated (SetMaximumFrameLatency(), SetFullscreenMode()) and never closed on destruction. It is now closed at the start of CreateDXGISwapChain(), where the previous swap chain has already been released, and in the destructor.

Validation

Windows x64, MSVC 2022, Debug build of DiligentCoreAPITest.

Regression test against the previous implementation (the pre-PR FenceD3D12Impl.cpp/.hpp temporarily restored, then this PR's version):

FenceTest.RepeatedCPUWait D3D12 WARP (--mode=d3d12_sw) D3D12 hardware (--mode=d3d12, RTX 5060)
Previous implementation failed 3/3 runs failed 3/3 runs
This PR passed 20/20 runs passed 20/20 runs

The previous implementation fails exactly as described in #816: the first wait is correct and every later one returns early:

FenceTest.cpp(365): error: Expected: (pFence->GetCompletedValue()) >= (Value), actual: 1 vs 2
FenceTest.cpp(365): error: Expected: (pFence->GetCompletedValue()) >= (Value), actual: 2 vs 3
FenceTest.cpp(365): error: Expected: (pFence->GetCompletedValue()) >= (Value), actual: 3 vs 4

Handle leak. The API tests render to a testing swap chain and never create a DXGI swap chain, so this was checked with a temporary test that is not part of the PR: create a D3D12 swap chain for a window, recreate it 32 times via SetMaximumFrameLatency(), destroy it, and compare the process handle count (measured after a warm-up pass; same results on WARP and hardware):

After 32 recreations After create + 32 recreations + destroy
Before this PR +32 +33
This PR 0 0

Existing tests (--gtest_filter=FenceTest.*:MultipleContextTest.*:TestingSwapChainBaseTest.*):

  • D3D12 WARP: 22/22 passed
  • D3D12 hardware: 22/22 passed
  • D3D11 WARP: 17 passed, 5 skipped (they require NativeFence or multiple queues)

hzqst and others added 2 commits October 24, 2026 15:16
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@fabiomarcellusdev

Copy link
Copy Markdown

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

D3D12: IFence::Wait() returns immediately after the first time it has blocked (the manual-reset event is never reset)

2 participants