Skip to main content

grafix_toolbox/kit/opengl/shader/
shader_ext.rs

1use super::{uniform::*, *};
2
3pub struct Shader {
4	name: ShdName,
5	prog: ShdProg,
6	uniforms: Uniforms,
7	dynamic: bool,
8}
9impl Shader {
10	pub fn pure<const N: usize>(args: [I; N]) -> Self {
11		Self::new(args).fail()
12	}
13	pub fn new(args: impl CompileArgs) -> Res<Self> {
14		let ShaderManager { sn, rx, .. } = ShaderManager::get();
15		let name = args.get();
16
17		sn.send(Create(name)).valid();
18		let ShdResult { prog, name } = rx.recv().valid().wait();
19		let (uniforms, dynamic) = Def();
20
21		Self { name, prog: prog?, uniforms, dynamic }.pipe(Ok)
22	}
23	pub fn watch(args: impl CompileArgs) -> Res<Self> {
24		let ShaderManager { sn, .. } = ShaderManager::get();
25		Self::new(args)?
26			.tap(|Self { name, dynamic, .. }| {
27				*dynamic = true;
28				sn.send(Watch(name.clone())).fail();
29			})
30			.pipe(Ok)
31	}
32	pub fn Bind(&mut self) -> ShaderBind {
33		let ShaderManager { sn, rx, mailbox } = ShaderManager::get();
34
35		if self.dynamic {
36			sn.send(Rebuild).valid();
37			while let Some(msg) = rx.try_recv() {
38				let ShdResult { name, prog } = msg.wait();
39				mailbox.insert(name, prog);
40			}
41
42			let Self { name, prog, uniforms, .. } = self;
43			if let Some(p) = mailbox.remove(name)
44				&& let Ok(p) = p.map_err(|e| WARN!(e))
45			{
46				(*prog, *uniforms) = (p, Def());
47				PRINT!(format!("Rebuilt shader {}", name.join(" ")).green().bold())
48			}
49		}
50
51		ShaderBind::new(self)
52	}
53}
54impl Drop for Shader {
55	fn drop(&mut self) {
56		let Self { name, dynamic, .. } = self;
57		if *dynamic {
58			let ShaderManager { sn, .. } = ShaderManager::get();
59			sn.send(Forget(mem::take(name))).valid();
60		}
61	}
62}
63
64pub struct ShaderBind<'l> {
65	shd: &'l mut Shader,
66}
67impl<'l> ShaderBind<'l> {
68	fn new(o: &'l mut Shader) -> Self {
69		ShaderT::Lock(o.prog.obj);
70		ShaderT::Bind(o.prog.obj);
71		Self { shd: o }
72	}
73	pub fn is_fresh(&self) -> bool {
74		self.shd.uniforms.is_empty()
75	}
76	pub fn Uniform(&mut self, (id, name): (u32, &str), args: impl UniformArgs) {
77		let Shader { name: shd_name, prog, uniforms, .. } = self.shd;
78		let (addr, cached) = match args.kind() {
79			ArgsKind::Uniform => get_addr(uniforms, (id, name), |n| {
80				let addr = GL!(gl::GetUniformLocation(prog.obj, n.as_ptr()));
81				if addr == -1 {
82					INFO!("No uniform {name:?} in {:?}", shd_name.join(" "));
83				}
84				addr
85			}),
86			ArgsKind::Ubo => get_addr(uniforms, (id, name), |n| {
87				let addr = GL!(gl::GetUniformBlockIndex(prog.obj, n.as_ptr()));
88				if addr == gl::INVALID_INDEX {
89					INFO!("No UBO {name:?} in {:?}", shd_name.join(" "));
90					return -1;
91				}
92				-2 - i32(addr)
93			}),
94			ArgsKind::Ssbo => return DEBUG!("GL SSBO {name:?} bound in {:?}", shd_name.join(" ")),
95		};
96		if addr != -1 {
97			args.apply(addr, cached);
98		}
99	}
100}
101impl Drop for ShaderBind<'_> {
102	fn drop(&mut self) {
103		ShaderT::Unlock();
104	}
105}
106fn get_addr<'l>(u: &'l mut Uniforms, (id, name): (u32, &str), addr: impl FnOnce(CString) -> i32) -> (i32, &'l mut Option<CachedUni>) {
107	ASSERT!(uniforms_use::id(name).0 == id, "Use Uniforms!()/Unibuffers!() macro to set uniforms");
108	ASSERT!(
109		LocalStatic!(HashMap<u32, String>).entry(id).or_insert(name.into()) == name,
110		"Unifrom name collision {name:?}"
111	);
112
113	let (addr, val) = u.entry(id).or_insert_with(|| (name.pipe(CString::new).valid().pipe(addr), None));
114	(*addr, val)
115}
116
117type Uniforms = HashMap<u32, (i32, Option<CachedUni>)>;