D2D con D3D11on12

En el ejemplo D3D1211on12 se muestra cómo representar contenido D2D a través de contenido D3D12 compartiendo recursos entre un dispositivo basado en 11 y un dispositivo basado en 12.

Crear un ID3D11On12Device

El primer paso consiste en crear un id3D11On12Device después de crear id3D12Device , lo que implica crear un id3D11Device que se encapsula alrededor del id3D12Device a través de la API D3D11On12CreateDevice. Esta API también toma, entre otros parámetros, un ID3D12CommandQueue para que el dispositivo 11On12 pueda enviar sus comandos. Una vez creada la interfaz ID3D11Device , puede consultar la interfaz ID3D11On12Device desde ella. Este es el objeto de dispositivo principal que se usará para configurar D2D.

En el método LoadPipeline , configure los dispositivos.

 // 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));
Flujo de llamadas Parámetros
ID3D11Device
D3D11On12CreateDevice

 

Creación de una factoría D2D

Ahora que tenemos un dispositivo 11On12, lo usamos para crear un generador D2D y un dispositivo igual que lo haría normalmente con D3D11.

Agregue al método 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));
    }
Flujo de llamadas Parámetros
D2D1_DEVICE_CONTEXT_OPTIONS
D2D1CreateFactory D2D1_FACTORY_TYPE
IDXGIDevice
ID2D1Factory3::CreateDevice
ID2D1Device::CreateDeviceContext
DWriteCreateFactory DWRITE_FACTORY_TYPE

 

Creación de un destino de representación para D2D

D3D12 posee la cadena de intercambio, por lo que si queremos representar en el búfer de reserva mediante nuestro dispositivo 11On12 (contenido D2D), es necesario crear recursos ajustados de tipo ID3D11Resource a partir de los búferes de reserva del tipo ID3D12Resource. Esto vincula id3D12Resource con una interfaz basada en D3D11 para que se pueda usar con el dispositivo 11On12. Después de tener un recurso ajustado, podemos crear una superficie de destino de representación para que D2D se represente, también en el método 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])));
    }
}
Flujo de llamadas Parámetros
GetDpiForWindow Un identificador de ventana
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

 

Creación de objetos de texto D2D básicos

Ahora tenemos un ID3D12Device para representar contenido 3D, un ID2D1Device que se comparte con nuestro dispositivo 12 a través de un ID3D11On12Device , que podemos usar para representar contenido 2D, y ambos están configurados para representarse en la misma cadena de intercambio. Este ejemplo simplemente usa el dispositivo D2D para representar texto sobre la escena 3D, similar a cómo los juegos representan su interfaz de usuario. Para ello, es necesario crear algunos objetos D2D básicos, todavía en el método 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));
    }
Flujo de llamadas Parámetros
ID2D1RenderTarget::CreateSolidColorBrush ColorF
IDWriteFactory::CreateTextFormat DWRITE_FONT_WEIGHT
IDWriteTextFormat::SetTextAlignment DWRITE_TEXT_ALIGNMENT
IDWriteTextFormat::SetParagraphAlignment DWRITE_PARAGRAPH_ALIGNMENT

 

Actualización del bucle de representación principal

Ahora que se ha completado la inicialización del ejemplo, podemos pasar al bucle de representación principal.

// 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();
}
Flujo de llamadas Parámetros
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::Present1

 

Lo único nuevo en nuestro bucle de representación es la llamada renderUI , que usará D2D para representar nuestra interfaz de usuario. Observe que ejecutamos primero todas nuestras listas de comandos D3D12 para representar nuestra escena 3D y, a continuación, representamos nuestra interfaz de usuario encima de eso. Antes de profundizar en RenderUI, debemos examinar un cambio en PopulateCommandLists. En otros ejemplos, normalmente colocamos una barrera de recursos en la lista de comandos antes de cerrarla para realizar la transición del búfer de reserva desde el estado de destino de representación al estado actual. Sin embargo, en este ejemplo se quita esa barrera de recursos, ya que todavía es necesario representar en los búferes de reserva con D2D. Tenga en cuenta que cuando creamos los recursos ajustados del búfer de reserva que especificó el estado de destino de representación como el estado "IN" y el estado actual como estado "OUT".

RenderUI es bastante sencillo en términos de uso D2D. Establecemos nuestro destino de representación y representamos nuestro texto. Sin embargo, antes de usar los recursos ajustados en un dispositivo 11On12, como nuestros destinos de representación del búfer de reserva, debemos llamar a la API AcquireWrappedResources en el dispositivo 11On12. Después de representar, llamamos a la API ReleaseWrappedResources en el dispositivo 11On12. Al llamar a ReleaseWrappedResources , incurramos en una barrera de recursos en segundo plano que pasará el recurso especificado al estado "OUT" especificado en el momento de la creación. En nuestro caso, este es el estado actual. Por último, para enviar todos nuestros comandos realizados en el dispositivo 11On12 al id3D12CommandQueue compartido, debemos llamar a Flush en 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();
}
Flujo de llamadas Parámetros
D2D1_SIZE_F
D2D1_RECT_F RectF
AcquireWrappedResources
ID2D1DeviceContext::SetTarget
ID2D1RenderTarget::BeginDraw
ID2D1RenderTarget::SetTransform Matrix3x2F
ID2D1RenderTarget::D rawTextW
ID2D1RenderTarget::EndDraw
ReleaseWrappedResources
ID3D11DeviceContext::Flush

 

Ejecución del ejemplo

la salida final de la muestra 11 en 12

Tutoriales de código D3D12

Direct3D 11 en 12

Interoperabilidad de Direct3D 12

Referencia de 11on12