DXGI的使用 --Direct2D 和 Direct3D 互操作性概述

From msdn

Direct2D 和 Direct3D 互操作性概述

硬件加速的二维和三维图形越来越多地出现在非游戏型应用程序中,而且大多数游戏应用程序以菜单和抬头显示器 (HUD) 的形式使用二维图形。因此,使传统的二维呈现方式与 Direct3D 呈现方式有效结合,就拥有极大的价值。

本主题说明如何通过使用 Direct2D 和 Direct3D(可能为英文网页),将二维图形和三维图形结合到一起。

文章包含以下部分。

必备条件 支持的 Direct3D 版本 通过 DXGI 进行互操作 使用 DXGI 图面呈现目标来写入到 Direct3D 图面 创建 DXGI 图面 将 Direct2D 内容写入交换链缓冲区 示例:绘制二维背景 使用 Direct2D 内容作为纹理 示例:使用 Direct2D 内容作为纹理 调整 DXGI 图面呈现目标的大小 相关主题 必备条件

此概述假设您熟悉基本的 Direct2D 绘制操作。有关教程,请参见 Direct2D 快速入门。它还假设您能够进行 Direct3D 10.1(可能为英文网页)编程。

支持的 Direct3D 版本

Direct2D 支持与 Direct3D 10.1 进行互操作。

通过 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 图面呈现器目标将 2-D 内容绘制到 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 Advanced Rasterization Platform (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 方法来创建 Direct 3D 设备,该设备可创建能与 Direct2D 结合使用的 DXGI 图面。

复制 // Create devicehr = 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,以便绘制二维内容。

请注意,此方法不会以三维形式呈现内容:它没有角度,也没有深度。但是,此方法对有些常见任务非常有用:

为三维场景创建二维背景。在三维场景前创建二维界面。在呈现 Direct2D 内容时使用 Direct3D 多重采样。

下一部分演示如何为三维场景创建二维背景。

示例:绘制二维背景

以下步骤说明如何创建 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 chainhr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );使用 DXGI 图面来创建 DXGI 呈现目标。 复制 // Create the DXGI Surface Render Target.FLOAT dpiX;FLOAT dpiY;m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);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 which can draw into the surface in the swap chainhr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pBackBuffer, &props, &m_pBackBufferRT );

使用呈现目标来绘制渐变背景。

复制 // Swap chain will tell us how big the back buffer isDXGI_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();}

此示例中省略了代码。有关完整代码,请参见 Direct3D 互操作性示例。

使用 Direct2D 内容作为纹理

将 Direct2D 内容与 Direct3D 结合使用的另一种方法是使用 Direct2D 来生成二维纹理,然后将该纹理应用到三维模型。通过创建 ID3D10Texture2D(可能为英文网页),从纹理获取 DXGI 图面,然后使用该图面来创建 DXGI 图面呈现目标,即可完成此任务。ID3D10Texture2D(可能为英文网页)图面必须使用 D3D10_BIND_RENDER_TARGET(可能为英文网页)绑定标志,并使用 DXGI 图面呈现目标所支持的 DXGI 格式。有关支持的 DXGI 格式的列表,请参见支持的像素格式和 Alpha 模式。

示例:使用 Direct2D 内容作为纹理

下面的示例显示如何创建 DXGI 图面呈现目标,以便呈现到二维纹理,该纹理由 ID3D10Texture2D(可能为英文网页)表示。

首先,使用 Direct3D 设备来创建二维纹理。请注意,该纹理使用 D3D10_BIND_RENDER_TARGET(可能为英文网页)和 D3D10_BIND_SHADER_RESOURCE(可能为英文网页)绑定标志,并且使用DXGI_FORMAT_B8G8R8A8_UNORM(可能为英文网页)DXGI 格式(Direct2D 所支持的 DXGI 格式之一)。

复制 // Allocate a offscreen D3D surface for D2D to render our 2D content intoD3D10_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 which 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 基元。

此示例中省略了代码。有关完整代码,请参见 Direct3D 互操作性示例。

调整 DXGI 图面呈现目标的大小

DXGI 图面呈现目标不支持 ID2D1RenderTarget::Resize 方法。要调整 DXGI 图面呈现目标的大小,应用程序必须释放该呈现目标,然后再重新创建。

此操作可能会带来性能问题。该呈现目标可能是最后一个活动的 Direct2D 资源,保留了对与呈现目标的 DXGI 图面相关联的 ID3D10Device1(可能为英文网页)的引用。如果应用程序释放该呈现目标并销毁了ID3D10Device1(可能为英文网页)引用,则必须重新创建一个新的引用。

通过在重新创建呈现目标时,至少保留一个由该呈现目标创建的 Direct2D 资源,即可避免这种可能代价很高的操作。下面是一些适用于此方法的 Direct2D 资源:

ID2D1Bitmap (可能直接由  ID2D1BitmapBrush  拥有) ID2D1Layer ID2D1Mesh

为实现此方法,您的调整大小方法应该进行测试,以查看是否有可用的 Direct3D 设备。如果存在,则释放并重新创建 DXGI 图面呈现目标,但保留它们以前创建的所有资源,并且重用这些资源。这种方法之所以有效,是因为如资源概述中所述,在两个呈现目标均与同一个 Direct3D 设备相关联的前提下,由这两个呈现目标创建的资源是相互兼容的。

有关演示如何实现此方法的代码示例,请参见 Direct3D 互操作示例。

另请参见 Direct3D 互操作示例 支持的像素格式和 Alpha 模式 CreateDxgiSurfaceRenderTarget

你可能感兴趣的:(DXGI的使用 --Direct2D 和 Direct3D 互操作性概述)