O D2D usando o D3D11on12
O exemplo D3D1211on12 demonstra como renderizar conteúdo D2D por conteúdo D3D12 compartilhando recursos entre um dispositivo baseado em 11 e um dispositivo baseado em 12.
- Criar um ID3D11On12Device
- Criar uma fábrica D2D
- Criar um destino de renderização para D2D
- Criar objetos de texto D2D básicos
- Atualizando o loop de renderização main
- Execute o exemplo
- Tópicos relacionados
Criar um ID3D11On12Device
A primeira etapa é criar um ID3D11On12Device após a criação do ID3D12Device , que envolve a criação de um ID3D11Device encapsulado em torno de ID3D12Device por meio da API D3D11On12CreateDevice. Essa API também recebe, entre outros parâmetros, um ID3D12CommandQueue para que o dispositivo 11On12 possa enviar seus comandos. Depois que o ID3D11Device for criado, você poderá consultar a interface ID3D11On12Device a partir dele. Esse é o objeto de dispositivo primário que será usado para configurar o D2D.
No método LoadPipeline , configure os 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));
Fluxo de chamada | Parâmetros |
---|---|
ID3D11Device | |
D3D11On12CreateDevice |
Criar uma fábrica D2D
Agora que temos um dispositivo 11On12, usamos-o para criar uma fábrica e um dispositivo D2D, assim como normalmente seria feito com D3D11.
Adicione ao 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));
}
Criar um destino de renderização para D2D
O D3D12 é proprietário da cadeia de troca, portanto, se quisermos renderizar para o buffer de fundo usando nosso dispositivo 11On12 (conteúdo D2D), precisamos criar recursos encapsulados do tipo ID3D11Resource dos buffers de fundo do tipo ID3D12Resource. Isso vincula o ID3D12Resource a uma interface baseada em D3D11 para que ele possa ser usado com o dispositivo 11On12. Depois de termos um recurso encapsulado, podemos criar uma superfície de destino de renderização para D2D renderizar, também no 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])));
}
}
Fluxo de chamada | Parâmetros |
---|---|
GetDpiForWindow | Um identificador de janela |
D2D1_BITMAP_PROPERTIES1 |
[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 |
Criar objetos de texto D2D básicos
Agora temos um ID3D12Device para renderizar conteúdo 3D, um ID2D1Device que é compartilhado com nosso dispositivo 12 por meio de um ID3D11On12Device - que podemos usar para renderizar conteúdo 2D - e ambos estão configurados para renderizar para a mesma cadeia de troca. Este exemplo simplesmente usa o dispositivo D2D para renderizar texto na cena 3D, semelhante à maneira como os jogos renderizam sua interface do usuário. Para isso, precisamos criar alguns objetos D2D básicos, ainda no 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));
}
Atualizando o loop de renderização main
Agora que a inicialização do exemplo foi concluída, podemos passar para o loop de renderização 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();
}
Fluxo de chamada | Parâmetros |
---|---|
ID3D12CommandList | |
ExecuteCommandLists | |
IDXGISwapChain1::Present1 |
A única novidade do loop de renderização é a chamada RenderUI , que usará D2D para renderizar nossa interface do usuário. Observe que executamos todas as nossas listas de comandos D3D12 primeiro para renderizar nossa cena 3D e, em seguida, renderizamos nossa interface do usuário sobre isso. Antes de nos aprofundarmos no RenderUI, devemos examinar uma alteração em PopulateCommandLists. Em outros exemplos, normalmente colocamos uma barreira de recursos na lista de comandos antes de fechá-la para fazer a transição do buffer de fundo do estado de destino de renderização para o estado atual. No entanto, neste exemplo, removemos essa barreira de recursos, pois ainda precisamos renderizar para os buffers de fundo com D2D. Observe que, quando criamos nossos recursos encapsulados do buffer de fundo, especificamos o estado de destino de renderização como o estado "IN" e o estado atual como o estado "OUT".
RenderUI é bastante simples em termos de uso de D2D. Definimos nosso destino de renderização e renderizamos nosso texto. No entanto, antes de usar qualquer recurso encapsulado em um dispositivo 11On12, como nossos destinos de renderização de buffer traseiro, devemos chamar a API AcquireWrappedResources no dispositivo 11On12. Após a renderização, chamamos a API ReleaseWrappedResources no dispositivo 11On12. Chamando ReleaseWrappedResources , incorremos em uma barreira de recursos nos bastidores que fará a transição do recurso especificado para o estado "OUT" especificado no momento da criação. Em nosso caso, esse é o estado atual. Por fim, para enviar todos os nossos comandos executados no dispositivo 11On12 para a ID3D12CommandQueue compartilhada, devemos chamar Flush no 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();
}
Execute o exemplo
Tópicos relacionados