다음을 통해 공유


Direct2D 및 Direct3D 상호 운용성 개요

하드웨어 가속 2차원 및 3차원 그래픽은 점점 비게임 애플리케이션의 일부가 되고 있으며, 대부분의 게임 애플리케이션은 메뉴와 Heads-Up 디스플레이(HUD) 형태로 2차원 그래픽을 사용합니다. 기존 2D 렌더링을 Direct3D 렌더링과 효율적으로 혼합할 수 있도록 하여 추가할 수 있는 많은 값이 있습니다.

이 항목에서는 Direct2D 및 Direct3D를 사용하여 2D 및 3D 그래픽을 통합하는 방법에 대해 설명합니다.

사전 요구 사항

이 개요에서는 기본 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를 함께 사용하는 두 가지 기본 방법이 있습니다.

DXGI 표면 렌더링 대상을 사용하여 Direct3D 표면에 쓰기

Direct3D 표면에 쓰려면 IDXGISurface 를 가져와 CreateDxgiSurfaceRenderTarget 메서드에 전달하여 DXGI 표면 렌더링 대상을 만듭니다. 그런 다음 DXGI 표면 렌더링 대상을 사용하여 DXGI 표면에 2차원 콘텐츠를 그릴 수 있습니다.

DXGI 표면 렌더링 대상은 일종의 ID2D1RenderTarget입니다. 다른 Direct2D 렌더링 대상과 마찬가지로 이를 사용하여 리소스를 만들고 그리기 명령을 실행할 수 있습니다.

DXGI 표면 렌더링 대상과 DXGI 표면은 동일한 DXGI 형식을 사용해야 합니다. 렌더링 대상을 만들 때 DXGI_FORMAT_UNKOWN 형식을 지정하면 표면의 형식이 자동으로 사용됩니다.

DXGI 표면 렌더링 대상은 DXGI 표면 동기화를 수행하지 않습니다.

DXGI 표면 만들기

Direct3D 10에서는 여러 가지 방법으로 DXGI 표면을 얻을 수 있습니다. 디바이스에 대한 IDXGISwapChain 을 만든 다음 스왑 체인의 GetBuffer 메서드를 사용하여 DXGI 표면을 가져올 수 있습니다. 또는 디바이스를 사용하여 텍스처를 만든 다음 해당 텍스처를 DXGI 표면으로 사용할 수 있습니다.

DXGI 표면을 만드는 방법에 관계없이 표면은 DXGI 표면 렌더링 대상에서 지원하는 DXGI 형식 중 하나를 사용해야 합니다. 목록은 지원되는 픽셀 형식 및 알파 모드를 참조하세요.

또한 DXGI 표면과 연결된 ID3D10Device1 은 Surface가 Direct2D에서 작동하도록 BGRA DXGI 형식을 지원해야 합니다. 이 지원을 보장하려면 D3D10CreateDevice1 메서드를 호출하여 디바이스를 만들 때 D3D10_CREATE_DEVICE_BGRA_SUPPORT 플래그를 사용합니다.

다음 코드는 ID3D10Device1을 만드는 메서드를 정의합니다. 사용 가능한 최상의 기능 수준을 선택하고 하드웨어 렌더링을 사용할 수 없는 경우 WARP(Windows Advanced Rasterization Platform) 로 대체됩니다.

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 메서드를 사용하여 Direct2D와 함께 사용할 DXGI 표면을 만들 수 있는 Direct3D 디바이스를 만듭니다.

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

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

스왑 체인 버퍼에 Direct2D 콘텐츠 쓰기

Direct3D 장면에 Direct2D 콘텐츠를 추가하는 가장 간단한 방법은 IDXGISwapChainGetBuffer 메서드를 사용하여 DXGI 표면을 가져온 다음, CreateDxgiSurfaceRenderTarget 메서드와 함께 표면을 사용하여 2D 콘텐츠를 그릴 ID2D1RenderTarget을 만드는 것입니다.

이 방법은 콘텐츠를 3차원으로 렌더링하지 않습니다. 큐브 뷰 또는 깊이가 없습니다. 그러나 다음과 같은 몇 가지 일반적인 작업에 유용합니다.

  • 3D 장면에 대한 2D 배경을 만듭니다.
  • 3D 장면 앞에 2D 인터페이스를 만듭니다.
  • Direct2D 콘텐츠를 렌더링할 때 Direct3D 다중 샘플링 사용.

다음 섹션에서는 3차원 장면에 대한 2차원 배경을 만드는 방법을 보여 드립니다.

예: 2D 배경 그리기

다음 단계에서는 DXGI 표면 렌더링 대상을 만들고 이를 사용하여 그라데이션 배경을 그리는 방법을 설명합니다.

  1. CreateSwapChain 메서드를 사용하여 ID3D10Device1(m_pDevice 변수)에 대한 스왑 체인을 만듭니다. 스왑 체인은 Direct2D에서 지원하는 DXGI 형식 중 하나인 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 형식을 사용합니다.

    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 콘텐츠를 텍스처로 사용

Direct3D에서 Direct2D 콘텐츠를 사용하는 또 다른 방법은 Direct2D를 사용하여 2차원 텍스처를 생성한 다음 해당 텍스처를 3차원 모델에 적용하는 것입니다. ID3D10Texture2D를 만들고, 텍스처에서 DXGI 표면을 가져온 다음, 표면을 사용하여 DXGI 표면 렌더링 대상을 만들어 이 작업을 수행합니다. ID3D10Texture2D 표면은 D3D10_BIND_RENDER_TARGET 바인딩 플래그를 사용하고 DXGI 표면 렌더링 대상에서 지원하는 DXGI 형식을 사용해야 합니다. 지원되는 DXGI 형식 목록은 지원되는 픽셀 형식 및 알파 모드를 참조하세요.

예: Direct2D 콘텐츠를 텍스처로 사용

다음 예제에서는 2차원 텍스처( ID3D10Texture2D로 표시됨)로 렌더링되는 DXGI 표면 렌더링 대상을 만드는 방법을 보여 줍니다.

  1. 먼저 Direct3D 디바이스를 사용하여 2차원 텍스처를 만듭니다. 텍스처는 D3D10_BIND_RENDER_TARGETD3D10_BIND_SHADER_RESOURCE 바인딩 플래그를 사용하며 Direct2D에서 지원하는 DXGI 형식 중 하나인 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 형식을 사용합니다.

    // 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. Surface를 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 표면 렌더링 대상의 크기를 조정하려면 애플리케이션이 DXGI 표면 렌더링 대상을 해제하고 다시 만들어야 합니다.

이 작업은 잠재적으로 성능 문제를 일으킬 수 있습니다. 렌더링 대상은 렌더링 대상의 DXGI 표면과 연결된 ID3D10Device1 에 대한 참조를 유지하는 마지막 활성 Direct2D 리소스일 수 있습니다. 애플리케이션이 렌더링 대상을 해제하고 ID3D10Device1 참조가 제거되면 새 참조를 다시 만들어야 합니다.

렌더링 대상을 다시 만드는 동안 렌더링 대상에서 만든 Direct2D 리소스를 하나 이상 유지하여 비용이 많이 드는 작업을 방지할 수 있습니다. 다음은 이 접근 방식에 적합한 몇 가지 Direct2D 리소스입니다.

이 접근 방식을 수용하려면 크기 조정 방법을 테스트하여 Direct3D 디바이스를 사용할 수 있는지 확인해야 합니다. 사용 가능한 경우 DXGI 표면 렌더링 대상을 해제하고 다시 만들지만 이전에 만든 모든 리소스를 유지하고 다시 사용합니다. 이는 리소스 개요에 설명된 대로 두 렌더링 대상이 모두 동일한 Direct3D 디바이스와 연결될 때 두 렌더링 대상에서 만든 리소스가 호환되기 때문에 작동합니다.

지원되는 픽셀 형식 및 알파 모드

CreateDxgiSurfaceRenderTarget

Windows DirectX 그래픽