wrend/uniforms/
uniform.rs1use crate::Callback;
2use crate::Id;
3use crate::UniformContext;
4use crate::UniformCreateUpdateCallback;
5use crate::UniformJs;
6use crate::UniformJsInner;
7use crate::UniformShouldUpdateCallback;
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::hash::Hash;
11use wasm_bindgen::JsValue;
12use web_sys::{WebGl2RenderingContext, WebGlProgram, WebGlUniformLocation};
13
14#[derive(Clone)]
20pub struct Uniform<ProgramId: Id, UniformId: Id> {
21 program_ids: Vec<ProgramId>,
22 uniform_id: UniformId,
23 uniform_locations: HashMap<ProgramId, WebGlUniformLocation>,
24 uniform_create_callback: UniformCreateUpdateCallback,
25 update_callback: Option<UniformCreateUpdateCallback>,
26 should_update_callback: Option<UniformShouldUpdateCallback>,
27 use_init_callback_for_update: bool,
28}
29
30impl<ProgramId: Id, UniformId: Id> Uniform<ProgramId, UniformId> {
31 pub(crate) fn new(
33 program_ids: Vec<ProgramId>,
34 uniform_id: UniformId,
35 uniform_locations: HashMap<ProgramId, WebGlUniformLocation>,
37 initialize_callback: UniformCreateUpdateCallback,
38 update_callback: Option<UniformCreateUpdateCallback>,
39 should_update_callback: Option<UniformShouldUpdateCallback>,
40 use_init_callback_for_update: bool,
41 ) -> Self {
42 Self {
43 program_ids,
44 uniform_id,
45 uniform_locations,
46 uniform_create_callback: initialize_callback,
47 update_callback,
48 should_update_callback,
49 use_init_callback_for_update,
50 }
51 }
52
53 pub fn program_ids(&self) -> &Vec<ProgramId> {
55 &self.program_ids
56 }
57
58 pub fn uniform_id(&self) -> &UniformId {
60 &self.uniform_id
61 }
62
63 pub fn uniform_locations(&self) -> &HashMap<ProgramId, WebGlUniformLocation> {
65 &self.uniform_locations
66 }
67
68 pub fn initialize_callback(&self) -> UniformCreateUpdateCallback {
70 self.uniform_create_callback.clone()
71 }
72
73 pub fn should_update_callback(&self) -> Option<UniformShouldUpdateCallback> {
76 self.should_update_callback.as_ref().map(Clone::clone)
77 }
78
79 pub fn update_callback(&self) -> Option<UniformCreateUpdateCallback> {
82 self.update_callback.as_ref().map(Clone::clone)
83 }
84
85 pub fn use_init_callback_for_update(&self) -> bool {
89 self.use_init_callback_for_update
90 }
91
92 pub fn update(
97 &self,
98 gl: &WebGl2RenderingContext,
99 now: f64,
100 programs: &HashMap<ProgramId, WebGlProgram>,
101 ) {
102 let uniform_locations = self.uniform_locations();
103
104 for (program_id, uniform_location) in uniform_locations.iter() {
105 let program = programs
106 .get(program_id)
107 .expect("Program id should correspond to a saved WebGlProgram");
108
109 gl.use_program(Some(program));
110
111 let ctx = UniformContext::new(gl.clone(), now, uniform_location.clone());
112 let should_update_callback = self.should_update_callback();
113
114 let should_call = if let Some(should_update_callback) = should_update_callback {
115 match &*should_update_callback {
116 Callback::Rust(rust_callback) => (rust_callback)(&ctx),
117 Callback::Js(js_callback) => {
118 JsValue::as_bool(&js_callback.call0(&JsValue::NULL).expect(
119 "Should be able to call `should_update_callback` JavaScript callback",
120 ))
121 .unwrap_or(false)
122 }
123 }
124 } else {
125 true
128 };
129
130 if should_call {
131 if self.use_init_callback_for_update {
132 self.uniform_create_callback.call_with_into_js_arg(&ctx);
133 } else if let Some(update_callback) = &self.update_callback {
134 update_callback.call_with_into_js_arg(&ctx)
135 }
136 }
137
138 gl.use_program(None);
139 }
140 }
141}
142
143impl<ProgramId: Id, UniformId: Id> Debug for Uniform<ProgramId, UniformId> {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 f.debug_struct("Uniform")
146 .field("id", &self.uniform_id)
147 .field("uniform_locations", &self.uniform_locations)
148 .finish()
149 }
150}
151impl<ProgramId: Id, UniformId: Id> Hash for Uniform<ProgramId, UniformId> {
152 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
153 self.uniform_id.hash(state);
154 }
155}
156
157impl<ProgramId: Id, UniformId: Id> PartialEq for Uniform<ProgramId, UniformId> {
158 fn eq(&self, other: &Self) -> bool {
159 self.uniform_id == other.uniform_id && self.uniform_locations == other.uniform_locations
160 }
161}
162
163impl<ProgramId: Id, UniformId: Id> Eq for Uniform<ProgramId, UniformId> {}
164
165impl From<UniformJsInner> for JsValue {
166 fn from(uniform: UniformJsInner) -> Self {
167 let js_uniform: UniformJs = uniform.into();
168 js_uniform.into()
169 }
170}
171
172impl From<UniformJs> for UniformJsInner {
173 fn from(js_uniform: UniformJs) -> Self {
174 js_uniform.into_inner()
175 }
176}