D2D à l’aide de D3D11on12

L’exemple D3D1211on12 montre comment restituer du contenu D2D sur du contenu D3D12 en partageant des ressources entre un appareil basé sur 11 et un appareil basé sur 12.

Créer un ID3D11On12Device

La première étape consiste à créer un ID3D11On12Device après la création de l’ID3D12Device , ce qui implique la création d’un ID3D11Device qui est encapsulé autour de l’ID3D12Device via l’API D3D11On12CreateDevice. Cette API prend également, entre autres paramètres, un ID3D12CommandQueue afin que l’appareil 11On12 puisse envoyer ses commandes. Une fois l’ID3D11Device créé, vous pouvez interroger l’interface ID3D11On12Device à partir de celui-ci. Il s’agit de l’objet d’appareil principal qui sera utilisé pour configurer D2D.

Dans la méthode LoadPipeline , configurez les appareils.

 // 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));
Flux des appels Paramètres
ID3D11Device
D3D11On12CreateDevice

 

Créer une fabrique D2D

Maintenant que nous avons un appareil 11On12, nous l’utilisons pour créer une fabrique et un appareil D2D, comme cela se ferait normalement avec D3D11.

Ajoutez à la méthode 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));
    }
Flux des appels Paramètres
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

Créer une cible de rendu pour D2D

D3D12 est propriétaire de la chaîne d’échange. Par conséquent, si nous voulons effectuer un rendu vers la mémoire tampon arrière à l’aide de notre appareil 11On12 (contenu D2D), nous devons créer des ressources encapsulées de type ID3D11Resource à partir des mémoires tampons arrière de type ID3D12Resource. Cela lie l’ID3D12Resource à une interface basée sur D3D11 afin qu’elle puisse être utilisée avec l’appareil 11On12. Une fois que nous avons une ressource encapsulée, nous pouvons créer une surface cible de rendu vers laquelle D2D doit être rendu, également dans la méthode 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])));
    }
}
Flux des appels Paramètres
GetDpiForWindow Un handle de fenêtre
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

 

Créer des objets texte D2D de base

Nous disposons maintenant d’un ID3D12Device pour restituer le contenu 3D, d’un ID2D1Device qui est partagé avec notre appareil 12 via un ID3D11On12Device - que nous pouvons utiliser pour afficher du contenu 2D - et ils sont tous deux configurés pour effectuer le rendu sur la même chaîne d’échange. Cet exemple utilise simplement l’appareil D2D pour afficher du texte sur la scène 3D, comme les jeux affichent leur interface utilisateur. Pour cela, nous devons créer des objets D2D de base, toujours dans la méthode 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));
    }
Flux des appels Paramètres
ID2D1RenderTarget::CreateSolidColorBrush ColorF
IDWriteFactory::CreateTextFormat DWRITE_FONT_WEIGHT
IDWriteTextFormat::SetTextAlignment DWRITE_TEXT_ALIGNMENT
IDWriteTextFormat::SetParagraphAlignment DWRITE_PARAGRAPH_ALIGNMENT

 

Mise à jour de la boucle de rendu main

Maintenant que l’initialisation de l’exemple est terminée, nous pouvons passer à la boucle de rendu main.

// 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();
}
Flux des appels Paramètres
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

La seule nouveauté de notre boucle de rendu est l’appel RenderUI , qui utilise D2D pour restituer notre interface utilisateur. Notez que nous exécutons d’abord toutes nos listes de commandes D3D12 pour afficher notre scène 3D, puis que nous restituons notre interface utilisateur par-dessus. Avant de plonger dans RenderUI, nous devons examiner une modification de PopulateCommandLists. Dans d’autres exemples, nous plaçons généralement une barrière de ressources dans la liste de commandes avant de la fermer pour faire passer la mémoire tampon arrière de l’état cible de rendu à l’état actuel. Toutefois, dans cet exemple, nous supprimons cette barrière de ressources, car nous devons toujours effectuer un rendu dans les mémoires tampons arrière avec D2D. Notez que lorsque nous avons créé nos ressources encapsulées de la mémoire tampon arrière, nous avons spécifié l’état cible de rendu comme état « IN » et l’état actuel comme état « OUT ».

RenderUI est assez simple en termes d’utilisation D2D. Nous définissons notre cible de rendu et nous restituons notre texte. Toutefois, avant d’utiliser des ressources encapsulées sur un appareil 11On12, comme nos cibles de rendu de mémoire tampon arrière, nous devons appeler l’API AcquireWrappedResources sur l’appareil 11On12. Après le rendu, nous appelons l’API ReleaseWrappedResources sur l’appareil 11On12. En appelant ReleaseWrappedResources, nous créons une barrière des ressources en arrière-plan qui fera passer la ressource spécifiée à l’état « OUT » spécifié au moment de la création. Dans notre cas, il s’agit de l’état actuel. Enfin, pour envoyer toutes nos commandes effectuées sur l’appareil 11On12 à l’ID3D12CommandQueue partagé, nous devons appeler Flush sur l’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();
}
Flux des appels Paramètres
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Matrix3x2F
ID2D1RenderTarget::D rawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

Exécution de l'exemple

la sortie finale de l’exemple 11 sur 12

Walk-Throughs de code D3D12

Direct3D 11 sur 12

Interopérabilité Direct3D 12

Référence 11on12