Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os gráficos 2D e 3D acelerados por hardware estão cada vez mais se tornando parte de aplicativos que não são voltados para jogos, e a maioria dos aplicativos de jogos utiliza gráficos 2D na forma de menus e exibições de HUDs (Heads-Up Displays). Há muito valor que pode ser adicionado permitindo que a renderização 2D tradicional seja misturada com a renderização do Direct3D de maneira eficiente.
Este tópico descreve como integrar elementos gráficos 2D e 3D usando Direct2D e Direct3D.
Pré-requisitos
Essa visão geral pressupõe que você esteja familiarizado com as operações básicas de desenho do Direct2D. Para obter um tutorial, consulte Criar um aplicativo Direct2D simples. Ele também pressupõe que você pode programar usando o Direct3D 10.1.
Versões do Direct3D com suporte
Com o DirectX 11.0, o Direct2D dá suporte apenas à interoperabilidade com dispositivos Direct3D 10.1. Com o DirectX 11.1 ou posterior, o Direct2D também dá suporte à interoperabilidade com o Direct3D 11.
Interoperabilidade por meio de DXGI
A partir do Direct3D 10, o runtime do Direct3D usa DXGI para gerenciamento de recursos. A camada de runtime DXGI fornece compartilhamento entre processos de superfícies de memória de vídeo e serve como base para outras plataformas de runtime baseadas em memória de vídeo. O Direct2D usa DXGI para interoperar com o Direct3D.
Há duas maneiras principais de usar Direct2D e Direct3D juntos:
- Você pode escrever conteúdo Direct2D em uma superfície Direct3D obtendo um IDXGISurface e usando-o com o CreateDxgiSurfaceRenderTarget para criar um ID2D1RenderTarget. Em seguida, você pode usar o destino de renderização para adicionar uma interface bidimensional ou plano de fundo a elementos gráficos tridimensionais ou usar um desenho Direct2D como uma textura para um objeto tridimensional.
- Usando CreateSharedBitmap para criar um ID2D1Bitmap de um IDXGISurface, você pode escrever uma cena do Direct3D em um bitmap e renderizá-la com o Direct2D.
Gravação em uma superfície Direct3D com um destino de renderização de superfície DXGI
Para gravar em uma superfície Direct3D, você obtém um IDXGISurface e passa para o método CreateDxgiSurfaceRenderTarget, para criar um alvo de renderização de superfície DXGI. Em seguida, você pode usar o alvo de renderização de superfície DXGI para desenhar conteúdo bidimensional na superfície DXGI.
Um alvo de renderização de superfície DXGI é um tipo de ID2D1RenderTarget. Assim como outros destinos de renderização do Direct2D, você pode usá-lo para criar recursos e emitir comandos de desenho.
O destino de renderização da superfície DXGI e a superfície DXGI devem usar o mesmo formato DXGI. Se você especificar o formato DXGI_FORMAT_UNKOWN ao criar o destino de renderização, ele usará automaticamente o formato da superfície.
O destino de renderização de superfície DXGI não executa a sincronização de superfície DXGI.
Criar uma superfície DXGI
Com o Direct3D 10, há várias maneiras de obter uma superfície DXGI. Você pode criar um IDXGISwapChain para um dispositivo e, em seguida, usar o método GetBuffer da cadeia de troca para obter uma superfície DXGI. Ou você pode usar um dispositivo para criar uma textura e, em seguida, usar essa textura como uma superfície DXGI.
Independentemente de como você cria a superfície DXGI, a superfície deve usar um dos formatos DXGI compatíveis com destinos de renderização de superfície DXGI. Para obter uma lista, consulte formatos de pixel com suporte e modos alfa.
Além disso, o ID3D10Device1, associado à superfície DXGI, deve dar suporte a formatos DXGI BGRA para que a superfície funcione com Direct2D. Para garantir esse suporte, use o sinalizador D3D10_CREATE_DEVICE_BGRA_SUPPORT ao chamar o método D3D10CreateDevice1 para criar o dispositivo.
O código a seguir define um método que cria um ID3D10Device1. Ele seleciona o melhor nível de recurso disponível e volta para WARP (Plataforma de Rasterização Avançada do Windows) quando a renderização de hardware não está disponível.
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;
}
O próximo exemplo de código usa o método CreateD3DDevice mostrado no exemplo anterior para criar um dispositivo Direct3D que pode criar superfícies DXGI para uso com o Direct2D.
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
Gravação de conteúdo Direct2D em um buffer de cadeia de troca
A maneira mais simples de adicionar conteúdo direct2D a uma cena Direct3D é usar o método GetBuffer de um IDXGISwapChain para obter uma superfície DXGI e, em seguida, usar a superfície com o método CreateDxgiSurfaceRenderTarget para criar um ID2D1RenderTarget com o qual desenhar seu conteúdo 2D.
Essa abordagem não renderiza seu conteúdo em três dimensões; ele não terá perspectiva ou profundidade. No entanto, é útil para várias tarefas comuns:
- Criando uma tela de fundo 2D para uma cena 3D.
- Criando uma interface 2D na frente de uma cena 3D.
- Usando o direct3D multisampling ao renderizar o conteúdo do Direct2D.
A próxima seção mostra como criar uma tela de fundo 2D para uma cena 3D.
Exemplo: Desenhar um plano de fundo 2D
As etapas a seguir descrevem como criar um destino de renderização de superfície DXGI e usá-lo para desenhar uma tela de fundo de gradiente.
Use o método CreateSwapChain para criar uma cadeia de troca para um ID3D10Device1 (a variável m_pDevice ). A cadeia de troca usa o formato DXGI_FORMAT_B8G8R8A8_UNORM DXGI, um dos formatos DXGI compatíveis com o 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); }
Use o método GetBuffer da cadeia de troca, para obter uma superfície DXGI.
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );
Utilize a superfície DXGI para criar um alvo de renderização 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);
Use o destino de renderização para desenhar um plano de fundo de gradiente.
// 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(); } ...
O código é omitido deste exemplo.
Usando o conteúdo direct2D como uma textura
Outra maneira de usar o conteúdo direct2D com Direct3D é usar o Direct2D para gerar uma textura 2D e, em seguida, aplicar essa textura a um modelo 3D. Faça isso criando uma ID3D10Texture2D, obtendo uma superfície DXGI da textura e, em seguida, usando a superfície para criar um destino de renderização de superfície DXGI. A superfície ID3D10Texture2D deve usar o sinalizador de associação D3D10_BIND_RENDER_TARGET e usar um formato DXGI compatível com destinos de renderização de superfície DXGI. Para obter uma lista de formatos DXGI com suporte, consulte formatos de pixel com suporte e modos alfa.
Exemplo: usar o conteúdo Direct2D como textura
Os exemplos a seguir mostram como criar um destino de renderização de superfície DXGI que renderiza para uma textura 2D (representada por uma ID3D10Texture2D).
Primeiro, use um dispositivo Direct3D para criar uma textura 2D. A textura usa os sinalizadores de associação D3D10_BIND_RENDER_TARGET e D3D10_BIND_SHADER_RESOURCE e usa o formato DXGI DXGI_FORMAT_B8G8R8A8_UNORM, um dos formatos DXGI compatíveis com o 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);
Use a textura para obter uma superfície DXGI.
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
Use a superfície com o método CreateDxgiSurfaceRenderTarget para obter um destino de renderização 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); }
Agora que você obteve um destino de renderização Direct2D e o associou a uma textura Direct3D, você pode usar o destino de renderização para desenhar conteúdo Direct2D para essa textura e aplicar essa textura a primitivas Direct3D.
O código é omitido deste exemplo.
Redimensionar um alvo de renderização de superfície DXGI
Os destinos de renderização de superfície DXGI não dão suporte ao método ID2D1RenderTarget::Resize . Para redimensionar um alvo de renderização de superfície DXGI, o aplicativo deve liberá-lo e criá-lo novamente.
Essa operação pode potencialmente criar problemas de desempenho. O destino de renderização pode ser o último recurso ativo do Direct2D que mantém uma referência à ID3D10Device1 associada à superfície DXGI do destino de renderização. Se o aplicativo liberar o destino de renderização e a referência ID3D10Device1 for destruída, um novo deverá ser recriado.
Você pode evitar essa operação potencialmente cara mantendo pelo menos um recurso Direct2D que foi criado pelo destino de renderização enquanto recria esse destino de renderização. Veja a seguir alguns recursos do Direct2D que funcionam para essa abordagem:
- ID2D1Bitmap (que pode ser mantido indiretamente por um ID2D1BitmapBrush)
- ID2D1Layer
- ID2D1Mesh
Para acomodar essa abordagem, seu método de redimensionamento deve testar para ver se o dispositivo Direct3D está disponível. Se estiver disponível, libere e recrie seus alvos de renderização de superfície DXGI, mas mantenha todos os recursos que eles criaram anteriormente e reutilize-os. Isso funciona porque, conforme descrito na Visão Geral de Recursos, os recursos criados por dois destinos de renderização são compatíveis quando ambos os destinos de renderização são associados ao mesmo dispositivo Direct3D.