Поделиться через


Обзор взаимодействия Direct2D и Direct3D

Аппаратное ускорение 2-D и трехмерной графики все чаще становится частью неигровых приложений, и большинство игровых приложений используют 2-D графику в виде меню и Heads-Up дисплеев (HUD). Существует значительная польза, которую можно получить, если обеспечить возможность эффективного совмещения традиционной 2-D отрисовки с отрисовкой Direct3D.

В этом разделе описывается интеграция графики 2-D и 3-D с помощью Direct2D и Direct3D.

Необходимые условия

В этом обзоре предполагается, что вы знакомы с основными операциями рисования Direct2D. Руководство см. в статье Создание простого приложения Direct2D. Кроме того, предполагается, что вы можете программировать с помощью Direct3D 10.1.

Поддерживаемые версии Direct3D

С помощью DirectX 11.0, Direct2D поддерживает взаимодействие только с устройствами Direct3D 10.1. С помощью DirectX 11.1 или более поздней версии Direct2D также поддерживает взаимодействие с Direct3D 11.

Взаимодействие через DXGI

По состоянию на Direct3D 10 среда выполнения Direct3D использует DXGI для управления ресурсами. Уровень среды выполнения DXGI обеспечивает межпроцессный общий доступ к поверхностям памяти видео и служит основой для других платформ среды выполнения на основе видео. Direct2D использует DXGI для взаимодействия с Direct3D.

Существует два основных способа совместного использования Direct2D и Direct3D:

  • Вы можете записать содержимое Direct2D на поверхность Direct3D, получив IDXGISurface и используя его с CreateDxgiSurfaceRenderTarget для создания ID2D1RenderTarget. Затем можно использовать целевой объект отрисовки для добавления двухмерного интерфейса или фона в трехмерную графику или использовать рисунок Direct2D в качестве текстуры для трехмерного объекта.
  • Используя CreateSharedBitmap для создания ID2D1Bitmap из IDXGISurface, можно перенести сцену Direct3D в растровое изображение и визуализировать ее с помощью Direct2D.

Запись на поверхность Direct3D с целевым объектом рендеринга поверхности DXGI

Чтобы записать данные на поверхность Direct3D, необходимо получить IDXGISurface и передать его в метод CreateDxgiSurfaceRenderTarget для создания целевой поверхности DXGI. Затем можно использовать DXGI-поверхность в качестве целевого объекта для рисования 2D-содержимого.

Целевой объект для отрисовки поверхности DXGI — это своего рода ID2D1RenderTarget. Как и другие целевые объекты отрисовки Direct2D, вы можете использовать его для создания ресурсов и выдачи команд рисования.

Целевой объект рендеринга поверхности DXGI и сама поверхность DXGI должны использовать один и тот же формат DXGI. Если при создании целевой поверхности отрисовки указать DXGI_FORMAT_UNKNOWN формат, он будет автоматически использовать формат поверхности.

Целевой объект отрисовки поверхности DXGI не выполняет синхронизацию поверхности DXGI.

Создание поверхности DXGI

При использовании Direct3D 10 существует несколько способов получения поверхности DXGI. Вы можете создать IDXGISwapChain для устройства, а затем использовать метод GetBuffer цепи обмена для получения поверхности DXGI. Кроме того, можно использовать устройство для создания текстуры, а затем использовать эту текстуру в качестве поверхности DXGI.

Независимо от того, как вы создаете поверхность DXGI, поверхность должна использовать один из форматов DXGI, поддерживаемых целевыми объектами отрисовки поверхности DXGI. Список см. в разделе Поддерживаемые форматы пикселей и альфа-режимы.

Кроме того, ID3D10Device1 , связанное с поверхностью DXGI, должно поддерживать форматы BGRA DXGI, чтобы поверхность могла работать с Direct2D. Чтобы обеспечить эту поддержку, используйте флаг D3D10_CREATE_DEVICE_BGRA_SUPPORT при вызове метода D3D10CreateDevice1 для создания устройства.

Следующий код определяет метод, который создает 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, показанный в предыдущем примере, для создания устройства Direct3D, которое может создавать поверхности DXGI для использования с Direct2D.

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

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

Запись содержимого Direct2D в буфер цепочки буферов

Самый простой способ добавления содержимого Direct2D в сцену Direct3D — использовать метод GetBuffer объекта IDXGISwapChain для получения поверхности DXGI, затем используйте поверхность с методом CreateDxgiSurfaceRenderTarget для создания объекта ID2D1RenderTarget, с помощью которого можно рисовать ваше 2-D содержимое.

Такой подход не отображает ваше содержимое в трех измерениях; он не будет иметь перспективы или глубины. Однако это полезно для нескольких распространенных задач:

  • Создание 2-D фона для трехмерной сцены.
  • Создание 2-D-интерфейса перед трехмерной сценой.
  • Использование мультисэмплинга Direct3D при рендеринге содержимого Direct2D.

В следующем разделе показано, как создать 2-D фон для трехмерной сцены.

Пример. Рисование 2-D фона

Ниже описано, как создать целевой объект визуализации поверхности DXGI и использовать его, чтобы создать градиентный фон.

  1. Используйте метод CreateSwapChain для создания цепочки буферов для ID3D10Device1 (переменная m_pDevice). В цепочке обмена используется формат DXGI DXGI_FORMAT_B8G8R8A8_UNORM, который является одним из форматов DXGI, поддерживаемых 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. Используйте метод GetBuffer для цепочки обмена, чтобы получить поверхность DXGI.

    // Get a surface in the swap chain
    hr = m_pSwapChain->GetBuffer(
        0,
        IID_PPV_ARGS(&pBackBuffer)
        );
    
  3. Используйте поверхность DXGI для создания целевого объекта отрисовки 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. Используйте целевой объект отрисовки для отрисовки градиентного фона.

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

Код опущен из этого примера.

Использование содержимого Direct2D в качестве текстуры

Другим способом использования содержимого Direct2D с Direct3D является использование Direct2D для создания 2-D текстуры, а затем применения этой текстуры к трехмерной модели. Для этого необходимо создать ID3D10Texture2D, получить поверхность DXGI из текстуры, а затем использовать поверхность для создания целевого объекта отрисовки поверхности DXGI. Поверхность ID3D10Texture2D должна использовать флаг привязки D3D10_BIND_RENDER_TARGET и использовать формат DXGI, поддерживаемый целевыми объектами отрисовки DXGI поверхности. Список поддерживаемых форматов DXGI см. в разделе Поддерживаемые форматы пикселей и альфа-режимы.

Пример. Использование содержимого Direct2D в качестве текстуры

В следующих примерах показано, как создать целевой объект отрисовки поверхности DXGI, который отрисовывается в 2-D текстуре (представленной ID3D10Texture2D).

  1. Во-первых, используйте устройство Direct3D для создания 2-D текстуры. Текстура использует флаги привязки D3D10_BIND_RENDER_TARGET и D3D10_BIND_SHADER_RESOURCE и использует формат DXGI_FORMAT_B8G8R8A8_UNORM DXGI, один из форматов DXGI, поддерживаемых 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. Получите поверхность DXGI, используя текстуру.

    IDXGISurface *pDxgiSurface = NULL;
    
    hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
    
  3. Используйте область с методом CreateDxgiSurfaceRenderTarget для получения целевого объекта отрисовки 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);
    }
    

Теперь, когда вы получили целевой объект отрисовки Direct2D и связали его с текстурой Direct3D, можно использовать целевой объект отрисовки для рисования содержимого Direct2D к этой текстуре, и вы можете применить эту текстуру к примитивам Direct3D.

Код опущен из этого примера.

Изменение размера целевого объекта отрисовки поверхности DXGI

Целевые объекты отрисовки поверхности DXGI не поддерживают метод ID2D1RenderTarget::Resize. Чтобы изменить размер поверхности целевого объекта отрисовки DXGI, приложение должно сначала освободить его, а затем снова создать.

Эта операция может создать проблемы с производительностью. Целевой объект отрисовки может быть последним активным ресурсом Direct2D, который сохраняет ссылку на ID3D10Device1, поверхность DXGI которого связана с целевым объектом отрисовки. Если приложение освобождает целевой объект отрисовки, а ссылка на ID3D10Device1 уничтожена, необходимо повторно создать новый объект.

Эту потенциально дорогостоящую операцию можно избежать, сохранив по крайней мере один ресурс Direct2D, созданный целевым объектом отрисовки при повторном создании целевого объекта отрисовки. Ниже приведены некоторые ресурсы Direct2D, которые работают для этого подхода:

Для этого подхода метод изменения размера должен проверить, доступно ли устройство Direct3D. Если это возможно, освободите и заново создайте целевые объекты отрисовки поверхности DXGI, но сохраните все ресурсы, созданные ранее, и повторно используйте их. Это работает потому, что, как описано в разделе Обзор ресурсов, ресурсы, созданные двумя целевыми поверхностями отрисовки, совместимы, если обе целевые поверхности отрисовки связаны с тем же устройством Direct3D.

поддерживаемые форматы пикселей и альфа-режимы

CreateDxgiSurfaceRenderTarget

Windows DirectX графика