Skip to content

Shader hot reload for debug builds - #224

Merged
CanReader merged 6 commits into
stagingfrom
Shader-HotReload
Nov 1, 2026
Merged

CanReader merged 6 commits into
stagingfrom
Shader-HotReload

Conversation

@CanReader

@CanReader CanReader commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

What this changes

In Debug builds, saving a shader while the game runs recompiles it and swaps it into the running renderer, on all four backends. Release builds compile none of it.

  • Core/FileWatcher (public): a generic, non-blocking watcher (inotify on Linux, ReadDirectoryChangesW on Windows). It only reports saved files, so later reloads (the Rust gameplay library, assets) can reuse it.
  • Debug/ShaderHotReload (public): watches registered shader folders and waits 150 ms after the last change so editors finish writing. It compiles changed GLSL with glslc (temp file, then an atomic rename, so the old .spv survives a failed compile) and copies the result into assets/shaders. Then it calls the new Renderer::ReloadShaders(stems). Folders registered later override earlier ones, matching how the build copies assets. It warns about an edit that will have no effect: a file overridden by a later folder, or a variant for another backend.
  • Vulkan swaps pipelines only. Images, render passes and descriptor sets stay as they are. A pipeline that fails to rebuild keeps its previous version, and custom-format pipelines are rebuilt as well. The SSR and TAA pipeline creation moved into their own functions so they can be rebuilt alone.
  • OpenGL relinks every live program built from a changed stem and keeps the old program if compiling or linking fails. This also fixes OpenGLShader::compile(), which leaked the previous program and set it to 0 when linking failed.
  • DX11 recompiles every live shader built from the changed .hlsl and rebuilds its input layout. Shaders compiled from the same files compile once and share the result. If either stage or the layout fails, the old ones stay. It also reloads the tonemap shaders, and fixes two leaks in CreateShader: the shader object when compiling fails, and an extra input-layout reference for every shader after the first.
  • DX12 waits for the GPU, recompiles the live shaders built from the changed _dx12.hlsl, and swaps each one's bytecode and PSO only once the new PSO exists. It also rebuilds the shadow, skybox and debug-line PSOs, and refreshes the default PSO. Replaced PSOs that nothing uses any more are removed from the pipeline cache, so it doesn't grow on every save.

Why

Iterating on shaders meant a rebuild and restart for every change. This is step one of the hot reload plan; the Rust gameplay library reload builds on the same watcher.

Follow-ups: #218, #219, #220, #221, #222, #223.

How it was verified

  • Builds on Linux (Debug, plus a syntax-only check of every changed file with SLEAK_HOT_RELOAD undefined, which is what Release compiles)
  • Builds on Windows. Not built or run on Windows yet. Every changed file passes a Windows cross syntax check (zig c++ for x86_64-windows-gnu with MinGW headers) with SLEAK_HOT_RELOAD both on and off, and the Windows CI jobs here are the first real MSVC build. DX11/DX12 reload and the Windows file watcher still need a manual test on Windows.
  • Ran on Vulkan, in SleakCraft rather than the sample scene, with --validate: a normal edit, a syntax error (logged, old shader kept), the fix, engine and game shaders, an overridden file, and two passes at once all behaved as expected with zero validation errors
  • Ran on OpenGL, in SleakCraft: programs relinked live
  • Two games running at once (Vulkan and OpenGL) watching the same folders both reload, and no temp files are left behind

Checklist

  • Public headers documented with /// comments
  • No new includes from src/ or include/private/ in public headers
  • No game-specific vocabulary added to engine API

@CanReader
CanReader merged commit 59c07c5 into staging Nov 1, 2026
8 of 10 checks passed
@CanReader CanReader mentioned this pull request Nov 1, 2026
3 of 4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant