Skip to content

Commit c24a896

Browse files
Critsium-xyclaude
andcommitted
D3D12: forget cached command list state after a driver callback
A driver callback hands the command buffer to native code the driver knows nothing about. Streamline, running DLSS, rebinds descriptor heaps, the pipeline state and the root signature inside that callback. D3D12 binds descriptor heaps to the command list rather than to a draw, so the driver caches what it believes is bound for the whole command buffer and sets the heaps only once. After the callback that cache is a lie: the driver kept feeding handles from its own heap to SetComputeRootDescriptorTable while Streamline's heap was bound, and the usermode driver dereferenced out of bounds and crashed. Only DLSS combined with SSIL reached it. SSIL copies the internal colour buffer into its last frame texture, and nothing consumes that copy until the next frame, so the graph is free to schedule those compute passes after the upscaling callback. SSAO and SSR are pinned ahead of the upscale by their dependency chains and never land behind it. Add RenderingDeviceDriver::command_buffer_forget_cached_state(), a no-op for drivers that cache nothing, and call it from the graph once every driver callback returns. The D3D12 override clears the same state command_buffer_end() already clears. Vulkan and FSR2 were never affected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017KCbSdUNssPYFx8HD2TMD3
1 parent 6706902 commit c24a896

4 files changed

Lines changed: 18 additions & 0 deletions

File tree

drivers/d3d12/rendering_device_driver_d3d12.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,6 +2823,18 @@ void RenderingDeviceDriverD3D12::command_buffer_end(CommandBufferID p_cmd_buffer
28232823
cmd_buf_info->descriptor_heaps_set = false;
28242824
}
28252825

2826+
void RenderingDeviceDriverD3D12::command_buffer_forget_cached_state(CommandBufferID p_cmd_buffer) {
2827+
CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id;
2828+
cmd_buf_info->graphics_pso = nullptr;
2829+
cmd_buf_info->graphics_root_signature_crc = 0;
2830+
cmd_buf_info->compute_pso = nullptr;
2831+
cmd_buf_info->compute_root_signature_crc = 0;
2832+
cmd_buf_info->nir_graphics_runtime_data_root_param_idx = UINT32_MAX;
2833+
cmd_buf_info->nir_compute_runtime_data_root_param_idx = UINT32_MAX;
2834+
cmd_buf_info->pending_dyn_params = true;
2835+
cmd_buf_info->descriptor_heaps_set = false;
2836+
}
2837+
28262838
void RenderingDeviceDriverD3D12::command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) {
28272839
const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id;
28282840
for (uint32_t i = 0; i < p_secondary_cmd_buffers.size(); i++) {

drivers/d3d12/rendering_device_driver_d3d12.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
540540
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
541541
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
542542
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
543+
virtual void command_buffer_forget_cached_state(CommandBufferID p_cmd_buffer) override final;
543544

544545
private:
545546
/********************/

servers/rendering/rendering_device_driver.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,10 @@ class RenderingDeviceDriver : public RenderingDeviceCommons {
459459
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) = 0;
460460
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) = 0;
461461
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) = 0;
462+
// Called after native code outside the driver has recorded commands into the command buffer.
463+
// Such code is free to rebind pipelines, root signatures or descriptor heaps, so a driver that
464+
// caches what it believes is currently bound has to forget it and set it again next time.
465+
virtual void command_buffer_forget_cached_state(CommandBufferID p_cmd_buffer) {}
462466

463467
/********************/
464468
/**** SWAP CHAIN ****/

servers/rendering/rendering_device_graph.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,7 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC
11421142
case RecordedCommand::TYPE_DRIVER_CALLBACK: {
11431143
const RecordedDriverCallbackCommand *driver_callback_command = reinterpret_cast<const RecordedDriverCallbackCommand *>(command);
11441144
driver_callback_command->callback(driver, r_command_buffer, driver_callback_command->userdata);
1145+
driver->command_buffer_forget_cached_state(r_command_buffer);
11451146
} break;
11461147
case RecordedCommand::TYPE_RAYTRACING_LIST: {
11471148
const RecordedRaytracingListCommand *raytracing_list_command = reinterpret_cast<const RecordedRaytracingListCommand *>(command);

0 commit comments

Comments
 (0)