Share via


D2D mit D3D11on12

Das D3D1211on12-Beispiel veranschaulicht, wie D2D-Inhalte über D3D12-Inhalte gerendert werden, indem Ressourcen zwischen einem 11-basierten Gerät und einem 12-basierten Gerät freigegeben werden.

Erstellen eines ID3D11On12Device

Der erste Schritt besteht darin, ein ID3D11On12Device zu erstellen, nachdem das ID3D12Device erstellt wurde. Dabei wird ein ID3D11Device erstellt, das über die API D3D11On12CreateDevice um den ID3D12Device umschlossen wird. Diese API akzeptiert neben anderen Parametern auch eine ID3D12CommandQueue , damit das 11On12-Gerät seine Befehle übermitteln kann. Nachdem das ID3D11Device erstellt wurde, können Sie die ID3D11On12Device-Schnittstelle abfragen. Dies ist das primäre Geräteobjekt, das zum Einrichten von D2D verwendet wird.

Richten Sie in der LoadPipeline-Methode die Geräte ein.

 // 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));
Anrufverlauf Parameter
ID3D11Device
D3D11On12CreateDevice

 

Erstellen einer D2D-Factory

Nachdem wir nun über ein 11On12-Gerät verfügen, verwenden wir es, um eine D2D-Factory und ein Gerät zu erstellen, wie es normalerweise mit D3D11 geschehen würde.

Fügen Sie zur LoadAssets-Methode hinzu.

 // 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));
    }
Anrufverlauf Parameter
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

Erstellen eines Renderziels für D2D

D3D12 besitzt die Swapchain. Wenn wir also mithilfe des 11On12-Geräts (D2D-Inhalt) in den Backpuffer rendern möchten, müssen wir umschlossene Ressourcen des Typs ID3D11Resource aus den Backpuffern des Typs ID3D12Resource erstellen. Dadurch wird die ID3D12Resource mit einer D3D11-basierten Schnittstelle verknüpft, sodass sie mit dem 11On12-Gerät verwendet werden kann. Nachdem wir über eine umschlossene Ressource verfügen, können wir eine Renderzieloberfläche erstellen, auf die D2D gerendert werden soll, auch in der LoadAssets-Methode .

// 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])));
    }
}
Anrufverlauf Parameter
GetDpiForWindow Ein Fensterhandle
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

 

Erstellen grundlegender D2D-Textobjekte

Jetzt verfügen wir über ein ID3D12Device zum Rendern von 3D-Inhalten, eine ID2D1Device , die mit unserem 12-Gerät über ein ID3D11On12Device freigegeben wird , das wir zum Rendern von 2D-Inhalten verwenden können, und beide sind für das Rendern in derselben Swapchain konfiguriert. In diesem Beispiel wird einfach das D2D-Gerät verwendet, um Text über die 3D-Szene zu rendern, ähnlich wie Spiele ihre Benutzeroberfläche rendern. Dazu müssen wir einige grundlegende D2D-Objekte erstellen, die sich noch in der LoadAssets-Methode verwenden.

 // 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));
    }
Anrufverlauf Parameter
ID2D1RenderTarget::CreateSolidColorBrush ColorF
IDWriteFactory::CreateTextFormat DWRITE_FONT_WEIGHT
IDWriteTextFormat::SetTextAlignment DWRITE_TEXT_ALIGNMENT
IDWriteTextFormat::SetParagraphAlignment DWRITE_PARAGRAPH_ALIGNMENT

 

Aktualisieren der Standard Renderschleife

Nachdem die Initialisierung des Beispiels abgeschlossen ist, können wir mit der Standard Renderschleife fortfahren.

// 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();
}
Anrufverlauf Parameter
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

Das einzige, was in unserer Renderschleife neu ist, ist der RenderUI-Aufruf , bei dem D2D zum Rendern unserer Benutzeroberfläche verwendet wird. Beachten Sie, dass wir zuerst alle D3D12-Befehlslisten ausführen, um unsere 3D-Szene zu rendern, und dann wird die Benutzeroberfläche darüber gerendert. Bevor wir uns mit RenderUI befassen, müssen wir uns eine Änderung zu PopulateCommandLists ansehen. In anderen Beispielen wird in der Regel eine Ressourcenbarriere in der Befehlsliste eingefügt, bevor sie geschlossen wird, um den Rückpuffer vom Renderzielzustand in den aktuellen Zustand zu überführen. In diesem Beispiel entfernen wir jedoch diese Ressourcenbarriere, da wir weiterhin mit D2D in den Backpuffern rendern müssen. Beachten Sie, dass wir beim Erstellen unserer umschlossenen Ressourcen des Backpuffers den Renderzielzustand als "IN"-Zustand und den aktuellen Zustand als "OUT"-Zustand angegeben haben.

RenderUI ist in Bezug auf die D2D-Nutzung recht einfach. Wir legen unser Renderziel fest und rendern unseren Text. Bevor wir jedoch umschlossene Ressourcen auf einem 11On12-Gerät verwenden, z. B. unsere Backpuffer-Renderziele, müssen wir die AcquireWrappedResources-API auf dem 11On12-Gerät aufrufen. Nach dem Rendern rufen wir die ReleaseWrappedResources-API auf dem 11On12-Gerät auf. Durch das Aufrufen von ReleaseWrappedResources entsteht im Hintergrund eine Ressourcenbarriere, die die angegebene Ressource in den zur Erstellungszeit angegebenen OUT-Zustand übergibt. In unserem Fall ist dies der aktuelle Zustand. Um schließlich alle auf dem 11On12-Gerät ausgeführten Befehle an die freigegebene ID3D12CommandQueue zu übermitteln, müssen wir Flush für den ID3D11DeviceContext aufrufen.

// 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();
}
Anrufverlauf Parameter
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Matrix3x2F
ID2D1RenderTarget::D rawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

Ausführen des Beispiels

die endgültige Ausgabe des 11:12-Beispiels

Exemplarische Vorgehensweisen zu D3D12-Code

Direct3D 11 on 12

Direct3D 12-Interop

11on12-Referenz