CNA-BUG-115: DIRECTX9 requests D3DPRESENT_INTERVAL_TWO without checking device support, and a Reset that rejects it leaves the device lost and retried every frame

CNA snapshot 3b43bd86  ·  Known Issues › Current bugs  ·  source links pinned to 3b43bd86

✓

Evidence basis: source-verified at the pinned commit; inferred from the source (the behaviour was not run); tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

PresentInterval::Two becomes D3DPRESENT_INTERVAL_TWO without a caps or windowed-mode check; when Reset rejects it the renderer marks the device lost and its recovery retries the same Reset every frame, skipping Draw and Present, while still reporting Two.

Identifier
CNA-BUG-115
Category
Bug
Subsystem
Graphics & renderers
Status
Open
Verified against
CNA 3b43bd86 (3b43bd8687ed46523d873f9bec4b32bd8481dcb5)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Inferred (probable): depends on a platform or toolchain step nobody executed
Tests touching this area
Yes: see Current tests
Affected contract
PresentationParameters.PresentationInterval = PresentInterval::Two on DIRECTX9

Expected behaviour

An unsupported interval should be clamped (and the applied value reported) or refused once.

Actual behaviour at TARGET

DirectX9Renderer.cpp's BuildPresentParameters switches on the interval with no caps check, so Two becomes D3DPRESENT_INTERVAL_TWO whatever the device or window mode. At construction a rejected value fails once and loudly (CreateDevice throws). After construction, SetSwapInterval (reached from GraphicsDevice::Reset and SetPresentationParameters) marks the presentation dirty; EnsureDeviceSize (run from TryBeginDrawEXT and Present) releases every D3DPOOL_DEFAULT resource and calls device_->Reset, and on failure keeps presentationDirty_, calls MarkDeviceLostFromScopedFailureEXT and logs ‘device lost, awaiting reset recovery’. The recovery path (PollDeviceLost, then PerformResetRecovery) rebuilds the same parameters, so the same Reset is attempted again on every draw gate. Direct3D 9 documents only DEFAULT, IMMEDIATE and ONE as valid for windowed swap chains, so a windowed device switched to Two after construction (for example by GraphicsDevice::Reset with new PresentationParameters; GraphicsDeviceManager only ever requests One or Immediate) can stay lost indefinitely: TryBeginDrawEXT refuses every frame, so Draw and Present are skipped while Update keeps running, with PresentationParameters still claiming Two.

Source locations

Evidence

Checked by reading the named sources at 3b43bd86; nothing was built or executed for this entry. The windowed-mode restriction is Direct3D 9's documented behaviour, not re-checked on a device; whether Wine/DXVK reject it the same way was not observed.

Independent re-verification: Checked by reading the named sources at 3b43bd86; nothing was built or executed for this entry. The windowed-mode restriction is Direct3D 9's documented behaviour (XNA's PresentInterval documentation likewise guarantees only One and Immediate for windowed swap chains and tells callers to check capabilities for Two), not re-checked on a device; whether Wine/DXVK reject it the same way was not observed, so the retry loop is inferred rather than verified. No DIRECTX9 test requests PresentInterval::Two.

Re-verified at c6dec91f: BuildPresentParameters is unchanged; d0cea7036 made a failed EnsureDeviceSize Reset enter device-loss recovery immediately instead of logging ‘retrying next frame’, which changes the route of the retry but not the outcome. Not executed.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

No DIRECTX9 test requests PresentInterval::Two.

Regression test

A DIRECTX9 test that requests Two in a window and asserts that Reset succeeds once (clamped to One, applied value written back) or fails once with a clear exception.

Blast radius

DIRECTX9 games that use PresentInterval::Two, especially windowed.

Workaround

Use PresentInterval::One with a fixed time step for 30 Hz pacing.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index