D3D11on12 を使用した D2D

D3D1211on12 のサンプルでは、11 ベースのデバイスと 12 ベースのデバイス間でリソースを共有することで、D3D12 コンテンツ上に D2D コンテンツをレンダリングする方法を示します。

ID3D11On12Device の作成

最初の手順は、ID3D12Device の作成後に ID3D11On12Device を作成することです。これには、API D3D11On12CreateDevice を介して ID3D12Device でラップされる ID3D11Device の作成が含まれます。 また、この API は、他のパラメーターのなかから、ID3D12CommandQueue を受け取り、11On12 デバイスがそのコマンドを送信できるようにします。 ID3D11Device の作成後、そこから ID3D11On12Device インターフェイスを照会できます。 これは、D2D を設定するために使用されるプライマリ デバイス オブジェクトです。

LoadPipeline メソッドで、デバイスをセットアップします。

 // Create an 11 device wrapped around the 12 device and share
    // 12's command queue.
    ComPtr<ID3D11Device> d3d11Device;
    ThrowIfFailed(D3D11On12CreateDevice(
        m_d3d12Device.Get(),
        d3d11DeviceFlags,
        nullptr,
        0,
        reinterpret_cast<IUnknown**>(m_commandQueue.GetAddressOf()),
        1,
        0,
        &d3d11Device,
        &m_d3d11DeviceContext,
        nullptr
        ));

    // Query the 11On12 device from the 11 device.
    ThrowIfFailed(d3d11Device.As(&m_d3d11On12Device));
呼び出しフロー パラメーター
ID3D11Device
D3D11On12CreateDevice

 

D2D ファクトリの作成

11On12 デバイスを用意したので、それを使用して D2D ファクトリとデバイスを作成します。これは、D3D11 で通常行うのと同様です。

LoadAssets メソッドに追加します。

 // Create D2D/DWrite components.
    {
        D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
        ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &m_d2dFactory));
        ComPtr<IDXGIDevice> dxgiDevice;
        ThrowIfFailed(m_d3d11On12Device.As(&dxgiDevice));
        ThrowIfFailed(m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice));
        ThrowIfFailed(m_d2dDevice->CreateDeviceContext(deviceOptions, &m_d2dDeviceContext));
        ThrowIfFailed(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &m_dWriteFactory));
    }
呼び出しフロー パラメーター
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

D2D のレンダー ターゲットの作成

D3D12 はスワップ チェーンを所有します。このため、11On12 デバイス (D2D コンテンツ) を使用してバック バッファーに対してレンダリングを行う場合、ID3D12Resource 型のバック バッファーから ID3D11Resource 型のラップされたリソースを作成する必要があります。 これにより、ID3D12Resource と D3D11 ベースのインターフェイスがリンクされ、11On12 デバイスで使用できるようになります。 ラップされたリソースを用意した後、LoadAssets メソッドでさらに、D2D でレンダリングするレンダー ターゲット サーフェスを作成できます。

// Initialize *hwnd* with the handle of the window displaying the rendered content.
HWND hwnd;

// Query the window's dpi settings, which will be used to create
// D2D's render targets.
float dpi = GetDpiForWindow(hwnd);
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
    D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
    D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
    dpi,
    dpi);  

// Create frame resources.
{
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());

    // Create a RTV, D2D render target, and a command allocator for each frame.
    for (UINT n = 0; n < FrameCount; n++)
    {
        ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
        m_d3d12Device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);

        // Create a wrapped 11On12 resource of this back buffer. Since we are 
        // rendering all D3D12 content first and then all D2D content, we specify 
        // the In resource state as RENDER_TARGET - because D3D12 will have last 
        // used it in this state - and the Out resource state as PRESENT. When 
        // ReleaseWrappedResources() is called on the 11On12 device, the resource 
        // will be transitioned to the PRESENT state.
        D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET };
        ThrowIfFailed(m_d3d11On12Device->CreateWrappedResource(
            m_renderTargets[n].Get(),
            &d3d11Flags,
            D3D12_RESOURCE_STATE_RENDER_TARGET,
            D3D12_RESOURCE_STATE_PRESENT,
            IID_PPV_ARGS(&m_wrappedBackBuffers[n])
            ));

        // Create a render target for D2D to draw directly to this back buffer.
        ComPtr<IDXGISurface> surface;
        ThrowIfFailed(m_wrappedBackBuffers[n].As(&surface));
        ThrowIfFailed(m_d2dDeviceContext->CreateBitmapFromDxgiSurface(
            surface.Get(),
            &bitmapProperties,
            &m_d2dRenderTargets[n]
            ));

        rtvHandle.Offset(1, m_rtvDescriptorSize);

        ThrowIfFailed(m_d3d12Device->CreateCommandAllocator(
            D3D12_COMMAND_LIST_TYPE_DIRECT,
            IID_PPV_ARGS(&m_commandAllocators[n])));
    }
}
呼び出しフロー パラメーター
GetDpiForWindow ウィンドウ ハンドル
D2D1_BITMAP_PROPERTIES1
BitmapProperties1
[D2D1_BITMAP_OPTIONS](/windows/desktop/api/d2d1_1/ne-d2d1_1-d2d1_bitmap_options)
[PixelFormat](/windows/desktop/api/d2d1helper/nf-d2d1helper-pixelformat)
[DXGI_FORMAT](/windows/desktop/api/dxgiformat/ne-dxgiformat-dxgi_format)
[D2D1_ALPHA_MODE](/windows/desktop/api/dcommon/ne-dcommon-d2d1_alpha_mode)
CD3DX12_CPU_DESCRIPTOR_HANDLE GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
CreateRenderTargetView
D3D11_RESOURCE_FLAGS D3D11_BIND_FLAG
CreateWrappedResource D3D12_RESOURCE_STATES
IDXGISurface
ID2D1DeviceContext::CreateBitmapFromDxgiSurface
CreateCommandAllocator D3D12_COMMAND_LIST_TYPE

 

基本 D2D テキスト オブジェクトの作成

3D コンテンツをレンダリングする ID3D12Device と、ID3D11On12Device を介して 12 デバイスと共有される ID2D1Device (これは、2D コンテンツをレンダリングするために使用できます) を用意し、これらの両方を、同じスワップ チェーンに対してレンダリングするように構成しました。 このサンプルでは、単純に D2D デバイスを使用して 3D シーン上にテキストをレンダリングします。これは、ゲームでその UI をレンダリングする方法と同じです。 このため、引き続き LoadAssets メソッドで、いくつかの基本 D2D オブジェクトを作成する必要があります。

 // Create D2D/DWrite objects for rendering text.
    {
        ThrowIfFailed(m_d2dDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_textBrush));
        ThrowIfFailed(m_dWriteFactory->CreateTextFormat(
            L"Verdana",
            NULL,
            DWRITE_FONT_WEIGHT_NORMAL,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            50,
            L"en-us",
            &m_textFormat
            ));
        ThrowIfFailed(m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
        ThrowIfFailed(m_textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
    }
呼び出しフロー パラメーター
ID2D1RenderTarget::CreateSolidColorBrush ColorF
IDWriteFactory::CreateTextFormat DWRITE_FONT_WEIGHT
IDWriteTextFormat::SetTextAlignment DWRITE_TEXT_ALIGNMENT
IDWriteTextFormat::SetParagraphAlignment DWRITE_PARAGRAPH_ALIGNMENT

 

メイン レンダー ループの更新

サンプルの初期化が完了したので、メイン レンダー ループに移ります。

// Render the scene.
void D3D1211on12::OnRender()
{
    // Record all the commands we need to render the scene into the command list.
    PopulateCommandList();

    // Execute the command list.
    ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
    m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

    RenderUI();

    // Present the frame.
    ThrowIfFailed(m_swapChain->Present(0, 0));

    MoveToNextFrame();
}
呼び出しフロー パラメーター
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

レンダー ループで新しいものは RenderUI の呼び出しのみです。この呼び出しで、D2D を使用して UI をレンダリングします。 最初に D3D12 コマンド リストをすべて実行して 3D シーンをレンダリングし、その上に UI をレンダリングすることに注意してください。 RenderUI を実行する前に、PopulateCommandLists に対する変更を確認する必要があります。 他のサンプルでは、バック バッファーをレンダー ターゲット状態から表示状態に移行するために、コマンド リストを閉じる前にコマンド リストにリソース バリアを通常配置しています。 ただし、このサンプルでは、このリソース バリアは削除しています。これは、引き続き D2D でバック バッファーに対してレンダリングを行う必要があるためです。 バック バッファーのラップされたリソースを作成したときに、レンダー ターゲット状態を "IN" 状態とし、表示状態を "OUT" 状態として指定したことに注意してください。

RenderUI は、D2D の使用の観点から見ると非常に単純です。 ここまででレンダー ターゲットを設定し、テキストをレンダリングしています。 ただし、バック バッファー レンダリング ターゲットなどのラップされたリソースを 11On12 デバイスで使用する前に、11On12 デバイスで AcquireWrappedResources API を呼び出す必要があります。 レンダリング後、11On12 デバイスで ReleaseWrappedResources API を呼び出します。 ReleaseWrappedResources を呼び出すことで、作成時に指定した "OUT" 状態に指定のリソースを移行させるリソース バリアを背後で発生させます。 この例では、これは表示状態です。 最後に、11On12 デバイスで実行されるすべてのコマンドを共有 ID3D12CommandQueue に送信するために、ID3D11DeviceContextFlush を呼び出す必要があります。

// Render text over D3D12 using D2D via the 11On12 device.
void D3D1211on12::RenderUI()
{
    D2D1_SIZE_F rtSize = m_d2dRenderTargets[m_frameIndex]->GetSize();
    D2D1_RECT_F textRect = D2D1::RectF(0, 0, rtSize.width, rtSize.height);
    static const WCHAR text[] = L"11On12";

    // Acquire our wrapped render target resource for the current back buffer.
    m_d3d11On12Device->AcquireWrappedResources(m_wrappedBackBuffers[m_frameIndex].GetAddressOf(), 1);

    // Render text directly to the back buffer.
    m_d2dDeviceContext->SetTarget(m_d2dRenderTargets[m_frameIndex].Get());
    m_d2dDeviceContext->BeginDraw();
    m_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Identity());
    m_d2dDeviceContext->DrawTextW(
        text,
        _countof(text) - 1,
        m_textFormat.Get(),
        &textRect,
        m_textBrush.Get()
        );
    ThrowIfFailed(m_d2dDeviceContext->EndDraw());

    // Release our wrapped render target resource. Releasing 
    // transitions the back buffer resource to the state specified
    // as the OutState when the wrapped resource was created.
    m_d3d11On12Device->ReleaseWrappedResources(m_wrappedBackBuffers[m_frameIndex].GetAddressOf(), 1);

    // Flush to submit the 11 command list to the shared command queue.
    m_d3d11DeviceContext->Flush();
}
呼び出しフロー パラメーター
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Matrix3x2F
ID2D1RenderTarget::DrawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

サンプルを実行する

11 on 12 サンプルの最終出力

D3D12 コードのチュートリアル

Direct3D 11 on 12

Direct3D 12 の相互運用

11on12 リファレンス