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

ゆとりーなの日記

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

デスクトップアプリでDirect3D11+Direct2Dの初期化󠄁まとめ

MSDNだとストアアプリの解說が多くて紛󠄁らはしいが色々見て見た感じだと最終󠄁的にこんな感じにすれば良ささう。Windows7でも動くやうにDirect3D11.1を使󠄁つた(が8.1でしか動作確認󠄁してゐないので動くかは知らない)。

// 窓を作る
HWND MakeWindow(int width, int height, const wchar_t *title) {
  // ウインドウクラスの登錄
  // Direct3Dを使つてPeekMessageで描畫を回すならstyleのCS_HREDRAW | CS_VREDRAWは多分󠄁要󠄁らない
  // hbrBackgroundもNULLで良ささう
  WNDCLASSEX wc = {};
  wc.cbSize = sizeof(wc);
  wc.lpfnWndProc = &WindowProcedure;
  wc.hInstance = GetModuleHandle(nullptr);
  wc.hCursor = LoadCursor(nullptr, MAKEINTRESOURCE(IDC_ARROW)); // LoadImageの方が良い說はある
  wc.lpszClassName = L"SampleWindowClass";
  const auto atom = RegisterClassEx(&wc);
  if (!atom) {
    ThrowLastError("ウィンドウクラスの登錄に失敗しました。");
  }
  // 描畫領域サイズを元に窓サイズを計算
  const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; // 最小化󠄁以外のサイズ變更󠄁ができないスタイル
  RECT rect = {0, 0, width, height};
  if (!AdjustWindowRect(&rect, style, FALSE)) {
    ThrowLastError("ウィンドウサイズの計算に失敗しました。");
  }
  // 窓を作る
  const auto hwnd = CreateWindow(MAKEINTATOM(atom),
                                 title,
                                 style,
                                 CW_USEDEFAULT,
                                 CW_USEDEFAULT,
                                 rect.right - rect.left,
                                 rect.bottom - rect.top,
                                 nullptr,
                                 nullptr,
                                 nullptr,
                                 nullptr);
  if (!hwnd) {
    ThrowLastError("ウィンドウの作成󠄁に失敗しました。");
  }
  return hwnd;
}

// WIC使󠄁ふならCOMの初期化󠄁が要󠄁るので簡單なRAIIラッパ
struct ComInitializer : private boost::noncopyable {
  ComInitializer() {
    ThrowIfFail(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED), "COMの初期化󠄁に失敗しました。");
  }

  ~ComInitializer() {
    CoUninitialize();
  }
};

// 單純な頂點
struct SimpleVertex {
  DirectX::XMFLOAT3 position;
  DirectX::XMFLOAT4 color;
};

class SampleApplication : boost::noncopyable {
 public:
  SampleApplication() : hwnd_{MakeWindow(800, 600, L"サンプル")} {
    MakeDeviceIndependentResources();
    MakeDeviceResources();
  }

  // メッセージループ
  int Run() {
    // ショートカットを使つて最大化󠄁とかされないやうにSW_RESTOREで表示
    ShowWindow(hwnd_, SW_RESTORE);
    MSG msg;
    do {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        // キーボード處理しないならTransformMessageは要󠄁らなさう
        DispatchMessage(&msg);
      } else {
        d3d11DeviceContext_->ClearRenderTargetView(d3d11RenderTargetView_.Get(), DirectX::Colors::DarkBlue);
        d3d11DeviceContext_->ClearDepthStencilView(d3d11DepthStencilView_.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
      // ここで3Dを描畫
        // 取敢ずテキトーな三角形
        d3d11DeviceContext_->IASetInputLayout(d3d11InputLayout_.Get());
        d3d11DeviceContext_->VSSetShader(d3d11VertexShader_.Get(), nullptr, 0);
        d3d11DeviceContext_->PSSetShader(d3d11PixelShader_.Get(), nullptr, 0);
        const std::array<SimpleVertex, 3> vertex = {{
          {{0.5f, -0.5f, 0.f}, {1.f, 1.f, 1.f, 1.f}}, {{-0.5f, 0.5f, 0.f}, {1.f, 1.f, 1.f, 1.f}}, {{0.5f, 0.5f, 0.f}, {1.f, 1.f, 1.f, 1.f}}
        }};
        D3D11_BUFFER_DESC desc = {};
        desc.ByteWidth = sizeof(vertex);
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        desc.StructureByteStride = sizeof(float);
        D3D11_SUBRESOURCE_DATA sub = {};
        sub.pSysMem = vertex.data();
        UINT offsets = 0;
        UINT strides = sizeof(SimpleVertex);
        Microsoft::WRL::ComPtr<ID3D11Buffer> buffer;
        ThrowIfFail(d3d11Device_->CreateBuffer(&desc, &sub, &buffer), "頂點バッファの作成󠄁に失敗しました。");
        d3d11DeviceContext_->IASetVertexBuffers(0, 1, buffer.GetAddressOf(), &strides, &offsets);
        d3d11DeviceContext_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        d3d11DeviceContext_->Draw(3, 0);
        d2d1DeviceContext_->BeginDraw();
        // ここで2Dを描畫
        // 取敢ずテキトーな文󠄁字列
        const wchar_t str[] = L"デスクトップアプリでDirect3D11+Direct2Dの初期化まとめ";
        d2d1DeviceContext_->DrawText(str,
                                     ARRAYSIZE(str),
                                     dwriteTextFormat_.Get(),
                                     D2D1::RectF(0.f, 0.f, 800.f, 600.f),
                                     d2d1SolidcolorBrush_.Get());
        // 9の頃みたいにさうさう起󠄁きるわけではないしデバイスロストは無視󠄁して落とすと云ふ選󠄁擇肢もアリ?
        ThrowIfFail(d2d1DeviceContext_->EndDraw(), "Direct2Dの描畫終󠄁了處理に失敗しました。");
        ThrowIfFail(dxgiSwapChain_->Present(1, 0), "DXGIの描畫終󠄁了處理に失敗しました。");
      }
    } while (msg.message != WM_QUIT);
    return static_cast<int>(msg.wParam);
  }
  
 private:
  // デバイスロストに對應しないにしてもデバイス非依存のリソースの初期化󠄁は分󠄁けておいて損はなささう
  void MakeDeviceIndependentResources() {
    // Direct2Dファクトリ
    D2D1_FACTORY_OPTIONS options = {};
#ifdef _DEBUG
    options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
    ThrowIfFail(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
	                                __uuidof(ID2D1Factory1),
		                              &options,
		                              &d2d1Factory_),
                "Direct2Dファクトリの作成󠄁に失敗しました。");
    // DirectWriteファクトリ
    ThrowIfFail(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory1), &dwriteFactory_),
                "IDWriteFactoryの作成󠄁に失敗しました。");
    // DirectWriteテクストフォーマット
    ThrowIfFail(dwriteFactory_->CreateTextFormat(L"メイリオ",
                                                 nullptr,
                                                 DWRITE_FONT_WEIGHT_NORMAL,
                                                 DWRITE_FONT_STYLE_NORMAL,
                                                 DWRITE_FONT_STRETCH_NORMAL,
                                                 32.f,
                                                 L"ja-jp",
                                                 &dwriteTextFormat_),
                "テクストフォーマットの作成󠄁に失敗しました。");
    // WICファクトリ
    ThrowIfFail(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wicFactory_)),
                "WICImagingFactoryの作成󠄁に失敗しました。");
  }
  
  // デバイス依存リソース
  void MakeDeviceResources() {
    // Direct3D11のデバイスとデバイスコンテクストを作る
    // Direct2Dとの共存にはD3D11_CREATE_DEVICE_BGRA_SUPPORTが必須
#ifdef _DEBUG
    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> device;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext> deviceContext;
    // Direct2Dと共存する場合はD3D_DRIVER_TYPE_HARDWAREしか選󠄁擇肢がないらしい
    ThrowIfFail(D3D11CreateDevice(nullptr,
                                  D3D_DRIVER_TYPE_HARDWARE,
                                  nullptr,
                                  flags,
                                  nullptr,
                                  0,
                                  D3D11_SDK_VERSION,
                                  &device,
                                  nullptr,
                                  &deviceContext),
                "Direct3D11デバイスとデバイスコンテクストの作成󠄁に失敗しました。");
    // Direct3D11のインターフェースからDirect3D11.1のインターフェースを取得
    // Asは要󠄁するにQueryInterface
    ThrowIfFail(device.As(&d3d11Device_), "Direct3D11デバイスの取得に失敗しました。");
    ThrowIfFail(deviceContext.As(&d3d11DeviceContext_), "Direct3D11デバイスコンテクストの取得に失敗しました。");
    Microsoft::WRL::ComPtr<IDXGIDevice2> dxgiDevice;
    ThrowIfFail(d3d11Device_.As(&dxgiDevice), "DXGIデバイスの取得に失敗しました。");
    // Direct2Dのデバイスとデバイスコンテクストを作る
    ThrowIfFail(d2d1Factory_->CreateDevice(dxgiDevice.Get(), &d2d1Device_),
                "Direct2Dデバイスの作成に失敗しました。");
    ThrowIfFail(d2d1Device_->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1DeviceContext_),
                "Direct2Dデバイスコンテクストの作成󠄁に失敗しました。");
    Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;
    ThrowIfFail(dxgiDevice->GetAdapter(&dxgiAdapter), "DXGIアダプタの取得に失敗しました。");
    Microsoft::WRL::ComPtr<IDXGIFactory2> dxgiFactory;
    ThrowIfFail(dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)), "DXGIファクトリの取得に失敗しました。");
    // DXGIスワップチェインを作る
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
    swapChainDesc.Width = 800;
    swapChainDesc.Height = 600;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapChainDesc.Stereo = false;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // DXGI_SWAP_EFFECT_DISCARDにしないとどうも3Dが上手く描畫できない
    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
    ThrowIfFail(dxgiFactory->CreateSwapChainForHwnd(d3d11Device_.Get(),
                                                    hwnd_,
                                                    &swapChainDesc,
                                                    nullptr,
                                                    nullptr,
                                                    &dxgiSwapChain_),
                "DXGIスワップチェインの作成󠄁に失敗しました。");
    ThrowIfFail(dxgiDevice->SetMaximumFrameLatency(1), "DXGIデバイスの遲延󠄂の設定に失敗しました。");
    // Direct3Dのレンダリングターゲットを設定
    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11RenderTargetTexture;
    ThrowIfFail(dxgiSwapChain_->GetBuffer(0, IID_PPV_ARGS(&d3d11RenderTargetTexture)),
                "Direct3Dテクスチャの取得に失敗しました。");
    ThrowIfFail(d3d11Device_->CreateRenderTargetView(d3d11RenderTargetTexture.Get(), nullptr, &d3d11RenderTargetView_),
                "Direct3Dレンダリングターゲットの作成に失敗しました。");
    // Direct3Dの深度バッファを作る
    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11DepthTexture;
    D3D11_TEXTURE2D_DESC depthTextureDesc = {};
    depthTextureDesc.Width = swapChainDesc.Width;
    depthTextureDesc.Height = swapChainDesc.Height;
    depthTextureDesc.MipLevels = 1;
    depthTextureDesc.ArraySize = 1;
    depthTextureDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthTextureDesc.SampleDesc = swapChainDesc.SampleDesc;
    depthTextureDesc.Usage = D3D11_USAGE_DEFAULT;
    depthTextureDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    ThrowIfFail(d3d11Device_->CreateTexture2D(&depthTextureDesc, nullptr, &d3d11DepthTexture),
                "Direct3D深度バッファテクスチャの作成󠄁に失敗しました。");
    D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = {};
    depthStencilViewDesc.Format = depthTextureDesc.Format;
    depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
    ThrowIfFail(d3d11Device_->CreateDepthStencilView(d3d11DepthTexture.Get(), &depthStencilViewDesc, &d3d11DepthStencilView_),
                "Direct3D深度ビューの作成󠄁に失敗しました。");
    d3d11DeviceContext_->OMSetRenderTargets(1, d3d11RenderTargetView_.GetAddressOf(), d3d11DepthStencilView_.Get());
    // ビューポートの設定
  D3D11_VIEWPORT viewport = {};
    viewport.Width = static_cast<float>(swapChainDesc.Width);
    viewport.Height = static_cast<float>(swapChainDesc.Height);
    viewport.MaxDepth = 1.f;
    d3d11DeviceContext_->RSSetViewports(1, &viewport);
    // 入力レイアウトの作成󠄁
    const std::array<D3D11_INPUT_ELEMENT_DESC, 2> sympleInputLayout = {{
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
      {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
    }};
    // 頂點シェーダ
    const auto vsmain = ReadFileData("../x64/Debug/simplevs.cso");
    ThrowIfFail(d3d11Device_->CreateInputLayout(sympleInputLayout.data(),
                                                boost::numeric_cast<UINT>(sympleInputLayout.size()),
                                                vsmain.data(),
                                                vsmain.size(),
                                                &d3d11InputLayout_),
                "入力レイアウトの作成󠄁に失敗しました。");
    ThrowIfFail(d3d11Device_->CreateVertexShader(vsmain.data(),
                                                 vsmain.size(),
                                                 nullptr,
                                                 &d3d11VertexShader_),
                "頂點シェーダの讀込󠄁みに失敗しました。");
    // ピクセルシェーダ
    const auto psmain = ReadFileData("../x64/Debug/simpleps.cso");
    ThrowIfFail(d3d11Device_->CreatePixelShader(psmain.data(),
                                                psmain.size(),
                                                nullptr,
                                                &d3d11PixelShader_),
                "ピクセルシェーダの讀込󠄁みに失敗しました。");
    // Direct2Dのレンダリングターゲットを設定
    Microsoft::WRL::ComPtr<IDXGISurface2> dxgiSurface;
    ThrowIfFail(dxgiSwapChain_->GetBuffer(0, IID_PPV_ARGS(&dxgiSurface)),
                "バックバッファの取得に失敗しました。");
    const auto bp = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
                                            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));
    ThrowIfFail(d2d1DeviceContext_->CreateBitmapFromDxgiSurface(dxgiSurface.Get(), &bp, &d2d1RenderTarget_),
                "Direct2Dレンダリングターゲットビットマップの作成󠄁に失敗しました。");
    d2d1DeviceContext_->SetTarget(d2d1RenderTarget_.Get());
    // 單色ブラシの作成󠄁
    ThrowIfFail(d2d1DeviceContext_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &d2d1SolidcolorBrush_),
                "ブラシの作成󠄁に失敗しました。");
  }

  ComInitializer com_;
  HWND hwnd_;
  Microsoft::WRL::ComPtr<ID3D11Device1> d3d11Device_;
  Microsoft::WRL::ComPtr<ID3D11DeviceContext1> d3d11DeviceContext_;
  Microsoft::WRL::ComPtr<ID3D11RenderTargetView> d3d11RenderTargetView_;
  Microsoft::WRL::ComPtr<ID3D11DepthStencilView> d3d11DepthStencilView_;
  Microsoft::WRL::ComPtr<ID3D11InputLayout> d3d11InputLayout_;
  Microsoft::WRL::ComPtr<ID3D11VertexShader> d3d11VertexShader_;
  Microsoft::WRL::ComPtr<ID3D11PixelShader> d3d11PixelShader_;
  Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgiSwapChain_;
  Microsoft::WRL::ComPtr<ID2D1Factory1> d2d1Factory_;
  Microsoft::WRL::ComPtr<ID2D1Device> d2d1Device_;
  Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1DeviceContext_;
  Microsoft::WRL::ComPtr<ID2D1Bitmap1> d2d1RenderTarget_;
  Microsoft::WRL::ComPtr<IDWriteFactory1> dwriteFactory_;
  Microsoft::WRL::ComPtr<IDWriteTextFormat> dwriteTextFormat_;
  Microsoft::WRL::ComPtr<IWICImagingFactory> wicFactory_;
};

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  SampleApplication app;
  return app.Run();
}
// 頂點シェーダ
struct VertexIn {
  float4 pos : POSITION0;
  float4 col : COLOR0;
};

struct VertexOut {
  float4 pos : SV_POSITION;
  float4 col : COLOR;
};

VertexOut main(VertexIn input) {
  VertexOut output;
  output.pos = input.pos;
  output.col = input.col;
  return output;
}

// ピクセルシェーダ
struct PixcelIn {
  float4 pos : SV_POSITION;
  float4 col : COLOR;
};

float4 main(PixcelIn input) : SV_Target0 {
  return input.col;
}

實行結果

f:id:nagoya313:20141112181232p:plain