syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

Direct3D 12 (DirectX 12) の簡単なサンプル

(本記事の内容は執筆時点の暫定仕様に基づくものです。実際の最終製品版とは一部異なる部分があります)

GDCの開催された2015年3月にはすでにDirect3D 12 (DirectX 12) の暫定ドキュメントとVisual Studio 2015 CTPが公開されていたのですが、肝心の実行環境であるWindows 10評価版用のWDDM 2.0対応ドライバーがWindows Update経由でしか取得できず、また新しいWUはプロキシ経由で正常に動作しない現象が出ていたため、Direct3D 12の評価はしばらく見送っていました。
その後、4月末にWindows 10 Insider Preview Build 10074、Visual Studio 2015 RC、そしてWindows 10向けのNVIDIA GeForceベータ ドライバーのスタンドアロン版が相次いでリリースされたため、Direct3D 12の検証を再開することにしました。

日本語版Windows 10 Build 10074 (64bit) 上でdxdiagを実行してみたところ、確かにWDDM 2.0対応になっているようです*1。検証に使ったグラフィックスカードはNVIDIA GeForce GTX 770 4GB版です*2
https://sygh-jp.github.io/content_hosting/software_ss/windows10ip_build10074_dxdiag_ss01.png

Visual Studio 2015 RCでDirectX 12プログラミングを行なう場合、インストール時に[ユニバーサル Windows アプリ開発 ツール] - [ツールおよび Windows SDK 10.0.10069]にチェックを入れておく必要があります。デフォルトの設定ではインストールされないので、初回インストール時にチェックし忘れていた場合は、[プログラムと機能]からVisual Studioの更新インストール機能を使って追加します。今回はデスクトップ アプリ上でさくっとテストしたかったので、フレームワークにはおなじみのMFCを使っていますが、MFCもデフォルト設定ではインストールされないので、[Microsoft Foundation Classes for C++]にもチェックを入れておきます。
https://sygh-jp.github.io/content_hosting/software_ss/vs2015rc_setup_mfc_sdk10_ss.png

サンプルプログラムは下記です。MSDNの暫定ドキュメント「Creating a basic Direct3D 12 component」に書かれてある断片コードを参考にしていますが、現時点では暫定だけあって実際のSDK実装と微妙に異なっていたりするので苦労しました。また、Windows SDK 8.0で廃止されたと思われていたD3DXが、今度はC++ヘッダーベースのヘルパーとして復活するようですが、Visual Studio 2015 RCにはまだ <d3dx12.h> は含まれていないようでした*3

https://sygh-jp.github.io/content_hosting/my_program_ss/MyMfcSimpleD3D12Test1_ss_2015_05_14a.png

※2017-01-09追記:
サンプルのプロジェクトファイルをVisual Studio 2015 Update 3にてビルドできるように修正しました。Windows 10 November 2015 Update (version 1511, build 10586) とNVIDIA GeForceドライバー358.91にて動作確認しています。

今回はとりあえずD3D12コアAPIの雰囲気をつかむため、トライアングルを描画するだけの簡単なコードにしました。ただしPipeline State Object (PSO) の切り替え方法(ID3D12GraphicsCommandList::SetPipelineState()メソッド)をテストするために、下記2つのGraphics PSOを作成して、複数描画しています。

  • 図形の回転なし、ブレンディングなし
  • 図形の回転あり、ブレンディングあり

今後はコンピュート機能の非同期実行効率がどうなっているのか、またWindowsストアアプリ(XAML連携)やDirect2D/DirectWriteとの相互運用に関しても調査する予定です。どうもD3D12デバイスからはDXGIデバイスインターフェイスが取得できなくなっているため、D3D11デバイスとの相互運用機能(D3D11On12CreateDevice()関数など)を経由する必要があるようです。

Direct3D 12 APIを触ってみた感想

今回のサンプルの目的自体は単純とはいえ、Direct3D 12はMantleのようにハードウェアに近いローレベルAPI*4を標榜しているだけあって、ユーザーコードの記述量はDirect3D 11までと比べて格段に増えています。特に難しいと感じたのがDescriptor Heapまわりです。一方、Direct3D 11ではID3D11VertexShader, ID3D11PixelShaderや、ID3D11Buffer, ID3D11Texture2Dなどのようにシェーダーステージやリソースの種類ごとに細かくインターフェイスが分かれていたのですが、Direct3D 12ではID3D12PipelineStateやID3D12Resourceのように統合・統一され、結果としてCOMインターフェイスの数はD3D11よりも少なくなっています。これによってD3D12ではオブジェクトやリソースの初期化が統一的に行なえるようになっているのですが、ID3D12Device::CreateGraphicsPipelineState()やID3D12Device::CreateCommittedResource()といった生成メソッドはパラメータ(に使う構造体のメンバー)が非常に多くなっていて大変です。また、これまでAPIおよびドライバー層が面倒をみてくれていたCPUとGPUの同期も、ユーザーコードで明示的に記述する必要があります。

なおDirect3D 12 with Visual Studio 2015 RCのサンプルとして、いくつかの実践的なテストプログラムを下記GitHubにアップロードされている日本人の方がいます。こちらも参考になるでしょう。

Direct3D 10以降、固定機能シェーダーが廃止されてプログラマブルシェーダーが必須となり、Direct3D 9のようなお手軽感はなくなっていたものの、単に3Dグラフィックスを描画するだけであれば、アプリケーションのコーディングが死ぬほど大変というほどのものではありませんでした。ハードウェアの限界性能を引き出して最大限最適化しなければならないというような厳しい要求がなければ、Direct3D 11は十分に完成形と言えるAPIで、Direct3D 9ユーザーでも時間をかければ確実に習得して使いこなせる易しいAPIだと思います。しかし、Direct3D 12は実行効率の向上を至上目的とした、完全に玄人向けのAPIです。ゲームエンジン設計者ならばいざしらず、純粋にアプリケーションコードのほうに注力したいであろう標準的なグラフィックスプログラマーが手を出せるシロモノではありません。おそらくVulkanもDirectX 12同様の玄人志向APIとなることでしょう。UnityやUnreal EngineはすでにDirectX 12への対応を表明しているため、単に3Dグラフィックス プログラミングをやりたいだけであれば、そういったミドルウェアを利用するか、DirectX 11.xやOpenGL/OpenGL ESを使い続けるのが賢明だと思います。

Windows 10に思うこと

先日、Windows 10が最後のWindowsメジャーバージョンアップとなることがマイクロソフトによって正式に言及されましたが、それにしてはOSの完成度が異様に低いように感じます。まだ評価版の段階ではありますが、7月末リリースを目標としているらしい状況にもかかわらず、現時点でGUIの設計にかなり粗が目立ちます。自分はPC主体のWindowsユーザーですが、ぶっちゃけ全体的にWindows 8.1のほうが使いやすいですし、タイトルバーやフォント、アイコンのデザインも8.xのほうが良いと思います。現状、Windows 10のタイトルバーはアライメントや配色のバランスが悪くて安っぽく、また日本語版の新たなシステムフォントとして使われている細身のYu Gothic UIフォントはClearTypeの調整不足もあるせいか貧弱・低品質な印象を受けます。ていうかおためごかしにフォントを変えるよりも、まずフォントレンダリングエンジンの品質をAppleAdobe並に強化するべきでしょう。また特に今回はタスクバーまわりに頻繁に手を入れているらしく、最もユーザーが使う機能でありながら評価版ではバグの温床となっているように見受けられます。そのほか、Windows 7で機能的にも充実し、完成形と思われていた、おなじみのデスクトップ版の電卓アプリ(calc.exe)が廃止され、大味なストアアプリ版の電卓アプリに一本化されたのも地味に悲しいです。

DirectX 12はおそらくWindows 7/8.xにはバックポートされず、結局Windows 10以降専用の機能となるように思われるのですが、最終製品版ではもっともっとOS全体の完成度を上げてくれないと、DirectX 12のためにアップグレードする気には到底なれません。
それにしてもフィードバックは専用のInsider Programに参加させるのではなく、MS Connect経由でも提出できればいいのに……

*1:ちなみにWindows 10上でもWindows 7/8/8.1向けのWDDM 1.1/1.2/1.3ドライバーを使うことはできるようです。2014-07-29にリリースされたNVIDIAの安定ドライバー340.52をインストールしたWindows 8Windows 10 TPにアップグレードすると、WDDM 1.3になっていました。ただしWDDM 1.x環境ではD3D12を使用することはできず、D3D12CreateDevice()関数がDXGIエラー (DXGI_ERROR_UNSUPPORTED, 0x887A0004) で失敗します。

*2:Kepler世代はいずれも機能レベル11_0になります。

*3:Direct3D 12ドキュメントの正式版「Helper Structures and Functions for D3D12」によると、d3dx12.hはGitHubにて補助ライブラリとして分割管理されているようです。また、正式版のVisual Studio 2015に含まれているDirectX 12のUWPアプリケーションプロジェクトテンプレートにも、d3dx12.hが含まれているようです。

*4:low-levelは日本語で「低レベル」「低水準」と訳されることが多いのですが、日本語で「低レベル」「低水準」というと、「低俗」「下等」といったイメージを与えかねないので、自分は「ローレベル」あるいは「下位レベル」と表記することが多いです。