本サイトでは、アフィリエイト広告およびGoogleアドセンスを利用しています。

D3D11On12 を試す (1)

DirectX12 の機能の1つで、 D3D11On12 を触り始めました。
DirectX12 の中ではおそらくマイナー側な機能で注目されないと思いますが、そういうものこそ「すらりん日記」には相応しいと感じてますので、紹介します。

D3D11On12 とは

D3D11On12 とは、 DirectX11 の API セットを DirectX12 の上に実装したものとなります。一見エミュレータなの?と思いますが、そうではありません。API の解釈を変えるという点で、マッピングレイヤーとマイクロソフトは表現しています。

つい最近、この D3D11On12 はオープンソースとして公開もされました。この先 OSに付属するという点は変わらないようで、OSコンポーネントの一部と表現されています。主に、 D3D12TranslationLayer の使い方の事例としてオープンソースとして公開したようです。

DirectX11 (D3D11) のコード

DirectX11 の次の描画コードが、DirectX12 API の上で動作可能でした。見て分かるように、ほぼ DirectX11 の時代に書かれたコードと同一の様相となっています。違う点があるとすれば、Present の手前で、Flush() を実行している点でしょうか。

void D3D11On12App::OnRender()
{
  m_d3d11DeviceContext->ClearState();

  float clearColor[4] = {
    0.25f, 0.25f, 0.25f, 0.0f
  };
  m_d3d11DeviceContext->ClearRenderTargetView(m_rtv.Get(), clearColor);

  HRESULT hr;
  D3D11_MAPPED_SUBRESOURCE mapped;
  hr = m_d3d11DeviceContext->Map(m_sceneCB.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
  if (SUCCEEDED(hr))
  {
    memcpy(mapped.pData, &m_sceneParams, sizeof(SceneParams));
    m_d3d11DeviceContext->Unmap(m_sceneCB.Get(), 0);
  }

  ID3D11RenderTargetView* rtViews[] = {
    m_rtv.Get()
  };

  D3D11_VIEWPORT viewport{};
  viewport.Width = float(GetWidth());
  viewport.Height = float(GetHeight());
  viewport.TopLeftX = 0; viewport.TopLeftY = 0;
  viewport.MinDepth = 0.0f;
  viewport.MaxDepth = 1.0f;

  m_d3d11DeviceContext->OMSetRenderTargets(1, rtViews, nullptr);
  m_d3d11DeviceContext->RSSetViewports(1, &viewport);
  m_d3d11DeviceContext->RSSetState(m_rs.Get());
  m_d3d11DeviceContext->OMSetDepthStencilState(m_ds.Get(), 0);
  m_d3d11DeviceContext->IASetInputLayout(m_inputLayout.Get());
  m_d3d11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  m_d3d11DeviceContext->VSSetShader(m_vs.Get(), nullptr, 0);
  m_d3d11DeviceContext->PSSetShader(m_ps.Get(), nullptr, 0);

  ID3D11Buffer* vertexBuffers[] = { m_bufferVB.Get() };
  UINT strides[] = { sizeof(Vertex) };
  UINT offsets[] = { 0 };
  m_d3d11DeviceContext->IASetVertexBuffers(0, 1, vertexBuffers, strides, offsets);
  ID3D11Buffer* constantBuffers[] = { m_sceneCB.Get() };
  m_d3d11DeviceContext->VSSetConstantBuffers(0, 1, constantBuffers);
  m_d3d11DeviceContext->Draw(3, 0);

  m_d3d11DeviceContext->Flush();

  if (m_swapChain)
  {
    m_swapChain->Present(1, 0);
  }
}

実行結果

実行結果は以下のように三角形ポリゴンを1つ出すだけです。あまり面白みはないです…

D3D11On12 を準備する

D3D11On12 として、 D3D11DeviceContext を生成するまでの手順は次の通りです。

  • DirectX12 のデバイスを準備(D3D12CreateDevice)
  • DirectX12 のコマンドキューを用意する(CreateCommandQueue)
  • DirectX11 デバイスを生成する(D3D11On12CreateDevice)
    • デバイスコンテキストもここで生成される
  • スワップチェインを生成する

従来は D3D11On12 として、スワップチェインは DirectX12 側で準備しなくてはいけないようでしたが、現在の環境では生成した DirectX11 のデバイスを引数にしてスワップチェインを準備できるようになったようです。

DirectX12 の初期化部分は省略して、DirectX11 のデバイスおよびスワップチェインを準備するまでのコードは以下の通りです。

  // D3D11デバイスの生成.
  ComPtr<ID3D11Device> d3d11Device;
  hr = D3D11On12CreateDevice(
    m_d3d12Device.Get(),
    d3d11DeviceFlags,
    nullptr,
    0,
    reinterpret_cast<IUnknown**>(m_commandQueue.GetAddressOf()),
    1,
    0,
    &d3d11Device,
    &m_d3d11DeviceContext,
    nullptr);
  ThrowIfFailed(hr);

  d3d11Device.As(&m_d3d11On12Device);
  m_d3d11Device = d3d11Device;

  factory->CreateSwapChainForHwnd(
    m_d3d11Device.Get(),
    Win32Application::GetHwnd(),
    &swapchainDesc, nullptr, nullptr, &swapChain);
  swapChain.As(&m_swapChain);

D3D11On12CreateDevice 関数でコマンドキューとDirectX12 のデバイスを引数に渡し、DirectX11 のデバイス及びデバイスコンテキストを生成します。そして、ここで得られた DirectX11 のデバイスを用いて、スワップチェインを用意します。

まとめ

初期化の部分が異なるだけで、DirectX11 を使っている(ように書いた)描画コードが動きました。DirectX12 の描画までのリソースの準備の長さに比べると DirectX11 は本当に手軽に短いコードで書くことが出来ます(今回 DirectX11 のリソースの準備に関しては省略してしまいましたが)。

D3D11On12 では必要に応じて DirectX12 の世界とも行き来できるようなので、これも徐々に試していきたいと思います。

補足1

純粋な DirectX11 と比べると、パフォーマンスの低下、デバッグレイヤーの機能が弱い、という弱点があります(公式に記述されています)。パフォーマンスクリティカルな用途では、素直に DirectX11 API のみを使用しましょう。

今回のコードで出遭ったのは、画面クリアの実行で以下のワーニングが通知される点です。

D3D12 WARNING: ID3D12CommandList::ClearRenderTargetView:
The application did not pass any clear value to resource creation. The clear operation is typically slower as a result; but will still clear to the desired value.
[ EXECUTION WARNING #820: CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE]

DirectX12 を使ってレンダーターゲットをクリアしたときに、初期化時の色指定と違うものを行ったときにこの警告が出たのを覚えています。この警告内容は抑制しておかないと、非常に使いづらいかなと感じました。

参考

ID3D12InfoQueue のお世話になった
描画用キューブマップを各面毎にクリアしたときに警告を出してしまったので、これらについて対処するための小ネタです。 レンダーターゲットを動的なカラーでクリアすると警告 レンダーターゲットを任意のカラーでクリアするとワーニングが発生します。レンダーターゲット生成時に指定したカラー値(デプス値)と違ったものでクリア処理を呼ぶと、パフォーマンスの観点から怒られるよう...

補足2

Microsoft のサンプルに D3D11On12 のものが存在しますが、どちらかと言えば DirectX12 色が濃かったため、 DirectX11 の観点からどうかという点で本ブログは進めていきたいと思います。

DirectX
すらりんをフォローする
すらりん日記
タイトルとURLをコピーしました