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