🐡

PIXとWinPixEventRuntimeを使って特定の処理が呼ばれたらPIXのGPUキャプチャをプログラムから呼び出す

2023/12/16に公開

はじめに

この記事は、グラフィックス全般 Advent Calendar 2023 16日目の記事になります。
今回はPIXWinPixEventRuntimeを使って、DirectX12の描画処理をプログラム上でキャプチャするやり方について書かせていただければと思います。

PIXとは

PIXは、Windows開発者向けに Microsoft によって作られた、ゲームのパフォーマンス チューニングとデバッグのツールです。
端的に言うと、特定のフレームの描画の詳細情報を取得できるツールで、特定のタイミングで描画がおかしい際に、

  • きちんとコマンドリストに描画のコマンドが登録されているのか?
  • シェーダーに渡しているリソースの設定はあっているのか?

など、描画系の不具合の原因調査に役立つツールです。
PIX以外にも同じようなツールとして、RenderDocNVIDIA Nsight Graphicsなどがあります。

PIXについてはこちらのスライドの説明が大変わかりやすかったため参考にさせていただいております。
https://www.docswell.com/s/imagire/Z9XYXK-PIX2022#p2

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つになります。

  1. WinPixEventRuntimeの機能を呼び出すためにpix3.hをインクルードする。
  2. 初期化のタイミングでPIXLoadLatestWinPixGpuCapturerLibrary関数を呼び出す。
  3. キャプチャしたいタイミング(今回はスペースキーを押したら)にPIXGpuCaptureNextFrames関数を呼び出す。

WinPixEventRuntimeの機能を呼び出すためにpix3.hをインクルードする

WinPixEventRuntimeの機能を呼び出すために、 D3D12Fullscreen.cppのインクルードしてる所にpix3.hを追加してインクルードさせます。

D3D12Fullscreen
#include "stdafx.h"
#include "D3D12Fullscreen.h"
#include "pix3.h" //追加

初期化のタイミングでPIXLoadLatestWinPixGpuCapturerLibrary関数を呼び出す

初期化のタイミングでPIXLoadLatestWinPixGpuCapturerLibrary()を呼び出します。
これを呼ぶことで、PCにインストールされている最新のGPUCaptureLibrary.dllというGPUキャプチャの機能が入ったDLLを作業ディレクトリにコピーしなくても実行できるようにしてくれます。

D3D12Fullscreen
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サンプルには既にスペースキーが押されたらイベントがフルスクリーンに切り替える処理が既に用意されているので、そのタイミングで一緒にキャプチャをさせてます。

D3D12Fullscreen
void D3D12Fullscreen::OnKeyDown(UINT8 key)
{
    switch (key)
    case VK_SPACE:
    {
        PIXGpuCaptureNextFrames(L"TestGPUCapture.wpix",1); //追加
        //以下元々あった処理
    }
}

検証

  1. DirectXで動作するexeを起動する
  2. スペースキーを押す
  3. exeがあるフォルダに下記画像のように保存されているか確認


.exeと同じ場所に保存されている様子


実際に「TestGPUCapture.wpix」を開くとこういう感じなる

ハマったこと

PIXGpuCaptureNextFramesがIntelligenceなどで参照できない

D3D12Fullscreenサンプルは既にWinPixEventRuntimeがインストールされているのですが、そのバージョンが古いと特定のPIXの処理がその関数はないとエラーがはかれてしまい呼べませんでした。
「WinPixEventRuntime 1.0.161208001」だったので、2023/12/15当時の最新バージョン「WinPixEventRuntime 1.0.231030001」にアップグレードしたら参照できるようになりました。

アップグレードする際、前のバージョンが存在するとエラーが出るようになるので、以下の記事を参考に、vcprojを開いて直接編集する必要があります。
https://learn.microsoft.com/ja-jp/xamarin/cross-platform/troubleshooting/questions/nuget-packages-missing

具体的には以下の行があるので、こちらを消去します

D3D12Fullscreen.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'))" /> //ここを消す

終わりに

今回はPIXWinPixEventRuntimeを使ってプログラム上でについての記事を書かせていただきました。
わかりにくい点などありましたら、お手数ですが記事のコメントに書いていただけると助かります。

PIXの日本語の解説記事はあまり多くはないので、今回の記事が誰かの役に立てれば幸いです。

Discussion