D2D z użyciem D3D11on12
W przykładzie D3D1211on12 pokazano, jak renderować zawartość D2D za pośrednictwem zawartości D3D12 przez udostępnianie zasobów między urządzeniem opartym na 11 a urządzeniem opartym na 12.
- Utwórz ID3D11On12Device
- Utwórz fabrykę D2D
- Utwórz element docelowy renderowania dla D2D
- Tworzenie podstawowych obiektów tekstowych D2D
- aktualizowanie głównej pętli renderowania
- Uruchom próbkę
- Tematy pokrewne
Tworzenie urządzenia ID3D11On12Device
Pierwszym krokiem jest utworzenie ID3D11On12Device po utworzeniu ID3D12Device, co obejmuje utworzenie ID3D11Device, które jest opakowane wokół ID3D12Device za pośrednictwem interfejsu API D3D11On12CreateDevice. Ten interfejs API przyjmuje również między innymi parametry ID3D12CommandQueue, aby urządzenie 11On12 mogło przesłać polecenia. Po utworzeniu ID3D11Device można pozyskać interfejs ID3D11On12Device. Jest to podstawowy obiekt urządzenia, który będzie używany do konfigurowania D2D.
W metodzie LoadPipeline skonfiguruj urządzenia.
// 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));
Przepływ wywołań | Parametry |
---|---|
ID3D11Urządzenia | |
D3D11On12CreateDevice |
Tworzenie fabryki D2D
Teraz, gdy mamy urządzenie 11On12, użyjemy go do utworzenia fabryki D2D i urządzenia tak jak zwykle w przypadku D3D11.
Dodaj do metody 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));
}
Tworzenie obiektu docelowego renderowania dla D2D
D3D12 jest właścicielem łańcucha wymiany, więc jeśli chcemy renderować do buforu wstecznego przy użyciu naszego urządzenia typu 11On12 (zawartość D2D), musimy utworzyć zasoby opakowane typu ID3D11Resource z buforów wstecznych typu ID3D12Resource. Łączy to ID3D12Resource z interfejsem opartym na D3D11, aby można było go używać z urządzeniem 11On12. Po utworzeniu opakowanego zasobu możemy również w metodzie LoadAssets utworzyć docelową powierzchnię do renderowania dla D2D.
// 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])));
}
}
Przepływ wywołań | Parametry |
---|---|
GetDpiForWindow | Uchwyt okna |
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 |
Tworzenie podstawowych obiektów tekstowych D2D
Teraz mamy ID3D12Device do renderowania zawartości 3D, a także ID2D1Device, który jest udostępniany naszemu urządzeniu 12 za pośrednictwem ID3D11On12Device — którego możemy użyć do renderowania zawartości 2D — i oba urządzenia są skonfigurowane do renderowania na tym samym łańcuchu wymiany. W tym przykładzie po prostu użyto urządzenia D2D do renderowania tekstu na scenie 3D, podobnie jak w przypadku renderowania interfejsu użytkownika przez gry. W tym celu musimy utworzyć kilka podstawowych obiektów D2D, nadal w metodzie 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));
}
Aktualizowanie głównej pętli renderowania
Po zakończeniu inicjowania przykładu możemy przejść do głównej pętli renderowania.
// 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();
}
Schemat przepływu połączeń | Parametry |
---|---|
ID3D12CommandList | |
ExecuteCommandLists | |
IDXGISwapChain1::Present1 |
Jedyną nowością w naszej pętli renderowania jest wywołanie RenderUI, które będzie używać D2D do renderowania naszego interfejsu użytkownika. Zwróć uwagę, że najpierw wykonamy wszystkie nasze listy poleceń D3D12, aby renderować naszą scenę 3D, a następnie renderujemy nasz interfejs użytkownika. Zanim przejdziemy do RenderUI, musimy najpierw przyjrzeć się zmianie PopulateCommandLists. W innych przykładach często umieszczamy barierę zasobów na liście poleceń przed zamknięciem go w celu przejścia buforu wstecznego ze stanu docelowego renderowania do bieżącego stanu. Jednak w tym przykładzie usuniemy tę barierę zasobów, ponieważ nadal musimy renderować do buforów tylnych za pomocą D2D. Należy pamiętać, że podczas tworzenia opakowanych zasobów buforu wstecznego określono stan docelowy renderowania jako stan "IN" i stan obecny jako stan "OUT".
RenderUI jest stosunkowo prosty pod względem użycia D2D. Ustawiamy cel renderowania i renderujemy nasz tekst. Jednak przed użyciem jakichkolwiek opakowanych zasobów na urządzeniu 11On12, takim jak obiekty docelowe renderowania buforu wstecznego, musimy wywołać AcquireWrappedResources interfejsu API na urządzeniu 11On12. Po renderowaniu się wywołujemy API ReleaseWrappedResources na urządzeniu 11On12. Wywołując ReleaseWrappedResources, powodujemy powstanie bariery zasobów w tle, która dokona przejścia określonego zasobu do stanu "OUT", zdefiniowanego w czasie tworzenia. W naszym przypadku jest to stan obecny. Na końcu, aby przesłać wszystkie nasze polecenia wykonywane na urządzeniu 11On12 do udostępnionego ID3D12CommandQueue, musimy wywołać Flush na 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();
}
Uruchamianie przykładu
Tematy pokrewne