D2D à l’aide de D3D11on12
L’exemple D3D1211on12 montre comment restituer du contenu D2D sur du contenu D3D12 en partageant des ressources entre un appareil basé sur 11 et un appareil basé sur 12.
- Créer un ID3D11On12Device
- Créer une fabrique D2D
- Créer une cible de rendu pour D2D
- Créer des objets texte D2D de base
- Mise à jour de la boucle de rendu main
- Exécuter l’exemple
- Rubriques connexes
Créer un ID3D11On12Device
La première étape consiste à créer un ID3D11On12Device après la création de l’ID3D12Device , ce qui implique la création d’un ID3D11Device qui est encapsulé autour de l’ID3D12Device via l’API D3D11On12CreateDevice. Cette API prend également, entre autres paramètres, un ID3D12CommandQueue afin que l’appareil 11On12 puisse envoyer ses commandes. Une fois l’ID3D11Device créé, vous pouvez interroger l’interface ID3D11On12Device à partir de celui-ci. Il s’agit de l’objet d’appareil principal qui sera utilisé pour configurer D2D.
Dans la méthode LoadPipeline , configurez les appareils.
// 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));
Flux des appels | Paramètres |
---|---|
ID3D11Device | |
D3D11On12CreateDevice |
Créer une fabrique D2D
Maintenant que nous avons un appareil 11On12, nous l’utilisons pour créer une fabrique et un appareil D2D, comme cela se ferait normalement avec D3D11.
Ajoutez à la méthode 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));
}
Créer une cible de rendu pour D2D
D3D12 est propriétaire de la chaîne d’échange. Par conséquent, si nous voulons effectuer un rendu vers la mémoire tampon arrière à l’aide de notre appareil 11On12 (contenu D2D), nous devons créer des ressources encapsulées de type ID3D11Resource à partir des mémoires tampons arrière de type ID3D12Resource. Cela lie l’ID3D12Resource à une interface basée sur D3D11 afin qu’elle puisse être utilisée avec l’appareil 11On12. Une fois que nous avons une ressource encapsulée, nous pouvons créer une surface cible de rendu vers laquelle D2D doit être rendu, également dans la méthode 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])));
}
}
Flux des appels | Paramètres |
---|---|
GetDpiForWindow | Un handle de fenêtre |
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 |
Créer des objets texte D2D de base
Nous disposons maintenant d’un ID3D12Device pour restituer le contenu 3D, d’un ID2D1Device qui est partagé avec notre appareil 12 via un ID3D11On12Device - que nous pouvons utiliser pour afficher du contenu 2D - et ils sont tous deux configurés pour effectuer le rendu sur la même chaîne d’échange. Cet exemple utilise simplement l’appareil D2D pour afficher du texte sur la scène 3D, comme les jeux affichent leur interface utilisateur. Pour cela, nous devons créer des objets D2D de base, toujours dans la méthode 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));
}
Mise à jour de la boucle de rendu main
Maintenant que l’initialisation de l’exemple est terminée, nous pouvons passer à la boucle de rendu 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();
}
Flux des appels | Paramètres |
---|---|
ID3D12CommandList | |
ExecuteCommandLists | |
IDXGISwapChain1::Present1 |
La seule nouveauté de notre boucle de rendu est l’appel RenderUI , qui utilise D2D pour restituer notre interface utilisateur. Notez que nous exécutons d’abord toutes nos listes de commandes D3D12 pour afficher notre scène 3D, puis que nous restituons notre interface utilisateur par-dessus. Avant de plonger dans RenderUI, nous devons examiner une modification de PopulateCommandLists. Dans d’autres exemples, nous plaçons généralement une barrière de ressources dans la liste de commandes avant de la fermer pour faire passer la mémoire tampon arrière de l’état cible de rendu à l’état actuel. Toutefois, dans cet exemple, nous supprimons cette barrière de ressources, car nous devons toujours effectuer un rendu dans les mémoires tampons arrière avec D2D. Notez que lorsque nous avons créé nos ressources encapsulées de la mémoire tampon arrière, nous avons spécifié l’état cible de rendu comme état « IN » et l’état actuel comme état « OUT ».
RenderUI est assez simple en termes d’utilisation D2D. Nous définissons notre cible de rendu et nous restituons notre texte. Toutefois, avant d’utiliser des ressources encapsulées sur un appareil 11On12, comme nos cibles de rendu de mémoire tampon arrière, nous devons appeler l’API AcquireWrappedResources sur l’appareil 11On12. Après le rendu, nous appelons l’API ReleaseWrappedResources sur l’appareil 11On12. En appelant ReleaseWrappedResources, nous créons une barrière des ressources en arrière-plan qui fera passer la ressource spécifiée à l’état « OUT » spécifié au moment de la création. Dans notre cas, il s’agit de l’état actuel. Enfin, pour envoyer toutes nos commandes effectuées sur l’appareil 11On12 à l’ID3D12CommandQueue partagé, nous devons appeler Flush sur l’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();
}
Exécution de l'exemple
Rubriques connexes