Udostępnij za pośrednictwem


D2D z użyciem D3D11on12

W przykładzie D3D1211on12 pokazano, jak renderować zawartość D2D za pośrednictwem zawartości D3D12 przez udostępnianie zasobów między urządzeniem opartym na 11 a urządzeniem opartym na 12.

Tworzenie urządzenia ID3D11On12Device

Pierwszym krokiem jest utworzenie ID3D11On12Device po utworzeniu ID3D12Device, co obejmuje utworzenie ID3D11Device, które jest opakowane wokół ID3D12Device za pośrednictwem interfejsu API D3D11On12CreateDevice. Ten interfejs API przyjmuje również między innymi parametry ID3D12CommandQueue, aby urządzenie 11On12 mogło przesłać polecenia. Po utworzeniu ID3D11Device można pozyskać interfejs ID3D11On12Device. Jest to podstawowy obiekt urządzenia, który będzie używany do konfigurowania D2D.

W metodzie LoadPipeline skonfiguruj urządzenia.

 // 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));
Przepływ wywołań Parametry
ID3D11Urządzenia
D3D11On12CreateDevice

 

Tworzenie fabryki D2D

Teraz, gdy mamy urządzenie 11On12, użyjemy go do utworzenia fabryki D2D i urządzenia tak jak zwykle w przypadku D3D11.

Dodaj do metody 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));
    }
Przepływ wywołań Parametry
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

Tworzenie obiektu docelowego renderowania dla D2D

D3D12 jest właścicielem łańcucha wymiany, więc jeśli chcemy renderować do buforu wstecznego przy użyciu naszego urządzenia typu 11On12 (zawartość D2D), musimy utworzyć zasoby opakowane typu ID3D11Resource z buforów wstecznych typu ID3D12Resource. Łączy to ID3D12Resource z interfejsem opartym na D3D11, aby można było go używać z urządzeniem 11On12. Po utworzeniu opakowanego zasobu możemy również w metodzie LoadAssets utworzyć docelową powierzchnię do renderowania dla 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])));
    }
}
Przepływ wywołań Parametry
GetDpiForWindow Uchwyt okna
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

 

Tworzenie podstawowych obiektów tekstowych D2D

Teraz mamy ID3D12Device do renderowania zawartości 3D, a także ID2D1Device, który jest udostępniany naszemu urządzeniu 12 za pośrednictwem ID3D11On12Device — którego możemy użyć do renderowania zawartości 2D — i oba urządzenia są skonfigurowane do renderowania na tym samym łańcuchu wymiany. W tym przykładzie po prostu użyto urządzenia D2D do renderowania tekstu na scenie 3D, podobnie jak w przypadku renderowania interfejsu użytkownika przez gry. W tym celu musimy utworzyć kilka podstawowych obiektów D2D, nadal w metodzie 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));
    }
Przepływ wywołań Parametry
ID2D1RenderTarget::CreateSolidColorBrush ColorF
IDWriteFactory::CreateTextFormat DWRITE_FONT_WEIGHT
IDWriteTextFormat::SetTextAlignment DWRITE_TEXT_ALIGNMENT
IDWriteTextFormat::SetParagraphAlignment DWRITE_PARAGRAPH_ALIGNMENT

 

Aktualizowanie głównej pętli renderowania

Po zakończeniu inicjowania przykładu możemy przejść do głównej pętli renderowania.

// 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();
}
Schemat przepływu połączeń Parametry
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

Jedyną nowością w naszej pętli renderowania jest wywołanie RenderUI, które będzie używać D2D do renderowania naszego interfejsu użytkownika. Zwróć uwagę, że najpierw wykonamy wszystkie nasze listy poleceń D3D12, aby renderować naszą scenę 3D, a następnie renderujemy nasz interfejs użytkownika. Zanim przejdziemy do RenderUI, musimy najpierw przyjrzeć się zmianie PopulateCommandLists. W innych przykładach często umieszczamy barierę zasobów na liście poleceń przed zamknięciem go w celu przejścia buforu wstecznego ze stanu docelowego renderowania do bieżącego stanu. Jednak w tym przykładzie usuniemy tę barierę zasobów, ponieważ nadal musimy renderować do buforów tylnych za pomocą D2D. Należy pamiętać, że podczas tworzenia opakowanych zasobów buforu wstecznego określono stan docelowy renderowania jako stan "IN" i stan obecny jako stan "OUT".

RenderUI jest stosunkowo prosty pod względem użycia D2D. Ustawiamy cel renderowania i renderujemy nasz tekst. Jednak przed użyciem jakichkolwiek opakowanych zasobów na urządzeniu 11On12, takim jak obiekty docelowe renderowania buforu wstecznego, musimy wywołać AcquireWrappedResources interfejsu API na urządzeniu 11On12. Po renderowaniu się wywołujemy API ReleaseWrappedResources na urządzeniu 11On12. Wywołując ReleaseWrappedResources, powodujemy powstanie bariery zasobów w tle, która dokona przejścia określonego zasobu do stanu "OUT", zdefiniowanego w czasie tworzenia. W naszym przypadku jest to stan obecny. Na końcu, aby przesłać wszystkie nasze polecenia wykonywane na urządzeniu 11On12 do udostępnionego ID3D12CommandQueue, musimy wywołać Flush na 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();
}
Przepływ połączeń Parametry
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Macierz3x2F
ID2D1RenderTarget::DrawTextW
ID2D1RenderTarget::EndDraw
ZwolnijZapakowaneZasoby
ID3D11DeviceContext::Flush

 

Uruchamianie przykładu

końcowe dane wyjściowe próbki 11 na 12

Przeglądy Kodów D3D12

Direct3D 11 na 12

międzyoperacyjność Direct3D 12

11on12 — referencja