Übersicht über die Interoperabilität von Direct2D und Direct3D
Hardwarebeschleunigte 2D- und 3D-Grafiken werden zunehmend Teil von Nicht-Gaming-Anwendungen, und die meisten Gaming-Anwendungen verwenden 2D-Grafiken in Form von Menüs und Heads-Up Displays (HUDs). Es gibt viele Mehrwerte, die hinzugefügt werden können, indem herkömmliches 2D-Rendering auf effiziente Weise mit Direct3D-Rendering gemischt werden kann.
In diesem Thema wird beschrieben, wie Sie 2D- und 3D-Grafiken mithilfe von Direct2D und Direct3D integrieren.
Voraussetzungen
In dieser Übersicht wird vorausgesetzt, dass Sie mit den grundlegenden Direct2D-Zeichnungsvorgängen vertraut sind. Ein Tutorial finden Sie unter Erstellen einer einfachen Direct2D-Anwendung. Außerdem wird davon ausgegangen, dass Sie mit Direct3D 10.1 programmieren können.
Unterstützte Direct3D-Versionen
Mit DirectX 11.0 unterstützt Direct2D nur die Interoperabilität mit Direct3D 10.1-Geräten. Mit DirectX 11.1 oder höher unterstützt Direct2D auch die Interoperabilität mit Direct3D 11.
Interoperabilität über DXGI
Ab Direct3D 10 verwendet die Direct3D-Runtime DXGI für die Ressourcenverwaltung. Die DXGI-Laufzeitebene ermöglicht die prozessübergreifende Freigabe von Videospeicheroberflächen und dient als Grundlage für andere videospeicherbasierte Laufzeitplattformen. Direct2D verwendet DXGI, um mit Direct3D zu arbeiten.
Es gibt zwei primäre Möglichkeiten, Direct2D und Direct3D zusammen zu verwenden:
- Sie können Direct2D-Inhalte auf eine Direct3D-Oberfläche schreiben, indem Sie eine IDXGISurface abrufen und diese mit createDxgiSurfaceRenderTarget verwenden, um ein ID2D1RenderTarget zu erstellen. Anschließend können Sie das Renderziel verwenden, um dreidimensionalen Grafiken eine zweidimensionale Schnittstelle oder einen Hintergrund hinzuzufügen, oder eine Direct2D-Zeichnung als Textur für ein dreidimensionales Objekt verwenden.
- Wenn Sie CreateSharedBitmap verwenden, um eine ID2D1Bitmap aus einem IDXGISurface zu erstellen, können Sie eine Direct3D-Szene in eine Bitmap schreiben und mit Direct2D rendern.
Schreiben auf eine Direct3D-Oberfläche mit einem DXGI-Oberflächenrenderziel
Um auf eine Direct3D-Oberfläche zu schreiben, rufen Sie eine IDXGISurface ab und übergeben sie an die CreateDxgiSurfaceRenderTarget-Methode , um ein DXGI-Oberflächenrenderingziel zu erstellen. Anschließend können Sie das DXGI-Oberflächenrenderziel verwenden, um 2D-Inhalte auf die DXGI-Oberfläche zu zeichnen.
Ein DXGI-Oberflächenrenderziel ist eine Art ID2D1RenderTarget. Wie bei anderen Direct2D-Renderzielen können Sie es verwenden, um Ressourcen zu erstellen und Zeichenbefehle auszugeben.
Das DXGI-Oberflächenrenderziel und die DXGI-Oberfläche müssen das gleiche DXGI-Format verwenden. Wenn Sie beim Erstellen des Renderziels das DXGI_FORMAT_UNKOWN Format angeben, wird automatisch das Format der Oberfläche verwendet.
Das DXGI-Oberflächenrenderziel führt keine DXGI-Oberflächensynchronisierung aus.
Erstellen einer DXGI-Oberfläche
Mit Direct3D 10 gibt es mehrere Möglichkeiten, eine DXGI-Oberfläche zu erhalten. Sie können eine IDXGISwapChain für ein Gerät erstellen und dann die GetBuffer-Methode der Swap chain verwenden, um eine DXGI-Oberfläche abzurufen. Alternativ können Sie ein Gerät verwenden, um eine Textur zu erstellen und diese Textur dann als DXGI-Oberfläche zu verwenden.
Unabhängig davon, wie Sie die DXGI-Oberfläche erstellen, muss die Oberfläche eines der DXGI-Formate verwenden, die von DXGI-Oberflächenrenderzielen unterstützt werden. Eine Liste finden Sie unter Unterstützte Pixelformate und Alphamodi.
Darüber hinaus muss der der DXGI-Oberfläche zugeordnete ID3D10Device1 BGRA DXGI-Formate unterstützen, damit die Oberfläche mit Direct2D funktioniert. Um diese Unterstützung sicherzustellen, verwenden Sie das D3D10_CREATE_DEVICE_BGRA_SUPPORT-Flag , wenn Sie die D3D10CreateDevice1-Methode aufrufen, um das Gerät zu erstellen.
Der folgende Code definiert eine Methode, die eine ID3D10Device1 erstellt. Es wählt die beste verfügbare Featureebene aus und greift auf Windows Advanced Rasterization Platform (WARP) zurück, wenn kein Hardwarerendering verfügbar ist.
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;
}
Im nächsten Codebeispiel wird die CreateD3DDevice-Methode verwendet, die im vorherigen Beispiel gezeigt wurde, um ein Direct3D-Gerät zu erstellen, das DXGI-Oberflächen für die Verwendung mit Direct2D erstellen kann.
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
Schreiben von Direct2D-Inhalten in einen Swap chain-Puffer
Die einfachste Möglichkeit zum Hinzufügen von Direct2D-Inhalten zu einer Direct3D-Szene besteht darin, mithilfe der GetBuffer-Methode eines IDXGISwapChain eine DXGI-Oberfläche abzurufen. Verwenden Sie dann die Oberfläche mit der CreateDxgiSurfaceRenderTarget-Methode , um ein ID2D1RenderTarget zu erstellen, mit dem Sie Ihre 2D-Inhalte zeichnen können.
Bei diesem Ansatz werden Ihre Inhalte nicht in drei Dimensionen gerendert. es wird keine Perspektive oder Tiefe haben. Es ist jedoch für mehrere allgemeine Aufgaben nützlich:
- Erstellen eines 2D-Hintergrunds für eine 3D-Szene
- Erstellen einer 2D-Schnittstelle vor einer 3D-Szene
- Verwenden von Direct3D-Multisampling beim Rendern von Direct2D-Inhalten
Im nächsten Abschnitt wird gezeigt, wie Sie einen 2D-Hintergrund für eine 3D-Szene erstellen.
Beispiel: Zeichnen eines 2D-Hintergrunds
In den folgenden Schritten wird beschrieben, wie Ein DXGI-Oberflächenrenderziel erstellt und zum Zeichnen eines Farbverlaufshintergrunds verwendet wird.
Verwenden Sie die CreateSwapChain-Methode , um eine Swap chain für eine ID3D10Device1 (die m_pDevice Variable) zu erstellen. Die Swapchain verwendet das DXGI_FORMAT_B8G8R8A8_UNORM DXGI-Format, eines der von Direct2D unterstützten DXGI-Formate.
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); }
Verwenden Sie die GetBuffer-Methode der Swapchain, um eine DXGI-Oberfläche abzurufen.
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );
Verwenden Sie die DXGI-Oberfläche, um ein DXGI-Renderziel zu erstellen.
// 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);
Verwenden Sie das Renderziel, um einen Farbverlaufshintergrund zu zeichnen.
// 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(); } ...
Code wird in diesem Beispiel nicht angegeben.
Verwenden von Direct2D-Inhalten als Textur
Eine weitere Möglichkeit zum Verwenden von Direct2D-Inhalten mit Direct3D besteht darin, direct2D zum Generieren einer 2D-Textur zu verwenden und diese Textur dann auf ein 3D-Modell anzuwenden. Dazu erstellen Sie eine ID3D10Texture2D, rufen eine DXGI-Oberfläche aus der Textur ab und verwenden dann die Oberfläche, um ein DXGI-Oberflächenrenderziel zu erstellen. Die ID3D10Texture2D-Oberfläche muss das D3D10_BIND_RENDER_TARGET-Bindungsflag verwenden und ein DXGI-Format verwenden, das von DXGI-Oberflächenrenderzielen unterstützt wird. Eine Liste der unterstützten DXGI-Formate finden Sie unter Unterstützte Pixelformate und Alphamodi.
Beispiel: Verwenden von Direct2D-Inhalten als Textur
Die folgenden Beispiele zeigen, wie Sie ein DXGI-Oberflächenrenderziel erstellen, das in einer 2D-Textur gerendert wird (dargestellt durch eine ID3D10Texture2D).
Verwenden Sie zunächst ein Direct3D-Gerät, um eine 2D-Textur zu erstellen. Die Textur verwendet die D3D10_BIND_RENDER_TARGET- und D3D10_BIND_SHADER_RESOURCE-Bindungsflags und verwendet das DXGI_FORMAT_B8G8R8A8_UNORM DXGI-Format, eines der von Direct2D unterstützten DXGI-Formate.
// 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);
Verwenden Sie die Textur, um eine DXGI-Oberfläche zu erhalten.
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
Verwenden Sie die Oberfläche mit der CreateDxgiSurfaceRenderTarget-Methode , um ein Direct2D-Renderziel abzurufen.
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); }
Nachdem Sie nun ein Direct2D-Renderziel erhalten und es einer Direct3D-Textur zugeordnet haben, können Sie das Renderziel verwenden, um Direct2D-Inhalte auf diese Textur zu zeichnen, und sie können diese Textur auf Direct3D-Grundtypen anwenden.
Code wird in diesem Beispiel nicht angegeben.
Ändern der Größe eines DXGI-Oberflächenrenderziels
DXGI-Oberflächenrenderziele unterstützen die ID2D1RenderTarget::Resize-Methode nicht. Um die Größe eines DXGI-Oberflächenrenderziels zu ändern, muss es von der Anwendung freigegeben und neu erstellt werden.
Dieser Vorgang kann zu Leistungsproblemen führen. Das Renderziel kann die letzte aktive Direct2D-Ressource sein, die einen Verweis auf die ID3D10Device1 enthält, die der DXGI-Oberfläche des Renderziels zugeordnet ist. Wenn die Anwendung das Renderziel freigibt und der VERWEIS ID3D10Device1 zerstört wird, muss ein neues neu erstellt werden.
Sie können diesen potenziell teuren Vorgang vermeiden, indem Sie mindestens eine Direct2D-Ressource beibehalten, die vom Renderziel erstellt wurde, während Sie dieses Renderziel erneut erstellen. Im Folgenden sind einige Direct2D-Ressourcen aufgeführt, die für diesen Ansatz funktionieren:
- ID2D1Bitmap (die indirekt von einem ID2D1BitmapBrush gehalten werden kann)
- ID2D1Layer
- ID2D1Mesh
Um diesen Ansatz zu berücksichtigen, sollte Ihre Methode zum Ändern der Größe testen, um zu ermitteln, ob das Direct3D-Gerät verfügbar ist. Wenn es verfügbar ist, lassen Sie Ihre DXGI-Oberflächenrenderziele frei, erstellen sie erneut, behalten sie jedoch alle Ressourcen bei, die sie zuvor erstellt haben, und verwenden Sie sie wieder. Dies funktioniert, da Ressourcen, die von zwei Renderzielen erstellt wurden, wie in der Ressourcenübersicht beschrieben, kompatibel sind, wenn beide Renderziele demselben Direct3D-Gerät zugeordnet sind.