Coming to DirectX 12: D3D9On12 and D3D11On12 Resource Interop APIs

Randy Tidd

D3D is introducing D3D9on12 with resource interop APIs and adding similar resource interop APIs to D3D11on12.  With this new support, callers can now retrieve the underlying D3D12 resource from the D3D11 or D3D9 resource object even when the resource was created with D3D11 or D3D9 API.   The new D3D9On12 API can be found in the insider SDK in D3D9on12.h.  These features are available in Windows Insider builds now and do not require new drivers to work.

You can explicitly create D3D9 with D3D9On12 using new overrides:

typedef struct _D3D9ON12_ARGS
{
    BOOL Enable9On12;
    IUnknown *pD3D12Device;
    IUnknown *ppD3D12Queues[MAX_D3D9ON12_QUEUES];
    UINT NumQueues;
    UINT NodeMask;
} D3D9ON12_ARGS;

typedef HRESULT (WINAPI *PFN_Direct3DCreate9On12Ex)(UINT SDKVersion, D3D9ON12_ARGS *pOverrideList, UINT NumOverrideEntries, IDirect3D9Ex** ppOutputInterface);
HRESULT WINAPI Direct3DCreate9On12Ex(UINT SDKVersion, D3D9ON12_ARGS *pOverrideList, UINT NumOverrideEntries, IDirect3D9Ex** ppOutputInterface);

typedef IDirect3D9* (WINAPI *PFN_Direct3DCreate9On12)(UINT SDKVersion, D3D9ON12_ARGS *pOverrideList, UINT NumOverrideEntries);
IDirect3D9* WINAPI Direct3DCreate9On12(UINT SDKVersion, D3D9ON12_ARGS *pOverrideList, UINT NumOverrideEntries);

D3D9 begins by creating an active display adapter enumerator.  These new entry points allow you to override each adapter to use D3D9On12 or not by setting Enable9On12 to TRUE and a D3D12 device with an adapter LUID that matches the adapter you want to provide.  Optionally use an entry with a nullptr D3D12 device to match any active display adapter and have D3D9On12 create the D3D12 device.

Call QueryInterface on the D3D9 device for IDirect3DDevice9On12 to find out if the device is running on 9On12.  QueryInterface will fail with E_NOINTERFACE when not running on D3D9On12.

From this interface, you can retrieve the underlying D3D12 device:

HRESULT GetD3D12Device(
    REFIID riid, 
   _COM_Outptr_ void** ppvDevice);

IDirect3DDevice9On12 also has resource interop APIs to access the underlying D3D12 resource.  Begin by calling UnwrapUnderlyingResource to retrieve the D3D12 resource pointer.  UnwrapUnderlyingResource also takes an ID3D12Queue instance as an input parameter.  Any pending work accessing the resource causes fence waits to be scheduled on this queue.  Callers can then queue further work on this queue, including a signal on a caller owned fence.

HRESULT UnwrapUnderlyingResource(
    _In_ IDirect3DResource9* pResource9, 
    _In_ ID3D12CommandQueue* pCommandQueue,
    REFIID riid,
    _COM_Outptr_ void** ppvResource12 );

Once D3D12 work has been scheduled, call ReturnUnderlyingResource.   ReturnUnderlyingResource API takes a list of ID3D12Fence instances and a parallel list of signal values.  This must include any pending work against the resource submitted by the caller.  The translation layer defers the waits for these resources until work is scheduled against the resource.

HRESULT ReturnUnderlyingResource(
    _In_ IDirect3DResource9* pResource9, 
    UINT NumSync,
    _In_reads_(NumSync) UINT64* pSignalValues,
    _In_reads_(NumSync) ID3D12Fence** ppFences );

Be aware that unwrapping a resource checks out the resource from the translation layer.  No translation layer usage through either the D3D9 API may be scheduled while the resource is checked out.

Similar support is also added to D3D11On12 with the ID3D11On12Device2.

ID3D11On12Device2 : public ID3D11On12Device1
{
public:
    virtual HRESULT STDMETHODCALLTYPE UnwrapUnderlyingResource( 
        _In_  ID3D11Resource *pResource11,
        _In_  ID3D12CommandQueue *pCommandQueue,
        REFIID riid,
        _COM_Outptr_  void **ppvResource12) = 0;
        
    virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource( 
        _In_  ID3D11Resource *pResource11,
        UINT NumSync,
        _In_reads_(NumSync)   UINT64 *pSignalValues,
        _In_reads_(NumSync)   ID3D12Fence **ppFences) = 0;
};

The main difference is that UnwrapUnderlyingResource does not flush and may schedule GPU work.  You should flush after calling this method if the you externally wait for completion.

This support is available now in the latest preview OS.  For further information, the spec is also available here.

0 comments

Discussion is closed.

Feedback usabilla icon