RenderDocを使ったUnrealEngineプロジェクトのGPUデバッグ
RenderDocのインストール
- RenderDocインストーラを公式からダウンロード
- ダウンロードしたインストーラを実行
- ※UnrealEngineを終了してからインストールを行う事
- セットアップウィザードが起動するので
Next
をクリック I accept the terms in the License Agreement
をチェックしてNext
をクリック- 個別設定をしないのならば
Typical
またはComplete
を選択後、Next
をクリック - インストール設定に問題が無ければ
Install
をクリック - インストールが完了したら
Finish
をクリック
UnrealEditor上からRenderDocを使う
- プラグイン設定を開く
- RenderDocプラグインを有効化
RenderDoc
で検索- プラグインにチェック
今すぐ再起動
でエディタを再起動する
- RenderDocプラグインの初期設定を行う
- プロジェクト設定を開く
- メニュー
『編集』→『プロジェクト設定』
- ツールバー
『設定』→『プロジェクト設定』
- メニュー
『プラグイン』→『RenderDoc』→『詳細設定』
を開くスタートアップ時に自動アタッチ
にチェック- UnrealEditorの起動引数に
-AttachRenderDoc
を追加することでも有効化可能
- UnrealEditorの起動引数に
RenderDoc実行ファイルパス
にRenderDocをインストールしたパスを設定- RenderDocインストール時、特に変更していなければ
C:\Program Files\RenderDoc
となっているはず
- RenderDocインストール時、特に変更していなければ
- エディタを再起動する
- プロジェクト設定を開く
- 有効化されたRenderDocを使ってシーンキャプチャを行う
- 右上のRenderDocアイコンをクリックしてキャプチャ
- コンソールコマンド
renderdoc.CaptureFrame
を実行してキャプチャ
- キャプチャに成功すれば自動的にRenderDocが起動する
パッケージ上からRenderDocを使う
- RenderDocを起動し、
Launch Application
タブを開く Executable Path
にプロファイルを行うアプリケーションの実行ファイルを指定し、Launch
をクリックして起動- 作業ディレクトリを実行ファイルと別の場所を指定する場合のみ、
Working Directory
を設定 - 必要があれば起動引数を
Command-line Arguments
へ指定- 複数のRHIを含んだパッケージ作成していれば、起動引数から使用するRHIを指定可能
- DirectX11:
-d3d11
- DirectX12:
-d3d12
- Vulkan:
-vulkan
- DirectX11:
- 複数のRHIを含んだパッケージ作成していれば、起動引数から使用するRHIを指定可能
- 通常UnrealEngineで作成したパッケージの場合、
『Capture Option』→『Capture Child Processes』
にチェックを入れる
- 作業ディレクトリを実行ファイルと別の場所を指定する場合のみ、
- 起動すると親プロセスタブが開くので、
『Child Process』→『[ProjectName].exe [PID ???]』
をダブルクリックして子プロセスのタブ開く - 任意のタイミングで
『Tools』→『Capture Frame(s) Immediately』
をクリックするか、ゲーム画面上からF12
を押すと、Captures collected
にキャプチャした内容が列挙される。 - 『Captures collected』に列挙されたキャプチャをダブルクリックで開くことで、詳細を確認することが出来る
RenderDoc以外のGPUプロファイラ
RenderDocは大抵のGPUでデバッグできる反面、プロファイル用途として使用するには取得できる内部カウンタが不正確などといった欠点がある。 正確な内部カウンタを取得したい場合には、プロファイルを行いたい環境に合ったプロファイラを使ってください
- NVIDIA製品専用
- AMD製品専用
- Intel製品専用
- Windows専用
- PIX on Windows
- Windows(x64, Arm64)
- DirectX12
- PIX on Windows
- Qualcomm Snapdragon(Adreno)専用
- Arm Mali専用
Win32 threadを使ったスレッド生成
Win32API
Microsoft Windows上で用いられるAPIで、スレッドに限らずあらゆる機能を提供する。 通常スレッドを生成するにはCreateThread関数を使用するが、諸事情で同じ機能を提供する_beginThread関数、AfxBeginThread関数なども存在する
Sample code
#include <stdio.h> #include <windows.h> #define STACK_SIZE (512 * 1024) struct thread_args { int r; int a; int b; int c; } // 生成したスレッドで実行する関数 UINT thread_entry(void *args) { auto targs = reinterpret_cast<thread_args*>(args); targs->r = targs->a * targs->b + targs->c; return 0; } int main() { thread_arg args; args.r = 0; args.a = 10; args.b = 20; args.c = 30; UINT thread_id = 0; auto thread_handle = reinterpret_cast<HANDLE>(_beginthreadex( nullptr, // セキュリティ属性 static_cast<unsigned int>(STACK_SIZE), // スタックサイズ thread_entry, // スレッドの実行を開始する関数 &args, // スレッド関数に渡される引数 CREATE_SUSPENDED, // 初期化フラグ。『CREATE_SUSPENDED』を指定すると一時停止状態で生成される &thread_id // スレッド識別子 )); // スレッド優先度を設定 // 『THREAD_PRIORITY_ABOVE_NORMAL』は通常より1ポイント上の優先順位 SetThreadPriority(thread_handle, THREAD_PRIORITY_ABOVE_NORMAL); // スレッドの実行を開始する ResumeThread(thread_handle); // スレッドの終了を待つ WaitForSingleObject(thread_handle, INFINITE); // スレッドハンドルを閉じる CloseHandle(thread_handle); // 結果を出力 printf("value = %d\n", args.r); return 0; }
出力
value = 230
std::threadを使ったスレッド生成
std::thread
C++11より導入された、新しい実行スレッドを生成、待機、その他の操作を行う仕組みを提供するテンプレートクラス。 std::threadのコンストラクタに与えられた関数オブジェクトと、このコンストラクタを呼び出したスレッドとの間で並行に処理が実行される。
コンストラクタの定義は、
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
このような可変引数の形になっていて、ラムダ式と共に用いられる場合が多い。
機能は最小限のものしか提供されない為、実装環境に依存したスレッドハンドルを取得するメソッドが提供されており、
native_handle_type native_handle();
このハンドルを使う事で高度なスレッド操作を行うことが出来る。 通常このハンドルは、
を表している。 各実装環境でのスレッド生成は以下を参照
kamai-tech-lab.hatenablog.com kamai-tech-lab.hatenablog.com
Sample code
#include <stdio.h> #include <thread> int main() { int value = 0; // スレッドを作成、発行 // ここで生成したスレッドの返り値は無視されるため、結果を受け取る変数の参照も渡しておく auto mad = std::thread([](int &r, int a, int b, int c) { r = a * b + c; }, std::ref(value), 10, 20, 30); #ifdef WIN32 // スレッド優先度を設定 // 『THREAD_PRIORITY_BELOW_NORMAL』は通常より1ポイント下の優先順位 SetThreadPriority(mad.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); #elif defined(__linux__) sched_param param; param.sched_priority = 10; // スケジューリングポリシー、優先度を設定 pthread_setschedparam(mad.native_handle(), SCHED_RR, ¶m); #endif // スレッドが完了するまで待つ mad.join(); // 結果を出力 printf("value = %d\n", value); return 0; }
出力
value = 230
pthreadを使ったスレッド生成
pthread
一般的にPOSIXスレッドの標準実装ライブラリの事を指し、スレッドの生成や基本的な操作を行うことが出来る。 POSIXをサポートしている多くの環境(主にUnix系)で利用できる反面、標準では基本的な機能のみが提供されるため、環境に依存した高度な処理を行う場合には拡張命令を使用する必要がある。
Sample code
#include <stdio.h> #include <pthread.h> #define STACK_SIZE (512 * 1024) #define SCHED_POLICY SCHED_OTHER #define SCHED_PRIORITY 10 #define CLAMP(x, min_value, max_value) (x < min_value ? min_value : (max_value < x ? max_value : x)) struct thread_arg { int r; int a; int b; int c; }; // 生成したスレッドで実行する関数 void* thread_entry(void *args) { auto targs = reinterpret_cast<thread_arg*>(args); targs->r = targs->a * targs->b + targs->c; return nullptr; } int main() { pthread_attr_t attr; pthread_attr_init(&attr); // 生成するスレッドのスタックサイズを設定 if (pthread_attr_setstacksize(&attr, STACK_SIZE) != 0) { pthread_attr_destroy(&attr); return 1; } // 呼び出しスレッドのスケジューリング属性を継承しないよう設定 if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) { pthread_attr_destroy(&attr); return 2; } // スケジューリングポリシーを設定 if (pthread_attr_setschedpolicy(&attr, SCHED_POLICY) != 0) { pthread_attr_destroy(&attr); return 3; } sched_param param; const int min_priority = sched_get_priority_min(SCHED_POLICY); const int max_priority = sched_get_priority_max(SCHED_POLICY); param.sched_priority = CLAMP(SCHED_PRIORITY, min_priority, max_priority); // スケジューリング優先度を設定 if (pthread_attr_setschedparam(&attr, ¶m) != 0) { pthread_attr_destroy(&attr); return 4; } thread_arg args; args.r = 0; args.a = 10; args.b = 20; args.c = 30; // スレッドのハンドル pthread_t thread; // スレッドを生成、発行 if (pthread_create(&thread, &attr, thread_entry, &args) != 0) { pthread_attr_destroy(&attr); return 5; } // スレッド属性を破棄 pthread_attr_destroy(&attr); // スレッドの終了を待つ // スレッドの内部メモリを解放する為、pthread_join または pthread_detach を必ず呼ばなければならない pthread_join(thread, nullptr); // 結果を出力 printf("value = %d\n", args.r); return 0; }
出力
value = 230