|
| 1 | +# D3D12: Debug Break |
| 2 | +v0.01 12 Mar 2026 |
| 3 | + |
| 4 | +# Contents |
| 5 | +- [D3D12: Debug Break](#d3d12-debug-break) |
| 6 | +- [Contents](#contents) |
| 7 | + - [Terms and Acronyms](#terms-and-acronyms) |
| 8 | +- [Summary](#summary) |
| 9 | +- [Motivation](#motivation) |
| 10 | +- [Proposed API](#proposed-api) |
| 11 | + - [Pipeline State Flags](#pipeline-state-flags) |
| 12 | + - [State Object Flags](#state-object-flags) |
| 13 | + - [D3D12\_FEATURE\_DEBUG\_BREAK](#d3d12_feature_debug_break) |
| 14 | +- [Proposed DDI](#proposed-ddi) |
| 15 | + - [D3D12DDI\_PIPELINE\_STATE\_FLAGS](#d3d12ddi_pipeline_state_flags) |
| 16 | + - [D3D12DDI\_STATE\_OBJECT\_FLAGS](#d3d12ddi_state_object_flags) |
| 17 | +- [Postmortem Debugging with DebugBreak()](#postmortem-debugging-with-debugbreak) |
| 18 | +- [Open Questions](#open-questions) |
| 19 | +- [Test Plan](#test-plan) |
| 20 | + - [Test App](#test-app) |
| 21 | + - [Functional test](#functional-test) |
| 22 | + - [Driver Conformance test](#driver-conformance-test) |
| 23 | +- [Spec History](#spec-history) |
| 24 | + |
| 25 | +--- |
| 26 | +## Terms and Acronyms |
| 27 | + |
| 28 | +| **Term/Acronym** | **Definition** | |
| 29 | +|------------------|----------------------------------------------------------------------------------------------------| |
| 30 | +| **TDR** | Timeout Detection and Recovery, a Windows feature that resets the GPU if it takes too long to respond, preventing system unresponsiveness. | |
| 31 | +| **ISV** | Independent Software Vendor, a developer using D3D12 for their application. | |
| 32 | +| **IHV** | Independent Hardware Vendor, a GPU manufacturer. | |
| 33 | +| **Dxgkrnl** | Port driver in the WDDM driver model, OS component written by Microsoft. | |
| 34 | +| **KMD** | Kernel Mode Driver, an IHV-supplied miniport driver in the WDDM driver model. | |
| 35 | +| **Backend/Driver Compiler** | IHV-supplied compiler responsible for compiling IL into hardware specific ISA. | |
| 36 | + |
| 37 | +# Summary |
| 38 | + |
| 39 | +This proposal introduces new pipeline state object and state object flags to control the behavior of the proposed [HLSL `DebugBreak()`](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0039-debugbreak.md) intrinsic in shaders. The flags will enable the following three scenarios: |
| 40 | + |
| 41 | +1. Default behavior; registered debuggers will be notified if a `DebugBreak()` is encountered or the instruction will be ignored if no debugger is registered. The decision to notify the debugger or ignore the `DebugBreak()` will be made at instruction execution time, not at backend compilation time. |
| 42 | +2. Halt on `DebugBreak()` such that regardless of if any debuggers are registered, shader execution will halt and dxgkrnl will be notified either by the KMD via interrupt, or dxgkrnl's TDR mechanism will trigger and a crash dump will be generated. |
| 43 | +3. Disable all `DebugBreak()` calls such that regardless of if any debuggers are attached, all debug breaks are backend compiled into no-ops. |
| 44 | + |
| 45 | +By adding a way to control `DebugBreak()` behavior per pipeline state object or state object, HLSL shaders can be compiled with `DebugBreak()` into DXIL bytecode once, then enabled or disabled at PSO creation time without developers needing to re-run frontend compilation to generate new DXIL bytecode. |
| 46 | + |
| 47 | +# Motivation |
| 48 | +As shaders become more complex, it becomes increasingly challenging to debug rare shader issues. Some games may ship with a minimal number of shader asserts that check for issues that would lead to catastrophic failures or corruptions if execution continued, and generate a crash dump for offline debugging if the assertion fails. However, there isn't a reliable way to trigger a GPU crash from a shader to generate a crash dump. The most common approach that game developers use today is to enter an infinite loop, however this is reliant on the TDR mechanism triggering, which may not trigger if the user's machine has modified TDR registry settings, and the compiler/driver crashing reliably. |
| 49 | + |
| 50 | +To support debugging these shader issues, the HLSL team has a [proposal](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0039-debugbreak.md) for a new `DebugBreak()` intrinsic that can be used to conditionally break-in like so: |
| 51 | + |
| 52 | +```hlsl |
| 53 | +[numthreads(8,1,1)] |
| 54 | +void main(uint GI : SV_GroupIndex) { |
| 55 | + ... |
| 56 | + // Manual breakpoint for debugging specific conditions |
| 57 | + if (someRareCondition) { |
| 58 | + DebugBreak(); |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +This should works well for [live shader debugging](D3D12GpuDumps.md) where if a shader debugger is attached and a shader hits a `DebugBreak()` instruction, a new shader debugging session can be started. |
| 64 | + |
| 65 | +To support retail scenarios where a live shader debugger may not exist, this proposal introduces new pipeline state object and state object flags to control the behavior of `DebugBreak()` functions in shaders. Game developers can compile their HLSL shaders with debug breaks into DXIL bytecode, and depending on the debug break behavior, the backend compiler will compile the DXIL debug break operation into an instruction that invokes a different handler depending on whether or not a shader debugger was registered for the process or remove the instruction entirely. In normal user scenarios, the game may choose to disable all `DebugBreak()` functions to reduce the performance overhead of executing the conditional checks. If a shader issue arises that is difficult to reproduce locally, but users are encountering in the wild, the game can enable halt on `DebugBreak()` functionality for shaders in a specific state object for a small proportion of users to generate DirectX dump files for offline debugging. |
| 66 | + |
| 67 | +# Proposed API |
| 68 | + |
| 69 | +## Pipeline State Flags |
| 70 | +```c++ |
| 71 | +typedef enum D3D12_PIPELINE_STATE_FLAGS { |
| 72 | + ... |
| 73 | + D3D12_PIPELINE_STATE_FLAG_HALT_ON_DEBUG_BREAK = 0x20, |
| 74 | + D3D12_PIPELINE_STATE_FLAG_DISABLE_DEBUG_BREAK = 0x40, |
| 75 | +} D3D12_PIPELINE_STATE_FLAGS; |
| 76 | +``` |
| 77 | + |
| 78 | +## State Object Flags |
| 79 | +```c++ |
| 80 | +typedef enum D3D12_STATE_OBJECT_FLAGS |
| 81 | +{ |
| 82 | + ... |
| 83 | + D3D12_STATE_OBJECT_FLAG_HALT_ON_DEBUG_BREAK = 0x200, |
| 84 | + D3D12_STATE_OBJECT_FLAG_DISABLE_DEBUG_BREAK = 0x400, |
| 85 | +} D3D12_STATE_OBJECT_FLAGS; |
| 86 | + |
| 87 | +typedef struct D3D12_STATE_OBJECT_CONFIG |
| 88 | +{ |
| 89 | + D3D12_STATE_OBJECT_FLAGS Flags; |
| 90 | +} D3D12_STATE_OBJECT_CONFIG; |
| 91 | +``` |
| 92 | + |
| 93 | +Flag | Definition |
| 94 | +--------- | ---------- |
| 95 | +`*_HALT_ON_DEBUG_BREAK` | If enabled, the backend compiler will promote all `DebugBreak()` functions in the set of shaders in the pipeline state object or state object to halt shader execution regardless of if a debugger is attached or not when the `DebugBreak()` is hit. It is only available if the IHV driver/compiler supports Shader Model 6.10. |
| 96 | +`*_DISABLE_DEBUG_BREAK` | If enabled, the backend compiler will compile all `DebugBreak()` functions in the set of shaders in the pipeline state object or state object to no-ops. This flag is only available if the IHV driver/compiler supports Shader Model 6.10. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +`*_HALT_ON_DEBUG_BREAK` and `*_DISABLE_DEBUG_BREAK` cannot be specified at the same time. If neither `*_HALT_ON_DEBUG_BREAK` nor `*_DISABLE_DEBUG_BREAK` are specified, the default behavior is that if a debugger is registered and a `DebugBreak()` is hit, the registered shader debugger will be notified. The backend compiler should not compile `DebugBreak()` functions to no-ops at compilation time, that decision should be made at shader execution time so that a debugger can be attached or detached at any point prior to hitting the `DebugBreak()` |
| 101 | + |
| 102 | +Compute and graphics pipeline state objects created via `CreateGraphicsPipelineState`, `CreateComputePipelineState`, and `CreatePipelineState` will support the new `D3D12_PIPELINE_STATE_FLAGS` enums. |
| 103 | + |
| 104 | +For state objects such as RTPSOs, collections, or executables with programs like work graphs or generic programs (with one modification stated further below): If the [state object config](https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#d3d12_state_object_config) subobject is not present, the default debug break behavior will apply. If the state object config subobject is present, all exports in the state object must be associated with the same subobject (or one with a matching definition). This consistency requirement for the `*_HALT_ON_DEBUG_BREAK` and `*_DISABLE_DEBUG_BREAK` flags also applies across existing collections that are included in a larger state object. |
| 105 | + |
| 106 | +Generic programs and pre-rasterization shaders and pixel shader partial programs will first use the debug break flags defined in the `D3D12_STATE_SUBOBJECT_TYPE_FLAGS` subobject if specified. Otherwise, the default behavior for state objects mentioned above applies. |
| 107 | + |
| 108 | +When [Advanced Shader Delivery](ShaderCompilerPlugin.md) is used, if either of the debug break flags are enabled, the driver/backend compiler will need to just-in-time compile the set of shaders in a pipeline state object or state object, or the shaders must be offline compiled with the matching debug break flag enabled. |
| 109 | + |
| 110 | +## D3D12_FEATURE_DEBUG_BREAK |
| 111 | +A new `D3D12_FEATURE` enum value will be added to represent which debug break capabilities the driver supports. |
| 112 | + |
| 113 | +```c++ |
| 114 | +typedef struct D3D12_FEATURE_DATA_DEBUG_BREAK |
| 115 | +{ |
| 116 | + [annotation("_Out_")] BOOL HaltSupported; // Enabled if the driver supports halting shader execution on the GPU when a DebugBreak() is hit and `*_HALT_ON_DEBUG_BREAK` is enabled |
| 117 | + [annotation("_Out_")] BOOL LiveDebuggingSupported; // Enabled if the driver supports notifying registered GPU debuggers when a DebugBreak() is hit when *_HALT_ON_DEBUG_BREAK or default behavior is enabled |
| 118 | + [annotation("_Out_")] BOOL CpuSupported; // Enabled if the driver supports triggering a CPU debug break when a DebugBreak() is hit. |
| 119 | +} D3D12_FEATURE_DATA_DEBUG_BREAK; |
| 120 | + |
| 121 | +typedef enum D3D12_FEATURE |
| 122 | +{ |
| 123 | + D3D12_FEATURE_D3D12_OPTIONS = 0, |
| 124 | + D3D12_FEATURE_ARCHITECTURE = 1, // Deprecated by D3D12_FEATURE_ARCHITECTURE1 |
| 125 | + ... |
| 126 | + D3D12_FEATURE_DEBUG_BREAK = 72, |
| 127 | +``` |
| 128 | +
|
| 129 | +If neither `HaltSupported` nor `LiveDebuggingSupported` are enabled for the GPU driver, the driver doesn't support generating postmortem hang dumps or live debugging on `DebugBreak()` and all `DebugBreak()` functions will be disabled. |
| 130 | +
|
| 131 | +# Proposed DDI |
| 132 | +
|
| 133 | +## D3D12DDI_PIPELINE_STATE_FLAGS |
| 134 | +```c++ |
| 135 | +typedef enum D3D12DDI_PIPELINE_STATE_FLAGS |
| 136 | +{ |
| 137 | + ... |
| 138 | + D3D12DDI_PIPELINE_STATE_FLAG_HALT_ON_DEBUG_BREAK = 0x20, |
| 139 | + D3D12DDI_PIPELINE_STATE_FLAG_DISABLE_DEBUG_BREAK = 0x40, |
| 140 | +} D3D12DDI_PIPELINE_STATE_FLAGS; |
| 141 | +``` |
| 142 | + |
| 143 | +## D3D12DDI_STATE_OBJECT_FLAGS |
| 144 | +```c++ |
| 145 | +typedef enum D3D12DDI_STATE_OBJECT_FLAGS |
| 146 | +{ |
| 147 | + ... |
| 148 | + D3D12DDI_STATE_OBJECT_FLAG_HALT_ON_DEBUG_BREAK = 0x200, |
| 149 | + D3D12DDI_STATE_OBJECT_FLAG_DISABLE_DEBUG_BREAK = 0x400, |
| 150 | +} D3D12DDI_STATE_OBJECT_FLAGS; |
| 151 | +``` |
| 152 | + |
| 153 | +# Postmortem Debugging with DebugBreak() |
| 154 | + |
| 155 | +If `D3D12_PIPELINE_STATE_FLAG_HALT_ON_DEBUG_BREAK/D3D12_STATE_OBJECT_FLAG_HALT_ON_DEBUG_BREAK` is enabled, a `DebugBreak()` function is hit in a shader in the pipeline state object or state object, and no shader debuggers are registered for the process, shader execution will halt and dxgkrnl will detect an engine timeout after the machine's [TdrDelay](https://learn.microsoft.com/en-us/windows-hardware/drivers/display/tdr-registry-keys#tdrdelay) registry setting, and call the new `pfnCollectProcessDebugBlob` DDI to collect application specific GPU debug blob and generate a crash dump in the same workflow as a [normal timeout](D3D12GpuDumps.md#system-workflow). |
| 156 | + |
| 157 | +If `*_DISABLE_DEBUG_BREAK` is enabled, all `DebugBreak()` functions in the shaders in the state object will be ignored and no crash dumps will be generated. |
| 158 | + |
| 159 | +# Open Questions |
| 160 | + |
| 161 | +1. [dxgkrnl/IHVs] Do we need to wait for dxgkrnl to detect a timeout for the postmortem debugging with promoted `DebugBreak()` calls? Can the IHV KMDs recognize that shader execution has been halted because of a promoted `DebugBreak()` and call the new `DXGK_INTERRUPT_TYPE` to notify dxgkrnl before the TDR delay? Can the new `DXGK_INTERRUPT_TYPE` interrupt be modified so that if a debugger is not registered, dxgkrnl will proceed with the normal timeout workflow? |
| 162 | +2. [IHVs] For the promoted debug break with a registered debugger, can IHVs support single stepping past the promoted debug break instruction without aborting? |
| 163 | + |
| 164 | +# Test Plan |
| 165 | + |
| 166 | +### Test App |
| 167 | +A test D3D application will be added that includes shaders that use the new `DebugBreak()` intrinsic. |
| 168 | + |
| 169 | +### Functional test |
| 170 | +Functional tests will be written using the TAEF framework. |
| 171 | + |
| 172 | +The important behaviors to test are: |
| 173 | +- Postmortem Crash Dump Creation |
| 174 | + - Verify that promoted `DebugBreak()`s trigger a dump file to be generated |
| 175 | + - Verify that disabling or default `DebugBreak()`s do not trigger a dump file to be generated |
| 176 | + |
| 177 | +- Live Debugging |
| 178 | + - Verify that default and promoted `DebugBreak()`s trigger the debugger to be notified of a potential live debugging session and we can continue shader debugging without aborting |
| 179 | + - Verify that disabling `DebugBreak()`s do not trigger the debugger to be notified |
| 180 | + |
| 181 | +### Driver Conformance test |
| 182 | +Verify drivers work correctly with the new pipeline state object and state object flags (TBD need to get familiar with existing driver tests) |
| 183 | + |
| 184 | +_TODO: Update test plan with more details_ |
| 185 | + |
| 186 | +# Spec History |
| 187 | + |
| 188 | +| Version | Date | Details | Author | |
| 189 | +|-|-|-|-| |
| 190 | +| v0.01 | 12 Mar 2026 | Initial draft spec | Grace Zhang (PIX) | |
0 commit comments