Поделиться через


D2D с помощью D3D11on12

В примере D3D1211on12 показано, как отображать содержимое D2D поверх содержимого D3D12, совместно используя ресурсы между устройством на базе 11 и устройством на базе 12.

Создайте ID3D11On12Device

Первым шагом является создание ID3D11On12Device после создания ID3D12Device, что включает создание ID3D11Device, обернутого вокруг ID3D12Device через API D3D11On12CreateDevice. Этот 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), необходимо создать упакованные ресурсы типа ID3D11Resource из задних буферов типа ID3D12Resource. Это связывает ID3D12Resource с интерфейсом, основанным на D3D11, так что он может быть использован с устройством 11On12. После того как у нас есть обернутый ресурс, мы можем создать целевую область отрисовки для D2D, а также в методе 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])));
    }
}
Поток вызовов Параметры
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

Теперь у нас есть ID3D12Device для рендеринга 3D-содержимого, ID2D1Device, который разделяется с нашим устройством 12 через ID3D11On12Device - который можно использовать для рендеринга 2D-содержимого, и оба они настроены для рендеринга в одной цепочке буферов. В этом примере просто используется устройство D2D для отрисовки текста по трехмерной сцене, аналогично тому, как игры отрисовывают свой пользовательский интерфейс. Для этого необходимо создать некоторые основные объекты D2D, которые по-прежнему доступны в методе 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));
    }
Поток вызовов Параметры
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 для отрисовки пользовательского интерфейса. Обратите внимание, что сначала мы выполняем все наши списки команд D3D12 для отрисовки трехмерной сцены, а затем мы отрисовываем наш пользовательский интерфейс поверх этого. Прежде чем изучить RenderUI, необходимо рассмотреть изменение в ЗаполнениеCommandLists. В других примерах мы обычно помещаем барьер ресурсов в список команд перед закрытием буфера обратного буфера из целевого состояния отрисовки в текущее состояние. Однако в этом примере мы устраняем этот барьер ресурсов, поскольку нам всё ещё необходимо выполнять отрисовку в задние буферы с помощью D2D. Обратите внимание, что при создании завернутых ресурсов заднего буфера, мы указали состояние целевого объекта отрисовки как состояние "IN", а состояние отображения как состояние "OUT".

RenderUI довольно прост в использовании с точки зрения применения D2D. Мы задали целевой объект отрисовки и отрисовываем наш текст. Однако перед использованием любых упакованных ресурсов для устройства 11On12, например, целей рендеринга обратного буфера, необходимо вызвать API AcquireWrappedResources на устройстве 11On12. После выполнения отрисовки вызовите API ReleaseWrappedResources на устройстве 11On12. Вызывая ReleaseWrappedResources мы вызываем барьер ресурсов за кулисами, который передаст указанный ресурс в состояние OUT, указанное во время создания. В нашем случае это текущее состояние. Наконец, чтобы отправить все наши команды, выполненные на устройстве 11On12, на общий ID3D12CommandQueue, необходимо вызвать Flush на 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();
}
Поток вызовов Параметры
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Matrix3x2F
ID2D1RenderTarget::DrawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

Запустите пример

окончательные выходные данные образца 11 на 12

пошаговые инструкции по коду D3D12

Direct3D 11 на 12

Direct3D 12 Interop

Справочник 11on12