D2D и D3D11on12

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

Создание ID3D11On12Device

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

 

Обновление цикла отрисовки main

Теперь, когда инициализация примера завершена, можно перейти к циклу 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();
}
Поток вызовов Параметры
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::D rawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

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

окончательный результат выборки 11 на 12

Пошаговые руководства по коду D3D12

Direct3D 11 на 12

Взаимодействие Direct3D 12

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