grafix_toolbox/kit/opengl/shader/
shader_ext.rs1use 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>)>;