読者です 読者をやめる 読者になる 読者になる

WPFとDirect3D11の連携 --- 導入

下記記事にてWPFとDirect3D9Exを連携する方法を記述しました。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

最近ではDirect3D11の利用がメジャーになっていますので、今回はWPFとDirect3D11を連携させてみたいと思います。

WPF自体はDirect3D9ですので、リソースを共有する仕組みでDirect3D11と連携します。

本記事で利用しているCliComPtr、AssertHR、D3D9Ex::CreateDeviceは上記記事を参照してください。

Device作成

DeviceとDeviceContextを作成しています。

createDeviceFlagやpFeatureLevelsは適宜カスタマイズしてください。(下記ではFeatureLevelは11、10.1、10、9.3、9.2、9.1から選択されます。)

#include <d3d11.h>
#pragma comment(lib, "d3d11.lib")

std::tuple<CComPtr<ID3D11Device>, CComPtr<ID3D11DeviceContext>> CreateDevice()
{
#ifdef _DEBUG
    DWORD createDeviceFlag = D3D11_CREATE_DEVICE_DEBUG;
#else
    DWORD createDeviceFlag = 0;
#endif

    CComPtr<ID3D11Device> device;
    CComPtr<ID3D11DeviceContext> context;
    D3D_FEATURE_LEVEL createdFeatureLevel;
    AssertHR(D3D11CreateDevice(
        NULL,
        D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        createDeviceFlag,
        NULL,
        0,
        D3D11_SDK_VERSION,
        &device.p,
        &createdFeatureLevel,
        &context.p));

    return { device, context };
}


共有RenderTarget作成(Direct3D9Ex)

Direct3D11で利用できるDirect3D9ExのRenderTargetを作成します。

CreateRenderTargetの最後の引数からHANDLEを取得しているとこが通常との違いです。

std::tuple<CComPtr<IDirect3DSurface9>, HANDLE> CreateSharedRenderTarget(IDirect3DDevice9Ex* device, int width, int height)
{
    CComPtr<IDirect3DSurface9> surface;
    HANDLE handle = nullptr;
    AssertHR(device->CreateRenderTarget(
        width,
        height,
        D3DFMT_A8R8G8B8,
        D3DMULTISAMPLE_NONE,
        0,
        FALSE,
        &surface.p,
        &handle));
       
    return { surface, handle };
}


共有RenderTargetを開く

Direct3D9Exで作成したRenderTargetをDirect3D11側で開きます。

CComPtr<ID3D11RenderTargetView> OpenRenderTarget(ID3D11Device* device, HANDLE handle)
{        
    CComPtr<ID3D11Resource> resource;
    AssertHR(device->OpenSharedResource(handle, __uuidof(ID3D11Resource), (void**)&resource.p));

    CComPtr<ID3D11RenderTargetView> renderTarget;
    AssertHR(device->CreateRenderTargetView(resource, NULL, &renderTarget.p));

    return renderTarget;
}


Renderer作成

赤い背景を描画するだけの内容となっています。

ref classとstd::tieを上手く連携させられなかったため、Tie2マクロを定義しています。

共有リソースを利用している場合は描画の最後にID3D11DeviceContext::Flushを呼び出す必要があります。

#define Tie2(x1, x2, expr) { auto t = expr; x1 = std::get<0>(t); x2 = std::get<1>(t); }

public ref class Renderer
{
public:
    property IntPtr Surface { IntPtr get() { return IntPtr(_surface9); } }
    Renderer(int width, int height)
    {
        _device9 = D3D9Ex::CreateDevice();

        HANDLE handle;
        Tie2(_surface9, handle, D3D9Ex::CreateSharedRenderTarget(_device9, width, height));

        Tie2(_device, _context, CreateDevice());

        _renderTarget = OpenRenderTarget(_device, handle);

        ID3D11RenderTargetView* renderTargets[1] = { _renderTarget };
        _context->OMSetRenderTargets(1, renderTargets, NULL);

        float clearColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
        _context->ClearRenderTargetView(_renderTarget, clearColor);

        _context->Flush();
    };
private:
    CliComPtr<IDirect3DDevice9Ex> _device9;
    CliComPtr<IDirect3DSurface9> _surface9;
    CliComPtr<ID3D11Device> _device;
    CliComPtr<ID3D11DeviceContext> _context;
    CliComPtr<ID3D11RenderTargetView> _renderTarget;
};


WPF

下記記事と同じコードを利用できます。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

下記のようなウィンドウが表示されます。
f:id:any-programming:20170502152556p:plain