最新の DirectX を利用するために OS のアップデート

DXR の実行もそうだが OS (Windows 10 のバージョン) をアップデートしないと、最新の DirectX の機能を利用できないのはナゼなんだろう。単純に sdk や driver や dll のバージョンを上げるだけでは、最新の機能は使えない。

参考リンクの記事に、DirectX は Windows のコンポーネントとして提供されるようになったため、dll 単体での更新が出来ないというのが理由だろうか。じゃあ、新しい Windows 10 から D3D12.dll だけをバージョンが古い Windows 10 にコピーした場合の挙動はどうなるんだろう。

driver とか dll とか、プログラムが実行できる仕組みとか、その辺りをちゃんと勉強する必要がありそう。

D3D12.dll は下記の両方に存在している。
C:\Windows\SysWOW64
C:\Windows\System32

参考リンク
https://support.microsoft.com/ja-jp/help/17r9113/how-to-install-the-latest-version-of-directx
https://powerpro.at.webry.info/201511/article_43.html
https://wa3.i-3-i.info/word14254.html
https://wa3.i-3-i.info/word14253.html

DirectX12 のサンプルはなぜ WM_PAINT で更新と描画をしているのか?

DirectX12 のサンプルを読んでいて不思議に思うのは、Update 関数と Render 関数が、メッセージループでは無くてウインドウプロシージャで処理されていることです。

DirectX9, 10, 11 のサンプルだと、更新と描画関数はメッセージループで処理されていたため、そのやり方が DirectX のお作法だと思っていたのですが、DirectX12 のサンプルからは突如として WndProc の WM_PAINT で処理されるように変更されました。

特に調べてもこれと言った理由にはたどり着けなかったのですが、推測も含めて自分なりの答えをメモとして残します。

なぜ、 WM_PAINT で更新と描画を行なっているのか?

恐らく UWP とコードの共通化を行いたかったためと思われます。DirectX12 のサンプルから、UWP と Desktop (WindowsAPI) のそれぞれに対応したサンプルが提供されています。

UWP の場合は Windows API のような、低レベルのメッセージループ処理を書く必要はありませんが、イベントに対応する関数をシステムに登録して、必要に応じてイベントドリブンで処理されます。この部分が、Windows API のウインドウプロシージャと同じなため、アプリケーション側はイベントハンドラー内で呼び出される関数のみを用意すれば良く、それが OnUpdate と OnRender として定義されており、Windows API からはウインドプロシージャで処理するようになったと推測します。

だがしかし、UWP 側は View::Run() の中で、ループを回して処理しているので、Windows API 側も似たようにメッセージループ内に書けば良かった気もし無いでも無いです。

なぜ、WM_PAINT が毎フレーム発行され続けるのか?

この挙動が不思議でした。Windows API の DirectX のサンプルの挙動を見ていると、WM_PAINT メッセージが毎フレーム呼び出されています。

通常、WM_PAINT は UpdateWindow() や InvalidateRect() が呼び出されて、再描画が必要な時に発行されるはずです。これらの関数呼び出しを行なっている箇所も見当たらないため、なぜ、毎フレームWM_PAINT に来るのか疑問でした。

少しテストしてみたら、WM_PAINT が発行された後に BeginPaint() などで描画処理を行わないと、WM_PAINT が発行され続ける挙動があるみたいです。DirectX ではウィンドウの更新は全て DirectX API が行うため、Windows API の描画命令が呼び出されることはありません。そのため、Windows は、再描画の依頼を出し続ける事により、連続的に WM_PAINT が呼び出される事で、毎フレームの更新が実現されています。

Visual Studio 2019 で FBX SDK

ViewScene サンプルプロジェクトをテスト実行。

プロジェクト構成は x64 に変更し、VS2019 で実行したときに、FBX SDK 以下にフォルダが作られるため、アクセス権を設定しておく。

DirectX Raytracing のドキュメント

DirectX Raytracing (DXR) Functional Spec
https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html

DXR のドキュメント (リファレンス) は、少し前まで word ファイルで提供されていましたが、今は github io から閲覧できるようになったのですね。

公式のドキュメントを補足する形の情報が、こちらの github 上にまとめられているみたいです。
https://microsoft.github.io/DirectX-Specs/

HLSL ByteAddressBuffer の使い方

ByteAddressBuffer は引数にバイト数を指定する。

ByteAddressBuffer byteBuffer : register(t0);
uint value = byteBuffer.Load(0);

4 Byte 単位でメモリーにアクセスしてデータを取得出来るバッファータイプ。戻り値は uint (4 Byte) になる。Load 関数の引数は 4 の倍数でなければいけない。Load2(0) で指定すれば uint2 型で 8 Byte の連続した領域を取得できる。Load3(0) で指定すれば uint3 型で 12 Byte の連続したメモリー。

uint4 value = byteBuffer.Load4(4);
という指定をすれば、アドレスの開始位置が先頭から 4 Byte 目から、16 Byte の連続した領域を取得出来る。

DirectX12 HLSL Texture2D.Load にはまる

Texture2D の Load 命令は引数として int3 を受け取るため、texture.Load(int3(0, 0, 0)); のような形式で呼び出す。この時 x, y には UV ではなくて テクスチャの width -1, height -1 の値を指定する。当たり前だが、-1 に注意。そして z は mipmap を指定できる。

Texture サイズが 256×256 だとして、Load(int3(256, 256, 0)); とサイズを超えてしまった場合は 0 が返るようになっている。この時に大切なのが存在しない mipmap にアクセスした場合も 0 が返るという事。

int3 を指定せずに Load(0) とやるのは特に問題無いが、テストで Load(1) としてしまったとき、Load(int3(1, 1, 1)); になるため、もし mipmap が存在しないテクスチャに対してこれをした場合は 0 が返ってしまう。ちょっとしたテストで Load(1) をやった結果、原因を突き止めるのに時間がかかった。

Texture2D.Load は int3 でテクスチャ座標とミップマップ値に気をつけて使用する。