grafix_toolbox/kit/opengl/shader/
shader_ext.rs

1use super::{args::*, compiler::*, parsing::*, uniform::*, *};
2use GL::offhand::*;
3
4pub struct Shader {
5	name: ShdName,
6	prog: ShdProg,
7	uniforms: Uniforms,
8	dynamic: bool,
9}
10impl Shader {
11	pub fn pure<const N: usize>(args: [I; N]) -> Self {
12		Self::new(args).fail()
13	}
14	pub fn new(args: impl CompileArgs) -> Res<Self> {
15		let ShaderManager { sn, rx, .. } = ShaderManager::get();
16		let name = args.get();
17
18		sn.send(Create(name)).valid();
19		let ShdResult { prog, name } = rx.recv().valid().wait();
20		let (uniforms, dynamic) = Def();
21
22		Ok(Self { name, prog: prog?, uniforms, dynamic })
23	}
24	pub fn watch(args: impl CompileArgs) -> Res<Self> {
25		let ShaderManager { sn, .. } = ShaderManager::get();
26		let mut s = Self::new(args)?;
27
28		sn.send(Watch(s.name.clone())).valid();
29		s.dynamic = true;
30
31		Ok(s)
32	}
33	pub fn Bind(&mut self) -> ShaderBinding {
34		let ShaderManager { sn, rx, mailbox, .. } = ShaderManager::get();
35
36		if self.dynamic {
37			sn.send(Rebuild).valid();
38			while let Some(msg) = rx.try_recv() {
39				let ShdResult { name, prog } = msg.wait();
40				mailbox.insert(name, Some(prog));
41			}
42			let Self { name, prog, uniforms, .. } = self;
43			if let Some(p @ Some(_)) = mailbox.get_mut(name) {
44				let p = p.take().valid();
45				match p {
46					Err(e) => WARN!(e),
47					Ok(p) => {
48						*prog = p;
49						*uniforms = Def();
50						PRINT!(format!("Rebuilt shader {}", name.join(" ")).green().bold())
51					}
52				}
53			}
54		}
55
56		ShaderBinding::new(self)
57	}
58}
59impl Drop for Shader {
60	fn drop(&mut self) {
61		let Self { name, dynamic, .. } = self;
62		if *dynamic {
63			let ShaderManager { sn, .. } = ShaderManager::get();
64			sn.send(Forget(mem::take(name))).valid();
65		}
66	}
67}
68
69pub struct ShaderBinding<'l> {
70	shd: &'l mut Shader,
71}
72impl<'l> ShaderBinding<'l> {
73	pub fn new(o: &'l mut Shader) -> Self {
74		ShaderProg::Lock(o.prog.obj);
75		ShaderProg::Bind(o.prog.obj);
76		Self { shd: o }
77	}
78	pub fn is_fresh(&self) -> bool {
79		self.shd.uniforms.is_empty()
80	}
81	pub fn Uniform(&mut self, (id, name): (u32, &str), args: impl UniformArgs) {
82		let Shader { name: shd_name, prog, uniforms, .. } = self.shd;
83		let (addr, cached) = match args.kind() {
84			ArgsKind::Uniform => get_addr(uniforms, (id, name), |n| {
85				let addr = GL!(gl::GetUniformLocation(prog.obj, n.as_ptr()));
86				if addr == -1 {
87					INFO!("No uniform {name:?} in shader {:?}, or it was optimized out", shd_name.join(" "));
88				}
89				addr
90			}),
91			ArgsKind::Ubo => get_addr(uniforms, (id, name), |n| {
92				let addr = GL!(gl::GetUniformBlockIndex(prog.obj, n.as_ptr()));
93				if addr == gl::INVALID_INDEX {
94					INFO!("No UBO {name:?} in shader {:?}, or it was optimized out", shd_name.join(" "));
95					return -1;
96				}
97				-2 - i32(addr)
98			}),
99			ArgsKind::Ssbo => return DEBUG!("GL SSBO {name:?} bound, shader {:?}", shd_name.join(" ")),
100		};
101		if addr != -1 {
102			args.apply(addr, cached);
103		}
104	}
105}
106impl Drop for ShaderBinding<'_> {
107	fn drop(&mut self) {
108		ShaderProg::Unlock();
109	}
110}
111fn get_addr<'l>(u: &'l mut Uniforms, (id, name): (u32, &str), addr: impl Fn(CString) -> i32) -> (i32, &'l mut Option<CachedUni>) {
112	ASSERT!(uniforms_use::id(name).0 == id, "Use Uniforms!()/Unibuffers!() macro to set uniforms");
113	ASSERT!(
114		LocalStatic!(HashMap<u32, String>).entry(id).or_insert(name.into()) == name,
115		"Unifrom collision at entry {name}"
116	);
117
118	let (addr, val) = u.entry(id).or_insert_with(|| (addr(CString::new(name).valid()), None));
119	(*addr, val)
120}
121
122pub struct ShaderManager {
123	sn: Sender<ShaderTask>,
124	rx: Offhand<ShdResult>,
125	mailbox: HashMap<ShdName, Option<Res<ShdProg>>>,
126}
127impl ShaderManager {
128	pub fn Initialize<'s>(args: impl InitArgs<'s>) {
129		let (window, i) = args.get();
130		Self::get_or_init(Some(window)).sn.send(Includes(i)).valid();
131	}
132	pub fn Load(filenames: impl LoadArgs) {
133		for n in filenames.get() {
134			let file = load(n.clone(), FS::Lazy::Text(n));
135			ShaderManager::get().sn.send(Load(file)).valid();
136		}
137	}
138	pub fn Watch(filenames: impl LoadArgs) {
139		for n in filenames.get() {
140			let file = load(n.clone(), FS::Watch::Text(n));
141			ShaderManager::get().sn.send(Load(file)).valid();
142		}
143	}
144	pub fn CleanCache() {
145		ShaderManager::get().sn.send(Clean).valid();
146	}
147	pub(super) fn inline_source(name: &str, source: &[&str]) {
148		ShaderManager::get().sn.send(Inline((name.into(), source.concat()))).valid();
149	}
150	fn get() -> &'static mut Self {
151		Self::get_or_init(None)
152	}
153	fn get_or_init(w: Option<&mut Window>) -> &'static mut Self {
154		LeakyStatic!(ShaderManager, {
155			let Some(w) = w else {
156				ERROR!("Must Initialize ShaderManager before first use");
157			};
158
159			let (sn, rx) = Offhand::from_fn(w, 64, compiler);
160			Self { sn, rx, mailbox: Def() }
161		})
162	}
163}
164
165type Uniforms = HashMap<u32, (i32, Option<CachedUni>)>;