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 11 et un appareil 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 principale
- Exécution de l'exemple
- Rubriques connexes
Créer un ID3D11On12Device
La première étape consiste à créer un ID3D11On12Device après la création du ID3D12Device , ce qui implique la création d’un ID3D11Device encapsulé autour du ID3D12Device via l’API D3D11On12CreateDevice. Cette API prend également, entre autres paramètres, un ID3D12CommandQueue afin que l’appareil 11On12 puisse soumettre ses commandes. Une fois le ID3D11Device créé, vous pouvez interroger l’interface ID3D11On12Device à partir de celui-ci. Il s’agit de l’objet périphérique 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));
| Workflow d’appel | Paramètres |
|---|---|
| ID3D11Device | |
| D3D11On12CreateDevice |
Créer une fabrique D2D
Maintenant que nous disposons d’un appareil 11On12, nous l’utilisons pour créer une fabrique et un appareil D2D de la même manière qu’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 de permutation. par conséquent, si vous souhaitez effectuer un rendu sur la mémoire tampon d’arrière-plan à l’aide de notre appareil 11On12 (contenu D2D), nous devons créer des ressources encapsulées de type ID3D11Resource à partir des mémoires tampons d’arrière-plan de type ID3D12Resource. Cela permet de lier le ID3D12Resource avec une interface basée sur d3d11 afin qu’il puisse être utilisé avec l’appareil 11On12. Une fois que nous avons une ressource encapsulée, nous pouvons créer une surface cible de rendu pour le rendu D2D, également dans la méthode LoadAssets .
// Query the desktop's dpi settings, which will be used to create
// D2D's render targets.
float dpiX;
float dpiY;
m_d2dFactory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpiX,
dpiY
);
// 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])));
}
}
| Workflow d’appel | Paramètres |
|---|---|
| ID2D1Factory::GetDesktopDpi | |
| 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 avons maintenant un ID3D12Device pour afficher le contenu 3D, un ID2D1Device partagé avec notre appareil via un ID3D11On12Device , que nous pouvons utiliser pour afficher le contenu 2D, et ils sont tous deux configurés pour être rendus dans la même chaîne de permutation. Cet exemple utilise simplement l’appareil D2D pour restituer du texte sur la scène 3D, de la même façon que 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 principale
Maintenant que l’initialisation de l’exemple est terminée, nous pouvons passer à la boucle de rendu principale.
// 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();
}
| Workflow d’appel | Paramètres |
|---|---|
| ID3D12CommandList | |
| ExecuteCommandLists | |
| IDXGISwapChain1::Present1 |
La seule chose à faire pour notre boucle de rendu est l’appel RenderUI , qui utilisera D2D pour afficher l’interface utilisateur. Notez que nous exécutons tout d’abord toutes les listes de commandes D3D12 pour afficher notre scène 3D, puis nous rendons notre interface utilisateur au-dessus. Avant de plonger dans RenderUI, nous devons examiner une modification apportée à PopulateCommandLists. Dans d’autres exemples, nous plaçons généralement une barrière de ressources sur la liste de commandes avant de la fermer pour faire passer la mémoire tampon d’arrière-plan de l’état de la cible de rendu à l’état actuel. Toutefois, dans cet exemple, nous supprimons ce cloisonnement de ressources, car nous devons toujours afficher les mémoires tampons d’arrière-plan avec D2D. Notez que lorsque nous avons créé nos ressources encapsulées de la mémoire tampon d’arrière-plan, nous avons spécifié l’état de la cible du rendu comme « IN » et l’état actuel comme « OUT ».
RenderUI est relativement simple en termes d’utilisation D2D. Nous définissons la cible de rendu et nous rendons notre texte. Toutefois, avant d’utiliser des ressources encapsulées sur un appareil 11On12, telles que les cibles de rendu de la mémoire tampon d’arrière-plan, 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 envoyons une barrière de 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 les commandes effectuées sur l’appareil 11On12 au ID3D12CommandQueuepartagé, vous devez appeler flush sur le 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
