D2D con D3D11on12

L'esempio D3D1211on12 illustra come eseguire il rendering del contenuto D2D su contenuto D3D12 condividendo le risorse tra un dispositivo basato su 11 e un dispositivo basato su 12.

Creare un dispositivo ID3D11On12Device

Il primo passaggio consiste nel creare un oggetto ID3D11On12Device dopo la creazione di ID3D12Device, che comporta la creazione di un oggetto ID3D11Device di cui è stato eseguito il wrapping intorno all'ID3D12Device tramite l'API D3D11On12CreateDevice. Questa API accetta anche, tra gli altri parametri, un ID3D12CommandQueue in modo che il dispositivo 11On12 possa inviare i comandi. Dopo aver creato id3D11Device , è possibile eseguire una query sull'interfaccia ID3D11On12Device . Si tratta dell'oggetto dispositivo primario che verrà usato per configurare D2D.

Nel metodo LoadPipeline configurare i dispositivi.

 // 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));
Flusso di chiamata Parametri
ID3D11Device
D3D11On12CreateDevice

 

Creare una factory D2D

Ora che è disponibile un dispositivo 11On12, viene usato per creare una factory D2D e un dispositivo esattamente come avvierebbe normalmente con D3D11.

Aggiungere al metodo 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));
    }
Flusso di chiamata Parametri
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

Creare una destinazione di rendering per D2D

D3D12 è proprietario della catena di scambio, quindi se si vuole eseguire il rendering nel buffer nascosto usando il dispositivo 11On12 (contenuto D2D), è necessario creare risorse di tipo ID3D11Resource dai buffer back di tipo ID3D12Resource. Questo collega ID3D12Resource con un'interfaccia basata su D3D11 in modo che possa essere usata con il dispositivo 11On12. Dopo aver eseguito il wrapping di una risorsa, è possibile creare una superficie di destinazione di rendering per D2D in cui eseguire il rendering, anche nel metodo LoadAssets .

// 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])));
    }
}
Flusso di chiamata Parametri
GetDpiForWindow Handle di finestra
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

 

Creare oggetti di testo D2D di base

Ora abbiamo un ID3D12Device per il rendering del contenuto 3D, un ID2D1Device condiviso con il dispositivo 12 tramite un dispositivo ID3D11On12Device , che possiamo usare per eseguire il rendering del contenuto 2D, e sono entrambi configurati per il rendering nella stessa catena di scambio. Questo esempio usa semplicemente il dispositivo D2D per eseguire il rendering del testo sulla scena 3D, in modo analogo al rendering dell'interfaccia utente dei giochi. A tale scopo, è necessario creare alcuni oggetti D2D di base, ancora nel metodo LoadAssets .

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

 

Aggiornamento del ciclo di rendering principale

Ora che l'inizializzazione dell'esempio è stata completata, è possibile passare al ciclo di rendering principale.

// 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();
}
Flusso di chiamata Parametri
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

L'unica novità del ciclo di rendering è la chiamata RenderUI , che userà D2D per eseguire il rendering dell'interfaccia utente. Si noti che tutti gli elenchi di comandi D3D12 vengono eseguiti prima per eseguire il rendering della scena 3D e quindi si esegue il rendering dell'interfaccia utente. Prima di approfondire RenderUI, è necessario esaminare una modifica a PopulateCommandLists. In altri esempi viene in genere inserita una barriera di risorse nell'elenco di comandi prima di chiuderla per eseguire la transizione del buffer indietro dallo stato di destinazione di rendering allo stato corrente. Tuttavia, in questo esempio viene rimossa la barriera di risorse, perché è comunque necessario eseguire il rendering nei buffer nascosto con D2D. Si noti che quando sono state create le risorse di cui è stato eseguito il wrapping del buffer nascosto è stato specificato lo stato di destinazione di rendering come stato "IN" e lo stato attuale come stato "OUT".

RenderUI è piuttosto semplice in termini di utilizzo D2D. Impostiamo la destinazione di rendering ed eseguiamo il rendering del testo. Tuttavia, prima di usare qualsiasi risorsa di cui è stato eseguito il wrapping in un dispositivo 11On12, ad esempio le destinazioni di rendering del buffer nascosto, è necessario chiamare l'API AcquireWrappedResources nel dispositivo 11On12. Dopo il rendering viene chiamata l'API ReleaseWrappedResources nel dispositivo 11On12. Chiamando ReleaseWrappedResources si crea una barriera di risorse dietro le quinte che eseguirà la transizione della risorsa specificata allo stato "OUT" specificato al momento della creazione. In questo caso, questo è lo stato attuale. Infine, per inviare tutti i comandi eseguiti sul dispositivo 11On12 all'ID3D12CommandQueue condiviso, è necessario chiamare Flush in ID3D11DeviceContext.

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

 

Eseguire l'esempio

l'output finale del campione 11 su 12

Procedure dettagliate del codice D3D12

D3D11On12

Interoperabilità direct3D 12

Informazioni di riferimento su 11on12