D3D12 Debug Layer Message Callback

Zichuan Gong

Finally, D3D12 messages that you can easily integrate with your own application logs! The D3D12 InfoQueue Message Callback feature allows developers to capture debug messages, with stack context, providing yet another essential debugging tool for error analysis. To date, D3D12 has had ID3D12InfoQueue API. However, developers always wants there to be message callback feature, which allows them to more easily investigate callstacks of live errors and to instrument customized error accounting and reporting mechanisms. If you have ever felt frustrated when you get DXGI_ERROR_DEVICE_REMOVED HRESULT from some much-later Create* or Present call, now you can probably capture Device-Removal events when they are seen by the D3D12 runtime.

Good news is this feature is now available in Windows 10 Release Preview build 20236. Let’s look at the API first:

interface ID3D12InfoQueue1 
    : ID3D12InfoQueue 
{ 
    HRESULT RegisterMessageCallback( 
        D3D12MessageFunc CallbackFunc,  
        D3D12_MESSAGE_CALLBACK_FLAGS CallbackFilterFlags, 
        void* pContext, 
        DWORD *pCallbackCookie); 

    HRESULT UnregisterMessageCallback( 
        DWORD CallbackCookie); 
} 
typedef void (*D3D12MessageFunc)(
    D3D12_MESSAGE_CATEGORY Category, 
    D3D12_MESSAGE_SEVERITY Severity, 
    D3D12_MESSAGE_ID ID, 
    LPCSTR pDescription, 
    void* pContext);

typedef enum D3D12_MESSAGE_CALLBACK_FLAGS
{
    D3D12_MESSAGE_CALLBACK_FLAG_NONE = 0x00,
    D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS = 0x01,
} D3D12_MESSAGE_CALLBACK_FLAGS;

To use the message callback feature, it is very simple, you just call RegisterMessageCallback when you want to register a callback and call UnregisterMessageCallback when you no longer wants it. RegisterMessageCallback registers a callback that is called at the time a debug message is produced, and with this method, you can create your own callback function. Besides, this function also takes a void* pContext as input, which means you can now put your own data into the callback function. This function returns the callback cookie to you and you can later use this cookie value to unregister any callbacks you no longer want. Also, we have CallbackFilterFlags as an input, which makes sure that you have the most commonly used two scenarios. 1. If you want your own message filtering mechanism in callback, you can set this value to D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS and this will disable message filtering. 2. If you want current callback to be filtered in the exact same way as what gets logged as debug text. You can set this value to D3D12_MESSAGE_CALLBACK_FLAG_NONE.

Something worth mentioning is that message callback supports multiple callbacks, since supporting multiple callback registration allows a D3D12 component within a process to handle messages without breaking callbacks registered by any other D3D12 components in the same process. And if multiple callbacks are active, they will be called sequentially in the same order that the callbacks were registered.

Before using this new API, there are several things to pay attention to, because this API does have some restrictions. The most important one is don’t make other D3D calls inside the callback. This restriction is only documented, and it is not strictly validated by the runtime. Callbacks may be invoked with the internal runtimes in states which are unsafe to make other D3D calls, so any D3D API calls from a callback implementation could result in deadlocks and crashes. Another thing to pay attention is that callback may execute on a thread other than the one that originally issued the API call, so you need to ensure that your callbacks can properly handle this.

Hope you’ll enjoy this feature!

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Pathogen David 0

    Very excited for this! Been wanting it for quite a while.

    One thing I wanted to double check: “Another thing to pay attention is that callback may execute on a thread other than the one that originally issued the API call”
    I assume “the API call” in this context is the call to ID3D12InfoQueue::RegisterMessageCallback, right? (As opposed to the API call that caused the message that caused the callback to be called.)

    • John Kelly 0

      I would suspect both – while D3D12 is generally a single-threaded API, I know in some scenarios the debug layer does use other threads to provide a more performant validation experience. I wouldn’t be surprised if the callback was invoked on a different thread to the thread performing the illegal API usage

Feedback usabilla icon