Repository navigation
Hot Reload Overview
SparkEngine contains hot-reload code for several subsystems: shaders, scripts, game modules, materials, and assets. This page provides a unified overview of those capabilities, how they work, and which ones are actually driven by the shipped engine. Shader hot reload is not active in production: the ShaderHotReload watcher is never initialized or enabled by the engine (only registered during compilation), so its per-frame Update() is a no-op.
Relevant sources:
-
SparkEngine/Source/Graphics/ShaderHotReload.h(dedicated shader hot-reload with file watcher) -
SparkEngine/Source/Graphics/Shader.h(shader management and compilation) -
SparkEngine/Source/Engine/Scripting/ScriptHotReload.h(script hot-reload) -
SparkEngine/Source/Core/ModuleHotReload.h(module hot-reload:Spark::ModuleHotReloadManager, polled by the platform frame loops; the olderEngine/HotReload/ModuleHotReloadsingleton is no longer in the lifecycle) -
SparkEngine/Source/Graphics/MaterialSystem.h(material hot-reload) -
SparkEngine/Source/Graphics/AssetPipeline.h(asset hot-reload)
- Overview
- Shader Hot Reload
- Script Hot Reload
- Module Hot Reload
- Material Hot Reload
- Asset Hot Reload
- Comparison
- Console Commands
- Best Practices
- See Also
Hot-reloading allows developers to modify assets, code, and data while the engine is running, seeing changes immediately without restarting. Each subsystem uses a similar pattern: monitor files for changes, detect modifications, reload the resource, and swap it into the running engine.
┌──────────────────────────────────────────────────────────────┐
│ Hot Reload Systems │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ Shader │ │ Script │ │ Module │ │
│ │ Hot Reload │ │ Hot Reload │ │ Hot Reload │ │
│ │ │ │ │ │ │ │
│ │ FILETIME │ │ File watcher │ │ DLL timestamp │ │
│ │ polling │ │ + debounce │ │ polling │ │
│ │ │ │ │ │ │ │
│ │ Recompile │ │ Recompile │ │ Unload → Reload │ │
│ │ HLSL → swap │ │ AS → swap │ │ DLL → reinit │ │
│ └─────────────┘ └──────────────┘ └───────────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Material │ │ Asset │ │
│ │ Hot Reload │ │ Hot Reload │ │
│ │ │ │ │ │
│ │ Material │ │ File change │ │
│ │ file watch │ │ detection │ │
│ │ → reparse │ │ → reimport │ │
│ └─────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
Not enabled in the shipped engine (stable-v1). ShaderHotReload::Initialize() /
SetEnabled() have no production caller; the watcher only registers directories
while shaders are compiled. When driven by a host or test, a modified file is now
really recompiled through D3DCompile (failures are reported instead of the old
fake success), but no consumer yet recreates the D3D11 shader object from the
reloaded bytecode. The description below is the intended flow, not shipped behavior.
How it works:
-
Shadertracks each shader file'sFILETIME(last write time) - Each frame (or on demand),
HotReloadShaders()checks if any file has been modified - Modified shaders are recompiled via D3DCompile
- On success, the old compiled shader is replaced with the new one
- On failure, the old shader remains active and an error is logged
Usage:
// Automatic: happens each frame if enabled
graphicsEngine.SetShaderHotReloadEnabled(true);
// Manual trigger:
graphicsEngine.HotReloadShaders();State preservation (design intent): constant buffers, render targets, and pipeline state would be unaffected; only the compiled shader bytecode is swapped. Nothing in the shipped engine performs that swap today.
Monitors AngelScript files for changes and recompiles modified scripts.
How it works:
-
ScriptHotReloadManagerwatches directories for file changes (Modified, Created, Deleted, Renamed) - A debounce timer (configurable, default ~200ms) prevents recompilation during rapid saves
- When the debounce timer expires, the recompile callback fires
- The script engine recompiles the modified file
- A
RecompileResultreports success/failure and error details
Usage:
ScriptHotReloadManager hotReload;
hotReload.AddWatchDirectory("Scripts/");
hotReload.SetRecompileCallback([&](const std::string& file)
{
scriptEngine.CompileFile(file);
});
hotReload.Start();
// In main loop:
hotReload.PollChanges();State preservation: Script global variables and object state may be lost on reload. Design scripts to re-initialize from component data rather than relying on persistent script-side state.
File change types:
| Type | Action |
|---|---|
Modified |
Recompile the changed file |
Created |
Compile and register the new script |
Deleted |
Unregister the script (existing instances keep running) |
Renamed |
Treated as delete + create |
Monitors game module DLLs for changes and reloads them via the ModuleManager.
How it works:
-
ModuleHotReloadManagerpolls module DLL/SO file timestamps - When a change is detected, the module is unloaded (
IModule::Shutdown) - The new DLL is loaded and the module is re-initialized (
IModule::Initialize) - A callback reports success or failure per module
Usage:
ModuleHotReloadManager moduleReload;
moduleReload.SetCallback([](const std::string& name, bool success)
{
if (success)
LOG_INFO("Module '{}' reloaded", name);
else
LOG_ERROR("Module '{}' reload failed", name);
});
// In main loop:
moduleReload.PollChanges();State preservation: Module state is lost on reload. The module must re-register its systems, components, and commands during Initialize(). Game state stored in ECS components persists across module reloads.
The MaterialSystem monitors material definition files for changes.
How it works:
- Material files are tracked by timestamp
- On change, the material definition is re-parsed
- Shader permutations are recompiled if the material's shader references changed
- Texture bindings are updated
- Objects using the material see the new appearance immediately
Trigger: Automatic when shader hot-reload detects material-related changes, or via console command.
The AssetPipeline detects changes to source asset files and re-imports them.
How it works:
- Source assets (textures, models, audio) are monitored via the
LocalFileCache - When a source file changes, the asset transitions to a "needs reimport" state
- The reimport pipeline processes the asset (compression, format conversion, etc.)
- The runtime resource is updated with the new data
Best for: Texture iteration, model tweaks, audio adjustments during development.
| System | Detection | Debounce | State Preserved | Latency |
|---|---|---|---|---|
| Shader | FILETIME polling (watcher not enabled in production) | No | n/a -- no device-shader swap implemented | n/a |
| Script | File watcher | Yes (~200ms) | Partial (globals lost) | ~200ms + compile |
| Module | DLL timestamp | No | None (re-initialize) | ~1s (DLL load) |
| Material | File timestamp | No | Full (seamless) | ~1 frame + compile |
| Asset | File cache | No | Full (resource swap) | Varies (reimport) |
| Command | Description |
|---|---|
shader_reload |
Force reload all modified shaders |
shader_reload_all |
Recompile all shaders regardless of timestamp |
material_reload <name> |
Reload a specific material |
asset_reimport <path> |
Force reimport a specific asset |
-
Store state in ECS components, not in reloadable code. Script variables and module-side state will be lost. ECS components survive script and module reloads.
-
Use
Initialize()/Shutdown()for module lifecycle. Modules must cleanly unregister everything inShutdown()and re-register inInitialize(). Leaked registrations cause crashes on reload. -
Keep shader interfaces stable. Changing constant buffer layouts requires all dependent code to update simultaneously. Add fields at the end of constant buffers when possible.
-
Test with hot-reload during development. Script and module hot-reload can be exercised in debug builds; shader hot-reload has no production enable path yet.
-
Don't rely on script constructor order. After script hot-reload, objects may initialize in a different order. Use explicit initialization functions instead.
- Shader Pipeline — Shader compilation and hot-reload details
- Scripting with AngelScript — Script engine and hot-reload
- Creating a Game Module — Module lifecycle and DLL boundaries
- Asset Pipeline — Asset import and caching
- SparkEditor — Editor integration with hot-reload
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers