grafix_toolbox/kit/opengl/shader/
compiler.rs1use super::{object::*, parsing::*, *};
2use GL::Fence;
3
4pub fn compiler(data_rx: Receiver<ShaderTask>, res_sn: Sender<(ShdResult, Fence)>) {
5 let mut includes: String = Def();
6 let mut files: Vec<Lazy<Vec<ShdSrc>>> = Def();
7 let mut sources: HashMap<Str, ShdState> = Def();
8 let mut watched: HashMap<Str, Vec<ShdName>> = Def();
9
10 while let Ok(msg) = data_rx.recv() {
11 fn coerce<T1, T2, T3, F: Fn(T1, &mut T2, &mut [T3])>(f: F) -> F {
12 f
13 }
14 let send = coerce(|name: ShdName, s, f: &mut [_]| {
15 let prog = compile(&includes, s, f, &name).map_err(|e| adjust_log(e, 1 - i32(includes.lines().count())));
16 res_sn.send((ShdResult { name, prog }, Fence::new())).fail();
17 });
18
19 match msg {
20 Includes(i) => includes = parse_includes(i).warn(),
21 Watch(name) => name.iter().for_each(|n| watched.entry(n.clone()).or_default().push(name.clone())),
22 Forget(name) => name.iter().for_each(|n| {
23 let w = watched.get_mut(n).valid();
24 let idx = w.iter().position(|n| *n == name).valid();
25 w.swap_remove(idx);
26 }),
27 Rebuild => {
28 let recipients = files
29 .iter_mut()
30 .flat_map(|p| {
31 if !p.changed() {
32 return vec![];
33 }
34
35 mem::take(p.get())
36 .drain(..)
37 .filter_map(|ShdSrc { name, src, .. }| {
38 let Some(s) = sources.get_mut(&name) else {
39 sources.insert(name, Source { src });
40 None?
41 };
42
43 if matches!(s, Source { src: s } | Compiled { src: s, .. } if *s == src) {
44 None?
45 }
46
47 *s = Source { src };
48
49 Some(name)
50 })
51 .collect()
52 })
53 .collect_vec();
54
55 for name in recipients {
56 if let Some(recipients) = watched.get(&name) {
57 for name in recipients {
58 send(name.clone(), &mut sources, &mut files);
59 }
60 }
61 }
62 }
63 Inline((name, source)) => {
64 ASSERT!(
65 sources.get(&name).map(|(Source { src } | Compiled { src, .. })| *src == source).unwrap_or(true),
66 "Shader {name:?} already exists"
67 );
68
69 sources.insert(name, Source { src: source });
70 }
71 Create(name) => send(name, &mut sources, &mut files),
72 Load(file) => files.push(file),
73 Clean => sources.iter_mut().for_each(|(_, v)| {
74 if let Compiled { src, .. } = v {
75 *v = Source { src: mem::take(src) };
76 }
77 }),
78 }
79 }
80}
81fn compile(includes: &str, sources: &mut HashMap<Str, ShdState>, files: &mut [Lazy<Vec<ShdSrc>>], name: &[Str]) -> Res<ShdProg> {
82 let get_object = |name: &Str| -> Res<_> {
83 let get = |s: &mut HashMap<_, _>| {
84 let state = s.get_mut(name).ok_or_else(|| format!("No shader {name:?} in loaded sources"))?;
85
86 Ok(match state {
87 Compiled { obj, .. } => obj.obj(),
88 Source { src } => {
89 let c_src = CString::new([includes, src].concat()).explain_err(|| format!("Malformed string in shader {name:?}")).fail();
90 let (o, new) = ShaderObj::new(name, &c_src).map(|obj| (obj.obj(), Compiled { src: mem::take(src), obj }))?;
91 *state = new;
92 o
93 }
94 })
95 };
96
97 if let Ok(o) = get(sources) {
98 return Ok(o);
99 }
100
101 files.iter_mut().for_each(|p| {
102 mem::take(p.get())
103 .drain(..)
104 .for_each(|ShdSrc { name, src, .. }| sources.insert(name.clone(), Source { src }).map(|_| WARN!("Replacing shader source {name:?}")).sink())
105 });
106
107 get(sources)
108 };
109
110 let objects = name.iter().map(get_object).collect::<Res<Vec<_>>>()?;
111 let prog = Object::new();
112 let obj = prog.obj;
113
114 objects.iter().for_each(|&o| GL!(gl::AttachShader(obj, o)));
115 GL!(gl::LinkProgram(obj));
116 let mut status: i32 = 0;
117 GL!(gl::GetProgramiv(obj, gl::LINK_STATUS, &mut status));
118 objects.iter().for_each(|&o| GL!(gl::DetachShader(obj, o)));
119
120 if GLbool::to(status) == gl::FALSE {
121 return Err(format!("Error linking program {:?}, {obj}\n{}", name.join(" "), print_shader_log(obj)));
122 }
123
124 DEBUG!("Compiled GL shader {}:{:?}", prog.obj, name.join(" "));
125 Ok(prog)
126}
127
128pub type ShdName = Box<[Str]>;
129pub type ShdProg = Object<ShaderProg>;
130
131pub struct ShdResult {
132 pub name: ShdName,
133 pub prog: Res<ShdProg>,
134}
135pub enum ShaderTask {
136 Includes(Vec<Astr>),
137 Watch(ShdName),
138 Forget(ShdName),
139 Rebuild,
140 Inline((Str, String)),
141 Create(ShdName),
142 Load(Lazy<Vec<ShdSrc>>),
143 Clean,
144}
145pub use ShaderTask::*;
146
147pub enum ShdState {
148 Source { src: String },
149 Compiled { src: String, obj: ShaderObj },
150}
151pub use ShdState::*;