レイトレ合宿11 参加レポート

こんにちわ、Pocolです!
毎年恒例のレイトレ合宿に参加してきました。
ついに第1回合宿から連続で作品提出しているのは,ykozwさんと私だけになってしまったので,どんなにへぼくても何とか来年も作品提出できるように頑張ろうと思いました。

本戦

今年は最多の22作品+1エクスビジョンという結果になりました。

さて、今年の私の提出作品ですが,下記のような作品を作りました。


ソースコードは下記にて公開しています。
https://github.com/ProjectAsura/ponzu/tree/rtcamp_2025

実装概要ですが,下記のような感じです。

今年はブリキのおもちゃ感をテーマにレンダリングしてみました。
まぁ、実装の方は大したことやっていないのですが,RISを導入してみました。
気持ち収束が早くなったかな?という感じがしていますが、ちゃんと比較していないので気のせいかもしれません。

昨年までは乱数生成にPCGを使っていたのですが,今年はAndanteさんのブログで紹介されているIbuki Hashに変更してみました。サクッと差し替えできるのでお勧めです。

あと、去年まではお手製デノイザーを使っていたのですが、このデノイザー内でクロスバイラテラルフィルタを使用していたのですが,この関係で最終出力結果がボケボケな絵になってしまうということが分かったので,今年は思い切ってクロスバイラテラルフィルタを一切使わないという割り切った実装にしてみました。そのおかげでスペキュラーがいい感じの見た目になるようになりました。

アセット作成にはAsset Forgeを使用しました。


Kenneyさんのサイトで配布しているキットバッシュとAsset Forgeを使って,レベルを作りました。
Asset Forgeを初めて使う状態でしたが,操作が簡単なので2時間ぐらいでレベルを作成しました。何とか間に合ってよかったです。お手軽にシーンが作成できるようになったので、来年も使おうかなとおもっています。おススメです。
 ちなみにAsset Forgeは下記から購入できます。日本円で約3000円ちょっとでそんなにお高くないので,自分で配置シーンツールとか作るのがかったるい人には良いと思います。
https://kenney.nl/tools/asset-forge

セミナー


今年も、非常に盛りだくさんの内容でした。
また、皆さんのセミナー資料がアップされたら、順次取り上げていこうと思います。

今年度は本当に時間がなかったので、比較的に誰にも分かる内容かつ知らない人には役立つ内容ということで,DXR 1.2の機能紹介をちらっとやりました。
以下に資料を上げているので,興味ある人は見てやってください!

…ということで本業で忙しい状態でしたが、今年も何とか参加できて良かったです。
このあと、参加者の方から続々とレポートが上がってくると思われるので,そちらも楽しみです。
みなさん、レイを飛ばしましょう!では。

恒例のBBQ会!

こんにちわ。Pocolです。
今年もCEDEC恒例のグラフィックスプログラマーで集まってやるBBQ会(グリル会)を開催するそうです!
昨年参加された方は今年もご参加ください!

詳細については幹事のnikqさんのツイートをご参照ください。

2025/07/07更新
※チケットは完売しました。

果報は寝て待て!

こんばんわ、Pocolです。

昨年に引き続き,グラフィックスプログラマーやTAさんなどでBBQ会を開催するようです。
昨年参加された方は是非奮ってご参加ください。
詳細は,近日中に主催者よりご案内あるそうなので,お待ちください。

そんなわけで,皆で肉喰いましょう!
今年も楽しみだ…。

それではまた!

Steam Deckでのデバッグ

こんにちわ。Pocolです。

Steam Deckでのデバッグ方法について,ググってもあんまりいい情報が出てこなかったので,ここにメモしておこうと思います。

前提

  • Steamの公式ドキュメントを見ておきましょう。
  • Microsoft Child Process Debugging Power Tool をインストールしておきましょう。設定も忘れずに
  • デバッグDLLはSteam Deck実機上に含まれないので,あらかじめ転送するフォルダにデバッグ用のDLLを含めるようにしましょう
  • 開発PCとSteam Deckは同一LAN上になるように接続しておきましょう。
  • Windowsで開発しているものであれば,Proton – Experimentalにするのを忘れないように。

Visual Studioを使ったデバッグ

リモートデバッグを使用して,デバッグを行います。
まず,SteamOS Devkit OSを立ち上げて,実行ファイルやアセットなどを1つのフォルダ下にまとめておきます。
次にUploadを実行する前に下記の設定を行っています。

  • Steam Playにチェック入れる(This title requires Steam Playの項目)
  • Steam Play debugにチェックを入れる(Start Visual Studio C++ debugger service on launchの項目)
  • Wait for attachにチェックを入れる(Wait for a debug client to attach)

チェックを入れてから,Uploadボタンを押して実行ファイルとアセットファイルなどを実機に転送します。
DISMISSにならずにちゃんとアップロードできていたら,Steam Deck実機上からゲームを起動します。
ゲームはSteam Deckのホーム画面から「ライブラリでもっと見る」を選択し,「非STEAM」の項目に居る場合があるので,R1ボタンで切り替えて,デバッグしたいアプリを選択してください。

ゲームが起動したらアタッチ待ちに入るので,Steam Deck上で,Steamアイコンが表示された状態でグルグル回っている画面で止まった状態になります。
この状態で,開発用PC側からアタッチをかけます。
Visual Studioを立ち上げて,リモートデバッグを選択します(「デバッグ」>「プロセスにアタッチ」)。

「リモート認証なしーWindows」を選択して,検索ボタンを押します。
ウィンドウが立ち上がるので,その中からsteamdeckを選択します。
選択するとプロセス一覧が表示されるようになるはずなので,steam.exeを選択します。
前述したChild Process Debugging Power Toolがインストールされて入れば,ゲームにアタッチできるようになります。
アタッチ後は通常のVisual Studioのデバッグと同様に進めていけばよいです。

RenderDocを使ったデバッグ

SteamOS Devkit Client を立ち上げ,Devkitsボタンを押して,下から3番目あたりにある 「RenderDoc captures enabled」にチェックを入れておきます。
チェックを入れたSteam Deck上でデバッグしたいゲームを起動します。
ちゃんと設定がされていれば,画面左上にRenderDocのオーバーレイが表示されるようになっているはずなので,これを確認します。

続いて,開発PCからRenderDocを立ち上げ、メニューから File > Attach to Running Instanceを選択します。
Remote Host Managerというウィンドウが立ち上がるので,Steam DeckのIPアドレスを打ち込み,Addします。
Referesh Allを押して,更新をかけます。ゲームが立ち上がっていれば,win64_preloaderというようなプロセスがあるので,これを選択して Connect to App ボタンを押します。
コネクションが確立されたら,RenderDoc上から,Capture Frame(s) Immediately ボタンを押して,フレームキャプチャーを実行します。
キャプチャーが成功すれば,Captures collected にフレーム画面が表示されるようになります。このデータを一度開発PC上に保存しておきます。
保存の仕方はフレーム画像を右クリックしてポップアップメニューからSave を選択すればファイルダイアログが立ち上がるので,好みの場所に保存しておいてください。

次にフレームデバッグの準備をします。Steam Deck上で,RenderDocのremoteserverを起動する必要があります。
SteamOS Devkit Clientに戻り,Devkitsボタンを押して切り替え,Remote Shell ボタンを押し,ターミナルを起動します。
ターミナルが起動したら renderdoccmd remoteserver -d を打ち込み,RenderDocリモートサーバーを起動させます。
成功すれば,
Spawning a replay host listening on *…
Detaching.
というようなメッセージが表示されるはずです。
次に開発PC側のRenderDocの接続設定を変更します。
RenderDocウィンドウの左下にReplay Contextがあるので,これを接続しているSteam Deckに変更してください。
接続できれば,ステータスバーが Remote server ready というように表示が変わるはずです。

ここまで来たら,あとはいつも通りです。
キャプチャーしたフレームを選択すると,ドローコールなどが見えるようになるので,通常通りにグラフィックスデバッグを進めていきます。

おわりに

かなり設定が面倒ですが,これでデバッグが進められるようになるはずです。
デバッグDLLがなかったり,依存DLLが無いとサイレントでゲームが落ちるようなので,まずはちゃんと起動する状態にするのと,RenderDocのフレームキャプチャーしても落ちない状態に持っていくのが,難所だったりするので頑張ってください。

またカスタムビルド

どうしてもエディタとしてVisual Studioを使いたいPocolです。

一番簡単なカスタムビルドのメモです。
.vcxprojをテキストエディタで開いて,一番下のほうに次ような感じでコマンドを挿入します。

  </ImportGroup>
  <Target Name="Build">
      <Exec Command="call build.bat" />
  </Target>
  <Target Name="Clean">
      <Exec Command="call clean.bat" />
  </Target>
</Project>

ちなみにExecタスクのドキュメントは下記ですので,細かい設定を追加した場合は下記を参考にしてください.
MSBuildリファレンス > タスクリファレンス > Execタスク
https://learn.microsoft.com/ja-jp/visualstudio/msbuild/exec-task?view=vs-2022

Visual Studio上からリビルドを実行した場合は,Clean —> Build の順番で呼び出しされます。
もしリビルド自体をカスタマイズしたいなら

  <Target Name="Rebuild">
    <Exec Command="call rebuild.bat" />
  </Target>

とやってあれば,自前にリビルドコマンドに変更できます。

実行時のコマンドのカスタマイズは,通常のVCと同じようにプロパティから変更すれば良いと思うので,それでやればよいかと思います。
…というわけでカスタムビルドのメモ書きでした。

どっかで

クリックしてtede-msbuild-2.0.pdfにアクセス

とか

あたりを参考にもうちょいちゃんとしたものを今後改造してみたいと思います。

DREDとAftermathのサンプルプログラムを作りました。

こんばんわんわん、Pocolです。

X(旧Tiwtter)でも書いたのですが,DRED(Device Removed Extended Data)のサンプルプログラムを書きました。
サンプルは以下に置いておきました。
https://github.com/ProjectAsura/D3D12Samples/tree/master/D3D12_DRED

DREDなんですが,意外とまともなドキュメントが無いです。ドキュメントあるんですけども,わかりづらい,「この変数の意味は?」みたいな痒いところに手が届くものが無い感じがしますね。(単純に、ドキュメントみて理解できない私がアホなだけなんですが…)
…というわけで,コードを書いてみました。
仕事で使っているのはちゃんと,Push/Popの入れ子とかも考慮しているやつですけども,まぁええでしょ。こまけぇこたぁいいんっすよ。
結局,DREDのサンプルで困るのは「これちゃんとGPUクラッシュ時に出るの?」という所で,故意にGPUクラッシュさせるようなプログラムがなかなかネットで見つからない。
それだと,動作確認に困るので,GPUクラッシュさせるプログラム書きました。
ここ最近,ずっとGPUクラッシュの調査していたので,どうやれば簡単にGPUクラッシュを引き起こせるかなどのノウハウが溜まったので,その知見を活かして書きました。
一番よくある例,実行中にテクスチャを解放しちゃうやつ。これが一番良くあるので,Tボタン押したら,テクスチャをRelease()するようにしました。これで簡単にPageFaultのGPUクラッシュが発生します。TDRはレジストリいじっている場合は,発生までに時間かかるし,意外とGPU側でちゃんと対処してくれちゃったりする場合もあるので,無理やりやろうとしても意外と発生しなかったりします。サクッといかない。
DirectX-Samplesにはhttps://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Tools/DXGIAdapterRemovalSupportTestというやつもあるみたいですが,こっちは触ったことないんで良く分からんっす(詳しい人教えてください)。

…で馬鹿の一つ覚えみたいな感じなんですが,PageFaultを発生できるようになったので,NVIDIA Aftermathのサンプルも書きました。
一応クラッシュ発生時に,ShaderBinaryとShaderPDBを吐き出して,クラッシュログを調査できる感じにしてみました。私のサンプルの場合は,クラッシュが発生している該当シェーダだけを出力するので,そんなにクラッシュダンプ出力に時間はかからないと思います(仕事でつかっているやつは,別の人が既に実装されたやつなんで,全部のシェーダのバイナリとPDBを出力しやがるんで,時間とディスク容量を食いまくって,困るんですよね。直すのは面倒ですし、時間の余裕もないので直す気はサラサラないです)。
サンプルプログラムは下記にあります。
https://github.com/ProjectAsura/D3D12Samples/tree/master/D3D12_NvAftermath
時間があれば,解説書いてもいいんですが,残念ながら,その時間がないのと,若干仕事のせいで鬱気味なのでやる気が起きないっす。(どうせみんなUEやらUnityつかうでしょ?こういう直叩きするひとがもう日本じゃ少数だから,親切に書いてあげても意味が無いんですよ。見る人いないから…)

…というわけで,リリースして精神的に落ち着いたら,のんびりゆったり解説を書こうかなと思います。
まぁ,そんなの期待する人はほぼいないと居ないと思いますが。

GPUクラッシュが激減した…

こんばんわ、Pocolです。

このところ,仕事でずっとGPUクラッシュを追っていたのですが,なんとなくノウハウが溜まってきました。
ほぼ、毎日のようにGPUクラッシュが何十件もあり,色々な人に手伝ってもらいながら,調査していたんですが,ようやくそれがパッタリ収まりました。

いくつか対策を入れていたのですが,結局どれが効いたのかは正直分からないのですが…
groupsharedで,UINT32_MAXでアクセスして,GPU上でメモリ破壊を発生させるコードがあったので,修正した所,謎に発生していたGPUクラッシュが治まりました。
多分,数年レベルで放置されていたバグなんじゃないかと思います。

NVIDIA AftermathとかでGPUクラッシュダンプを調べても,PageFault,さらにシェーダ情報もでない,挙句の果てにはResourceBarrierを実行するとGPUクラッシュする,定数バッファが壊れて無限ループして,TDRで落ちるなど,過去のクラッシュログを見ても,まったく共通性もなく,しかも結構頻発する。でも全然再現性が無い,手元で全く発生しない…という困ったちゃんで,困りまくった挙句何かないのか?と調べてみたら,NVIDIA Aftermathに,GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReportingというフラグがあるのですが,これを有効にしたところ即クラッシュするようになりました。
何で無効だったのか聞いたんですが,こちらはヘッダファイルにも書かれているようにパフォーマンスペナルティがあるということで,ゲームプレイに支障があるとのことで無効化していたとのことでした。
結局,このフラグを有効にしたところ,先ほど述べたgroupsharedのバグを発見できたのと,他にもクラッシュする原因が見つかって,大いに役立ちました。

今後は,確定で発生しないものはGPU上のメモリ破壊を疑った方が良いという知見が得られました。
基本的にはUAVとかSRVとかのLoad()とかoperator []あたりミスっているんじゃないかって思いがちなんですが,これらはAPIドキュメントを見ると、安全に良しなにしてくれそうなことがあるので,メモリ破壊が発生するのは,CPU上での書き換えか,groupsharedのアクセスミスによる2パターンしかないように思えます(他のパターンがあったら教えてください)。CPU上でのメモリ破壊の可能性がほとんど低いことが分かったなら,groupsharedでの破壊が無いかどうかを調べましょう。
今回のバグは,

groupshared g_Variable[XXX][XXX];
みたいなのが定義されていて,
float temp0 = saturate(XXX);
uint temp1 = temp0 * MAX_VALUE – 1;
uint index = min(temp, MAX_VALUE);
g_Variable[index][XXX] = ….;
っぽいような,謎の計算がされていて(uintをマイナス1する時点でぞわぞわしちゃんですが…)
temp0がゼロになったときに,アボンするみたいケースでした。
よくよく見ると「馬鹿か!」って怒鳴りたくなったっちゃうような,不具合なんですが,変数がごちゃごちゃ定義されていたりとか,ジュニアレベルのエンジニアだとこういうチェックがおざなりになりがちなんで,気を付けた方が良いよ!…という良い事例になりました。

…というわけで,社内でも共有したんですが,この場でも共有してみました。
謎バグに困っている方は,groupsharedで変なことしていないかどうか確認してみると良いかもです。
(※ちなみ1個あったら,他にも絶対あるだろうと思って全シェーダをチェックしてみたのですが,確認した所発生しているのは該当シェーダの1個だけでした。)

パンくずリスト

こんばんわ。Pocolです。

最近ずっとGPUクラッシュ調査の仕事をやっていて疲弊しています。
基本的には,DREDとAftermathを使っています。
DREDを使うと,自動でパンくずリストを作ってくれます。
これ,基本的にはマーカー名とどこまで進んだかを教えてくれます。

GPUクラッシュは大半がTDRかPageFaultだと思います。
TDRでよくあるのは,ループ終了条件を定数バッファのメンバとして渡すパターン。
バッファがどっかで壊れて,終了条件値が想定外の値になって,無限ループ扱いになってクラッシュするとかが良くあります。

で、クラッシュ調査時に欲しいのはどのシェーダ?設定されているのはどのバッファ?中身どうなっている?
…あたりの情報が知りたくなります。

そこで,思いました。
DREDの自動パンくずリストをやめて,自前でカスタムのパンくずリストを作った方が便利なのでは?
そう思ったので,パンくずリストについて調査してみます。

Githubやググったんですが,まともな情報がほぼありません。
FidelityFX Breadcrumbs 1.0が唯一信じられるまともな実装です。
これを調査してみます。
ドキュメントは下記です。
https://gpuopen.com/manuals/fidelityfx_sdk/fidelityfx_sdk-page_techniques_breadcrumbs/

ソースコードは

…あたりを見ると良いです。

基本的な仕組みとしては,
D3D12の場合は,WriteBufferImmedidate() で実行済みフラグを立てていくだけみたいです。
Vulkanの場合は,AMD拡張が使える場合は,vkCmdWriteBufferMaker2AMD()やvkCmdWriteBufferMarkerAMD(),そうでない場合はvkCmdFillBuffer()を使って実行済みフラグを立てていくようです。
現在困っているのは,D3D12環境なので以下D3D12として説明します。

で、WriteBufferImmediate()でどこに書き込むか?なのですが,次のような感じで書き込むメモリを用意するようです。
・VirtualAlloc(nullptr, bufferSize, MEM_COMMIT, PAGE_READWRITE)でメモリを用意。— (A)
・(A)で用意したメモリを引数として,ID3D12Device3::OpenExistingHeapFromAddress() をコールして,ID3D12Heapを取得し,CreatePlacedResource()でID3D12Resourceを生成。 — (B)
・(B)に失敗した場合は,CreateCommittedResource()でID3D12Resourceを生成し,メモリはMap()して取る — (C)
・(B)または(C)にてID3D12Resourceが出来上がるので,ID3D12Resource::GetGPUVirtualAddress()して,BaseAddressを取得 — (D)
・(D)で取得したBaseAddressを開始点として,uint(4byte)で,フラグをWriteBufferImmedidate()で書き込んでいく。

これでコマンドリストに記録されるようになるので,あとはクラッシュした際に(B)または(C)で用意してあるメモリをReadしていきます。これで書き込まれていればフラグが立っていくはずなので,どこまでコマンドが進んだかどうかが判定できます。
マーカー名などのデータはCPU側で管理して,Readしたデータと照合して一致させて,デバッグログなどに表示させれば良いようです。

細かい実装は,FidelityFX Breadcrumbsのソースコードを参照してみてください。

…というわけで,パンくずリストを自前実装する際の基本的な仕組みが分かりました。
あとは,CPU側で管理するデータをリッチにしていけば,色々とデータが取れそうです。

まずは、これらの情報を元にカスタムパンくずリストの実装を始めてみようかなと思いました。
そんなわけで、パンくずリストの話でした。
もし、ノウハウを色々とお持ちの方は是非教えてください。

———————
同僚の方にノウハウを教えてもらいました。
メモリが直接見えるコンソール機では,WriteBufferImmedidate()に対応するような命令が無いことが多く,その場合はタイムスタンプを使って,コマンドがどこまで進んだかを調べると良いそうです。
これは確かに良いなと思いました。

本年もよろしくお願い致します。

Pocolです。
喪中につき、新年のご挨拶は控えさせていただきます。
寒い日が続きますが,どうぞ皆様ご自愛くださいませ。
本年もよろしくお願い致します。