Direct2D 和 Direct3D 互通性概觀
硬體加速的 2D 和 3D 圖形逐漸成為非遊戲應用程式的一部分,而大部分的遊戲應用程式會以功能表和Heads-Up顯示 (HUD) 的形式使用 2D 圖形。 透過讓傳統 2D 轉譯以有效率的方式與 Direct3D 轉譯混合,即可新增許多價值。
本主題描述如何使用 Direct2D 和 Direct3D來整合 2D 和 3D 圖形。
必要條件
此概觀假設您已熟悉基本的 Direct2D 繪圖作業。 如需教學課程,請參閱 建立簡單的 Direct2D 應用程式。 它也假設您可以使用 Direct3D 10.1進行程式設計。
支援的 Direct3D 版本
使用 DirectX 11.0 時,Direct2D 僅支援與 Direct3D 10.1 裝置的互通性。 使用 DirectX 11.1 或更新版本,Direct2D 也支援與 Direct3D 11 的互通性。
透過 DXGI 的互通性
從 Direct3D 10 起,Direct3D 執行時間會使用 DXGI 來管理資源。 DXGI 執行時間層提供視訊記憶體介面的跨進程共用,並做為其他視訊記憶體型執行時間平臺的基礎。 Direct2D 使用 DXGI 與 Direct3D 交互操作。
有兩種主要方式可以同時使用 Direct2D 和 Direct3D:
- 您可以取得 IDXGISurface 並將它與 CreateDxgiSurfaceRenderTarget 搭配使用,以建立 ID2D1RenderTarget,將 Direct2D內容寫入 Direct3D 表面。 然後,您可以使用轉譯目標,將二維介面或背景新增至三維圖形,或使用 Direct2D 繪圖做為三維物件的紋理。
- 藉由使用CreateSharedBitmap從IDXGISurface建立ID2D1Bitmap,您可以將 Direct3D 場景寫入點陣圖,並使用 Direct2D 轉譯它。
使用 DXGI 表面轉譯目標寫入 Direct3D 表面
若要寫入 Direct3D 表面,您會取得 IDXGISurface ,並將它傳遞給 CreateDxgiSurfaceRenderTarget 方法來建立 DXGI 表面轉譯目標。 然後,您可以使用 DXGI 表面轉譯目標,將 2D 內容繪製到 DXGI 表面。
DXGI 表面轉譯目標是一種 ID2D1RenderTarget。 如同其他 Direct2D 轉譯目標,您可以使用它來建立資源併發出繪圖命令。
DXGI 表面轉譯目標和 DXGI 表面必須使用相同的 DXGI 格式。 如果您在建立轉譯目標時指定 DXGI_FORMAT_UNKOWN 格式,它會自動使用表面的格式。
DXGI 表面轉譯目標不會執行 DXGI 表面同步處理。
建立 DXGI 表面
使用 Direct3D 10 時,有數種方式可以取得 DXGI 表面。 您可以為裝置建立 IDXGISwapChain ,然後使用交換鏈結的 GetBuffer 方法來取得 DXGI 表面。 或者,您可以使用裝置來建立紋理,然後使用該紋理作為 DXGI 表面。
不論您如何建立 DXGI 表面,表面都必須使用 DXGI 表面轉譯目標所支援的其中一種 DXGI 格式。 如需清單,請參閱 支援的像素格式和 Alpha 模式。
此外,與 DXGI 表面相關聯的 ID3D10Device1 必須支援 BGRA DXGI 格式,讓表面能夠使用 Direct2D。 若要確保支援,請在呼叫D3D10CreateDevice1方法來建立裝置時,請使用D3D10_CREATE_DEVICE_BGRA_SUPPORT旗標。
下列程式碼會定義建立 ID3D10Device1的方法。 它會選取可用的最佳功能層級,並在無法使用硬體轉譯時回復為 Windows 進階點陣化平臺 (WARP) 。
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;
}
下一個程式碼範例會使用上一個範例所示的 CreateD3DDevice 方法來建立 Direct3D 裝置,以建立 DXGI 表面以搭配 Direct2D 使用。
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
將 Direct2D 內容寫入交換鏈結緩衝區
將 Direct2D 內容新增至 Direct3D 場景的最簡單方式是使用IDXGISwapChain的GetBuffer方法來取得 DXGI 表面,然後使用介面搭配 CreateDxgiSurfaceRenderTarget方法來建立ID2D1RenderTarget,以繪製您的 2D 內容。
此方法不會以三個維度轉譯您的內容;它不會有檢視方塊或深度。 不過,這適用于數個常見工作:
- 建立 3D 場景的 2D 背景。
- 在 3D 場景前面建立 2D 介面。
- 在轉譯 Direct2D 內容時使用 Direct3D 多重取樣。
下一節說明如何建立 3D 場景的 2D 背景。
範例:繪製 2D 背景
下列步驟說明如何建立 DXGI 表面轉譯目標,並用它來繪製漸層背景。
使用 CreateSwapChain 方法來建立 ID3D10Device1 的交換鏈結, (m_pDevice 變數) 。 交換鏈結使用 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 格式,這是 Direct2D 支援的其中一種 DXGI 格式。
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); }
使用交換鏈結的 GetBuffer 方法來取得 DXGI 表面。
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );
使用 DXGI 表面建立 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);
使用轉譯目標繪製漸層背景。
// 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(); } ...
此範例會省略程式碼。
使用 Direct2D 內容作為紋理
搭配 Direct3D 使用 Direct2D 內容的另一種方式是使用 Direct2D 來產生 2D 紋理,然後將該紋理套用至 3D 模型。 您可以藉由建立 ID3D10Texture2D、從紋理取得 DXGI 表面,然後使用表面建立 DXGI 表面轉譯目標來執行此動作。 ID3D10Texture2D表面必須使用D3D10_BIND_RENDER_TARGET系結旗標,並使用 DXGI 表面轉譯目標支援的 DXGI 格式。 如需支援的 DXGI 格式清單,請參閱 支援的像素格式和 Alpha 模式。
範例:使用 Direct2D 內容作為紋理
下列範例示範如何建立 DXGI 表面轉譯目標,以 ID3D10Texture2D) 呈現為 2D 紋理 (。
首先,使用 Direct3D 裝置來建立 2D 紋理。 紋理會使用 D3D10_BIND_RENDER_TARGET 和 D3D10_BIND_SHADER_RESOURCE 系結旗標,並使用 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 格式,這是 Direct2D 支援的其中一種 DXGI 格式。
// 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);
使用紋理來取得 DXGI 表面。
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
搭配 CreateDxgiSurfaceRenderTarget 方法來取得 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); }
既然您已取得 Direct2D 轉譯目標,並將其與 Direct3D 紋理相關聯,您可以使用轉譯目標將 Direct2D 內容繪製至該紋理,並將該紋理套用至 Direct3D 基本類型。
此範例會省略程式碼。
調整 DXGI 表面轉譯目標的大小
DXGI 表面轉譯目標不支援 ID2D1RenderTarget::Resize 方法。 若要調整 DXGI 表面轉譯目標的大小,應用程式必須釋放並重新建立它。
這項作業可能會產生效能問題。 轉譯目標可能是最後一個作用中的 Direct2D 資源,該資源會保留與轉譯目標 DXGI 表面相關聯 之 ID3D10Device1 的參考。 如果應用程式釋放轉譯目標,且 ID3D10Device1 參考已終結,則必須重新建立新的參考。
您可以在重新建立轉譯目標時,保留至少一個轉譯目標所建立的 Direct2D 資源,以避免這種可能耗費資源的作業。 以下是一些適用于此方法的 Direct2D 資源:
若要配合此方法,您的調整大小方法應該測試是否可使用 Direct3D 裝置。 如果可用,請釋出並重新建立 DXGI 表面轉譯目標,但保留先前建立的所有資源,並重複使用它們。 這可運作,因為如 資源概觀中所述,當兩個轉譯目標都與相同的 Direct3D 裝置相關聯時,由兩個轉譯目標建立的資源都相容。