SwapChainの作成 - yoshimune/LearningDirectX11 GitHub Wiki

SwapChain とは

スワップチェーンは、レンダリング結果と画面表示の切り替えを行って、レンダリング結果を画面に表示するために使う機能です。データ構造はレンダリングするためのバッファのコレクションとなっています。

今回のサンプルでは、スワップチェーンにフロントバッファとバックバッファの2つのバッファを設定します。フロントバッファはディスプレイデバイスに渡して画面表示に使います。バックバッファにはレンダリング結果を書き込みます。フロントバッファとバックバッファを切り替えることにより、レンダリング結果を次々と画面に表示することを実現します。

SwapChain の作成

とりあえず、SwapChainを作成するコードを載せます

// D3D Device から DXGI Device を検索します
ComPtr<IDXGIDevice1> dxgi_device;
DX::ThrowIfFailed(d3d_device_.As(&dxgi_device));

// 現在のデバイスで動作中の物理アダプタ(GPU またはカード)を初期化します。
ComPtr<IDXGIAdapter> dxgi_adapter;
DX::ThrowIfFailed(dxgi_device->GetAdapter(dxgi_adapter.GetAddressOf()));

// アダプタから親ファクトリを入手します
ComPtr<IDXGIFactory2> dxgi_factory;
DX::ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf())));

// スワップチェーン作成のためのディスクリプタを作成します。
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = back_buffer_width;
swap_chain_desc.Height = back_buffer_height;
swap_chain_desc.Format = back_buffer_format;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.SampleDesc.Quality = 0;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = back_buffer_count;

DXGI_SWAP_CHAIN_DESC fs_swap_chain_desc = {};
fs_swap_chain_desc.Windowed = TRUE;

// Win32 windowからスワップチェーンを作成します。
DX::ThrowIfFailed(dxgi_factory->CreateSwapChainForHwnd(
	d3d_device_.Get(),
	window_,
	&swap_chain_desc,
	&fs_swap_chain_desc,
	nullptr,
	dxgi_swap_chain_.ReleaseAndGetAddressOf()
));

SwapChainのうち重要なプロパティ

Windowed
SwapChainがフルスクリーン化ウィンドウクリップであるかを支持します。TRUEにするとウィンドウクリップとなります。

fs_swap_chain_desc.Windowed = TRUE;

BufferUsage
DXGI_USAGE_RENDER_TARGET_OUTPUT を設定します。これはスワップチェーンが描画面となり、Direct3Dのレンダーターゲットとして使用することを指示します。

swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

Format
DXGI_FORMAT_B8G8R8A8_UNORM フォーマットは32bitカラーを指定します。

DXGI_FORMAT back_buffer_format = DXGI_FORMAT_B8G8R8A8_UNORM;
swap_chain_desc.Format = back_buffer_format;

BufferCount
バッファ数を指定します。通常のダブルバッファ形式の場合は2を指定します。

UINT back_buffer_count = 2;
swap_chain_desc.BufferCount = back_buffer_count;

SampleDesc
このフィールドはマルチサンプリングを制御します。flip-modelスワップチェーンの場合はCount を1、Quality を0にしてください。

swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.SampleDesc.Quality = 0;

アダプタとファクトリ

SwapChain(IDXGISwapChain1)は、デバイス( ID3D11Device )とおなじDXGIファクトリーから作成する必要があります。よって以下のような流れになります

  1. ID3D11DeviceインスタンスからDXGIアダプタ(IDXGIAdapter)インスタンスを取得
  2. IDXGIAdapterインスタンスからDXGIファクトリ(IDXGIFactory2)インスタンスを取得
  3. IDXGIFactory2インスタンスからスワップチェーン(IDXGISwapChain1)インスタンスを作成

※アダプタ・ファクトリの詳しい説明は省きます。(まだわかっていない)

バックバッファへ書き込む準備をする

スワップチェーンの用意はできました。次はスワップチェーンのバックバッファへレンダリング結果を書き込む必要があります。そのための準備をします。

用意するもの

  • ID3D11Texture2D
    • テクセルデータを管理する2Dテクスチャインターフェイス。メモリ上に構築されます。
    • ピクセルシェーダーからの書き込み先として、つまりバックバッファとして利用します。
  • ID3D11RenderTargetView
    • レンダーターゲットのレンダリング中にアクセス可能なサブリソースです。
    • Direct3Dでの view は特定のリソースにアクセスする手段です。
    • 今回用意するビューは、ピクセルシェーダが処理を完了した後、結果をテクスチャに書き込むことを可能にします。

手順

  1. スワップチェーンのバックバッファ(のポインタ)を取得します。(格納先の型はComPtr<ID3D11Texture2D>とします)
// 最終的な3Dレンダーターゲットとなるこのウィンドウのバックバッファーを含めます。
ComPtr<ID3D11Texture2D> back_buffer;
DX::ThrowIfFailed(dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(back_buffer.GetAddressOf())));
  1. バックバッファとレンダーターゲットビューを作成します
// レンダーターゲットビューインターフェイスをバインドします
DX::ThrowIfFailed(d3d_device_->CreateRenderTargetView(back_buffer.Get(), nullptr, d3d_render_target_view_.ReleaseAndGetAddressOf()));

レンダーターゲットビューのバインドは完了しました。これはバックバッファにバインドされ、画面の表示に使われます。

デプス・ステンシルビュー

レンダーターゲットビュー以外に、デプス・ステンシルビューも用意する必要があります。

// 2Dサーフェイスをデプス・ステンシルバッファとして割り当てます。
// デプス・ステンシルビューをバインドするためこのサーフェイス上に作成します。
CD3D11_TEXTURE2D_DESC depth_stencil_desc(depth_buffer_format, back_buffer_width, back_buffer_height, 1, 1, D3D11_BIND_DEPTH_STENCIL);

ComPtr<ID3D11Texture2D> depth_stencil;
DX::ThrowIfFailed(d3d_device_->CreateTexture2D(&depth_stencil_desc, nullptr, depth_stencil.GetAddressOf()));

CD3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc(D3D11_DSV_DIMENSION_TEXTURE2D);
DX::ThrowIfFailed(d3d_device_->CreateDepthStencilView(depth_stencil.Get(), &depth_stencil_view_desc, d3d_depth_stencil_view_.ReleaseAndGetAddressOf()));

スワップチェーンの作成・再作成するタイミングについて

今回の例では初回とウィンドウサイズが変更されたタイミングでGame::CreateResourcesを呼び、スワップチェーンを含むリソースを更新しています。ウィンドウサイズ変更のタイミング取得はウィンドウプロシージャで行っています。

参考

Work with DirectX device resources How To: Create a Swap Chain DXGI プログラミング ガイド