Skip to main content

par_term_render/custom_shader_renderer/
hot_reload.rs

1//! Hot-reload watcher callbacks for custom shader renderer.
2//!
3//! Provides the ability to reload a shader from a new GLSL source string at
4//! runtime without recreating the full renderer.  This is called by the
5//! file-watcher when the shader file on disk changes.
6
7use anyhow::{Context, Result};
8use wgpu::*;
9
10use super::pipeline::create_render_pipeline;
11use super::transpiler::transpile_glsl_to_wgsl_source;
12use super::{CustomShaderRenderer, write_debug_shader_wgsl};
13
14impl CustomShaderRenderer {
15    /// Reload the shader from a GLSL source string.
16    ///
17    /// Transpiles the provided GLSL source to WGSL, validates it, and
18    /// recreates the render pipeline.  The uniform buffer and all textures
19    /// remain intact; only the pipeline is replaced.
20    ///
21    /// # Arguments
22    /// * `device` - The wgpu device
23    /// * `source` - GLSL shader source code
24    /// * `name`   - Shader name used for diagnostic messages and WGSL debug output
25    pub fn reload_from_source(&mut self, device: &Device, source: &str, name: &str) -> Result<()> {
26        let control_parse = par_term_config::parse_shader_controls(source);
27        for warning in &control_parse.warnings {
28            log::warn!(
29                "Shader control warning line {}: {}",
30                warning.line,
31                warning.message
32            );
33        }
34        let custom_controls = control_parse.controls;
35        let wgsl_source = transpile_glsl_to_wgsl_source(source, name)?;
36
37        log::info!(
38            "Reloading custom shader from source ({} bytes GLSL -> {} bytes WGSL)",
39            source.len(),
40            wgsl_source.len()
41        );
42        log::debug!("Generated WGSL:\n{}", wgsl_source);
43        write_debug_shader_wgsl(name, &wgsl_source);
44
45        // Pre-validate WGSL
46        let module = naga::front::wgsl::parse_str(&wgsl_source)
47            .context("Custom shader WGSL parse failed")?;
48        let _info = naga::valid::Validator::new(
49            naga::valid::ValidationFlags::all(),
50            naga::valid::Capabilities::empty(),
51        )
52        .validate(&module)
53        .context("Custom shader WGSL validation failed")?;
54
55        let shader_module = device.create_shader_module(ShaderModuleDescriptor {
56            label: Some("Custom Shader Module (reloaded)"),
57            source: ShaderSource::Wgsl(wgsl_source.into()),
58        });
59
60        self.pipeline = create_render_pipeline(
61            device,
62            &shader_module,
63            &self.bind_group_layout,
64            self.surface_format,
65            Some("Custom Shader Pipeline (reloaded)"),
66        );
67        self.custom_controls = custom_controls;
68
69        log::info!("Custom shader reloaded successfully from source");
70        Ok(())
71    }
72}