1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::error::Error;
use std::fs::File;
use std::io::{self, Read};
use std::iter::Iterator;
use std::path::PathBuf;
use std::sync::mpsc::{self, Receiver};
use std::time::{Duration, Instant};
use gfx;
use notify::{self, DebouncedEvent, RecommendedWatcher, Watcher, RecursiveMode};
use super::*;
pub struct ShaderDirectoryWatcher {
directory: PathBuf,
_watcher: RecommendedWatcher,
watcher_rx: Receiver<DebouncedEvent>,
last_modification: Instant,
}
impl ShaderDirectoryWatcher {
pub fn new<P>(directory: P) -> Result<Self, notify::Error>
where
PathBuf: From<P>,
{
let directory = PathBuf::from(directory);
let (tx, watcher_rx) = mpsc::channel();
let mut watcher = notify::watcher(tx, Duration::from_millis(50))?;
watcher.watch(&directory, RecursiveMode::Recursive)?;
Ok(Self {
directory,
_watcher: watcher,
watcher_rx,
last_modification: Instant::now(),
})
}
fn detect_changes(&mut self) {
while let Ok(event) = self.watcher_rx.try_recv() {
if let DebouncedEvent::Write(_) = event {
self.last_modification = Instant::now();
}
}
}
}
fn concat_file_contents<'a, I: Iterator<Item = &'a PathBuf>>(filenames: I) -> io::Result<String> {
let mut contents = String::new();
for filename in filenames {
File::open(filename)?.read_to_string(&mut contents)?;
}
Ok(contents)
}
pub struct Shader<R: gfx::Resources> {
shader_set: gfx::ShaderSet<R>,
vertex_filenames: Vec<PathBuf>,
pixel_filenames: Vec<PathBuf>,
last_update: Instant,
}
impl<R: gfx::Resources> Shader<R> {
pub fn simple<F: gfx::Factory<R>>(
factory: &mut F,
watcher: &mut ShaderDirectoryWatcher,
vertex_source: ShaderSource,
pixel_source: ShaderSource,
) -> Result<Self, Box<Error>> {
let vertex_filenames = vertex_source
.filenames
.unwrap()
.into_iter()
.map(|f| watcher.directory.join(f))
.collect();
let pixel_filenames = pixel_source
.filenames
.unwrap()
.into_iter()
.map(|f| watcher.directory.join(f))
.collect();
Ok(Self {
shader_set: Self::load(factory, &vertex_filenames, &pixel_filenames)?,
last_update: Instant::now(),
vertex_filenames,
pixel_filenames,
})
}
pub fn as_shader_set(&self) -> &gfx::ShaderSet<R> {
&self.shader_set
}
pub fn refresh<F: gfx::Factory<R>>(
&mut self,
factory: &mut F,
directory_watcher: &mut ShaderDirectoryWatcher,
) -> bool {
directory_watcher.detect_changes();
if directory_watcher.last_modification > self.last_update {
let new = Self::load(factory, &self.vertex_filenames, &self.pixel_filenames);
self.last_update = Instant::now();
if let Ok(shader_set) = new {
self.shader_set = shader_set;
return true;
}
}
false
}
fn load<F: gfx::Factory<R>>(
factory: &mut F,
vertex_filenames: &Vec<PathBuf>,
pixel_filenames: &Vec<PathBuf>,
) -> Result<gfx::ShaderSet<R>, Box<Error>> {
let v = create_vertex_shader(
factory,
concat_file_contents(vertex_filenames.iter())?.as_bytes(),
)?;
let f = create_pixel_shader(
factory,
concat_file_contents(pixel_filenames.iter())?.as_bytes(),
)?;
Ok(gfx::ShaderSet::Simple(v, f))
}
}