Übersicht über die Interoperabilität von Direct2D und Direct3D

Hardwarebeschleunigte 2D- und 3D-Grafiken werden zunehmend zu einem Teil von Nicht-Gaminganwendungen, und die meisten Gaminganwendungen verwenden 2D-Grafiken in Form von Menüs und Heads-Up Displays (HUDs). Es gibt viele Werte, die hinzugefügt werden können, indem herkömmliches 2D-Rendering auf effiziente Weise mit Direct3D-Rendering gemischt werden kann.

In diesem Thema wird beschrieben, wie 2D- und 3D-Grafiken mit Direct2D und Direct3D integriert werden.

Der Abschnitt ist wie folgt gegliedert.

Voraussetzungen

In dieser Übersicht wird davon ausgegangen, dass Sie mit grundlegenden Direct2D-Zeichnungsvorgängen vertraut sind. Ein Tutorial finden Sie im Direct2D-Schnellstart. Außerdem wird davon ausgegangen, dass Sie mit Direct3D 10.1 programmieren können.

Unterstützte Direct3D-Versionen

Mit DirectX 11.0 unterstützt Direct2D nur die Interoperabilität mit Direct3D 10.1-Geräten. Mit DirectX 11.1 oder höher unterstützt Direct2D auch die Interoperabilität mit Direct3D 11.

Interoperabilität durch DXGI

Ab Direct3D 10 verwendet die Direct3D-Runtime DXGI für die Ressourcenverwaltung. Die DXGI-Laufzeitebene ermöglicht die prozessübergreifende Freigabe von Videospeicheroberflächen und dient als Grundlage für andere speicherbasierte Laufzeitplattformen für Videos. Direct2D verwendet DXGI für die Zusammenarbeit mit Direct3D.

Es gibt zwei haupt möglichkeiten, Direct2D und Direct3D zusammen zu verwenden:

  • Sie können Direct2D-Inhalt auf eine Direct3D-Oberfläche schreiben, indem Sie eine IDXGISurface abrufen und mit CreateDxgiSurfaceRenderTarget verwenden, um eine ID2D1RenderTarget zuerstellen. Sie können dann das Renderziel verwenden, um eine zweidimensionale Schnittstelle oder einen Hintergrund zu dreidimensionalen Grafiken hinzuzufügen, oder eine Direct2D-Zeichnung als Textur für ein dreidimensionales Objekt verwenden.
  • Mit CreateSharedBitmap zum Erstellen einer ID2D1Bitmap aus einer IDXGISurfacekönnen Sie eine Direct3D-Szene in eine Bitmap schreiben und mit Direct2D rendern.

Schreiben auf eine Direct3D-Oberfläche mit einem DXGI-Oberflächenrenderingziel

Um auf eine Direct3D-Oberfläche zu schreiben, erhalten Sie eine IDXGISurface und übergeben sie an die CreateDxgiSurfaceRenderTarget-Methode, um ein DXGI-Oberflächenrenderingziel zu erstellen. Sie können dann das DXGI-Oberflächenrenderingziel verwenden, um 2D-Inhalt auf die DXGI-Oberfläche zu zeichnen.

Ein DXGI-Oberflächenrenderingziel ist eine Art VON ID2D1RenderTarget. Wie bei anderen Direct2D-Renderzielen können Sie damit Ressourcen erstellen und Zeichnungsbefehle ausführen.

Das DXGI-Oberflächenrenderingziel und die DXGI-Oberfläche müssen das gleiche DXGI-Format verwenden. Wenn Sie beim Erstellen des Renderziels das _ FORMAT DXGI FORMAT _ UNGISN angeben, wird automatisch das Format der Oberfläche verwendet.

Das DXGI-Oberflächenrenderingziel führt keine DXGI-Oberflächensynchronisierung durch.

Erstellen einer DXGI-Oberfläche

Mit Direct3D 10 gibt es mehrere Möglichkeiten, eine DXGI-Oberfläche zu erhalten. Sie können eine IDXGISwapChain für ein Gerät erstellen und dann die GetBuffer-Methode der Austauschkette verwenden, um eine DXGI-Oberfläche zu erhalten. Oder Sie können ein Gerät verwenden, um eine Textur zu erstellen, und dann diese Textur als DXGI-Oberfläche verwenden.

Unabhängig davon, wie Sie die DXGI-Oberfläche erstellen, muss die Oberfläche eines der DXGI-Formate verwenden, die von DXGI-Oberflächenrenderingzielen unterstützt werden. Eine Liste finden Sie unter Unterstützte Pixelformate und Alphamodi.

Darüber hinaus muss die ID3D10Device1, die der DXGI-Oberfläche zugeordnet ist, BGRA DXGI-Formate unterstützen, damit die Oberfläche mit Direct2D funktioniert. Um diese Unterstützung sicherzustellen, verwenden Sie das Flag D3D10 _ CREATE DEVICE _ _ BGRA _ SUPPORT, wenn Sie die D3D10CreateDevice1-Methode aufrufen, um das Gerät zu erstellen.

Der folgende Code definiert eine Methode, die eine ID3D10Device1 erstellt. Es wählt die beste verfügbare Featureebene aus und fällt auf Windows Advanced Rasterization Platform (WARP) zurück, wenn kein Hardwarerendering verfügbar ist.

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

Im nächsten Codebeispiel wird die im vorherigen Beispiel gezeigte Methode verwendet, um ein Direct3D-Gerät zu erstellen, das DXGI-Oberflächen für die Verwendung mit CreateD3DDevice Direct2D erstellen kann.

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

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

Schreiben von Direct2D-Inhalt in einen Swap Chain-Puffer

Die einfachste Möglichkeit zum Hinzufügen von Direct2D-Inhalt zu einer Direct3D-Szene ist die Verwendung der GetBuffer-Methode einer IDXGISwapChain, um eine DXGI-Oberfläche zu erhalten. Verwenden Sie dann die Oberfläche mit der CreateDxgiSurfaceRenderTarget-Methode, um ein ID2D1RenderTarget-Objekt zu erstellen, mit dem Sie Ihren 2D-Inhalt zeichnen können.

Bei diesem Ansatz werden Ihre Inhalte nicht in drei Dimensionen gerendert. sie hat keine Perspektive oder Tiefe. Dies ist jedoch für mehrere allgemeine Aufgaben nützlich:

  • Erstellen eines 2D-Hintergrunds für eine 3D-Szene.
  • Erstellen einer 2D-Schnittstelle vor einer 3D-Szene.
  • Verwenden von Direct3D-Multisampling beim Rendern von Direct2D-Inhalt.

Im nächsten Abschnitt wird gezeigt, wie Sie einen 2D-Hintergrund für eine 3D-Szene erstellen.

Beispiel: Zeichnen eines 2D-Hintergrunds

In den folgenden Schritten wird beschrieben, wie Sie ein DXGI-Oberflächenrenderingziel erstellen und damit einen Farbverlaufshintergrund zeichnen.

  1. Verwenden Sie die CreateSwapChain-Methode, um eine Swapkette für eine ID3D10Device1 (die m _ pDevice-Variable) zu erstellen. Die Swapkette verwendet das DXGI _ FORMAT _ B8G8R8A8 _ UNORM DXGI-Format, eines der DXGI-Formate, die von Direct2D unterstützt werden.
    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);
    }
  1. Verwenden Sie die GetBuffer-Methode der Austauschkette, um eine DXGI-Oberfläche zu erhalten.
    // Get a surface in the swap chain
    hr = m_pSwapChain->GetBuffer(
        0,
        IID_PPV_ARGS(&pBackBuffer)
        );
  1. Verwenden Sie die DXGI-Oberfläche, um ein DXGI-Renderziel zu erstellen.
    // 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 chain

    
hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget(
        pBackBuffer,
        &props,
        &m_pBackBufferRT
        );
    
  1. Verwenden Sie das Renderziel, um einen Farbverlaufshintergrund zu zeichnen.
    // 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();
    }

Code wird in diesem Beispiel ausgelassen.

Verwenden von Direct2D-Inhalt als Textur

Eine weitere Möglichkeit, Direct2D-Inhalt mit Direct3D zu verwenden, ist die Verwendung von Direct2D zum Generieren einer 2D-Textur und das anschließende Anwenden dieser Textur auf ein 3D-Modell. Hierzu erstellen Sie eine ID3D10Texture2D,erhalten eine DXGI-Oberfläche aus der Textur und verwenden dann die Oberfläche, um ein DXGI-Oberflächenrenderingziel zu erstellen. Die ID3D10Texture2D-Oberfläche muss das Bindungsflag D3D10 _ BIND RENDER _ _ TARGET verwenden und ein DXGI-Format verwenden, das von DXGI-Oberflächenrenderingzielen unterstützt wird. Eine Liste der unterstützten DXGI-Formate finden Sie unter Unterstützte Pixelformate und Alphamodi.

Beispiel: Verwenden von Direct2D-Inhalt als Textur

Die folgenden Beispiele zeigen, wie Sie ein DXGI-Oberflächenrenderingziel erstellen, das in einer 2D-Textur gerendert wird (dargestellt durch id3D10Texture2D).

  1. Verwenden Sie zunächst ein Direct3D-Gerät, um eine 2D-Textur zu erstellen. Die Textur verwendet die Bindungsflags D3D10 _ BIND RENDER _ _ TARGET und D3D10 _ BIND _ SHADER _ RESOURCE und das DXGI FORMAT _ _ B8G8R8A8 _ UNORM DXGI-Format, eines der dxgi-Formate, die von Direct2D unterstützt werden.
    // Allocate a 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);
  1. Verwenden Sie die Textur, um eine DXGI-Oberfläche zu erhalten.
    IDXGISurface *pDxgiSurface = NULL;

    
hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
  1. Verwenden Sie die Oberfläche mit der CreateDxgiSurfaceRenderTarget-Methode, um ein Direct2D-Renderziel zu erhalten.
    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
            );
    }

Nachdem Sie nun ein Direct2D-Renderziel erhalten und es einer Direct3D-Textur zugeordnet haben, können Sie das Renderziel verwenden, um Direct2D-Inhalt auf diese Textur zu zeichnen, und Sie können diese Textur auf Direct3D-Primitive anwenden.

Code wird in diesem Beispiel ausgelassen.

Ändern der Größe eines DXGI-Oberflächenrenderingziels

DXGI-Oberflächenrenderingziele unterstützen die ID2D1RenderTarget::Resize-Methode nicht. Um die Größe eines DXGI-Oberflächenrenderingziels zu ändern, muss die Anwendung es veröffentlichen und neu erstellen.

Dieser Vorgang kann zu Leistungsproblemen führen. Das Renderziel ist möglicherweise die letzte aktive Direct2D-Ressource, die einen Verweis auf die ID3D10Device1 beibebe, die der DXGI-Oberfläche des Renderziels zugeordnet ist. Wenn die Anwendung das Renderziel frei gibt und der ID3D10Device1-Verweis zerstört wird, muss ein neues neu erstellt werden.

Sie können diesen potenziell teuren Vorgang vermeiden, indem Sie mindestens eine Direct2D-Ressource behalten, die vom Renderziel erstellt wurde, während Sie dieses Renderziel neu erstellen. Im Folgenden finden Sie einige Direct2D-Ressourcen, die für diesen Ansatz funktionieren:

Um diesem Ansatz gerecht zu werden, sollte Ihre Größenänderungsmethode testen, ob das Direct3D-Gerät verfügbar ist. Falls verfügbar, geben Sie Ihre DXGI-Oberflächenrenderziele frei, und erstellen Sie sie neu. Behalten Sie jedoch alle zuvor erstellten Ressourcen bei, und verwenden Sie sie wieder. Dies funktioniert, da Ressourcen, die von zwei Renderzielen erstellt wurden, kompatibel sind, wie in der Ressourcenübersichtbeschrieben, wenn beide Renderziele demselben Direct3D-Gerät zugeordnet sind.

Unterstützte Pixelformate und Alpha-Modi

CreateDxgiSurfaceRenderTarget

Windows DirectX-Grafiken