Why is D3D12GetDebugInterface failing with "no such interface supported" using Rust's windows-rs crate?

164 views Asked by At

I'm getting the following error when getting a D3D12DebugInterface in Rust using the windows-rs crate:

failed to get d3d12 debug interface: No such interface supported (0x80004002)

Here's the code that I think should work:

#[derive(Default)]
struct DebugLayer {
    d3d12_debug: Option<ID3D12Debug6>,
    dxgi_debug: Option<IDXGIDebug1>,
}

impl DebugLayer {
    fn init() -> Self {
        unsafe {
            let mut debug_layer = Self::default();

            match D3D12GetDebugInterface(&mut debug_layer.d3d12_debug) {
                Ok(()) => {
                    if let Some(ref d3d12_debug) = debug_layer.d3d12_debug {
                        d3d12_debug.EnableDebugLayer();
                        println!("d3d12: debug layer enabled");

                        match DXGIGetDebugInterface1::<IDXGIDebug1>(0) {
                            Ok(dxgi_debug) => {
                                dxgi_debug.EnableLeakTrackingForThread();
                                println!("d3d12: leak tracking enabled");

                                debug_layer.dxgi_debug = Some(dxgi_debug);
                            }

                            Err(err) => println!("failed to get dxgi debug interface: {}", err),
                        }
                    }
                }

                Err(err) => println!("failed to get d3d12 debug interface: {}", err),
            }

            debug_layer
        }
    }
}

And the features that I'm enabling in my Cargo.toml:

[target.'cfg(target_os = "windows")'.dependencies.windows]
version = "0.52.0"
features = [
    "Win32_Foundation",
    "Win32_Graphics_Gdi",
    "Win32_Graphics_Direct3D_Fxc",
    "Win32_Graphics_Direct3D12",
    "Win32_Graphics_Dxgi_Common",
    "Win32_Security",
    "Win32_System_LibraryLoader",
    "Win32_System_Threading",
    "Win32_System_WindowsProgramming",
    "Win32_UI_Input_KeyboardAndMouse",
    "Win32_UI_WindowsAndMessaging",
]

I'm on Windows 10 and I've check in my settings and I have the Graphics Tools installed. What am I missing / doing wrong?

1

There are 1 answers

1
IInspectable On BEST ANSWER

The error diagnostic is spot-on: ID3D12Debug6 isn't available on your system (or mine1). That's what the error code 0x80004002 (i.e. E_NOINTERFACE) communicates.

Here is a minimal sample that illustrates the issue.

Cargo.toml

[package]
name = "d3d12_debug_layer"
version = "0.0.0"
edition = "2021"

[dependencies.windows]
version = "0.52.0"
features = ["Win32_Graphics_Direct3D12"]

src/main.rs

use windows::{
    core::Result,
    Win32::Graphics::Direct3D12::{D3D12GetDebugInterface, ID3D12Debug6},
};

fn main() -> Result<()> {
    let mut dbg_itf: Option<ID3D12Debug6> = None;
    unsafe { D3D12GetDebugInterface(&mut dbg_itf) }?;

    Ok(())
}

Issuing cargo run produces the following output1:

Error: Error { code: HRESULT(0x80004002), message: "No such interface supported" }

This isn't something you can "fix" by changing your code. The requested interface (ID3D12Debug6) isn't implemented in binaries that ship with your OS distribution.


Availability of the debug interfaces depends on the system where the code executes. This is outside your control, and you have to write code that adapts to available features instead.

To illustrate this, the following code initially requests the ID3D12Debug interface and subsequently checks whether later interfaces are implemented. It does this by calling the cast() generic of the ComInterface trait. This is windows-rs' implementation of QueryInterface(). The trailing ok() converts Result<T, E> to Option<T> for convenience.

src/main.rs

use windows::{
    core::{ComInterface, Result},
    Win32::Graphics::Direct3D12::{
        D3D12GetDebugInterface, ID3D12Debug, ID3D12Debug1, ID3D12Debug2, ID3D12Debug3,
        ID3D12Debug4, ID3D12Debug5, ID3D12Debug6,
    },
};

fn main() -> Result<()> {
    let dbg_itf = {
        let mut dbg_itf: Option<ID3D12Debug> = None;
        unsafe { D3D12GetDebugInterface(&mut dbg_itf) }?;
        dbg_itf.expect("D3D12GetDebugInterface failed its contract")
    };

    let dbg1_itf = dbg_itf.cast::<ID3D12Debug1>().ok();
    println!("{dbg1_itf:?}");

    let dbg2_itf = dbg_itf.cast::<ID3D12Debug2>().ok();
    println!("{dbg2_itf:?}");

    let dbg3_itf = dbg_itf.cast::<ID3D12Debug3>().ok();
    println!("{dbg3_itf:?}");

    let dbg4_itf = dbg_itf.cast::<ID3D12Debug4>().ok();
    println!("{dbg4_itf:?}");

    let dbg5_itf = dbg_itf.cast::<ID3D12Debug5>().ok();
    println!("{dbg5_itf:?}");

    let dbg6_itf = dbg_itf.cast::<ID3D12Debug6>().ok();
    println!("{dbg6_itf:?}");

    Ok(())
}

Running this produces the following output1:

Some(ID3D12Debug1(IUnknown(0x1d2f23d50b0)))
Some(ID3D12Debug2(IUnknown(0x1d2f23d50b8)))
Some(ID3D12Debug3(IUnknown(0x1d2f23d50a0)))
None
None
None

The code is rather verbose, but you would generally only ask for interfaces you are interested in using. The benefit of the added complexity is that it can take advantage of features as they become available without having to be recompiled and redeployed.


Features become available in one of two ways:

  • A user running the binary on a later OS version
  • An OS update backporting functionality to earlier OS versions

The latter seems to have happened in the past. The documentation for ID3D12Debug3 lists the minimum system requirements as:

Minimum supported client Windows 10 Build 20348
Minimum supported server Windows 10 Build 20348

As documented, the ID3D12Debug3 interface shouldn't be available on my system, but the output above clearly disagrees. Writing "version-adaptive code" that checks for feature availability rather than OS version is thus strongly recommended.


1 Windows 10 Version 22H2 (OS Build 19045.3693).