D2D и D3D11on12
В примере D3D1211on12 показано, как отображать содержимое D2D поверх содержимого D3D12 путем совместного использования ресурсов между 11 и 12 устройствами.
- Создание ID3D11On12Device
- Создание фабрики D2D
- Создание целевого объекта отрисовки для D2D
- Создание базовых текстовых объектов D2D
- Обновление цикла отрисовки main
- Запуск примера
- Связанные темы
Создание 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));
}
Создание целевого объекта отрисовки для 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 |
[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));
}
Обновление цикла отрисовки 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();
}
Запуск примера
Связанные темы