Tuesday, June 26, 2012

Using the D3DCompiler DLL with older DX9 SDKs via dynamic linking

If you want to compile shaders at runtime without D3DX, you may want to use the D3DCompiler_xx.dll.

Here is an example of how to dynamically load the D3DCompiler_xx.dll under DX9 without requiring a newer DXSDK installation which includes the complete D3D10/11 headers/libraries.

This approach has the added benefit that it works with the mingw compiler / QtCreator as well.

First we declare some required COM interfaces, D3D structures and the D3DCompile() function prototype (ofcourse you can add more functions like D3DCompileFromFile, etc. if needed):

#ifndef __ID3D10Blob_FWD_DEFINED__
#define __ID3D10Blob_FWD_DEFINED__
typedef interface ID3D10Blob ID3D10Blob;
#endif

#define D3DCOMPILER_DLL_A "d3dcompiler_43.dll"

typedef struct {
    LPCSTR Name;
    LPCSTR Definition;
} D3D_SHADER_MACRO;

DEFINE_GUID(IID_ID3D10Blob, 0x8ba5fb08, 0x5195, 0x40e2, 0xac, 0x58,
 0xd, 0x98, 0x9c, 0x3a, 0x1, 0x2);

typedef struct ID3D10BlobVtbl {
    BEGIN_INTERFACE

    HRESULT ( STDMETHODCALLTYPE *QueryInterface )
            (ID3D10Blob * This, REFIID riid, void **ppvObject);

    ULONG   ( STDMETHODCALLTYPE *AddRef )(ID3D10Blob * This);
    ULONG   ( STDMETHODCALLTYPE *Release )(ID3D10Blob * This);
    LPVOID  ( STDMETHODCALLTYPE *GetBufferPointer )(ID3D10Blob * This);
    SIZE_T  ( STDMETHODCALLTYPE *GetBufferSize )(ID3D10Blob * This);

    END_INTERFACE
} ID3D10BlobVtbl;

#define ID3D10Blob_QueryInterface(This,riid,ppvObject) \
    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )

#define ID3D10Blob_AddRef(This) \
    ( (This)->lpVtbl -> AddRef(This) )

#define ID3D10Blob_Release(This) \
    ( (This)->lpVtbl -> Release(This) )

#define ID3D10Blob_GetBufferPointer(This) \
    ( (This)->lpVtbl -> GetBufferPointer(This) )

#define ID3D10Blob_GetBufferSize(This) \
    ( (This)->lpVtbl -> GetBufferSize(This) )

interface ID3D10Blob
{
    CONST_VTBL struct ID3D10BlobVtbl *lpVtbl;
};

typedef ID3D10Blob ID3DBlob;

typedef HRESULT (WINAPI *pD3DCompile)
    (LPCVOID                         pSrcData,
     SIZE_T                          SrcDataSize,
     LPCSTR                          pFileName,
     CONST D3D_SHADER_MACRO*         pDefines,
     void*/*ID3DInclude* */          pInclude,
     LPCSTR                          pEntrypoint,
     LPCSTR                          pTarget,
     UINT                            Flags1,
     UINT                            Flags2,
     ID3DBlob**                      ppCode,
     ID3DBlob**                      ppErrorMsgs);
Now we can load the D3DCompiler DLL, import the function via GetProcAddress and then compile our shader:
    HINSTANCE hD3DCompiler = LoadLibraryA(D3DCOMPILER_DLL_A);
    pD3DCompile D3DCompile = NULL;

    if (hD3DCompiler) {
        D3DCompile = (pD3DCompile) GetProcAddress(hD3DCompiler, "D3DCompile");
    }

    ID3DBlob* shaderBlob = NULL;
    ID3DBlob* errorMsg = NULL;
    D3DCompile(vsSource, strlen(vsSource), NULL, NULL, NULL, "main", "vs_2_0",
               0, 0, &shaderBlob, &errorMsg);
    if (errorMsg) {
        char text[1024];
        sprintf(text, "D3DCompile failed:\n%s",
               (char*) ID3D10Blob_GetBufferPointer(errorMsg));
        MessageBoxA(NULL, text, "Error", MB_OK|MB_ICONERROR|MB_TASKMODAL);
        ID3D10Blob_Release(errorMsg);
        exit(-1);
    }

    IDirect3DVertexShader9* shader = NULL;
    if (shaderBlob) {
        gD3DDevice->CreateVertexShader(
           (DWORD *) ID3D10Blob_GetBufferPointer(shaderBlob),
            &shader
        );
        ID3D10Blob_Release(shaderBlob);
    }

No comments:

Post a Comment