Panoramica sull'interoperabilità tra Direct2D e Direct3D
La grafica 2D e 3D accelerata hardware diventa sempre più una parte delle applicazioni non di gioco e la maggior parte delle applicazioni di gioco usa grafica 2D sotto forma di menu e Heads-Up Display (HUD). Esiste un sacco di valore che può essere aggiunto abilitando il rendering 2D tradizionale per essere misto con il rendering Direct3D in modo efficiente.
Questo argomento descrive come integrare grafica 2D e 3D usando Direct2D e Direct3D.
Prerequisiti
Questa panoramica presuppone che si abbia familiarità con le operazioni di disegno Direct2D di base. Per un'esercitazione, vedere Creare un'applicazione Direct2D semplice. Presuppone anche che sia possibile programmare usando Direct3D 10.1.
Versioni Direct3D supportate
Con DirectX 11.0, Direct2D supporta l'interoperabilità solo con dispositivi Direct3D 10.1. Con DirectX 11.1 o versione successiva, Direct2D supporta anche l'interoperabilità con Direct3D 11.
Interoperabilità tramite DXGI
A partire da Direct3D 10, il runtime Direct3D usa DXGI per la gestione delle risorse. Il livello di runtime DXGI fornisce la condivisione tra processi delle superfici di memoria video e funge da base per altre piattaforme di runtime basate su memoria video. Direct2D usa DXGI per interoperabilità con Direct3D.
Esistono due modi principali per usare Direct2D e Direct3D insieme:
- È possibile scrivere contenuto Direct2D in una superficie Direct3D ottenendo un IDXGISurface e usandolo con CreateDxgiSurfaceRenderTarget per creare un ID2D1RenderTarget. È quindi possibile usare la destinazione di rendering per aggiungere un'interfaccia bidimensionale o uno sfondo alla grafica tridimensionale oppure usare un disegno Direct2D come trama per un oggetto tridimensionale.
- Usando CreateSharedBitmap per creare un ID2D1Bitmap da un IDXGISurface, è possibile scrivere una scena Direct3D in una bitmap e eseguirne il rendering con Direct2D.
Scrittura in una superficie Direct3D con una destinazione di rendering della superficie DXGI
Per scrivere in una superficie Direct3D, ottenere un IDXGISurface e passarlo al metodo CreateDxgiSurfaceRenderTarget per creare una destinazione di rendering della superficie DXGI. È quindi possibile usare la destinazione di rendering della superficie DXGI per disegnare contenuto 2D nella superficie DXGI.
Una destinazione di rendering della superficie DXGI è un tipo di ID2D1RenderTarget. Come altre destinazioni di rendering Direct2D, è possibile usarla per creare risorse e rilasciare comandi di disegno.
La destinazione di rendering della superficie DXGI e la superficie DXGI devono usare lo stesso formato DXGI. Se si specifica il formato DXGI_FORMAT_UNKOWN quando si crea la destinazione di rendering, verrà usato automaticamente il formato della superficie.
La destinazione di rendering della superficie DXGI non esegue la sincronizzazione della superficie DXGI.
Creazione di una superficie DXGI
Con Direct3D 10, esistono diversi modi per ottenere una superficie DXGI. È possibile creare un IDXGISwapChain per un dispositivo, quindi usare il metodo GetBuffer della catena di scambio per ottenere una superficie DXGI. In alternativa, è possibile usare un dispositivo per creare una trama, quindi usare tale trama come superficie DXGI.
Indipendentemente dalla modalità di creazione della superficie DXGI, la superficie deve usare uno dei formati DXGI supportati dalle destinazioni di rendering della superficie DXGI. Per un elenco, vedere Formati pixel supportati e modalità alfa.
Inoltre, l'ID3D10Device1 associato alla superficie DXGI deve supportare i formati BGRA DXGI per l'uso della superficie con Direct2D. Per garantire questo supporto, usare il flag D3D10_CREATE_DEVICE_BGRA_SUPPORT quando si chiama il metodo D3D10CreateDevice1 per creare il dispositivo.
Il codice seguente definisce un metodo che crea un ID3D10Device1. Seleziona il livello di funzionalità migliore disponibile e torna a Windows Advanced Rasterization Platform (WARP) quando il rendering hardware non è disponibile.
HRESULT DXGISampleApp::CreateD3DDevice(
IDXGIAdapter *pAdapter,
D3D10_DRIVER_TYPE driverType,
UINT flags,
ID3D10Device1 **ppDevice
)
{
HRESULT hr = S_OK;
static const D3D10_FEATURE_LEVEL1 levelAttempts[] =
{
D3D10_FEATURE_LEVEL_10_0,
D3D10_FEATURE_LEVEL_9_3,
D3D10_FEATURE_LEVEL_9_2,
D3D10_FEATURE_LEVEL_9_1,
};
for (UINT level = 0; level < ARRAYSIZE(levelAttempts); level++)
{
ID3D10Device1 *pDevice = NULL;
hr = D3D10CreateDevice1(
pAdapter,
driverType,
NULL,
flags,
levelAttempts[level],
D3D10_1_SDK_VERSION,
&pDevice
);
if (SUCCEEDED(hr))
{
// transfer reference
*ppDevice = pDevice;
pDevice = NULL;
break;
}
}
return hr;
}
L'esempio di codice successivo usa il metodo CreateD3DDevice illustrato nell'esempio precedente per creare un dispositivo Direct3D che può creare superfici DXGI da usare con Direct2D.
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
Scrittura di contenuto Direct2D in un buffer della catena di scambio
Il modo più semplice per aggiungere contenuto Direct2D a una scena Direct3D consiste nell'usare il metodo GetBuffer di un IDXGISwapChain per ottenere una superficie DXGI, quindi usare la superficie con il metodo CreateDxgiSurfaceRenderTarget per creare un ID2D1RenderTarget con cui disegnare il contenuto 2D.
Questo approccio non esegue il rendering del contenuto in tre dimensioni; non avrà una prospettiva o una profondità. Tuttavia, è utile per diverse attività comuni:
- Creazione di uno sfondo 2D per una scena 3D.
- Creazione di un'interfaccia 2D davanti a una scena 3D.
- Uso di Direct3D multisampling durante il rendering del contenuto Direct2D.
La sezione successiva illustra come creare uno sfondo 2D per una scena 3D.
Esempio: Disegnare uno sfondo 2D
I passaggi seguenti descrivono come creare una destinazione di rendering della superficie DXGI e usarla per disegnare uno sfondo sfumatura.
Usare il metodo CreateSwapChain per creare una catena di scambio per un ID3D10Device1 (variabile m_pDevice ). La catena di scambio usa il formato DXGI DXGI_FORMAT_B8G8R8A8_UNORM , uno dei formati DXGI supportati da Direct2D.
if (SUCCEEDED(hr)) { hr = pDevice->QueryInterface(&m_pDevice); } if (SUCCEEDED(hr)) { hr = pDevice->QueryInterface(&pDXGIDevice); } if (SUCCEEDED(hr)) { hr = pDXGIDevice->GetAdapter(&pAdapter); } if (SUCCEEDED(hr)) { hr = pAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory)); } if (SUCCEEDED(hr)) { DXGI_SWAP_CHAIN_DESC swapDesc; ::ZeroMemory(&swapDesc, sizeof(swapDesc)); swapDesc.BufferDesc.Width = nWidth; swapDesc.BufferDesc.Height = nHeight; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Quality = 0; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; swapDesc.OutputWindow = m_hwnd; swapDesc.Windowed = TRUE; hr = pDXGIFactory->CreateSwapChain(m_pDevice, &swapDesc, &m_pSwapChain); }
Usare il metodo GetBuffer della catena di scambio per ottenere una superficie DXGI.
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );
Usare la superficie DXGI per creare una destinazione di rendering DXGI.
// Initialize *hwnd* with the handle of the window displaying the rendered content. HWND hwnd; // Create the DXGI Surface Render Target. float dpi = GetDpiForWindow(hwnd); D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY); // Create a Direct2D render target that can draw into the surface in the swap chain hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pBackBuffer, &props, &m_pBackBufferRT);
Usare la destinazione di rendering per disegnare uno sfondo sfumatura.
// Swap chain will tell us how big the back buffer is DXGI_SWAP_CHAIN_DESC swapDesc; hr = m_pSwapChain->GetDesc(&swapDesc); if (SUCCEEDED(hr)) { // Draw a gradient background. if (m_pBackBufferRT) { D2D1_SIZE_F targetSize = m_pBackBufferRT->GetSize(); m_pBackBufferRT->BeginDraw(); m_pBackBufferGradientBrush->SetTransform( D2D1::Matrix3x2F::Scale(targetSize) ); D2D1_RECT_F rect = D2D1::RectF( 0.0f, 0.0f, targetSize.width, targetSize.height ); m_pBackBufferRT->FillRectangle(&rect, m_pBackBufferGradientBrush); hr = m_pBackBufferRT->EndDraw(); } ...
Il codice viene omesso da questo esempio.
Uso del contenuto Direct2D come trama
Un altro modo per usare il contenuto Direct2D con Direct3D consiste nell'usare Direct2D per generare una trama 2D e quindi applicare tale trama a un modello 3D. A tale scopo, creare un ID3D10Texture2D, ottenere una superficie DXGI dalla trama e quindi usare la superficie per creare una destinazione di rendering della superficie DXGI. La superficie ID3D10Texture2D deve usare il flag di associazione D3D10_BIND_RENDER_TARGET e usare un formato DXGI supportato dalle destinazioni di rendering della superficie DXGI. Per un elenco dei formati DXGI supportati, vedere Formati pixel supportati e modalità alfa.
Esempio: Usare il contenuto Direct2D come trama
Gli esempi seguenti illustrano come creare una destinazione di rendering della superficie DXGI che esegue il rendering in una trama 2D (rappresentata da un ID3D10Texture2D).
Usare prima di tutto un dispositivo Direct3D per creare una trama 2D. La trama usa i flag di associazione D3D10_BIND_RENDER_TARGET e D3D10_BIND_SHADER_RESOURCE e usa il formato DXGI DXGI_FORMAT_B8G8R8A8_UNORM, uno dei formati DXGI supportati da Direct2D.
// Allocate an offscreen D3D surface for D2D to render our 2D content into D3D10_TEXTURE2D_DESC texDesc; texDesc.ArraySize = 1; texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; texDesc.CPUAccessFlags = 0; texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; texDesc.Height = 512; texDesc.Width = 512; texDesc.MipLevels = 1; texDesc.MiscFlags = 0; texDesc.SampleDesc.Count = 1; texDesc.SampleDesc.Quality = 0; texDesc.Usage = D3D10_USAGE_DEFAULT; hr = m_pDevice->CreateTexture2D(&texDesc, NULL, &m_pOffscreenTexture);
Usare la trama per ottenere una superficie DXGI.
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
Utilizzare la superficie con il metodo CreateDxgiSurfaceRenderTarget per ottenere una destinazione di rendering Direct2D.
if (SUCCEEDED(hr)) { // Create a D2D render target that can draw into our offscreen D3D // surface. Given that we use a constant size for the texture, we // fix the DPI at 96. D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 96, 96); hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pDxgiSurface, &props, &m_pRenderTarget); }
Dopo aver ottenuto una destinazione di rendering Direct2D e associata a una trama Direct3D, è possibile usare la destinazione di rendering per disegnare contenuto Direct2D a tale trama e applicare tale trama alle primitive Direct3D.
Il codice viene omesso da questo esempio.
Ridimensionamento di una destinazione di rendering della superficie DXGI
Le destinazioni di rendering della superficie DXGI non supportano il metodo ID2D1RenderTarget::Resize . Per ridimensionare una destinazione di rendering della superficie DXGI, l'applicazione deve rilasciarla e ricrearla.
Questa operazione può potenzialmente creare problemi di prestazioni. La destinazione di rendering potrebbe essere l'ultima risorsa Direct2D attiva che mantiene un riferimento alla superficie DXGI di ID3D10Device1 associata alla superficie DXGI della destinazione di rendering. Se l'applicazione rilascia la destinazione di rendering e il riferimento ID3D10Device1 viene eliminato, è necessario ricreare un nuovo oggetto.
È possibile evitare questa operazione potenzialmente costosa mantenendo almeno una risorsa Direct2D creata dalla destinazione di rendering durante la ricreazione della destinazione di rendering. Di seguito sono riportate alcune risorse Direct2D che funzionano per questo approccio:
- ID2D1Bitmap (che può essere tenuto indirettamente da un ID2D1BitmapBrush)
- ID2D1Layer
- ID2D1Mesh
Per soddisfare questo approccio, il metodo di ridimensionamento deve verificare se il dispositivo Direct3D è disponibile. Se è disponibile, rilasciare e ricreare le destinazioni di rendering della superficie DXGI, ma mantenere tutte le risorse create in precedenza e riutilizzarle. Ciò funziona perché, come descritto in Panoramica delle risorse, le risorse create da due destinazioni di rendering sono compatibili quando entrambe le destinazioni di rendering sono associate allo stesso dispositivo Direct3D.