grafix_toolbox/kit/opengl/shader/
compiler.rs

1use 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::*;