DirectX12 のプロジェクトに Agility SDK を導入する

# はじめに
この記事は[グラフィックスアドベントカレンダー](https://qiita.com/advent-calendar/2023/graphics)の20日目の記事です。

2021年に DirectX12 には [Agility SDK](https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/) と呼ばれる仕組みが導入され、既に DirectX12 に対応した多くのゲームでも採用されるようになっています。

# DirectX12のバージョン
DirectX の現行バージョンである DirecX12 がリリースされたのは2015年7月だったため、少し気が早いですがそろそろ10年に近い歳月が経とうとしています。

ユーザーから見えるバージョン情報としては **12** が長く続いていますが、その間に開発者から見たときの Direct3D12 の機能には大小含め様々なアップデートが行われてきました。（一例として DirectX 12 Ultimate もしくは DirectX12.2 と呼ばれる機能など）

代表的な更新として DirectX Raytracing（DXR）や Variable Rate Shading、Mesh Shader などが挙げられると思います。これらの更新は、グラフィックスの品質を高め、パフォーマンスやゲーム体験を改善することが可能なため、導入にはユーザーにとって大きなメリットがあります。


しかし、このような DirectX 12 の新しい機能の利用には、対応した GPU や GPUドライバーを使用しているのはもちろん、場合によっては **Windows 自体のアップデート**も行う必要があったため、動作環境や互換性の問題から新しい機能の導入に踏み切れないこともありました。

今回説明する DirectX12 の Agilisty SDK は、この Windwos Update の問題を解決します。


# Agility SDK とは

DirectX 12 に対応したゲームは実行時に Windows の Sytem32 以下にある d3d12.dll や d3d12core.dll にリンクする形で動作しています。これらの dll は基本的には Windows 21H2、22H2 などの大型アップデートと一緒に配布されることが多いため、グラフィックスの新しい機能を利用するためにはアップデートを待つ必要がありました。

また、Windwos のアップデートが配布されてもユーザーが更新を入れているとも限らず、ゲームを動作させるためだけに Windwos Update を適用してもらうのも非常に負荷がかかる作業となっていました。


これらの問題を解決するために、DirectX 12 では実行に必要なランタイム（dll）を直接ゲームアプリケーション内に含めて配布し、実行時は OS 側の dll ではなくアプリケーション側が持っている dll にリンクする機能が追加されました。これが Agility SDK と呼ばれています。

Agility SDK を導入することにより開発者は、実行環境のランタイムのバージョンを気にする必要が無くなり、 GPU とドライバーさえ対応していれば、DirectX 12 の新しい機能をユーザーは利用できるようになりました。

DirectX12 などグラフィックス API の進化は非常に早く、年に数回は新しい機能やバグフィックスなどが含まれた新しい Agility SDK がリリースされています。Agility SDK の更新状況などは[こちら](https://devblogs.microsoft.com/directx/directx12agility/)のページで確認することが出来ます。

# Agility SDK の導入

Agility SDK は NuGet によって配布されています。導入には幾つか方法がありますが、今回は一番簡単な NuGet UI を利用する方法で行います。それ以外の方法は[こちら](https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/#howto)を参照してください。

### パッケージの追加
NuGet を利用して dll とヘッダーを導入します。

![2023-12-20_12h09_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/9efac20e-377e-dee9-f07b-4ffef30b638a.png)

DirectX のプロジェクトを右クリックして「Manage NuGet Packages」を選択します。

![2023-12-20_12h10_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/212dd188-f31e-4e52-fd1e-6b551bcf196c.png)

NuGet UI が開くため、Browse タブに切り替え「Microsoft.Direct3D.D3D12（または DirectX 12 Agility）」と入力して表示された項目を確認します。

「DirectX12 Agility SDK」と表示されているものが目的としているものです。2023年12月現在、Agility SDK の最新安定バージョンはSDK 1.611.2になっていました。

右側の「Install」ボタンからインストールを行います。プロジェクトへの組み込みはこれで完了です。

### 定数のエクスポート

プロジェクトへの組み込みが完了したら、次は ```D3D12SDKVersion``` と ```D3D12SDKPath``` 2つの定数データのエクスポートを行います。

~~~c
extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = 611; }
extern "C" { __declspec(dllexport) extern const char* D3D12SDKPath = u8".\\D3D12\\"; }
~~~
この2行をプロジェクト内の cpp ファイルの先頭に追加します。cpp 内であれば何のファイルでも構いません。

このエクスポートを行うことで、DirectX はどの場所から何のバージョンの DirectX ランタイム（dll）をロードするのかを決定出来ます。

```D3D12SDKVersion``` に指定している 611 という数字は導入した Agility SDK のバージョンによって指定が異なります。

![2023-12-20_14h00_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/7dc81499-5104-af2f-f59e-da7a0f8e39b4.png)

ここには[Agility SDK のダウンロードページ](https://devblogs.microsoft.com/directx/directx12agility/) に書かれている D3D12SDKVersion の数字を設定します。今回 ```SDK 1.611.2``` を導入したので **611** を指定しています。

もう一つの ```D3D12SDKPath``` への指定は、明示的にパスの変更を行わない限りは、デフォルトの D3D12 指定で構いません。


### d3d12.h ヘッダーの確認
Agility SDK が正しく導入されれば DirectX12 の d3d12.h への参照パスがプロジェクト以下にある（Agility SDK 内の）d3d12.h を指すようになります。

通常の DirectX 12 は Windows SDK 以下にある d3d12.h を見に行きますが（例えば C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um/d3d12.h など）Agilisty SDK を利用した場合は NuGet によってインストールされたフォルダ以下にある d3d12.h を参照するようになります。

:::note info
Agility SDK をインストールすることで「Microsoft.Direct3D.D3D12.props」と「Microsoft.Direct3D.D3D12.targets」がプロジェクトに組み込まれます。このファイルによって d3d12.h の  include の優先度が変わるみたいです。
:::

ちなみに、d3d12.h ヘッダーには D3D12_SDK_VERSION マクロが定義されています。

![2023-12-20_13h09_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/0f0a48aa-8e64-3cf7-968a-da22dc6d2c71.png)

この数字を確認することで、開発時には現在使用している SDK のバージョンを知ることも可能です。

~~~
#if D3D12_SDK_VERSION == 611
    #pragma message("Using new directx12 runtime.")
#endif
~~~


### 実行ファイル

![2023-12-20_12h58_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/cb84e203-b32f-657f-7e41-ec050e76a80e.png)

Agility SDK を導入した実行ファイルは、D3D12 以下にある .dll をロードするようになります。


![2023-12-20_12h58_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/79599c08-b0da-7c1f-e74f-607702c85908.png)

D3D12 フォルダ以下には、最新のランタイムが含まれています。

アプリケーションをリリースするときは、実行ファイルと一緒に D3D12 フォルダも含めるようにします。（**※D3D12 フォルダ以下にある D3d12SDKLayers.dll は開発時のみ利用するものなのでリリース時には削除します。D3D12Core.dll のみで大丈夫です。**）

# Agility SDK の確認

本当に、最新の dll が読み込まれているのでしょうか。[ProcessExplorer](https://learn.microsoft.com/ja-jp/sysinternals/downloads/process-explorer) を利用して exe からロードされている dll を見てみます。

### Agility SDK 利用時
![2023-12-20_13h34_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/fa247c76-1a78-0bd9-c6e6-3c12d4973e3e.png)

Agility SDK を有効にした状態では、D3D12 以下の dll が読み込まれていました。

### Agility SDK 無効
![2023-12-20_13h36_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/e79ebc9f-0a23-5736-05c6-b9e306483c03.png)

Agility SDK を無効にした状態では、OS の System32 以下にあるランタイムがロードされていました。 

:::note info
補足ですが、インストールした Agility SDK の dll より System32 以下の dll の方が新しい場合は System32 以下の dll が読み込まれます。
:::

# まとめ

Agility SDK を利用することで Windows のアップデートを行わずに最新の DirectX12 のランタイムが読み込まれるようになりました。また Agility SDK では将来的にリリース予定のプレビュー版の提供も行なわれており [Work Graphs](https://devblogs.microsoft.com/directx/agility-sdk-1-711/) と言った先駆的な機能をいち早く試すことも可能です。

![2023-12-20_10h39_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415281/e576e9dc-6216-cd26-d04f-c3dcdb810f80.png)

DirectX12 の開発者の方で、まだ Agility SDK を利用していないという方がいましたら、ぜひ参考にしてみて下さい。

# 参考
https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/