neovide/neovide · error

Failed to set maximum frame latency

Error message

Failed to set maximum frame latency

What it means

This error is thrown in D3DRenderer::new when IDXGISwapChain3::SetMaximumFrameLatency(1) returns a failure HRESULT. The call configures the swap chain (created with the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT flag) to allow at most one queued frame, enabling the frame-latency waitable object used for pacing. A failure means the swap chain rejected the latency setting, typically because the swap chain was not created with the waitable-object flag, the device/factory is invalid, or the DXGI runtime/driver rejected the request (e.g. Remote Desktop session).

Source

Thrown at src/renderer/d3d.rs:193

            window.window_handle().expect("Failed to fetch window handle").as_raw()
        {
            HWND(handle.hwnd.get() as *mut _)
        } else {
            panic!("Not a Win32 window");
        };

        let swap_chain = unsafe {
            tracy_zone!("create swap_chain");
            dxgi_factory
                .CreateSwapChainForComposition(&command_queue, &swap_chain_desc, None)
                .expect("Failed to create the Direct3D swap chain")
        };

        let swap_chain: IDXGISwapChain3 =
            IDXGISwapChain1::cast(&swap_chain).expect("Failed to cast");

        unsafe {
            swap_chain.SetMaximumFrameLatency(1).expect("Failed to set maximum frame latency");
        }
        let composition_device: IDCompositionDevice = unsafe {
            DCompositionCreateDevice2(None).expect("Could not create composition device")
        };
        let target = unsafe {
            composition_device
                .CreateTargetForHwnd(hwnd, true)
                .expect("Could not create composition target")
        };
        let visual = unsafe {
            composition_device.CreateVisual().expect("Could not create composition visual")
        };

        unsafe {
            visual.SetContent(&swap_chain).expect("Failed to set composition content");
            target.SetRoot(&visual).expect("Failed to set composition root");
            composition_device.Commit().expect("Failed to commit composition");
        }

View on GitHub (pinned to ade2d9cda7)

Solutions

  1. Ensure the swap chain is created with DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT in swap_chain_desc.Flags (it is, in this code) and that CreateSwapChainForComposition succeeded on a valid device
  2. Update GPU drivers / verify the session has a real DXGI adapter (not WARP/Remote) before creating the renderer
  3. Check the HRESULT returned for DXG_ERROR_DEVICE_REMOVED and reinitialize the device
  4. Lower latency expectations or fall back to a non-waitable swap chain path on unsupported platforms
Defensive patterns

Strategy: try-catch

Validate before calling

let hr = unsafe { swap_chain.SetMaximumFrameLatency(1) };
if hr.is_err() {
    eprintln!("SetMaximumFrameLatency failed: {:?}", hr); // inspect HRESULT before proceeding
}

Type guard

fn swap_chain_supports_latency(sc: &IDXGISwapChain3) -> bool {
    let mut desc = Default::default();
    unsafe { sc.GetDesc1(&mut desc).is_ok() && (desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT.0 as u32) != 0 }
}

Try / catch

match unsafe { swap_chain.SetMaximumFrameLatency(1) } {
    Ok(()) => {},
    Err(e) => { log::error!("SetMaximumFrameLatency failed: {e}"); return Err(RendererInitError::Latency(e)); }
}

Prevention

When it happens

Trigger: Calling SetMaximumFrameLatency(1) on a swap chain lacking DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT or DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING prerequisites; corrupted/lost D3D12 device; swap chain created in an unsupported session (RDP, headless, or missing GPU/driver support).

Common situations: Running the app over Remote Desktop or in a VM without GPU support; old/broken graphics drivers; mixing a swap chain desc missing the frame-latency flag with GetFrameLatencyWaitableObject usage; device removed earlier during adapter enumeration.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of neovide/neovide@ade2d9cda7 (2026-09-06). Data as JSON: /api/errors/0e5742ad05b1ad60. Report an issue: GitHub.