neovide/neovide · error

Could not create composition target

Error message

Could not create composition target

What it means

Thrown when IDCompositionDevice::CreateTargetForHwnd(hwnd, true) fails. This creates a composition target bound to the given top-level window; the 'true' flag means topmost. Failure occurs when the HWND is invalid, not a top-level window, already has a composition target (from another device), or access is denied (different integrity level / UIPI).

Source

Thrown at src/renderer/d3d.rs:201

            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");
        }

        let swap_chain_waitable = unsafe { swap_chain.GetFrameLatencyWaitableObject() };
        if swap_chain_waitable.is_invalid() {
            panic!("Failed to get swapchain waitable object");
        }

        // use a high value to make it easier to track these in PIX
        let fence_values = vec![10000; swap_chain_desc.BufferCount as usize];

View on GitHub (pinned to ade2d9cda7)

Solutions

  1. Ensure hwnd is a top-level window (use the winit outer window handle, not a child)
  2. Close/release any previous composition target for the same HWND before creating a new one
  3. Recreate the renderer rather than calling new twice on the same window
  4. Run the app at the same or higher integrity level than the window's owner
Defensive patterns

Strategy: validation

Validate before calling

fn is_top_level_window(hwnd: HWND) -> bool {
    unsafe { GetAncestor(hwnd, GA_ROOT) == hwnd && IsWindow(hwnd).as_bool() }
}
// call before CreateTargetForHwnd
assert!(is_top_level_window(hwnd), "CreateTargetForHwnd needs a top-level HWND");

Try / catch

let target = composition_device.CreateTargetForHwnd(hwnd, true)
    .map_err(|e| { log::error!("CreateTargetForHwnd({hwnd:?}) failed: {e}"); RendererInitError::Target(e) })?;

Prevention

When it happens

Trigger: HWND is child/control window instead of a top-level window; another IDCompositionDevice already created a target for the same HWND; hwnd handle invalid or belongs to another process with higher integrity; running elevated vs non-elevated mismatch.

Common situations: Passing a winit child window handle; two renderer instances for one window; mixed elevation (app elevated, window created unelevated or vice versa).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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