Direct2D導入(ID2D1DeviceContext)
下記記事にて、ID2D1HwndRenderTargetを利用してDirect2Dを利用してみました。
Direct2D導入(ID2D1HwndRenderTarget) - 何でもプログラミング
Direct2D 1.1からは、ID2D1DeviceContextを利用して実装するようになっています。
今回はID2D1DeviceContextを用いて上記記事と同じアプリを作成してみたいと思います。
記事中の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, ¶ms)); } 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; }