DRED を使った D3D12 の GPU デバッグ手法
0.はじめに
はじめまして。研究開発室に所属する川口です。普段はリアルタイムレンダリング技術の研究開発を行っています。
今回は DirectX 12 のクラッシュ追跡に利用できる DRED という機能について紹介したいと思います。なおこの記事は CEDEC 2020 の NVIDIA 竹重様の講演およびその補完資料を大いに参考にさせていたいただいております。
https://cedec.cesa.or.jp/2020/session/detail/s5e6f80234bee4.html
https://shikihuiku.github.io/post/cedec2020_prescriptions_for_deviceremoval/
DirectX の開発中に Device Removed というエラーに遭遇して原因究明に非常に手間取った経験のある人は多いと思います。この Device Removed とは GPU やドライバーで発生したエラーが原因で、アプリケーションの実行を継続できなくなったときに現れるメッセージです。
GPU の応答が一定時間無かった時に発生する TDR (Timeout Detection and Recovery) と、GPU がリソースにアクセスできないといった致命的な問題が要因のエラーの二種類に大別されます。
TDR については TdrLimit というレジストリキーの設定で制限時間を変更することもできます。根本的な問題解決には繋がりませんが、単に非常に重い処理なのか、あるいは何かしらのエラーで GPU が応答していないのかといった問題の切り分けには役立ちます。
https://devblogs.microsoft.com/pix/tdr-debugging/
Device Removed は GPU で発生するものなので、エラーが検出された時点での CPU の処理とは時間差があり、メッセージ発生時の処理を追っても原因の究明は困難です。
1. D3DConfig について
Device Removed を追う前に、デバッグに必須のツールである D3DConfig について紹介します。D3DConfig は DirectX のデバッグ設定を行う機能です。デバッグ設定とは DirectX のデバッグレイヤーや GPU バリデーション、そして今回紹介する DRED の設定を指します。
D3DConfig ではなく DirectX Control Panel (DXCPL) で DirectX のデバッグ設定を行っている方も多いかもしれません。この DXCPL でも十分便利なのですが、DirectX 9 SDK で提供されているツールということもあり機能が古く DirectX 12 の開発には不十分です。
既に DXCPL の機能拡張は停止しており、新規のデバッグ機能の追加は D3DConfig のみになってます。今回紹介する DRED も D3DConfig でのみサポートされています。
D3DConfig はコマンドプロンプトや Powershell、Visual Studio 上のターミナル等で実行できます。
https://devblogs.microsoft.com/directx/d3dconfig-a-new-tool-to-manage-directx-control-panel-settings/
1.1 D3DConfig のコマンド
ターミナル上で引数無しで `d3dconfig` を実行すると現在のすべての設定の状態が得られます。
> d3dconfig
apps
----------------
HOGEHOGE.exe
debug-layer
----------------
conservative-state-tracking=false
debug-layer-mode=force-on
gbv-allow-state-tracking=true
gbv-mode=app-controlled
gbv-pso-create-frontload=false
gbv-shader-patch-mode=unguarded-validation
sync-command-queues=app-controlled
device
----------------
feature-level-limit=none
force-warp=false
no-feature-level-upgrade=false
dred
----------------
auto-breadcrumbs=forced-on
breadcrumb-contexts=forced-on
page-faults=forced-on
watson-dumps=forced-on
message-break
----------------
allow-debug-breaks=false
cat-app-defined=false
cat-cleanup=false
cat-compilation=false
cat-creation=false
cat-execution=false
cat-init=false
cat-misc=false
cat-resource-manip=false
cat-shader=false
cat-state-getting=false
cat-state-setting=false
sev-corruption=false
sev-error=true
sev-info=false
sev-message=false
sev-warning=true
Break-On D3D11 Message Ids:
<none>
Break-On D3D12 Message Ids:
<none>
message-mute
----------------
cat-app-defined=false
cat-cleanup=false
cat-compilation=false
cat-creation=false
cat-execution=false
cat-init=false
cat-misc=false
cat-resource-manip=false
cat-shader=false
cat-state-getting=false
cat-state-setting=false
mute-all=false
sev-corruption=false
sev-error=false
sev-info=false
sev-message=false
sev-warning=false
Muted D3D11 Message Ids:
<none>
Muted D3D12 Message Ids:
<none>
よく利用するコマンドを紹介します。
> d3dconfig apps
# D3DConfig の設定を利用するアプリケーションの一覧を確認します
> d3dconfig apps --add TestApp.exe
# アプリケーションを登録します
> d3dconfig debug-layer debug-layer-mode=force-on
> d3dconfig debug-layer debug-layer-mode=force-off
> d3dconfig debug-layer debug-layer-mode=app-controlled
# デバッグレイヤーを強制オン・オフ・アプリケーション設定にします
> d3dconfig debug-layer gbv-mode=force-on
> d3dconfig debug-layer gbv-mode=force-off
> d3dconfig debug-layer gbv-mode=app-controlled
# GPU-based Validation を強制オン・オフ・アプリケーション設定にします
> d3dconfig message-break allow-debug-breaks=true
# メッセージブレークを有効にします
> d3dconfig message-break --add-id-12 722
# DX12 の ID=722(D3D12_MESSAGE_ID_CREATERESOURCE_INVALIDMIPLEVELS) でのブレークを有効にします
app-controlled
はアプリケーション側の “デバッグレイヤーを有効” といった設定に依存します。force-on/off
はアプリケーション側の設定関係なく登録されているアプリケーションに全てに機能の有効・無効化を強制します。
正直なところ GUI ツールである DXCPL と比べると設定に対応したコマンドの把握が必要なのは面倒です。しかしコマンドラインツールになったことでスクリプトによる自動化が可能になり、必要に応じて .bat ファイルを用意しておくなど開発のワークフローに組み込むことは容易になっています。
2. DRED について
今回は詳しく紹介しませんが、GPU の問題の調査には Debug Layer と GPU Based Validation が大いに役立ちます。これらの機能は、適切でない D3D の API の利用など、アプリケーション実行時に問題の起きた処理をデバッグ出力に表示してくれます。非常に便利な機能ですが、あくまでエラー検出に過ぎず Device Removed が実際に発生した状況を追跡するには少し手が届かない部分があります。
DRED は Device Removed Extended Data の略称で、名前の通り Device Removed に対して追加の情報を付与する機能です。Debug Layer とは独立した機能で実行のオーバーヘッドも少ないので少なくとも開発中は常に有効にしても問題ありません。(ゼロコストではなく AAA の D3D12 ゲームエンジンで 2~5% のパフォーマンス低下があったそうです)
DRED の詳しい仕様はここで確認できます。
https://microsoft.github.io/DirectX-Specs/d3d/DeviceRemovedExtendedData.html
2.1 Auto-Breadcrumbs
DRED の主要な機能の一つが Auto-Breadcrumb です。なお DRED の利用はバージョン1.2 から導入された Breadcrumb Context という便利な機能を使うため Windows 10 の 20H1 以降を推奨します。
Auto-Breadcrumbs は、童話のヘンゼルとグレーテルのように Breadcrumb つまりパンくずを道しるべとして残す機能です(童話内ではパンくずは鳥が食べてしまい結局役には立たないのですが・・・)。ここでいう道しるべとは GPU のイベント実行の流れを指し、Auto-Breadcrumb を有効にするとイベントのたびに自動で記録されていきます。
GPU イベントが実行されるたびに Breadcrumb は記録されていきます。その過程で Device Removed が発生してある時点以降のイベントが実行できなくなると Breadcrumb か途切れるので、どのタイミングで Device Removed が発生したかの特定が容易になる、という仕組みです。
Auto-Breadcrumb で記録されるイベント(GPU operations)は以下の enum で定義されています。
https://microsoft.github.io/DirectX-Specs/d3d/DeviceRemovedExtendedData.html#d3d12_auto_breadcrumb_op
イベントを記録するだけでは、Draw のようにメッシュごとに連続で呼ばれることの多いイベントではどのコールで問題が発生したかの特定が困難です。そこで Breadcrumb Context という機能で PIX のマーカーの文字列を Breadcrumb に付与することができるようになっています。
2.2 GPU Page Fault Data
DRED のもう一つの機能が GPU Page Fault Data です。GPU Page Falut は GPU 上のメモリ不正アクセスで Device Removed の原因の一つです。削除済みのオブジェクトや範囲外の SRV などにアクセスしようとした際に発生します。
DRED は Page Fault が起きた仮想アドレスに一致する既存あるいは直近に解放された API オブジェクトの名前と種類を報告してくれます。DRED は直近に解放された最大65000個のオブジェクトを記録してるので、オブジェクトの早期解放の追跡に有用です。
GPU Page Fault Data の種類(メモリアロケーションの種類)は以下の enum で定義されています。
https://microsoft.github.io/DirectX-Specs/d3d/DeviceRemovedExtendedData.html#d3d12_dred_allocation_type
3. DRED の有効化
DRED を利用する方法は二つあります。一つはアプリケーションから有効化する方法、もう一つは D3DConfig を使う方法です。アプリケーション内部で有効にする際は、そのアプリケーションが D3DConfig に登録されている場合は sytem-controlled になっている必要があります。
3.1 アプリケーションから設定
D3D Decice を作成する前に以下の処理を行うことで DRED を有効化できます。
ID3D12DeviceRemovedExtendedDataSettings1* pDredSettings1 = nullptr;
HRESULT hr = g_D3D12FnTable.GetDebugInterface( __uuidof( ID3D12DeviceRemovedExtendedDataSettings1 ), (void**)&pDredSettings1 );
if ( SUCCEEDED( hr ) )
{
pDredSettings1->SetAutoBreadcrumbsEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON );
pDredSettings1->SetBreadcrumbContextEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON );
pDredSettings1->SetPageFaultEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON );
// LOG("[DRED 1.2] Device Removed Extended Data (Auto Breadcrumbs, Breadcrumb Context, Page Fault) are enabled");
}
else
{
// LOG("[DRED 1.2] Failed to enable DRED 1.2.");
}
なお ID3D12DeviceRemovedExtendedDataSettings1
は DRED1.2 を示しています。わざわざ使う必要はないですが DRED1.1 は ID3D12DeviceRemovedExtendedDataSettings
です。DRED1.1 では SetBreadcrumbContextEnablement
は使えません。
DRED が利用可能かどうかはプリプロセッサで確認できます。
#if defined(__ID3D12DeviceRemovedExtendedDataSettings1_INTERFACE_DEFINED__)
// DRED 1.2 が有効
#elif defined(__ID3D12DeviceRemovedExtendedDataSettings_INTERFACE_DEFINED__)
// DRED 1.1 が有効
#endif
#余談ですが DRED 1.0 は Windows 10 1809 から導入された機能ですが、その時点ではユーザには API が公開されていなかったので、API 上は DRED 1.1 からとなりバージョンと API の数字がずれています。
3.2 D3DConfig から設定
D3DConfig からの設定は d3dconfig dred
で行えます。以下に設定の例を示しました。
> d3dconfig dred
# 現在の DRED の設定を表示します
auto-breadcrumbs=system-controlled
breadcrumb-contexts=system-controlled
page-faults=system-controlled
watson-dumps=system-controlled
> d3dconfig dred auto-breadcrumbs=forced-on
> d3dconfig dred breadcrumb-contexts=forced-on
# Auto-Breadcrumbs、Breadcrumb Context を有効化します
> d3dconfig dred page-faults=forced-on
# GPU Page Fault Data を有効化します
4. DRED の情報を読む
DRED の情報を読むためには、自前で DRED のデータを処理してログに書き出すか、もしくは、クラッシュ時に作成されるダンプファイルを利用する方法があります。
ダンプファイルの読み取りは、対象のアプリケーションがクラッシュした際にフルダンプを作成するように設定しておく必要があります。
https://docs.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
作成されたダンプファイルは windbg.exe や D3DDred.js で読み込むことができます。
https://github.com/Microsoft/DirectX-Debugging-Tools
今回はダンプファイルは使用せずにアプリケーション内に DRED のログを表示する方法を実装してみました。
4.1 DRED ログ表示の実装
今回実装した DRED ログ表示の疑似実装を示します。(LOG など一部社内 SDK の関数を仮の名前に置き換えています)。ログの実装は Unreal Engine の実装が非常に参考になったので興味があればご参照ください。この関数を D3D API の各種エラーチェックでエラーが返ってきたタイミングなどに呼び出すことでログを表示することができます。
void D3D12DREDLog(ID3D12Device *pDevice)
{
// D3D12_AUTO_BREADCRUMB_OP に対応するイベント名を用意しておきます
// バージョンによってイベントが増えるので注意する
static const TCHAR* OpNames[] =
{
_T("SETMARKER"), // 0
_T("BEGINEVENT"), // 1
_T("ENDEVENT"), // 2
_T("DRAWINSTANCED"), // 3
_T("DRAWINDEXEDINSTANCED"), // 4
_T("EXECUTEINDIRECT"), // 5
_T("DISPATCH"), // 6
_T("COPYBUFFERREGION"), // 7
_T("COPYTEXTUREREGION"), // 8
_T("COPYRESOURCE"), // 9
_T("COPYTILES"), // 10
_T("RESOLVESUBRESOURCE"), // 11
_T("CLEARRENDERTARGETVIEW"), // 12
_T("CLEARUNORDEREDACCESSVIEW"), // 13
_T("CLEARDEPTHSTENCILVIEW"), // 14
_T("RESOURCEBARRIER"), // 15
_T("EXECUTEBUNDLE"), // 16
_T("PRESENT"), // 17
_T("RESOLVEQUERYDATA"), // 18
_T("BEGINSUBMISSION"), // 19
_T("ENDSUBMISSION"), // 20
_T("DECODEFRAME"), // 21
_T("PROCESSFRAMES"), // 22
_T("ATOMICCOPYBUFFERUINT"), // 23
_T("ATOMICCOPYBUFFERUINT64"), // 24
_T("RESOLVESUBRESOURCEREGION"), // 25
_T("WRITEBUFFERIMMEDIATE"), // 26
_T("DECODEFRAME1"), // 27
_T("SETPROTECTEDRESOURCESESSION"), // 28
_T("DECODEFRAME2"), // 29
_T("PROCESSFRAMES1"), // 30
_T("BUILDRAYTRACINGACCELERATIONSTRUCTURE"), // 31
_T("EMITRAYTRACINGACCELERATIONSTRUCTUREPOSTBUILDINFO"), // 32
_T("COPYRAYTRACINGACCELERATIONSTRUCTURE"), // 33
_T("DISPATCHRAYS"), // 34
_T("INITIALIZEMETACOMMAND"), // 35
_T("EXECUTEMETACOMMAND"), // 36
_T("ESTIMATEMOTION"), // 37
_T("RESOLVEMOTIONVECTORHEAP"), // 38
_T("SETPIPELINESTATE1"), // 39
_T("INITIALIZEEXTENSIONCOMMAND"), // 40
_T("EXECUTEEXTENSIONCOMMAND"), // 41
_T("DISPATCHMESH"), // 42
};
// D3D12_DRED_ALLOCATION_TYPE に対応する名前を用意しておきます
static const TCHAR* AllocTypesNames[] =
{
_T("COMMAND_QUEUE"), // 19 start is not zero!
_T("COMMAND_ALLOCATOR"), // 20
_T("PIPELINE_STATE"), // 21
_T("COMMAND_LIST"), // 22
_T("FENCE"), // 23
_T("DESCRIPTOR_HEAP"), // 24
_T("HEAP"), // 25
_T("UNKNOWN"), // 26 not exist
_T("QUERY_HEAP"), // 27
_T("COMMAND_SIGNATURE"), // 28
_T("PIPELINE_LIBRARY"), // 29
_T("VIDEO_DECODER"), // 30
_T("UNKNOWN"), // 31 not exist
_T("VIDEO_PROCESSOR"), // 32
_T("UNKNOWN"), // 33 not exist
_T("RESOURCE"), // 34
_T("PASS"), // 35
_T("CRYPTOSESSION"), // 36
_T("CRYPTOSESSIONPOLICY"), // 37
_T("PROTECTEDRESOURCESESSION"), // 38
_T("VIDEO_DECODER_HEAP"), // 39
_T("COMMAND_POOL"), // 40
_T("COMMAND_RECORDER"), // 41
_T("STATE_OBJECT"), // 42
_T("METACOMMAND"), // 43
_T("SCHEDULINGGROUP"), // 44
_T("VIDEO_MOTION_ESTIMATOR"), // 45
_T("VIDEO_MOTION_VECTOR_HEAP"), // 46
_T("VIDEO_EXTENSION_COMMAND"), // 47
_T("INVALID"), // 0xffffffff
};
// DRED Auto-Breadcrumb の先頭ノード
const D3D12_AUTO_BREADCRUMB_NODE1* pBreadcrumbHead = nullptr;
// DRED のインタフェースを取得
ID3D12DeviceRemovedExtendedData1* pDred = nullptr;
if ( SUCCEEDED( pDevice->QueryInterface( __uuidof( ID3D12DeviceRemovedExtendedData1 ), (void**)&pDred ) ) )
{
// Auto-Breadcrunmb の先頭ノードを取得
D3D12_DRED_AUTO_BREADCRUMBS_OUTPUT1 DredAutoBreadcrumbsOutput;
HRESULT hr = pDred->GetAutoBreadcrumbsOutput1( &DredAutoBreadcrumbsOutput );
if ( SUCCEEDED( hr ) )
{
pBreadcrumbHead = DredAutoBreadcrumbsOutput.pHeadAutoBreadcrumbNode;
} // End if
else if ( hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE )
{
LOG( _T( "[DRED 1.2] The device is not in a removed state." ) );
return;
}
else if ( hr == DXGI_ERROR_UNSUPPORTED )
{
LOG( _T( "[DRED 1.2] auto-breadcrumbs have not been enabled." ) );
return;
}
} // End if (QueryInterface)
if ( pDred )
{
// Auto-Breadcrunmb を先頭からたどって出力していく
// ノードはグラフィクスコマンドリストごとに一つ作成されている
// 詳細:https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_auto_breadcrumb_node
TCHAR str[64];
if ( pBreadcrumbHead )
{
LOG( _T( "[DRED 1.2] Last tracked GPU operations:" ) );
UINT TracedCommandLists = 0;
auto pNode = pBreadcrumbHead;
while ( pNode )
{
// 最後に実行された Breadcrumb の値
UINT LastCompletedOp = *pNode->pLastBreadcrumbValue;
// 最後に実行された Op の値と 全体の Breadcrumb の数が違うなら途中で止まっている
if ( LastCompletedOp != pNode->BreadcrumbCount && LastCompletedOp != 0 )
{
LOG( _T( "[DRED 1.2] Auto Breadcrunmb Info") );
StrWtoT( str, 64, pNode->pCommandListDebugNameW );
LOG( _T( "\tCommandlistName: %s" ), str );
StrWtoT( str, 64, pNode->pCommandQueueDebugNameW );
LOG( _T( "\tCommandQueue: %s" ), str );
LOG( _T( "\tNum Breadcrumbs: %d" ), pNode->BreadcrumbCount );
LOG( _T( "\tLast Breadcrumb: %d" ), LastCompletedOp );
LOG( _T( "\tNum Breadcrumb Contexts: %d" ), pNode->BreadcrumbContextsCount);
LOG( _T( "------------------------------------" ) );
TracedCommandLists++;
// Breadcrumb の数だけ Context (PIX のマーカー) 用の文字列を用意しておく
// Map を使った方が効率がいい
Array<StringT> Contexts;
Contexts.resize( pNode->BreadcrumbCount );
// Breadcrumb Context を走査して対応する Breadcrumb の番号に Context の文字列を格納する
for ( UINT i = 0; i< pNode->BreadcrumbContextsCount; ++i )
{
D3D12_DRED_BREADCRUMB_CONTEXT Context = pNode->pBreadcrumbContexts[i];
StrWtoT( str, 64, Context.pContextString );
Contexts[Context.BreadcrumbIndex].Format( _T( "[%s]" ), str );
}
// すべての Breadcrumb を出力する
for ( UINT Op = 0; Op < pNode->BreadcrumbCount; ++Op )
{
// 最後に実行された Op より前の Breadcrumb は実行されている([reached]とする)
const TCHAR* OpRearched = ( Op <= LastCompletedOp ) ? _T( "[reached]" ) : _T( "" );
// イベント名を解決する
D3D12_AUTO_BREADCRUMB_OP BreadcrumbOp = pNode->pCommandHistory[Op];
const TCHAR* OpName = ( BreadcrumbOp < ARRAYSIZE( OpNames ) ) ? OpNames[BreadcrumbOp] : _T( "Unknown Op" );
// コンテキストのテキストを解決する
const TCHAR* OpContext = ( !Contexts[Op].empty() ) ? Contexts[Op].GetBuffer() : _T( "" );
// 出力
LOG( _T( "\t%d %s| %s%s" ), Op, OpRearched, OpName, OpContext );
} // End for (BreadcrumbCount)
} // End if (LastCompletedOp)
pNode = pNode->pNext;
} // End while (pNode)
if ( TracedCommandLists == 0 )
{
LOG( _T( "[DRED 1.2] No command list found." ) );
} // End if (TracedCommandLists)
} // End if (pBreadcrumbHead)
else
{
LOG( _T( "[DRED 1.2] No Breadcrumb data." ) );
} // End else
// Page Fault Data を取得する
D3D12_DRED_PAGE_FAULT_OUTPUT1 DredPageFaultOutput;
if ( SUCCEEDED( pDred->GetPageFaultAllocationOutput1( &DredPageFaultOutput ) ) && DredPageFaultOutput.PageFaultVA != 0 )
{
// 取得した Page Fault の仮想アドレス
LOG( _T( "[DRED 1.2] PageFault VA: 0x%llX" ), (long long)DredPageFaultOutput.PageFaultVA );
// アロケート済みのノードを走査する
const D3D12_DRED_ALLOCATION_NODE1* pNode = DredPageFaultOutput.pHeadExistingAllocationNode;
if ( pNode )
{
LOG( _T( "[DRED 1.2] Allocation Nodes:" ) );
while ( pNode )
{
// アロケーションの名前を解決する
// enum D3D12_DRED_ALLOCATION_TYPE is start with D3D12_DRED_ALLOCATION_TYPE_COMMAND_QUEUE(19)
UINT index = pNode->AllocationType - D3D12_DRED_ALLOCATION_TYPE_COMMAND_QUEUE;
const TCHAR* AllocTypeName = ( index < ARRAYSIZE( AllocTypesNames ) ) ? AllocTypesNames[index] : _T( "Unknown Alloc" );
StrWtoT( str, 64, pNode->ObjectNameW );
LOG( _T( "\tName: %s (Type: %s)" ), str, AllocTypeName );
pNode = pNode->pNext;
} // End while (pNode)
} // End if (pNode)
// 直近に解放されたノードを走査する
pNode = DredPageFaultOutput.pHeadRecentFreedAllocationNode;
if ( pNode )
{
LOG( _T( "[DRED 1.2] Freed Nodes:" ) );
while ( pNode )
{
// アロケーションの名前を解決する
UINT index = pNode->AllocationType - D3D12_DRED_ALLOCATION_TYPE_COMMAND_QUEUE;
const TCHAR* AllocTypeName = ( index < ARRAYSIZE( AllocTypesNames ) ) ? AllocTypesNames[index] : _T( "Unknown Alloc" );
StrWtoT( str, 64, pNode->ObjectNameW );
LOG( _T( "\tName: %s (Type: %s)" ), str, AllocTypeName );
pNode = pNode->pNext;
} // End while (pNode)
} // End if (pNode)
} // End if (DredPageFaultOutput)
else
{
LOG( _T( "[DRED 1.2] No PageFault data." ) );
} // End else
} // End if (pDred)
} // End function
社内レンダリングエンジン上で実際に得られたログも示します。一部省略しつつ、イベント名などは社内エンジンのものから適当に置き換えています
[DRED 1.2] Last tracked GPU operations:
[DRED 1.2] Auto Breadcrunmb Info
CommandlistName: CommandList_10
CommandQueue: D3D12CommandList
Num Breadcrumbs: 268
Last Breadcrumb: 61
Num Breadcrumb Contexts: 114
------------------------------------
42 [reached]| BeginEvent[GBuffer]
43 [reached]| DrawIndexedInstanced
44 [reached]| DrawIndexedInstanced
45 [reached]| DrawIndexedInstanced
46 [reached]| DrawIndexedInstanced
47 [reached]| DrawIndexedInstanced
48 [reached]| DrawIndexedInstanced
49 [reached]| DrawIndexedInstanced
50 [reached]| DrawIndexedInstanced
51 [reached]| EndEvent
52 [reached]| BeginEvent[...]
53 [reached]| ResourceBarrier
54 [reached]| DrawIndexedInstanced
55 [reached]| EndEvent
56 [reached]| BeginEvent[...]
57 [reached]| EndEvent
58 [reached]| BeginEvent[RayTracing]
59 [reached]| BeginEvent[RayTracing_0]
60 [reached]| ResourceBarrier
61 [reached]| DispatchRays
62 | EndEvent
63 | EndEvent
64 | BeginEvent[...]
65 | ResourceBarrier
66 | Dispatch
67 | EndEvent
68 | BeginEvent[...]
69 | ResourceBarrier
70 | Dispatch
71 | EndEvent
72 | BeginEvent[...]
73 | ResourceBarrier
74 | Dispatch
232 | BeginEvent[LightCulling]
233 | ResourceBarrier
234 | CopyBufferRegion
235 | ResourceBarrier
236 | Dispatch
237 | EndEvent
238 | BeginEvent[Forward]
239 | ResourceBarrier
240 | DrawIndexedInstanced
241 | DrawIndexedInstanced
242 | DrawIndexedInstanced
243 | DrawIndexedInstanced
244 | DrawIndexedInstanced
245 | DrawIndexedInstanced
246 | DrawIndexedInstanced
247 | DrawIndexedInstanced
248 | EndEvent
249 | BeginEvent[SkyMap]
250 | DrawIndexedInstanced
251 | EndEvent
61 番目の DispatchRays まで reached になっていることが確認できます。つまりレイトレーシングを行った際に何かしらの問題があり Device Removed が生じたと推測できます。
実際に、このログはレイトレーシングの Shader Table のアドレスをわざとおかしく設定して Device Removed を発生させました。
Page Fault はちょうどよいログが用意できなっかったので省略していますが、意図しないタイミングでリソースの開放を行うなどすればと確認できると思います。
ログの中の […] で示されているテキストが Breadcrumb Context の機能で、ここでは PIXBeginEvent
で指定したレンダリングパスの名前が表示されています。PIXSetMarker
で任意の位置にマーカーを仕込むこともできます。
DrawIndexedInstanced
が並んでいる部分などは実際に何のオブジェクトの描画が実行されているかわかりませんが、マーカーでオブジェクトの名前を設定してあげれば問題が発生した際に原因箇所の特定に役立ちます。
PIX の機能は以下に詳しく載っています。
https://devblogs.microsoft.com/pix/winpixeventruntime/
5. まとめ
今回は DirectX 12 のデバッグ機能の一つである DRED を紹介しました。すべての機能を紹介しきれたわけではありませんが、困難な GPU デバッグの一助になれば幸いです。
DirectX の新しいデバッグ機能としては、Debug Object Auto-Naming というものがすでにがアナウンスされていますし、DRED 1.3 へのアップデートもありそうです。デバッグ作業の効率化について新しい情報があればまたブログ記事で紹介していきたいと思います。