如何使用 Direct2D 设备上下文呈现

在本主题中,您将了解到如何在 Windows 8 中创建 Direct2D 设备上下文。 如果您正在使用 Direct2D 开发 Windows 应用商店应用或桌面应用,则此信息适用于您。 本主题介绍 Direct2D 设备上下文对象的用途、如何创建该对象,以及有关呈现和显示 Direct2D 基元和图像的分步指南。 您还将了解到如何切换呈现目标以及向应用添加效果。

什么是 Direct2D 设备?

您需要 Direct2D 设备和 Direct3D 设备才能创建 Direct2D 设备上下文。 Direct2D 设备(公开 ID2D1Device 接口指针)表示显示适配器。 Direct3D 设备(公开 ID3D11Device 接口指针)与 Direct2D 设备相关联。 每个应用必须具有一个 Direct2D 设备,但可以有多个 设备

什么是 Direct2D 设备上下文?

Direct2D 设备上下文(公开 ID2D1DeviceContext 接口指针)表示一组用于呈现到目标的状态和命令缓冲区。 您可以在设备上下文中调用方法来设置管道状态,并使用设备拥有的资源生成呈现命令。

在 Windows 8 上使用 Direct2D 进行呈现

在 Windows 7 及更早版本中,您可以使用 ID2D1HwndRenderTarget 或其他呈现目标接口呈现到窗口或表面。 从 Windows 8 开始,我们不建议使用依赖于 ID2D1HwndRenderTarget 等接口的方法进行呈现,因为它们不适用于 Windows 应用商店应用。 如果要创建桌面应用,但仍利用设备上下文的其他功能,可以使用设备上下文呈现给 Hwnd。 但是,使用 Direct2D 在 Windows 应用商店应用中呈现内容需要设备上下文

为什么使用设备上下文来呈现?

如何创建用于呈现的 Direct2D 设备上下文

此处的代码演示如何创建 Direct3D11 设备、获取关联的 DXGI 设备、创建 Direct2D 设备,最后创建用于呈现的 Direct2D 设备上下文

下面是方法调用和此代码使用的接口的示意图。

direct2d 和 direct3d 设备和设备上下文的示意图。

注意

此代码假定您已有 ID2D1Factory1 对象,有关详细信息,请参阅 ID2D1Factory 参考页

 

    // This flag adds support for surfaces with a different color channel ordering than the API default.
    // You need it for compatibility with Direct2D.
    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
    // This array defines the set of DirectX hardware feature levels this app  supports.
    // The ordering is important and you should  preserve it.
    // Don't forget to declare your app's minimum required feature level in its
    // description.  All apps are assumed to support 9.1 unless otherwise stated.
    D3D_FEATURE_LEVEL featureLevels[] = 
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    // Create the DX11 API device object, and get a corresponding context.
    ComPtr<ID3D11Device> device;
    ComPtr<ID3D11DeviceContext> context;

    DX::ThrowIfFailed(
        D3D11CreateDevice(
            nullptr,                    // specify null to use the default adapter
            D3D_DRIVER_TYPE_HARDWARE,
            0,                          
            creationFlags,              // optionally set debug and Direct2D compatibility flags
            featureLevels,              // list of feature levels this app can support
            ARRAYSIZE(featureLevels),   // number of possible feature levels
            D3D11_SDK_VERSION,          
            &device,                    // returns the Direct3D device created
            &m_featureLevel,            // returns feature level of device created
            &context                    // returns the device immediate context
            )
        );

    ComPtr<IDXGIDevice> dxgiDevice;
    // Obtain the underlying DXGI device of the Direct3D11 device.
    DX::ThrowIfFailed(
        device.As(&dxgiDevice)
        );

    // Obtain the Direct2D device for 2-D rendering.
    DX::ThrowIfFailed(
        m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
        );

    // Get Direct2D device's corresponding device context object.
    DX::ThrowIfFailed(
        m_d2dDevice->CreateDeviceContext(
            D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
            &m_d2dContext
            )
        );

让我们回顾前面代码示例中的步骤。

  1. 获取 ID3D11Device 接口指针,创建设备上下文时将需要该指针。

  2. 查询 Direct3D 11 设备以获取其 DXGI 设备接口。

  3. 通过调用 ID2D1Factory::CreateDevice 方法并传入 IDXGIDevice 对象来创建 ID2D1Device 对象。

  4. 使用 ID2D1Device::CreateDeviceContext 方法创建 ID2D1DeviceContext 指针。

选择目标

此处的代码演示如何获取窗口后退缓冲区的二维 Direct3D 纹理,并创建一个位图目标,该位图目标链接到 Direct2D 设备上下文所呈现的此纹理。

        // Allocate a descriptor.
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
        swapChainDesc.Width = 0;                           // use automatic sizing
        swapChainDesc.Height = 0;
        swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format
        swapChainDesc.Stereo = false; 
        swapChainDesc.SampleDesc.Count = 1;                // don't use multi-sampling
        swapChainDesc.SampleDesc.Quality = 0;
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.BufferCount = 2;                     // use double buffering to enable flip
        swapChainDesc.Scaling = DXGI_SCALING_NONE;
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect
        swapChainDesc.Flags = 0;

        // Identify the physical adapter (GPU or card) this device is runs on.
        ComPtr<IDXGIAdapter> dxgiAdapter;
        DX::ThrowIfFailed(
            dxgiDevice->GetAdapter(&dxgiAdapter)
            );

        // Get the factory object that created the DXGI device.
        ComPtr<IDXGIFactory2> dxgiFactory;
        DX::ThrowIfFailed(
            dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
            );

        // Get the final swap chain for this window from the DXGI factory.
        DX::ThrowIfFailed(
            dxgiFactory->CreateSwapChainForCoreWindow(
                device.Get(),
                reinterpret_cast<IUnknown*>(m_window),
                &swapChainDesc,
                nullptr,    // allow on all displays
                &m_swapChain
                )
            );

        // Ensure that DXGI doesn't queue more than one frame at a time.
        DX::ThrowIfFailed(
            dxgiDevice->SetMaximumFrameLatency(1)
            );

    // Get the backbuffer for this window which is be the final 3D render target.
    ComPtr<ID3D11Texture2D> backBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
        );

    // Now we set up the Direct2D render target bitmap linked to the swapchain. 
    // Whenever we render to this bitmap, it is directly rendered to the 
    // swap chain associated with the window.
    D2D1_BITMAP_PROPERTIES1 bitmapProperties = 
        BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
            m_dpi,
            m_dpi
            );

    // Direct2D needs the dxgi version of the backbuffer surface pointer.
    ComPtr<IDXGISurface> dxgiBackBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
        );

    // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
    DX::ThrowIfFailed(
        m_d2dContext->CreateBitmapFromDxgiSurface(
            dxgiBackBuffer.Get(),
            &bitmapProperties,
            &m_d2dTargetBitmap
            )
        );

    // Now we can set the Direct2D render target.
    m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

让我们回顾前面代码示例中的步骤。

  1. 分配 DXGI_SWAP_CHAIN_DESC1 结构并定义交换链的设置。

    这些设置举例说明了如何创建 Windows 应用商店应用可以使用的交换链。

  2. 获取 Direct3D 设备 DXGI 设备正在运行的适配器,并获取与其关联的 IDXGIFactory 对象。 您必须使用此 DXGI 工厂来确保在同一适配器上创建交换链

  3. 调用 IDXGIFactory2::CreateSwapChainForCoreWindow 方法以创建交换链。 将 Windows::UI::CoreWindow 类用于 Windows 应用商店应用的主窗口。

    请确保将最大帧延迟设置为 1,以最大程度地减少能耗。

    如果要在 Windows 应用商店应用中呈现 Direct2D 内容,请参阅 CreateSwapChainForComposition 方法。

  4. 交换链中获取后退缓冲区。 后台缓冲区公开由 交换链分配的 ID3D11Texture2D 接口。

  5. 声明 D2D1_BITMAP_PROPERTIES1 结构并设置属性值。 将像素格式设置为 BGRA,因为这是 Direct3D 设备DXGI 设备使用的格式。

  6. 获取作为 IDXGISurface 的后缓冲区,以传递给 Direct2D。 Direct2D 不直接接受 ID3D11Texture2D

    使用 ID2D1DeviceContext::CreateBitmapFromDxgiSurface 方法从后台缓冲区创建 ID2D1Bitmap 对象。

  7. 现在 Direct2D 位图 已链接到后台缓冲区。 将 Direct2D 设备上下文上的目标设置为位图

如何呈现和显示

有了目标位图后,您可以使用 Direct2D 设备上下文将基元、图像、图像效果和文本绘制到目标位图上。 此处的代码演示如何绘制矩形。

ComPtr<ID2D1SolidColorBrush> pBlackBrush;
DX::ThrowIfFailed(
   m_d2dContext->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF::Black),
        &pBlackBrush
        )
);

m_d2dContext->BeginDraw();

m_d2dContext->DrawRectangle(
    D2D1::RectF(
        rc.left + 100.0f,
        rc.top + 100.0f,
        rc.right - 100.0f,
        rc.bottom - 100.0f),
        pBlackBrush);

DX::ThrowIfFailed(
    m_d2dContext->EndDraw()
);

DX::ThrowIfFailed(
    m_swapChain->Present1(1, 0, &parameters);
);

让我们回顾前面代码示例中的步骤。

  1. 调用 CreateSolidColorBrush 以创建画笔来绘制矩形。
  2. 在发出任何绘图命令之前调用 BeginDraw 方法。
  3. 调用要绘制的矩形和画笔的 DrawRectangle 方法。
  4. 完成发出绘图命令后,调用 EndDraw 方法。
  5. 通过调用 IDXGISwapChain::Present 方法显示结果。

现在,您可以使用 Direct2D 设备上下文 将基元、图像、图像效果和文本绘制到屏幕。