Partilhar via


Visão geral da interoperabilidade entre Direct2D e Direct3D

Os gráficos 2D e 3D acelerados por hardware estão se tornando cada vez mais parte de aplicativos que não são de jogos, e a maioria dos aplicativos de jogos usa gráficos 2D na forma de menus e monitores Heads-Up (HUDs). Há muito valor que pode ser adicionado ao permitir que a renderização 2D tradicional seja misturada com a renderização Direct3D de maneira eficiente.

Este tópico descreve como integrar gráficos 2D e 3D usando Direct2D e Direct3D.

Pré-requisitos

Esta visão geral pressupõe que você esteja familiarizado com operações básicas de desenho Direct2D. Para obter um tutorial, consulte Criar um aplicativo Direct2D simples. Ele também pressupõe que você pode programar usando Direct3D 10.1.

Versões Direct3D suportadas

Com o DirectX 11.0, o Direct2D suporta interoperabilidade apenas com dispositivos Direct3D 10.1. Com o DirectX 11.1 ou posterior, o Direct2D também suporta interoperabilidade com o Direct3D 11.

Interoperabilidade através do DXGI

A partir do Direct3D 10, o tempo de execução do Direct3D usa DXGI para gestão de recursos. A camada de tempo de execução DXGI fornece compartilhamento entre processos de superfícies de memória de vídeo e serve como base para outras plataformas de tempo de execução baseadas em memória de vídeo. O Direct2D usa DXGI para interoperar com o Direct3D.

Há duas maneiras principais de usar o Direct2D e o Direct3D juntos:

  • Você pode gravar 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 gráficos tridimensionais ou usar um desenho Direct2D como textura para um objeto tridimensional.
  • Usando CreateSharedBitmap para criar umID2D1Bitmapa partir de umIDXGISurface, você pode gravar uma cena Direct3D em um bitmap e renderizá-la com Direct2D.

Escrevendo numa superfície Direct3D com um alvo de renderização de superfície DXGI

Para gravar em uma superfície Direct3D, você obtém um IDXGISurface e o passa para o método CreateDxgiSurfaceRenderTarget para criar um alvo de renderização de superfície DXGI. Posteriormente, você pode usar o destino de renderização da superfície DXGI para desenhar conteúdo 2-D na superfície DXGI.

Um destino de renderização de superfície DXGI é um tipo de ID2D1RenderTarget. Como outros destinos de renderização 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 alvo de renderização de superfície DXGI não executa a sincronização da superfície DXGI.

Criando 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 swap chain 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 suportados pelos destinos de renderização de superfície DXGI. Para obter uma lista, consulte Formatos de pixel suportados e Modos Alpha.

Além disso, o ID3D10Device1 associado à superfície DXGI deve suportar formatos BGRA DXGI para que a superfície funcione com Direct2D. Para garantir esse suporte, use a flag 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 retorna ao Windows Advanced Rasterization Platform (WARP) 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 Direct2D.

// Create device
hr = CreateD3DDevice(
    NULL,
    D3D10_DRIVER_TYPE_HARDWARE,
    nDeviceFlags,
    &pDevice
    );

if (FAILED(hr))
{
    hr = CreateD3DDevice(
        NULL,
        D3D10_DRIVER_TYPE_WARP,
        nDeviceFlags,
        &pDevice
        );
}

Gravando conteúdo Direct2D em um buffer da cadeia de troca

A maneira mais simples de adicionar conteúdo Direct2D a uma cena Direct3D é usar o método GetBuffer de umIDXGISwapChainpara obter uma superfície DXGI e, em seguida, usar a superfície com o métodoCreateDxgiSurfaceRenderTarget para criar umID2D1RenderTargetcom o qual desenhar seu conteúdo 2D.

Essa abordagem não renderiza seu conteúdo em três dimensões; não terá perspetiva nem profundidade. No entanto, é útil para várias tarefas comuns:

  • Criação de um fundo 2D para uma cena 3D.
  • Criação de uma interface 2D na frente de uma cena 3D.
  • Usando multiamostragem Direct3D ao renderizar conteúdo Direct2D.

A próxima seção mostra como criar um plano de fundo 2D para uma cena 3D.

Exemplo: Desenhar um fundo 2D

As etapas a seguir descrevem como criar um destino de renderização de superfície DXGI e usá-lo para desenhar um plano de fundo gradiente.

  1. Use o método CreateSwapChain para criar uma cadeia de permuta para um ID3D10Device1 (a variável m_pDevice). A cadeia de troca usa o formato DXGI_FORMAT_B8G8R8A8_UNORM DXGI, um dos formatos DXGI suportados pelo 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);
    }
    
  2. Utilize 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)
        );
    
  3. Utilize uma 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);
    
  4. Use o destino de renderização para desenhar um plano de fundo 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 conteúdo Direct2D como textura

Outra maneira de usar conteúdo Direct2D com Direct3D é usar Direct2D para gerar uma textura 2D e, em seguida, aplicar essa textura a um modelo 3D. Para fazer isso, crie um ID3D10Texture2D, obtendo uma superfície DXGI da textura e, em seguida, usando a superfície para criar um alvo de renderização de superfície DXGI. A superfície ID3D10Texture2D deve usar o sinalizador D3D10_BIND_RENDER_TARGET e um formato DXGI suportado pelos destinos de renderização de superfícies DXGI. Para obter uma lista de formatos DXGI suportados, consulte Formatos de pixel suportados e modos Alpha.

Exemplo: Usar conteúdo Direct2D como textura

Os exemplos seguintes mostram como criar um alvo de renderização de superfície DXGI que renderiza numa textura 2D (representada por um ID3D10Texture2D).

  1. Primeiro, use um dispositivo Direct3D para criar uma textura 2D. A textura utiliza os sinalizadores de ligação D3D10_BIND_RENDER_TARGET e D3D10_BIND_SHADER_RESOURCE e o formato DXGI_FORMAT_B8G8R8A8_UNORM DXGI, que é um dos formatos DXGI suportados pelo 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);
    
  2. Use a textura para obter uma superfície DXGI.

    IDXGISurface *pDxgiSurface = NULL;
    
    hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
    
  3. Utiliza a superfície com o método CreateDxgiSurfaceRenderTarget para obter um alvo 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 pode 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 suportam o método ID2D1RenderTarget::Resize. Para redimensionar um destino de renderização de superfície DXGI, o aplicativo deve libertá-lo e recriá-lo.

Esta operação pode potencialmente criar problemas de desempenho. O destino de renderização pode ser o último recurso Direct2D ativo que mantém uma referência ao ID3D10Device1 associado à superfície DXGI do destino de renderização. Se a aplicação libertar o alvo de renderização e a referência do ID3D10Device1 for destruída, uma nova precisa ser recriada.

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. A seguir estão alguns recursos Direct2D que funcionam para essa abordagem:

Para acomodar essa abordagem, seu método de redimensionamento deve testar para ver se o dispositivo Direct3D está disponível. Se estiver disponível, liberte e recrie os seus alvos de renderização de superfície DXGI, mas mantenha todos os recursos que criaram anteriormente e reutilize-os. Isso funciona porque, conforme descrito no Visão geral dos recursos do, os recursos criados por dois destinos de renderização são compatíveis quando ambos os destinos de renderização estão associados ao mesmo dispositivo Direct3D.

Formatos de pixel suportados e modos alfa

CreateDxgiSurfaceRenderTarget

Windows DirectX Graphics