読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

Direct2Dの初期化󠄁今昔

Direct2Dが出た當初はレンダリングにID2D1HwndRenderTargetと云ふものを使󠄁ふのが普通󠄁で、これだけで初期化󠄁と描畫ができた(あの頃既󠄀にMicrosoft::WRL::ComPtrがあつたかどうかは怪しいけど)。

Microsoft::WRL::ComPtr<ID2D1Factory> d2d1Factory;
Microsoft::WRL::ComPtr<ID2D1HwndRenderTarget> d2d1HwndRenderTarget;

bool InitOldDirect2D(HWND wnd, const D2D1_SIZE_U &size) {
  // ID2D1Factoryを作つて
  if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&d2d1Factory))) {
    return false;
  }
  // ID2D1HwndRenderTargetを作るだけ!
  if (FAILED(d2d1Factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
                                                 D2D1::HwndRenderTargetProperties(hwnd, size),
                                                 &d2d1HwndRenderTarget))) {
    return false;
  }
  return true;
}

void Render() {
  d2d1HwndRenderTarget->BeginDraw();
  // ここで描畫
  d2d1HwndRenderTarget->EndDraw();
}

しかし今ID2D1HwndRenderTargetを使󠄁ふとなると色々描畫面で物足りなさが出てくる。最近󠄁はどうもID2D1DeviceContextで描畫するのが主流らしい(あとID2D1HwndRenderTargetはストアアプリだと動作しないらしい)のだがこれの初期化󠄁はとてつもなく面倒臭󠄁い。

Microsoft::WRL::ComPtr<ID2D1Factory1> d2d1Factory;
Microsoft::WRL::ComPtr<ID2D1Device> d2d1Device;
Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1Context;
Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgiSwapChain;
Microsoft::WRL::ComPtr<ID2D1Bitmap1> d2d1Bitmap;

bool InitNewDirect2D(HWND wnd) {
#ifdef _DEBUG
  // なんとDirect3D11のデバイス、ID3D11Deviceを作る!
  const UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED;
#else
  const UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED;
#endif
  Microsoft::WRL::ComPtr<ID3D11Device> d3d11Device;
  if (FAILED(D3D11CreateDevice(nullptr, 
                               D3D_DRIVER_TYPE_HARDWARE,
                               nullptr.
                               flags
                               nullptr,
                               0,
                               D3D11_SDK_VERSION,
                               &d3d11Device,
                               nullptr,
                               nullptr))) {
    return false;
  }
  // ID3D11DeviceのDXGIのデバイスのIDXGIDevice1を取る
  Microsoft::WRL::ComPtr<IDXGIDevice1> dxgiDevice;
  if (FAILED(d3d11Device.As(&dxgiDevice))) {
    return false;
  }
  // ID2D1Factory1を作つて
  if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&d2d1Factory)))) {
    return false;
  }
  // ID2D1Deviceを作つて
  if (FAILED(d2d1Factory->CreateDevice(dxgiDevice.Get(), &d2d1Device))) {
    return false;
  }
  // 漸くID2D1DeviceContextを作る
  if (FAILED(d2d1Dev->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1Context))) {
    return false;
  }
  // まだまだ續くよDXGIのデバイスのIDXGIAdapterを取得して
  Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;
  if (FAILED(dxgiDevice->GetAdapter(&dxgiAdapter))) {
    return false;
  }
  // IDXGIFactory2を取得して
  Microsoft::WRL::ComPtr<IDXGIFactory2> dxgiFactory;
  if (FAILED(dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)))) {
    return false;
  }
  // IDXGISwapChain1を作る
  DXGI_SWAP_CHAIN_DESC1 desc = {0};
  desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  desc.SampleDesc.Count = 1;
  desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  desc.BufferCount = 2;
  desc.Scaling = DXGI_SCALING_NONE;
  desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
  if (FAILED(dxgiFactory->CreateSwapChainForHwnd(d3d11Device.Get(),
                                                 wnd,
                                                 &desc,
                                                 nullptr,
                                                 nullptr,
                                                 &dxgiSwapChain))) {
    return false;
  }
  if (FAILED(dxgiDevice->SetMaximumFrameLatency(1))) {
    return false;
  }
  // IDXGISwapChain1のバックバッファをIDXGISurfaceで取つて
  Microsoft::WRL::ComPtr<IDXGISurface> dxgiSurface;
  if (FAILED(dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiSurface)))) {
    return false;
  }
  // それを元にID2D1Bitmap1を作つて
  const auto bp = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, 
                                          D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));
  if (FAILED(d2d1Context->CreateBitmapFromDxgiSurface(dxgiSurface.Get(), &bp, &d2d1Bitmap))) {
    return false;
  }
  // ID2D1DeviceContextのレンダリング先に指定して完結!
  d2d1Context->SetTarget(d2d1Bitmap.Get());
  return true;
}

void Render() {
  d2d1HwndRenderTarget->BeginDraw();
  // ここで描畫
  d2d1Context->EndDraw();
  DXGI_PRESENT_PARAMETERS parameters = {};
  dxgiSwapChain->Present1(1, 0, &parameters);
}

描畫部分はあまり變らないのだが初期化󠄁部分󠄁が4倍くらゐに膨れてゐる。しかもID2D1HwndRenderTargetの時はDirect2D内で完結してゐたのに、ID2D1DeviceContextになるとDirect3Dの低レイヤであるDXGIを介してDirect3Dと連󠄀繫しないといけないと云ふのがなんとも面倒である。
ストアアプリならID2D1DeviceContextしか選󠄁擇肢がない訣だが、デスクトップアプリでもID2D1HwndRenderTargetでは簡單にはできなかつた加算合成󠄁がメンバ函數1つで出來るやうになつてゐたりするので初期化󠄁を頑張つただけの甲斐󠄁はある。と思ふ。