[DirectX] PIX と Graphics Debugger

[DirectX] PIX と Graphics Debugger

PIX は Microsoft から提供されている、DirectX アプリケーションの Graphics Debugger ツールです。フレームキャプチャやパフォーマンスチューニングの機能を持っています。

DirectX 11(Windows 7)の頃までは DirectX SDK に付属する形で「PIX for Windows(PIXWin.exe)」というスタンドアロンのツールが提供されていましたが、Winodws 8 の時代になると DirectX SDK は Windows SDK に統合される形になり、そのタイミングで PIX for Windows は機能の開発や提供は一時的に終了となりました。

代わりに出てきたのが、Visual Studio に統合する形で提供された Graphics Debugger(Graphics Diagnostics)というツールでした。これは Visual Studio 2012 から利用できるようになり、Visual Studio 上でコーディングから DIrectX アプリケーションのデバッグまでを行うことが可能となりました。当初は DirectX 10/11 の対応でしたが、2015年にリリースされた Windows 10 と Visual Studio 2015 からは DirectX 12 にも対応しています。

DirectX Tools for Windows 10 in Visual Studio 2015
https://learn.microsoft.com/en-us/shows/connecton-demand/212

動画を見てもらえれば分かるように Visual Studio 上で DirectX のパフォーマンス解析や Frame Capture などが行えています。

新規 PIX の提供

https://devblogs.microsoft.com/pix/introducing-pix-on-windows-beta/

暫くの間、Windows のグラフィックスデバッガは Visual Studio 上のツールとして提供されていましたが、2017年1月には別の動きがありました。DirectX SDK での更新を最後に止まっていた Windows 向けの PIX が新規ツールとして再び提供されるようになりました。

こちらは DirectX 12 のみに対応しており(後にD3D11on12には対応)UI なども一新され、名称も「PIX for Windows」から「PIX on Windows」と僅かに変更がありました。

そして、新規 PIX がリリースされた約1年後、2018年にリリースされた Windows10 1809 からは、以前まで利用できていた Visual Studio 上の Graphics Debugging のサポートも打ち切りとなりました。

DirectX12 debugging in Visual Studio is no longer supported by the Windows team in 1809 and the recommended path forward is to use their PIX for Windows app.

https://developercommunity.visualstudio.com/t/visual-studio-directx-graphics-debugging-tool-can/417292

そのため、Microsoft から提供される DirectX アプリケーションの Graphics Debugger は再び PIX に一本化されることとなりました。

PIX の更新

2024年現在 PIX は年に数回と、かなりの高頻度でアップデートが行われています。

特に Agility SDK の更新に追随する形でデバッグ機能もサポートした PIX も同時にリリースしてくれているので、手探りの状態で進めないといけない新規機能の対応などでは PIX が非常に活躍してくれます。

参考

https://devblogs.microsoft.com/pix/download/

[DirectX12] WinPixEventRuntimeを使ってPIX向けにEvent名を仕込む

[DirectX12] WinPixEventRuntimeを使ってPIX向けにEvent名を仕込む

PIX は DirectX で作成されたアプリケーションのパフォーマンスチューニングや描画の解析を行うためのツールです。1フレームの中でどのような順番で何が描かれたのかを観測できたり、1パスの中で描画に掛かった時間を計測できたりするので、意図しない描画結果が生成された場合や想像以上にアプリケーションが重い場合などのデバッグに役立ちます。

DirectX 12で作成されたアプリケーションであれば、そのままでも PIX 経由で Launch すれば GPU Capture などを取ることが可能ですが、ここで紹介する WinPixEventRuntime を使用することで、デバッグに役立つラベルが付いた状態でキャプチャーを取ることが可能です。

なぜラベル付けが必要か?

DirectX のアプリケーションは、非常に膨大なグラフィックスコマンドの呼び出し(記録)によって処理が行われます。

適宜、これが Shadow pass なのか G-buffer pass なのか Lighting pass なのか、ラベルが付いていると視認しやすくなります。

WinPixEventRuntime の使い方

インストール方法はこのページに記載があります。WinPixEventRuntime は NuGet パッケージ経由でインストールを行います。

PIXBeginEvent/PIXEndEvent の内部は?

pix3.h の関数はヘッダー内で実装が完結しているものが多いため、ブレークポイントで止めて Stepin して中身の実装を確認することが可能です。実は commandlist::beginevent をラップしたようなものになっています。

そのため、PIXBeginEvent はどこか特殊なバッファに情報を記録しているという訳ではなく、実際はアプリケーション側で作成した DirectX12 の commandlist/commandqueue に対して、計測用の情報を記録している形になっています。

https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-beginevent

PIXBeginEvent で仕込んだイベント名は、PIX でしか使えないと思われるかもしれませんが、NSight や RenderDoc でキャプチャーした場合でもイベント名を取得することが可能です。PIXBeginEvent で設定さえすれば他のグラフィックスデバッガーでも同様に使えるので非常に便利です。

[iOS] Object Capture を使ったモデル生成と UE5 への配置

[iOS] Object Capture を使ったモデル生成と UE5 への配置

iOS17 (iPhone Pro シリーズのみ)から Object Capture と呼ばれる機能が利用できるようになった。これを使うことで、3D モデルを iPhone 単体で手軽に生成することが出来る。

アプリのインストール

Object Capture に対応したアプリを iPhone にインストールする。

Photogrammetry というアプリが日本語で利用できた。ただ、中身は Apple が提供しているサンプルコードそのものなので、自分で xcode からビルドできる人は、サンプルからビルドして転送した方が良いかもしれない。その方が、広告も無いし、知識があれば自分で使いやすいようにカスタマイズも出来る。

https://developer.apple.com/documentation/realitykit/guided-capture-sample

アプリを導入して、画面の指示に従いつつキャプチャーすれば、およそ5分~10分で 3D データの生成が完了でき、iPhone 端末内に保存される。なお、ファイル形式は usdz になっていた。

PC に転送

転送方法はいろいろある。iCloud で共有して、PC でダウンロードしてもいいが、私は iPhone 内に保存されたファイルを選択して AirDrop で Mac PC に送信した。

Blender で開く

Blender 3.5 から?usdz の import に対応している。

Import から usd を選択して、PC に転送たい usdz ファイルを選択する。

画像に alt 属性が指定されていません。ファイル名: 2023-09-28_13h56_17-1024x591.png

読み込みが完了した後に、Viewport の右上を変えることで、テクスチャの表示に切り替えることができる。

UE5 インポート

Blender への読み込みが完了すれば、あとは FBX で出力して、UE5 に持ってくれば良い。

[Unity] Apple Silicon の mac で iOS/Android ビルド

[Unity] Apple Silicon の mac で iOS/Android ビルド

Unity Hub のインストール

私の探し方が悪かったのか、なぜか通常の Download のページからでは mac 版のインストーラーが見つからなかった。そのため、下記フォーラムを参照した。

https://forum.unity.com/threads/installing-the-native-apple-silicon-editor.1188253/
Spoiler: You are starting from scratch を選択すると、Unity Hub インストーラーへのリンクが表示される。Unity Hub の dmg ファイルが落とせたのでこれをインストールする。

バージョンは 3.5.2-beta.1 だった。

プロジェクトの作成

プロジェクトテンプレートから新規プロジェクトを作成するが、この時に「3D Mobile」を選択したら Android の Build に失敗した。そのため、プロジェクトを選ぶ時は「3D」を使用した。(恐らく 3D Sample Scene URP でも大丈夫だと思われる)

Android ビルド

Unity インストール時に Android Build Support にチェックを入れておく。

プロジェクトを開いて File -> Build Settings を選択する。Platform が選べるので Android を選んで「Switch Platform」を押す。少し待てば Unity が Android に切り替わる。これで Android 向けのビルドできるようになる。

Android 端末を接続した状態で「Build and Run」を押せば apk の作成と実機への転送まで自動で行われる。

なお、ビルド前に Scenes In Build に、シーンをドラッグして追加しておく必要がある。0 と書かれているのは起動時に読み込まれるシーン。

iOS ビルド

Unity インストール時に iOS Build Support にチェックを入れておく。

プロジェクトを開いて File -> Build Settings を選択する。ビルドは Platform を iOS を選択して、Switch Platform をしてから「Build and Run」を行う。手順は Android と一緒だが、iOS の場合は ipa を作成するために、xcode でビルドしないといけない。

Unity からは実行ファイルではなく xcodeproj が出力される。これを xcode で開いてビルドする。

xcode ビルド

xcodeproj を開いただけではビルドできない。適切な Signing 設定を行う。私は「Automatically manage signing」にチェックを入れて、Bundle Identifier に任意の値を入れたらビルドが通った。

[FBX] FBX SDK を使用する

[FBX] FBX SDK を使用する

FBX SDK をインストール

2022年10月現在、2022.2.1が最新版になっている。
https://www.autodesk.com/products/fbx/overview

サンプルをビルド

samples フォルダ以下の README.txtを参照。

CMake のインストール

CMake を使用して、Visual Studio 用のプロジェクトファイルを生成する。公式サイトからダウンロード/インストールするか、もしくは、Visual Studio 2019 インストール時のコンポーネント設定で、「Windows 用 C++CMakeツール」にチェックを入れておけば CMake がインストールされる。

VS 用プロジェクトを生成

  1. cd samples\ ViewScene
  2. mkdir build
  3. cd build
  4. cmake -G “Visual Studio 16 2019”

これで、VS 用 sinなどが生成される。Visual Studio を開いてビルドすれば実行できる。

ビルドして実行

私の環境の問題なのか、何か手順を間違えたのか分からないですが、そのままだとリンクが通らない
2> LINK : fatal error LNK1104: cannot open file ‘libfbxsdk-md.lib’
参照している lib のパスになぜか vs2017 が来ていた。プロジェクトを右クリックから開き、Linker -> Input -> Additional Dependecies に指定されているパスを vs2017 から vs2019 に書き換える。

[Git] SourceTree でコンフリクトの解消

[Git] SourceTree でコンフリクトの解消

ブランチのマージ

Github でプルリクからマージする時に、コンフリクトしてマージが行えなかった。

その解決にはまず、main のデータを作業ブランチに取り込む。

main ブランチを取得した状態で「現在のブランチに main をマージ」を選択する。リモートを右クリックして、リモートの「現在のブランチに origin/main をプル」で可。

コンフリクトしているファイルを確認して右クリックから「競合を解決」から動作を選択。

  • 自分(mine)の変更内容で解決
    • 自分の変更を採用する場合はこちらを選択
  • 相手(theirs)の変更内容で解決
    • 自分の変更を破棄して相手の変更を採用する場合はこちらを選択
  • 解決とマーク
    • 手動でマージした場合はこちらを選択

コンフリクトが解消できたらコミットしてプッシュすれば良い。

リベースでコンフリクト

リベースしようとしてコンフリクトした場合は「操作」から「リベースを中止」を選択。

この場合はマージした方シンプルな気がするので、対象のブランチを右クリックして「現在のブランチに main をマージ」を選択する。コンフリクトを解消してからコミット&プッシュする。

[Git] SourceTree の基本操作

[Git] SourceTree の基本操作

新規ブランチ作成

ブランチを右クリックして「名前を変更」を選択して、出現したダイアログにブランチ名を入力して OK を押すとブランチが作られる。

main ブランチに戻る

リモートの origin/main を右クリックして「origin/main をチェックアウト」を選択。

「新規ブランチを作成してチェックアウト」を行えば、origin/main ブランチが取得できる。

現在のブランチに origin/main をマージ

リモートから「現在のブランチに origin/main をプル」を選択。

コンフリクトしなければ、origin/main の変更コミットが取り込まれる。コンフリクトした場合は、ダイアログが表示されるので、ファイルステータスからコンフリクトを解消して、変更をコミットする。

リベース

リベースする場合は Origin に Push したかどうかによって、挙動が異なる?ので注意。コミットは問題ないが、基本的に Push した後はリベースはしない方がよい。

タイムラインからリベース

フェッチしてタイムラインが更新された後に、origin/main を右クリックして、そこから「リベース」を選択してもリベース。

ブランチからリベース

new_feature5 ブランチをリベースしたい。origin/main を取得して、new_feature5 ブランチにいる状態で、main ブランチを右クリックして「現在の変更を main にリベース」を選択する。

リベースでコンフリクト

リベースでコンフリクトした場合は、コンフリクトを解消しつつ、メニューの「操作」からリベースを続けるのか中止するのか、選択していく。ちなみにコンフリクトした場合は、コミットごとに数回のコンフリクト解消が必要みたい。

「リベースを続ける」が選べない状態になるまで、メニューを選択してコンフリクトを解消していく。

[DirectX12] Compute Shader の Thread とシステム値

[DirectX12] Compute Shader の Thread とシステム値

Compute Shader で起動するスレッド数は、コマンドバッファに記録する Dispatch() の引数と、シェーダー内に記述する numthreads の Thread Group 数によって決まる。Dispatch() の引数は、アプリ実行時に動的に変更することが可能だが、numthreads への指定はシェーダーコンパイル時に決まるため、こちらは固定の数になる。

Compute Shader で起動したスレッドは、後述するシステムセマンティックの ID によって、シェーダー内で一意の ID を取得することが出来る。その ID によって処理やデータを分岐して処理する。

Dispatch

実行する Thread Group の数を X, Y, Z で指定する。例えば 1,1,1 で Dispatch した場合は、1つの Thread Group が起動する。

numthreads

コンピュートシェーダーが Dispatch された(起動した)ときの、一つの Thread Group 内で起動するスレッド数を numthreads で指定する。シェーダーコンパイル時に値が決まるため、動的な変更はできない。

System Value

Compute Shader で取得できるシステムセマンティックは4つある。

uint3 gidSV_GroupID
uint3 dtidSV_DispatchThreadID
uint3 gtidSV_GroupThreadID
uint giSV_GroupIndex

SV_GroupID (uint3)

スレッドが、どこの Thread Group で起動しているかを取得できる。Dispatch に指定した値によって変化する。
例えば、Dispatch(2,1,1) を呼び出すと、2*1*1 = 2 Thread Group が起動して、SV_GroupID の値としては(0,0,0)と(1,0,0)という値がスレッド内で得られる。

SV_GroupThreadID (uint3)

スレッドが、Thread Group 内のどの Thread かを表す。numthread に指定した値によって変化する。
例えば、numthreads(3,2,1) が指定された場合は 3*2*1 = 6 Thread 起動する。SV_GroupThreadID としては (0-2, 0-1, 0) 範囲の値が取得できる。

SV_DispatchThreadID (uint3)

Dispatch と numthread の組み合わせによって変化する。

SV_GroupIndex (uint)

起動例 1

Distach(4, 4, 4) / numthread[1,1,1]

SV_GroupID には (0,0,0)~(3,3,3) の値が来る。
SV_GroupID から、一意な Index を取得したい場合は、下記で変換できる。

// index = (gid.z * dispatch_y * dispatch_x) + (gid.y * dispatch_x) + gid.z
uint index = (gid.z * 4 * 4) + (gid.y * 4) + gid.z;

index は 0~63 までの値になる。

[DirectX12] View Matrix での変換は何をしているか

[DirectX12] View Matrix での変換は何をしているか

View 行列を掛けることによって、カメラを基準にした相対座標に変換される。

# ローカルの頂点座標
LocalPos = [0.0,0.0,0.0])

# Model のワールド座標
WorldPos = [1.0,0.0,3.0]

# カメラポジション
eye = [1.0, 0.0,-3.0]
at  = [1.0,0.0,0.0]
up  = [0.0,1.0,0.0]

この座標を使い View 変換行った結果は下記になる。

[0.0, 0.0, 6.0]

Model が(1.0, 0.0, 3.0)、Eye が(1.0, 0.0, -3.0)のところにあるため、カメラから見た相対座標は Z 軸が 6.0 だけ離れているという事。

カメラの向いている方向

View 行列から、カメラの視線の Direction を取得できる。まず、View 行列の計算式は下記の通り。

zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)

 xaxis.x           yaxis.x           zaxis.x          0
 xaxis.y           yaxis.y           zaxis.y          0
 xaxis.z           yaxis.z           zaxis.z          0
-dot(xaxis, eye)  -dot(yaxis, eye)  -dot(zaxis, eye)  1

式を見れば分かるが、zaxisは Eye から見た注視点へのベクトルになる(正規化済み)。

eye = [1.0, 0.0,-3.0]
at  = [1.0,0.0,0.0]

この場合の View Matrix の zaxis は (0.0, 0.0, 1.0) になる。

eye = [1.0, 0.0,3.0]
at  = [1.0,0.0,0.0]

この場合はマイナス軸方向を向くことになるので、zaxis は (0.0, 0.0, -1.0) の値になる。

View Matrix

例1

XMVECTOR Eye = XMVectorSet( 0.0f, 0.0f, 3.0f, 0.0f );
XMVECTOR At = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
g_View = XMMatrixLookAtLH( Eye, At, Up );

こちらで定義した場合は、下記のような Matrix が作られる。

{-1.0, 0.0, 0.0, 0.0}
{0.0, 1.0, 0.0, 0.0}
{0.0, 0.0, -1.0, 0.0}
{0.0, -0.0, 3.0, 1.0}

例2

XMVECTOR Eye = XMVectorSet( 1.0f, 0.0f, 3.0f, 0.0f );
XMVECTOR At = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
g_View = XMMatrixLookAtLH( Eye, At, Up );

Eye の X 位置が 1.0f にした状態。

{-0.948, 0.0, -0.316, 0.0}
{0.0, 1.0, 0.0, 0.0}
{0.316, 0.0, -0.948, 0.0}
{-5.96046448e-08, -0.0, 3.16, 1.0}