Vue d’ensemble de l’interopérabilité entre Direct2D et Direct3D

Les graphiques 2D et 3D accélérés par le matériel font de plus en plus partie des applications hors jeu, et la plupart des applications de jeu utilisent des graphiques 2D sous la forme de menus et d’affichages Heads-Up (HUD). Il existe de nombreuses valeurs qui peuvent être ajoutées en permettant de mélanger le rendu 2D traditionnel avec le rendu Direct3D de manière efficace.

Cette rubrique explique comment intégrer des graphiques 2D et 3D à l’aide de Direct2D et Direct3D.

Prérequis

Cette vue d’ensemble suppose que vous êtes familiarisé avec les opérations de dessin Direct2D de base. Pour obtenir un didacticiel, consultez Créer une application Direct2D simple. Il suppose également que vous pouvez programmer à l’aide de Direct3D 10.1.

Versions de Direct3D prises en charge

Avec DirectX 11.0, Direct2D prend uniquement en charge l’interopérabilité avec les appareils Direct3D 10.1. Avec DirectX 11.1 ou version ultérieure, Direct2D prend également en charge l’interopérabilité avec Direct3D 11.

Interopérabilité via DXGI

Depuis Direct3D 10, le runtime Direct3D utilise DXGI pour la gestion des ressources. La couche d’exécution DXGI fournit un partage inter-processus de surfaces de mémoire vidéo et sert de base pour d’autres plateformes d’exécution basées sur la mémoire vidéo. Direct2D utilise DXGI pour interagir avec Direct3D.

Il existe deux principales façons d’utiliser Direct2D et Direct3D ensemble :

  • Vous pouvez écrire du contenu Direct2D sur une surface Direct3D en obtenant un IDXGISurface et en l’utilisant avec CreateDxgiSurfaceRenderTarget pour créer un ID2D1RenderTarget. Vous pouvez ensuite utiliser la cible de rendu pour ajouter une interface ou un arrière-plan à deux dimensions à des graphiques en trois dimensions, ou utiliser un dessin Direct2D comme texture pour un objet en trois dimensions.
  • En utilisant CreateSharedBitmap pour créer un ID2D1Bitmap à partir d’un IDXGISurface, vous pouvez écrire une scène Direct3D dans une bitmap et la restituer avec Direct2D.

Écriture sur une surface Direct3D avec une cible de rendu de surface DXGI

Pour écrire sur une surface Direct3D, vous obtenez un IDXGISurface et passez-le à la méthode CreateDxgiSurfaceRenderTarget pour créer une cible de rendu de surface DXGI. Vous pouvez ensuite utiliser la cible de rendu de surface DXGI pour dessiner du contenu 2D sur la surface DXGI.

Une cible de rendu de surface DXGI est une sorte d’ID2D1RenderTarget. Comme d’autres cibles de rendu Direct2D, vous pouvez l’utiliser pour créer des ressources et émettre des commandes de dessin.

La cible de rendu de surface DXGI et la surface DXGI doivent utiliser le même format DXGI. Si vous spécifiez le format DXGI_FORMAT_UNKOWN lorsque vous créez la cible de rendu, elle utilise automatiquement le format de la surface.

La cible de rendu de surface DXGI n’effectue pas la synchronisation de surface DXGI.

Création d’une surface DXGI

Avec Direct3D 10, il existe plusieurs façons d’obtenir une surface DXGI. Vous pouvez créer un IDXGISwapChain pour un appareil, puis utiliser la méthode GetBuffer de la chaîne d’échange pour obtenir une surface DXGI. Vous pouvez également utiliser un appareil pour créer une texture, puis utiliser cette texture comme surface DXGI.

Quelle que soit la façon dont vous créez la surface DXGI, la surface doit utiliser l’un des formats DXGI pris en charge par les cibles de rendu de surface DXGI. Pour obtenir la liste, consultez Formats de pixels et modes alpha pris en charge.

En outre, l’ID3D10Device1 associé à la surface DXGI doit prendre en charge les formats BGRA DXGI pour que la surface fonctionne avec Direct2D. Pour garantir cette prise en charge, utilisez l’indicateur D3D10_CREATE_DEVICE_BGRA_SUPPORT lorsque vous appelez la méthode D3D10CreateDevice1 pour créer l’appareil.

Le code suivant définit une méthode qui crée un ID3D10Device1. Elle sélectionne le meilleur niveau de fonctionnalité disponible et revient à la plateforme WARP (Advanced Rasterization Platform) Windows lorsque le rendu matériel n’est pas disponible.

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;
}

L’exemple de code suivant utilise la méthode CreateD3DDevice illustrée dans l’exemple précédent pour créer un appareil Direct3D qui peut créer des surfaces DXGI à utiliser avec Direct2D.

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

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

Écriture de contenu Direct2D dans une mémoire tampon de chaîne d’échange

Le moyen le plus simple d’ajouter du contenu Direct2D à une scène Direct3D consiste à utiliser la méthode GetBuffer d’un IDXGISwapChain pour obtenir une surface DXGI, puis à utiliser la surface avec la méthode CreateDxgiSurfaceRenderTarget pour créer un ID2D1RenderTarget avec lequel dessiner votre contenu 2D.

Cette approche ne restitue pas votre contenu en trois dimensions ; il n’aura pas de perspective ou de profondeur. Toutefois, il est utile pour plusieurs tâches courantes :

  • Création d’un arrière-plan 2D pour une scène 3D.
  • Création d’une interface 2D devant une scène 3D.
  • Utilisation de l’échantillonnage multiple Direct3D lors du rendu du contenu Direct2D.

La section suivante montre comment créer un arrière-plan 2D pour une scène 3D.

Exemple : Dessiner un arrière-plan 2D

Les étapes suivantes décrivent comment créer une cible de rendu de surface DXGI et l’utiliser pour dessiner un arrière-plan dégradé.

  1. Utilisez la méthode CreateSwapChain pour créer une chaîne d’échange pour un ID3D10Device1 (variable m_pDevice ). La chaîne d’échange utilise le format DXGI DXGI_FORMAT_B8G8R8A8_UNORM , l’un des formats DXGI pris en charge par 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. Utilisez la méthode GetBuffer de la chaîne d’échange pour obtenir une surface DXGI.

    // Get a surface in the swap chain
    hr = m_pSwapChain->GetBuffer(
        0,
        IID_PPV_ARGS(&pBackBuffer)
        );
    
  3. Utilisez la surface DXGI pour créer une cible de rendu 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. Utilisez la cible de rendu pour dessiner un arrière-plan dégradé.

    // 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();
        }
    ...
    

Le code est omis dans cet exemple.

Utilisation du contenu Direct2D comme texture

Une autre façon d’utiliser du contenu Direct2D avec Direct3D consiste à utiliser Direct2D pour générer une texture 2D, puis à appliquer cette texture à un modèle 3D. Pour ce faire, créez un ID3D10Texture2D, obtenez une surface DXGI à partir de la texture, puis utilisez la surface pour créer une cible de rendu de surface DXGI. La surface ID3D10Texture2D doit utiliser l’indicateur de liaison D3D10_BIND_RENDER_TARGET et utiliser un format DXGI pris en charge par les cibles de rendu de surface DXGI. Pour obtenir la liste des formats DXGI pris en charge, consultez Formats de pixels et modes alpha pris en charge.

Exemple : utiliser du contenu Direct2D comme texture

Les exemples suivants montrent comment créer une cible de rendu de surface DXGI qui s’affiche sur une texture 2D (représentée par un ID3D10Texture2D).

  1. Tout d’abord, utilisez un appareil Direct3D pour créer une texture 2D. La texture utilise les indicateurs de liaison D3D10_BIND_RENDER_TARGET et D3D10_BIND_SHADER_RESOURCE , ainsi que le format DXGI DXGI_FORMAT_B8G8R8A8_UNORM , l’un des formats DXGI pris en charge par 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. Utilisez la texture pour obtenir une surface DXGI.

    IDXGISurface *pDxgiSurface = NULL;
    
    hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
    
  3. Utilisez la surface avec la méthode CreateDxgiSurfaceRenderTarget pour obtenir une cible de rendu 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);
    }
    

Maintenant que vous avez obtenu une cible de rendu Direct2D et que vous l’avez associée à une texture Direct3D, vous pouvez utiliser la cible de rendu pour dessiner du contenu Direct2D sur cette texture, et vous pouvez appliquer cette texture aux primitives Direct3D.

Le code est omis dans cet exemple.

Redimensionnement d’une cible de rendu de surface DXGI

Les cibles de rendu de surface DXGI ne prennent pas en charge la méthode ID2D1RenderTarget::Resize . Pour redimensionner une cible de rendu de surface DXGI, l’application doit la libérer et la recréer.

Cette opération peut potentiellement créer des problèmes de performances. La cible de rendu peut être la dernière ressource Direct2D active qui conserve une référence à l’ID3D10Device1 associé à la surface DXGI de la cible de rendu. Si l’application libère la cible de rendu et que la référence ID3D10Device1 est détruite, une nouvelle cible doit être recréée.

Vous pouvez éviter cette opération potentiellement coûteuse en conservant au moins une ressource Direct2D créée par la cible de rendu pendant que vous recréez cette cible de rendu. Voici quelques ressources Direct2D qui fonctionnent pour cette approche :

Pour prendre en charge cette approche, votre méthode de redimensionnement doit tester pour voir si l’appareil Direct3D est disponible. Si elle est disponible, relâchez et recréez vos cibles de rendu de surface DXGI, mais conservez toutes les ressources qu’elles ont créées précédemment et réutilisez-les. Cela fonctionne car, comme décrit dans la vue d’ensemble des ressources, les ressources créées par deux cibles de rendu sont compatibles lorsque les deux cibles de rendu sont associées au même appareil Direct3D.

Formats de pixel et modes alpha pris en charge

CreateDxgiSurfaceRenderTarget

Windows DirectX Graphics