Skip to main content

vk_graph_hot/
graphics.rs

1//! Hot-reload graphics pipeline support.
2
3use {
4    super::{HotPipeline, compile_shaders_and_watch, create_watcher, pipeline, shader::HotShader},
5    log::info,
6    std::sync::{
7        Arc, RwLock,
8        atomic::{AtomicBool, Ordering},
9    },
10    vk_graph::{
11        cmd::{Command, Pipeline},
12        driver::{
13            DriverError,
14            device::Device,
15            graphics::{GraphicsPipeline, GraphicsPipelineInfo},
16        },
17    },
18};
19
20/// A graphics pipeline wrapper that recompiles its shaders when source files change.
21#[derive(Debug)]
22pub struct HotGraphicsPipeline {
23    cache: RwLock<HotPipeline<GraphicsPipeline>>,
24    device: Device,
25    has_changes: Arc<AtomicBool>,
26    shaders: Box<[HotShader]>,
27}
28
29impl HotGraphicsPipeline {
30    /// Creates a hot-reload graphics pipeline from one or more shader files.
31    pub fn create<S>(
32        device: &Device,
33        info: impl Into<GraphicsPipelineInfo>,
34        shaders: impl IntoIterator<Item = S>,
35    ) -> Result<Self, DriverError>
36    where
37        S: Into<HotShader>,
38    {
39        let shaders = shaders.into_iter().map(Into::into).collect::<Box<_>>();
40
41        let has_changes = Default::default();
42        let mut watcher = create_watcher(&has_changes);
43
44        let pipeline = {
45            GraphicsPipeline::create(
46                device,
47                info,
48                compile_shaders_and_watch(&shaders, &mut watcher)?,
49            )
50        }?;
51
52        Ok(Self {
53            cache: RwLock::new(HotPipeline { pipeline, watcher }),
54            device: device.clone(),
55            has_changes,
56            shaders,
57        })
58    }
59
60    fn compile_shader_and_bind_cmd<'a>(
61        &self,
62        cmd: Command<'a>,
63    ) -> <GraphicsPipeline as Pipeline<'a>>::Command {
64        if self.has_changes.swap(false, Ordering::Relaxed) {
65            info!("Shader change detected");
66
67            let mut cache = self.cache_mut();
68
69            if let Ok(shaders) = compile_shaders_and_watch(&self.shaders, &mut cache.watcher)
70                && let Ok(pipeline) =
71                    GraphicsPipeline::create(&self.device, cache.pipeline.info(), shaders)
72            {
73                cache.pipeline = pipeline;
74            }
75        }
76
77        self.cache().pipeline.clone().bind_cmd(cmd)
78    }
79}
80
81pipeline!(GraphicsPipeline);