[Vulkan] Crash Diagnostic Layerを使ってどのCommandでクラッシュしているのかを見る
はじめに
Vulkan 1.4で追加されたCrash Diagnostic Layerの導入とやってみるとどうなったかの備忘録的なメモです。
Crash Diagnostic Layer
変なシェーダーなどを走らせてしまった結果、Command実行時にクラッシュすることがしばしばありますが、Validation LayerだけではどのCommandがクラッシュしたのかは検知することができません(WarningやErrorはあるかもしれません)。最悪の場合、Device Lostしましたという程度の情報しか出てこないなんてことがあります。
この原因を探すとなるとどのCommandがいけないのか総当たり的に調べる羽目になったりと、中々にキツいことになりがちです。そこでVulkanはversion 1.4から、クラッシュ時にその原因となったCommandの内容とそれらに関係する情報をログとして書き出してくれるCrash Diagnostic Layer(CDL)が追加されました。
CDLの導入
導入自体はシンプルです。Validation layerと同様にInstance生成時にLayerの名前を指定して、有効かするだけです。
m_layers.push_back("VK_LAYER_LUNARG_crash_diagnostic");
~~
vk::InstanceCreateInfo createInfo{};
createInfo.enabledLayerCount = m_layers.size();
createInfo.ppEnabledLayerNames = m_layers.data();
CDLの設定についてはVulkan SDKのbin/vkconfig-gui.exeから見ることができます。もし、Layerを追加してもダメだった場合はこちらで有効になっているかチェックしてみるといいかもしれません。

クラッシュすると
実際にクラッシュすると%USERPROFILE%/cdlにクラッシュログが保存されます。実際にユーザーのところを見るとcdlというフォルダがあり、その中にクラッシュした日時でログが保存されています。

CDLのlogは.yamlファイル形式で書き出されており、中身はこんな感じです。丁度レイトレーシングのシェーダーでクラッシュした事例があったので、それの内容がこれになります。この事例ではレイトレーシングのCommandでクラッシュしており、ログでもそこで止まっている(state: INCOMPLETE)ことが確認できます。
# ----------------------------------------------------------------
# - CRASH DIAGNOSTIC LAYER -
# ----------------------------------------------------------------
version: 1.4.309
startTime: 2025-04-28 00:34:39
timeSinceStart: 00:00:03.425
Settings:
output_path: ""
trace_on: false
dump_queue_submits: running
dump_command_buffers: running
dump_commands: running
dump_shaders: on_crash
watchdog_timeout_ms: 30000
track_semaphores: true
trace_all_semaphores: false
instrument_all_commands: false
sync_after_commands: false
SystemInfo:
osName: Windows 10 Home
osVersion: 26100
osBitdepth: 64-bit
osAdditional:
build: ge_release
cpuName: x64
numCpus: 28
totalRam: 31 GB
totalDiskSpace: 951 GB
availDiskSpace: 49 GB
Instance:
handle: 0x0000025C4D155330 []
applicationInfo:
application: Hello Triangle
applicationVersion: 4202496
engine: No Engine
engineVersion: 4202496
apiVersion: 1.4.0 (0x00404000)
extensions:
- VK_KHR_surface
- VK_KHR_win32_surface
- VK_EXT_debug_utils
- VK_KHR_portability_enumeration
Device:
handle: 0x0000025C4FAD1270[]
deviceName: NVIDIA GeForce RTX 4070 SUPER
apiVersion: 1.3.280 (0x00403118)
driverVersion: 0x8C178000 (2350350336)
vendorID: 0x000010DE
deviceID: 0x00002783
extensions:
- VK_KHR_swapchain
- VK_KHR_pipeline_library
- VK_KHR_ray_tracing_pipeline
- VK_KHR_acceleration_structure
- VK_KHR_deferred_host_operations
- VK_KHR_buffer_device_address
Queues:
- # Queue
handle: 0x0000025C58968960[]
queueFamilyIndex: 0
index: 0
flags:
- graphics
- compute
- transfer
- sparse
completedSeq: 21
submittedSeq: 24
IncompleteSubmits:
- type: vkQueueSubmit
startSeq: 21
endSeq: 24
SubmitInfos:
- startSeq: 21
endSeq: 24
state: INCOMPLETE
CommandBuffers:
- 0x0000025C5D4F79D0[] INCOMPLETE 23
WaitSemaphores:
- handle: 0xCA0B160000000085[]
type: Binary
SignalSemaphores:
- handle: 0xC079B30000000088[]
type: Binary
CommandBuffers:
- # CommandBuffer
state: INCOMPLETE
handle: 0x0000025C5D4F79D0[]
commandPool: 0xCFEF35000000000A[]
queue: 0x0000025C58968960[]
fence: 0x0000000000000000[]
queueSeq: 23
level: Primary
simultaneousUse: false
beginValue: 0x00000001
endValue: 0x0000FFFF
topCheckpointValue: 0x00000005
bottomCheckpointValue: 0x00000003
lastStartedCommand: 4
lastCompletedCommand: 2
Commands:
- # Command:
id: 2
checkpointValue: 0x00000003
name: vkCmdBindPipeline
state: COMPLETED
parameters:
pipelineBindPoint: VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR
pipeline: 0xA182620000000079[]
message: "'>>>>>>>>>>>>>> LAST COMPLETE COMMAND <<<<<<<<<<<<<<'"
- # Command:
id: 3
checkpointValue: 0x00000004
name: vkCmdBindDescriptorSets
state: INCOMPLETE
parameters:
pipelineBindPoint: VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR
layout: 0x3F36830000000078[]
firstSet: 0
descriptorSetCount: 1
pDescriptorSets: # VkDescriptorSet
- 0x5EB05E000000003B[]
dynamicOffsetCount: 0
pDynamicOffsets: nullptr
- # Command:
id: 4
checkpointValue: 0x00000005
name: vkCmdTraceRaysKHR
state: INCOMPLETE
parameters:
pRaygenShaderBindingTable:
deviceAddress: 0x00000000193F4000
stride: 64
size: 64
pMissShaderBindingTable:
deviceAddress: 0x00000000193F4040
stride: 32
size: 64
pHitShaderBindingTable:
deviceAddress: 0x00000000193F4080
stride: 32
size: 64
pCallableShaderBindingTable:
deviceAddress: 0x0000000000000000
stride: 0
size: 0
width: 800
height: 600
depth: 1
internalState:
pipeline:
{}
descriptorSets:
- # descriptorSet
index: 0
set: 0x5EB05E000000003B[]
message: "'^^^^^^^^^^^^^^ LAST STARTED COMMAND ^^^^^^^^^^^^^^'"
嬉しいことに直接Crashに関係ないResourceの内容だとかCommandの詳細だとかも出してくれるので純粋にValidation Checkにも使えそうです。SBTの内容も書いてあるのは中々にうれしいところです。
終わり
実際にやってみるとCDLはかなりCommand周りの情報を出してくれるのでなかなかに有用な感じがあります。GPUデバッグは本当に大変なのでこうしたデバックの手段が増えてくれるのはありがたい限りです。
ただしCDLは具体的にShaderのinvocationまでは見れないみたいなのでちょっとそこは厳しさを感じます(configにそれっぽいのがあったんですがうまく設定できなかった)。とはいえCommandの特定は直ぐにできそうなので今後とも使っていきたいですね。
Discussion