PIXとWinPixEventRuntimeを使って特定の処理が呼ばれたらPIXのGPUキャプチャをプログラムから呼び出す
はじめに
この記事は、グラフィックス全般 Advent Calendar 2023 16日目の記事になります。
今回はPIX
とWinPixEventRuntime
を使って、DirectX12の描画処理をプログラム上でキャプチャするやり方について書かせていただければと思います。
PIXとは
PIXは、Windows開発者向けに Microsoft によって作られた、ゲームのパフォーマンス チューニングとデバッグのツールです。
端的に言うと、特定のフレームの描画の詳細情報を取得できるツールで、特定のタイミングで描画がおかしい際に、
- きちんとコマンドリストに描画のコマンドが登録されているのか?
- シェーダーに渡しているリソースの設定はあっているのか?
など、描画系の不具合の原因調査に役立つツールです。
PIX
以外にも同じようなツールとして、RenderDocやNVIDIA Nsight Graphicsなどがあります。
PIX
についてはこちらのスライドの説明が大変わかりやすかったため参考にさせていただいております。
WinPixEventRuntimeは、そのPIXの機能をプログラム上で実行できるSDKみたいなものです。
普段使う際はPIX
のGUIを起動して、確認したいDirectXで動いてるexeを実行させ、確認したいタイミングで添付してある画像のボタンを押すとGPUキャプチャ
という、描画情報をキャプチャしてくれる機能が実行されます。
GUIとGPUキャプチャするまでの手順
実際にGPUキャプチャされた様子
ただ、この処理が呼ばれたときにキャプチャしてほしいなど、特定のタイミングでGPUキャプチャ
を行う場合は、GUIを使う際は目視で確認出来ないタイミングなどがあったりして難しい場面がありました。
調べた所、今回のWinPixEventRuntime
を使うことでそれを解決できたので、そのことについて書こうと思います。
確認する環境
筆者の実行環境は以下になります。それ以外は確認してないので、万が一別途対応が必要になることがあるかもしれません。予めご了承ください。
OS : Windows11
CPU: AMD Ryzen 7 5700X 8-Core Processor
GPU: NVIDIA GeForce RTX 4600 Ti
今回はMicrosoftが出しているDirectX-Graphics-Samplesを使って解説します。
やること
やることはこちらの記事を参考にし、以下の3つになります。
-
WinPixEventRuntime
の機能を呼び出すためにpix3.h
をインクルードする。 - 初期化のタイミングで
PIXLoadLatestWinPixGpuCapturerLibrary
関数を呼び出す。 - キャプチャしたいタイミング(今回はスペースキーを押したら)に
PIXGpuCaptureNextFrames
関数を呼び出す。
WinPixEventRuntime
の機能を呼び出すためにpix3.h
をインクルードする
WinPixEventRuntime
の機能を呼び出すために、 D3D12Fullscreen.cppのインクルードしてる所にpix3.hを追加してインクルードさせます。
#include "stdafx.h"
#include "D3D12Fullscreen.h"
#include "pix3.h" //追加
PIXLoadLatestWinPixGpuCapturerLibrary
関数を呼び出す
初期化のタイミングで初期化のタイミングでPIXLoadLatestWinPixGpuCapturerLibrary()
を呼び出します。
これを呼ぶことで、PCにインストールされている最新のGPUCaptureLibrary.dll
というGPUキャプチャ
の機能が入ったDLLを作業ディレクトリにコピーしなくても実行できるようにしてくれます。
void D3D12Fullscreen::OnInit()
{
PIXLoadLatestWinPixGpuCapturerLibrary(); //追加
LoadPipeline();
LoadAssets();
}
PIXGpuCaptureNextFrames
関数を呼び出す
キャプチャしたいタイミング(今回はスペースキーを押したら)にキャプチャしたいタイミングでにPIXGpuCaptureNextFrames
を呼び出します。
この関数の説明はこちらの記事によると、以下になってます。
HRESULT WINAPI PIXGpuCaptureNextFrames(PCWSTR fileName, UINT32 numFrames)
GPU Capture only
This API enqueues a Present-to-Present GPU capture that will begin next time a qualifying Present() call is made. This is equivalent to pressing the Print-Screen key or hitting the capture button in the PIX UI.
The filename should use the .wpix file extension.
google翻訳などで翻訳すると、以下の様な内容になります。
この API は、次回適切な Present() 呼び出しが行われたときに開始される Present-to-Present > GPU キャプチャをキューに入れます。これは、Print-Screen キーを押すか、PIX UI のキャプチャ > ボタンを押すことと同じです。
ファイル名には .wpix ファイル拡張子を使用する必要があります。
要約すると、スワップチェーンのPresent関数が呼ばれたタイミングで、そのフレームで描画された情報をGPUキャプチャに取り込んでくれる関数です。
fileName
は説明にもある通り、ファイル名の最後に.wpix
をつけることと、保存したいファイル名はPCWSTR
なので最初にLを入れます。今回は「L"TestGPUCapture.wpix」"にしてます。
numFrames
はキャプチャするフレーム数を指定します。今回は最初のフレームだけ欲しかったので1を入れてます
今回使っているD3D12Fullscreenサンプルには既にスペースキーが押されたらイベントがフルスクリーンに切り替える処理が既に用意されているので、そのタイミングで一緒にキャプチャをさせてます。
void D3D12Fullscreen::OnKeyDown(UINT8 key)
{
switch (key)
case VK_SPACE:
{
PIXGpuCaptureNextFrames(L"TestGPUCapture.wpix",1); //追加
//以下元々あった処理
}
}
検証
- DirectXで動作するexeを起動する
- スペースキーを押す
- exeがあるフォルダに下記画像のように保存されているか確認
.exeと同じ場所に保存されている様子
実際に「TestGPUCapture.wpix」を開くとこういう感じなる
ハマったこと
PIXGpuCaptureNextFramesがIntelligenceなどで参照できない
D3D12Fullscreenサンプルは既にWinPixEventRuntime
がインストールされているのですが、そのバージョンが古いと特定のPIXの処理がその関数はないとエラーがはかれてしまい呼べませんでした。
「WinPixEventRuntime 1.0.161208001」だったので、2023/12/15当時の最新バージョン「WinPixEventRuntime 1.0.231030001」にアップグレードしたら参照できるようになりました。
アップグレードする際、前のバージョンが存在するとエラーが出るようになるので、以下の記事を参考に、vcprojを開いて直接編集する必要があります。
具体的には以下の行があるので、こちらを消去します
<Error Condition="!Exists('$(SolutionDir)packages\WinPixEventRuntime.1.0.161208001\build\WinPixEventRuntime.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\WinPixEventRuntime.1.0.161208001\build\WinPixEventRuntime.targets'))" /> //ここを消す
終わりに
今回はPIX
とWinPixEventRuntime
を使ってプログラム上でについての記事を書かせていただきました。
わかりにくい点などありましたら、お手数ですが記事のコメントに書いていただけると助かります。
PIX
の日本語の解説記事はあまり多くはないので、今回の記事が誰かの役に立てれば幸いです。
Discussion