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

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

В этом разделе описывается интеграция объемной и трехмерной графики с помощью 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 для рисования 2-D содержимого на поверхности DXGI.

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

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

Целевой объект отрисовки поверхности 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;
}

В следующем примере кода используется метод CreateD3Device , показанный в предыдущем примере, для создания устройства 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 — использовать метод GetBufferIDXGISwapChain для получения поверхности DXGI, а затем использовать поверхность с методом CreateDxgiSurfaceRenderTarget для создания ID2D1RenderTarget , с помощью которого будет отрисовываться двухмерное содержимое.

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

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

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

Пример. Рисование двухуровневого фона

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

  1. Используйте метод CreateSwapChain , чтобы создать цепочку буферов для ID3D10Device1 (переменная m_pDevice ). Цепочка буферов использует формат DXGI_FORMAT_B8G8R8A8_UNORM DXGI, один из форматов 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 для создания объемной текстуры, а затем применить ее к трехмерной модели. Для этого создайте ID3D10Texture2D, получите поверхность DXGI из текстуры, а затем с помощью поверхности создайте целевой объект отрисовки поверхности DXGI. Поверхность ID3D10Texture2D должна использовать флаг привязки D3D10_BIND_RENDER_TARGET и формат DXGI, поддерживаемый целевыми объектами отрисовки поверхности DXGI. Список поддерживаемых форматов DXGI см. в разделе Поддерживаемые форматы пикселей и альфа-режимы.

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

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

  1. Сначала используйте устройство Direct3D для создания объемной текстуры. Текстура использует флаги 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