Direct2D導入(ID2D1DeviceContext)

下記記事にて、ID2D1HwndRenderTargetを利用してDirect2Dを利用してみました。
Direct2D導入(ID2D1HwndRenderTarget) - 何でもプログラミング

Direct2D 1.1からは、ID2D1DeviceContextを利用して実装するようになっています。

今回はID2D1DeviceContextを用いて上記記事と同じアプリを作成してみたいと思います。

f:id:any-programming:20171026193608p:plain

記事中のCreateHWND関数、Run関数は上記記事を参照してください。

IDXGIDevice1の作成

まずはIDXGIDevice1を作成します。

ID3D11Deviceを作成して、そこからIDXGIDevice1を取得します。

#include <dxgi1_2.h>

#include <d3d11.h>
#pragma comment(lib, "d3d11.lib")

ComPtr<IDXGIDevice1> CreateDXGIDevice()
{
    ComPtr<ID3D11Device> device;
    AssertHR(D3D11CreateDevice(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        0,
        D3D11_CREATE_DEVICE_BGRA_SUPPORT, // Direct2Dではこのフラグが必要
        nullptr,
        0,
        D3D11_SDK_VERSION,
        &device,
        nullptr,
        nullptr
    ));

    ComPtr<IDXGIDevice1> dxgiDevice;
    AssertHR(device.As(&dxgiDevice));
    AssertHR(dxgiDevice->SetMaximumFrameLatency(1)); // レイテンシを最小にしておく

    return dxgiDevice;
}


ID2D1DeviceContextの作成

IDXGIDevice1を利用して、ID2D1DeviceContextを作成します。

#include <d2d1_1.h>
#pragma comment(lib, "d2d1.lib")

ComPtr<ID2D1DeviceContext> CreateD2DContext(IDXGIDevice1* dxgiDevice)
{
    ComPtr<ID2D1Factory1> factory;
    AssertHR(D2D1CreateFactory<ID2D1Factory1>(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory));

    ComPtr<ID2D1Device> device;
    AssertHR(factory->CreateDevice(dxgiDevice, &device));

    ComPtr<ID2D1DeviceContext> context;
    AssertHR(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &context));

    return context;
}


IDXGISwapChain1の作成

Windowに描画結果を表示するため、SwapChainを作成します。

またSwapChainのBackBufferのID2D1Bitmap1を作成する関数も準備します。

ComPtr<IDXGISwapChain1> CreateSwapChain(IDXGIDevice1* device, HWND hwnd)
{
    ComPtr<IDXGIAdapter> adapter;
    AssertHR(device->GetAdapter(&adapter));

    ComPtr<IDXGIFactory2> factory;
    AssertHR(adapter->GetParent(IID_PPV_ARGS(&factory)));

    DXGI_SWAP_CHAIN_DESC1 desc = { 0 };
    desc.Width = 0; // 自動でWindowのサイズが設定される
    desc.Height = 0;
    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc.Stereo = false;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc.BufferCount = 2;
    desc.Scaling = DXGI_SCALING_NONE;
    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
    desc.Flags = 0;

    ComPtr<IDXGISwapChain1> swapChain;
    AssertHR(factory->CreateSwapChainForHwnd(
        device,
        hwnd,
        &desc,
        nullptr,
        nullptr,
        &swapChain
    ));

    return swapChain;
}
ComPtr<ID2D1Bitmap1> CreateBackBufferBitmap(ID2D1DeviceContext* context, IDXGISwapChain1* swapChain)
{
    ComPtr<IDXGISurface> backBuffer;
    AssertHR(swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)));

    auto prop = D2D1::BitmapProperties1(
        D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)
    );
    ComPtr<ID2D1Bitmap1> bitmap;
    AssertHR(context->CreateBitmapFromDxgiSurface(backBuffer.Get(), &prop, &bitmap));

    return bitmap;
}


楕円描画関数

Windowいっぱいに楕円を描画します。

void Draw(ID2D1DeviceContext* context)
{
    context->BeginDraw();

    context->Clear(D2D1::ColorF(D2D1::ColorF::LightYellow));

    D2D1_SIZE_F size = context->GetSize();
    float rx = size.width / 2;
    float ry = size.height / 2;
    D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(rx, ry), rx, ry);
    ComPtr<ID2D1SolidColorBrush> brush;
    AssertHR(context->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Orange), &brush));
    context->FillEllipse(ellipse, brush.Get());

    AssertHR(context->EndDraw());
}


アプリケーションコード

Drawで描画した際、Windowに表示するためにSwapChainのPresentを呼び出す必要があります。

サイズ変更は、一度ContextのTargetから外し、ResizeBuffersで変更した後、再度Targetに設定する必要があります。

ComPtr<ID2D1DeviceContext> g_context;
ComPtr<IDXGISwapChain1> g_swapChain;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_PAINT:
        {
            Draw(g_context.Get());
            DXGI_PRESENT_PARAMETERS params = { 0 };
            AssertHR(g_swapChain->Present1(1, 0, &params));
        }
        return 0;
    case WM_SIZE:
        g_context->SetTarget(nullptr);
        AssertHR(g_swapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0)); // サイズ0で、自動でWindowのサイズが設定される
        g_context->SetTarget(CreateBackBufferBitmap(g_context.Get(), g_swapChain.Get()).Get());
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
int main()
{
    HWND hwnd = CreateHWND(WndProc);

    auto dxgiDevice = CreateDXGIDevice();
    g_context = CreateD2DContext(dxgiDevice.Get());
    g_swapChain = CreateSwapChain(dxgiDevice.Get(), hwnd);
    g_context->SetTarget(CreateBackBufferBitmap(g_context.Get(), g_swapChain.Get()).Get());

    Run(hwnd);

    return 0;
}