azul_glium/program/
uniforms_storage.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::hash::BuildHasherDefault;
4use RawUniformValue;
5
6use smallvec::SmallVec;
7use fnv::FnvHasher;
8
9use gl;
10use Handle;
11use context::CommandContext;
12use version::Version;
13use version::Api;
14use program::reflection::ShaderStage;
15
16pub struct UniformsStorage {
17    values: RefCell<HashMap<gl::types::GLint, Option<RawUniformValue>,
18                            BuildHasherDefault<FnvHasher>>>,
19    uniform_blocks: RefCell<SmallVec<[Option<gl::types::GLuint>; 4]>>,
20    shader_storage_blocks: RefCell<SmallVec<[Option<gl::types::GLuint>; 4]>>,
21    subroutine_uniforms: RefCell<HashMap<ShaderStage, Vec<gl::types::GLuint>,
22                                         BuildHasherDefault<FnvHasher>>>,
23}
24
25impl UniformsStorage {
26    /// Builds a new empty storage.
27    #[inline]
28    pub fn new() -> UniformsStorage {
29        UniformsStorage {
30            values: RefCell::new(HashMap::with_hasher(Default::default())),
31            uniform_blocks: RefCell::new(SmallVec::new()),
32            shader_storage_blocks: RefCell::new(SmallVec::new()),
33            subroutine_uniforms: RefCell::new(HashMap::with_hasher(Default::default())),
34        }
35    }
36
37    /// Compares `value` with the value stored in this object. If the values differ, updates
38    /// the storage and calls `glUniform`.
39    pub fn set_uniform_value(&self, ctxt: &mut CommandContext, program: Handle,
40                             location: gl::types::GLint, value: &RawUniformValue)
41    {
42        let mut values = self.values.borrow_mut();
43
44        // TODO: don't assume that, instead use DSA if the program is not current
45        assert!(ctxt.state.program == program);
46
47        macro_rules! uniform(
48            ($ctxt:expr, $uniform:ident, $uniform_arb:ident, $($params:expr),+) => (
49                unsafe {
50                    if $ctxt.version >= &Version(Api::Gl, 1, 5) ||
51                       $ctxt.version >= &Version(Api::GlEs, 2, 0)
52                    {
53                        $ctxt.gl.$uniform($($params),+)
54                    } else {
55                        assert!($ctxt.extensions.gl_arb_shader_objects);
56                        $ctxt.gl.$uniform_arb($($params),+)
57                    }
58                }
59            )
60        );
61
62        macro_rules! uniform_f64(
63            ($ctxt:expr, $uniform:ident, $($params:expr),+) => (
64                unsafe {
65                    if $ctxt.extensions.gl_arb_gpu_shader_fp64 {
66                        $ctxt.gl.$uniform($($params),+)
67                    } else {
68                        panic!("Double precision floats are not supported on this system.")
69                    }
70                }
71            )
72        );
73
74        macro_rules! uniform_i64(
75            ($ctxt:expr, $uniform:ident, $($params:expr),+) => (
76                unsafe {
77                    if $ctxt.extensions.gl_arb_gpu_shader_int64 {
78                        $ctxt.gl.$uniform($($params),+)
79                    } else {
80                        panic!("64 bit integers are not supported on this system.")
81                    }
82                }
83            )
84        );
85
86        match (value, values.entry(location).or_insert(None)) {
87            (&RawUniformValue::SignedInt(a), &mut Some(RawUniformValue::SignedInt(b))) if a == b => (),
88            (&RawUniformValue::UnsignedInt(a), &mut Some(RawUniformValue::UnsignedInt(b))) if a == b => (),
89            (&RawUniformValue::Float(a), &mut Some(RawUniformValue::Float(b))) if a == b => (),
90            (&RawUniformValue::Mat2(a), &mut Some(RawUniformValue::Mat2(b))) if a == b => (),
91            (&RawUniformValue::Mat3(a), &mut Some(RawUniformValue::Mat3(b))) if a == b => (),
92            (&RawUniformValue::Mat4(a), &mut Some(RawUniformValue::Mat4(b))) if a == b => (),
93            (&RawUniformValue::Vec2(a), &mut Some(RawUniformValue::Vec2(b))) if a == b => (),
94            (&RawUniformValue::Vec3(a), &mut Some(RawUniformValue::Vec3(b))) if a == b => (),
95            (&RawUniformValue::Vec4(a), &mut Some(RawUniformValue::Vec4(b))) if a == b => (),
96            (&RawUniformValue::IntVec2(a), &mut Some(RawUniformValue::IntVec2(b))) if a == b => (),
97            (&RawUniformValue::IntVec3(a), &mut Some(RawUniformValue::IntVec3(b))) if a == b => (),
98            (&RawUniformValue::IntVec4(a), &mut Some(RawUniformValue::IntVec4(b))) if a == b => (),
99            (&RawUniformValue::UnsignedIntVec2(a), &mut Some(RawUniformValue::UnsignedIntVec2(b))) if a == b => (),
100            (&RawUniformValue::UnsignedIntVec3(a), &mut Some(RawUniformValue::UnsignedIntVec3(b))) if a == b => (),
101            (&RawUniformValue::UnsignedIntVec4(a), &mut Some(RawUniformValue::UnsignedIntVec4(b))) if a == b => (),
102            (&RawUniformValue::Double(a), &mut Some(RawUniformValue::Double(b))) if a == b => (),
103            (&RawUniformValue::DoubleMat2(a), &mut Some(RawUniformValue::DoubleMat2(b))) if a == b => (),
104            (&RawUniformValue::DoubleMat3(a), &mut Some(RawUniformValue::DoubleMat3(b))) if a == b => (),
105            (&RawUniformValue::DoubleMat4(a), &mut Some(RawUniformValue::DoubleMat4(b))) if a == b => (),
106            (&RawUniformValue::DoubleVec2(a), &mut Some(RawUniformValue::DoubleVec2(b))) if a == b => (),
107            (&RawUniformValue::DoubleVec3(a), &mut Some(RawUniformValue::DoubleVec3(b))) if a == b => (),
108            (&RawUniformValue::DoubleVec4(a), &mut Some(RawUniformValue::DoubleVec4(b))) if a == b => (),
109            (&RawUniformValue::Int64(a), &mut Some(RawUniformValue::Int64(b))) if a == b => (),
110            (&RawUniformValue::Int64Vec2(a), &mut Some(RawUniformValue::Int64Vec2(b))) if a == b => (),
111            (&RawUniformValue::Int64Vec3(a), &mut Some(RawUniformValue::Int64Vec3(b))) if a == b => (),
112            (&RawUniformValue::Int64Vec4(a), &mut Some(RawUniformValue::Int64Vec4(b))) if a == b => (),
113            (&RawUniformValue::UnsignedInt64(a), &mut Some(RawUniformValue::UnsignedInt64(b))) if a == b => (),
114            (&RawUniformValue::UnsignedInt64Vec2(a), &mut Some(RawUniformValue::UnsignedInt64Vec2(b))) if a == b => (),
115            (&RawUniformValue::UnsignedInt64Vec3(a), &mut Some(RawUniformValue::UnsignedInt64Vec3(b))) if a == b => (),
116            (&RawUniformValue::UnsignedInt64Vec4(a), &mut Some(RawUniformValue::UnsignedInt64Vec4(b))) if a == b => (),
117
118            (&RawUniformValue::SignedInt(v), target) => {
119                *target = Some(RawUniformValue::SignedInt(v));
120                uniform!(ctxt, Uniform1i, Uniform1iARB, location, v);
121            },
122
123            (&RawUniformValue::UnsignedInt(v), target) => {
124                *target = Some(RawUniformValue::UnsignedInt(v));
125
126                // Uniform1uiARB doesn't exist
127                unsafe {
128                    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
129                       ctxt.version >= &Version(Api::GlEs, 2, 0)
130                    {
131                        ctxt.gl.Uniform1ui(location, v)
132                    } else {
133                        assert!(ctxt.extensions.gl_arb_shader_objects);
134                        ctxt.gl.Uniform1iARB(location, v as gl::types::GLint)
135                    }
136                }
137            },
138
139            (&RawUniformValue::Float(v), target) => {
140                *target = Some(RawUniformValue::Float(v));
141                uniform!(ctxt, Uniform1f, Uniform1fARB, location, v);
142            },
143
144            (&RawUniformValue::Mat2(v), target) => {
145                *target = Some(RawUniformValue::Mat2(v));
146                uniform!(ctxt, UniformMatrix2fv, UniformMatrix2fvARB,
147                         location, 1, gl::FALSE, v.as_ptr() as *const f32);
148            },
149
150            (&RawUniformValue::Mat3(v), target) => {
151                *target = Some(RawUniformValue::Mat3(v));
152                uniform!(ctxt, UniformMatrix3fv, UniformMatrix3fvARB,
153                         location, 1, gl::FALSE, v.as_ptr() as *const f32);
154            },
155
156            (&RawUniformValue::Mat4(v), target) => {
157                *target = Some(RawUniformValue::Mat4(v));
158                uniform!(ctxt, UniformMatrix4fv, UniformMatrix4fvARB,
159                         location, 1, gl::FALSE, v.as_ptr() as *const f32);
160            },
161
162            (&RawUniformValue::Vec2(v), target) => {
163                *target = Some(RawUniformValue::Vec2(v));
164                uniform!(ctxt, Uniform2fv, Uniform2fvARB, location, 1, v.as_ptr() as *const f32);
165            },
166
167            (&RawUniformValue::Vec3(v), target) => {
168                *target = Some(RawUniformValue::Vec3(v));
169                uniform!(ctxt, Uniform3fv, Uniform3fvARB, location, 1, v.as_ptr() as *const f32);
170            },
171
172            (&RawUniformValue::Vec4(v), target) => {
173                *target = Some(RawUniformValue::Vec4(v));
174                uniform!(ctxt, Uniform4fv, Uniform4fvARB, location, 1, v.as_ptr() as *const f32);
175            },
176
177            (&RawUniformValue::IntVec2(v), target) => {
178                *target = Some(RawUniformValue::IntVec2(v));
179                uniform!(ctxt, Uniform2iv, Uniform2ivARB, location, 1, v.as_ptr() as *const gl::types::GLint);
180            },
181
182            (&RawUniformValue::IntVec3(v), target) => {
183                *target = Some(RawUniformValue::IntVec3(v));
184                uniform!(ctxt, Uniform3iv, Uniform3ivARB, location, 1, v.as_ptr() as *const gl::types::GLint);
185            },
186
187            (&RawUniformValue::IntVec4(v), target) => {
188                *target = Some(RawUniformValue::IntVec4(v));
189                uniform!(ctxt, Uniform4iv, Uniform4ivARB, location, 1, v.as_ptr() as *const gl::types::GLint);
190            },
191
192            (&RawUniformValue::UnsignedIntVec2(v), target) => {
193                *target = Some(RawUniformValue::UnsignedIntVec2(v));
194
195                // Uniform2uivARB doesn't exist
196                unsafe {
197                    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
198                       ctxt.version >= &Version(Api::GlEs, 2, 0)
199                    {
200                        ctxt.gl.Uniform2uiv(location, 1, v.as_ptr() as *const gl::types::GLuint)
201                    } else {
202                        assert!(ctxt.extensions.gl_arb_shader_objects);
203                        ctxt.gl.Uniform2ivARB(location, 1, v.as_ptr() as *const gl::types::GLint)
204                    }
205                }
206            },
207
208            (&RawUniformValue::UnsignedIntVec3(v), target) => {
209                *target = Some(RawUniformValue::UnsignedIntVec3(v));
210
211                // Uniform3uivARB doesn't exist
212                unsafe {
213                    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
214                       ctxt.version >= &Version(Api::GlEs, 2, 0)
215                    {
216                        ctxt.gl.Uniform3uiv(location, 1, v.as_ptr() as *const gl::types::GLuint)
217                    } else {
218                        assert!(ctxt.extensions.gl_arb_shader_objects);
219                        ctxt.gl.Uniform3ivARB(location, 1, v.as_ptr() as *const gl::types::GLint)
220                    }
221                }
222            },
223
224            (&RawUniformValue::UnsignedIntVec4(v), target) => {
225                *target = Some(RawUniformValue::UnsignedIntVec4(v));
226
227                // Uniform4uivARB doesn't exist
228                unsafe {
229                    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
230                       ctxt.version >= &Version(Api::GlEs, 2, 0)
231                    {
232                        ctxt.gl.Uniform4uiv(location, 1, v.as_ptr() as *const gl::types::GLuint)
233                    } else {
234                        assert!(ctxt.extensions.gl_arb_shader_objects);
235                        ctxt.gl.Uniform4ivARB(location, 1, v.as_ptr() as *const gl::types::GLint)
236                    }
237                }
238            },
239            (&RawUniformValue::Double(v), target) => {
240                *target = Some(RawUniformValue::Double(v));
241                uniform_f64!(ctxt, Uniform1d, location, v);
242            },
243
244            (&RawUniformValue::DoubleMat2(v), target) => {
245                *target = Some(RawUniformValue::DoubleMat2(v));
246                uniform_f64!(ctxt, UniformMatrix2dv,
247                         location, 1, gl::FALSE, v.as_ptr() as *const gl::types::GLdouble);
248            },
249
250            (&RawUniformValue::DoubleMat3(v), target) => {
251                *target = Some(RawUniformValue::DoubleMat3(v));
252                uniform_f64!(ctxt, UniformMatrix3dv,
253                         location, 1, gl::FALSE, v.as_ptr() as *const gl::types::GLdouble);
254            },
255
256            (&RawUniformValue::DoubleMat4(v), target) => {
257                *target = Some(RawUniformValue::DoubleMat4(v));
258                uniform_f64!(ctxt, UniformMatrix4dv,
259                         location, 1, gl::FALSE, v.as_ptr() as *const gl::types::GLdouble);
260            },
261
262            (&RawUniformValue::DoubleVec2(v), target) => {
263                *target = Some(RawUniformValue::DoubleVec2(v));
264                uniform_f64!(ctxt, Uniform2dv, location, 1, v.as_ptr() as *const gl::types::GLdouble);
265            },
266
267            (&RawUniformValue::DoubleVec3(v), target) => {
268                *target = Some(RawUniformValue::DoubleVec3(v));
269                uniform_f64!(ctxt, Uniform3dv, location, 1, v.as_ptr() as *const gl::types::GLdouble);
270            },
271
272            (&RawUniformValue::DoubleVec4(v), target) => {
273                *target = Some(RawUniformValue::DoubleVec4(v));
274                uniform_f64!(ctxt, Uniform4dv, location, 1, v.as_ptr() as *const gl::types::GLdouble);
275            },
276            (&RawUniformValue::Int64(v), target) => {
277                *target = Some(RawUniformValue::Int64(v));
278                uniform_i64!(ctxt, Uniform1i64ARB, location, v);
279            },
280            (&RawUniformValue::Int64Vec2(v), target) => {
281                *target = Some(RawUniformValue::Int64Vec2(v));
282                uniform_i64!(ctxt, Uniform2i64vARB, location, 1, v.as_ptr() as *const gl::types::GLint64);
283            },
284
285            (&RawUniformValue::Int64Vec3(v), target) => {
286                *target = Some(RawUniformValue::Int64Vec3(v));
287                uniform_i64!(ctxt, Uniform3i64vARB, location, 1, v.as_ptr() as *const gl::types::GLint64);
288            },
289
290            (&RawUniformValue::Int64Vec4(v), target) => {
291                *target = Some(RawUniformValue::Int64Vec4(v));
292                uniform_i64!(ctxt, Uniform4i64vARB, location, 1, v.as_ptr() as *const gl::types::GLint64);
293            },
294            (&RawUniformValue::UnsignedInt64(v), target) => {
295                *target = Some(RawUniformValue::UnsignedInt64(v));
296                uniform_i64!(ctxt, Uniform1ui64ARB, location, v);
297            },
298            (&RawUniformValue::UnsignedInt64Vec2(v), target) => {
299                *target = Some(RawUniformValue::UnsignedInt64Vec2(v));
300                uniform_i64!(ctxt, Uniform2ui64vARB, location, 1, v.as_ptr() as *const gl::types::GLuint64);
301            },
302
303            (&RawUniformValue::UnsignedInt64Vec3(v), target) => {
304                *target = Some(RawUniformValue::UnsignedInt64Vec3(v));
305                uniform_i64!(ctxt, Uniform3ui64vARB, location, 1, v.as_ptr() as *const gl::types::GLuint64);
306            },
307
308            (&RawUniformValue::UnsignedInt64Vec4(v), target) => {
309                *target = Some(RawUniformValue::UnsignedInt64Vec4(v));
310                uniform_i64!(ctxt, Uniform4ui64vARB, location, 1, v.as_ptr() as *const gl::types::GLuint64);
311            },
312        }
313    }
314
315    /// Compares `value` with the value stored in this object. If the values differ, updates
316    /// the storage and calls `glUniformBlockBinding`.
317    pub fn set_uniform_block_binding(&self, ctxt: &mut CommandContext, program: Handle,
318                                     location: gl::types::GLuint, value: gl::types::GLuint)
319    {
320        let mut blocks = self.uniform_blocks.borrow_mut();
321
322        if blocks.len() <= location as usize {
323            for _ in blocks.len() .. location as usize + 1 {
324                blocks.push(None);
325            }
326        }
327
328        // TODO: don't assume that, instead use DSA if the program is not current
329        assert!(ctxt.state.program == program);
330
331        match (value, &mut blocks[location as usize]) {
332            (a, &mut Some(b)) if a == b => (),
333
334            (a, target) => {
335                *target = Some(a);
336                match program {
337                    Handle::Id(id) => unsafe {
338                        ctxt.gl.UniformBlockBinding(id, location, value);
339                    },
340                    _ => unreachable!()
341                }
342            },
343        }
344    }
345
346    /// Compares `value` with the value stored in this object. If the values differ, updates
347    /// the storage and calls `glShaderStorageBlockBinding`.
348    pub fn set_shader_storage_block_binding(&self, ctxt: &mut CommandContext, program: Handle,
349                                            location: gl::types::GLuint, value: gl::types::GLuint)
350    {
351        let mut blocks = self.shader_storage_blocks.borrow_mut();
352
353        if blocks.len() <= location as usize {
354            for _ in blocks.len() .. location as usize + 1 {
355                blocks.push(None);
356            }
357        }
358
359        // TODO: don't assume that, instead use DSA if the program is not current
360        assert!(ctxt.state.program == program);
361
362        match (value, &mut blocks[location as usize]) {
363            (a, &mut Some(b)) if a == b => (),
364
365            (a, target) => {
366                *target = Some(a);
367                match program {
368                    Handle::Id(id) => unsafe {
369                        ctxt.gl.ShaderStorageBlockBinding(id, location, value);
370                    },
371                    _ => unreachable!()
372                }
373            },
374        }
375    }
376
377    /// Compares `indices` to the value stored in this object. If the values differ,
378    /// updates the programs subroutine uniform bindings.
379    pub fn set_subroutine_uniforms_for_stage(&self, ctxt: &mut CommandContext,
380                                         program: Handle,
381                                         stage: ShaderStage,
382                                         indices: &[gl::types::GLuint])
383    {
384        let mut subroutine_uniforms = self.subroutine_uniforms.borrow_mut();
385        if let Some(stored_indices) = subroutine_uniforms.get(&stage) {
386            if &stored_indices[..] == indices {
387                return
388            }
389        }
390        // TODO: don't assume that, instead use DSA if the program is not current
391        assert!(ctxt.state.program == program);
392        subroutine_uniforms.insert(stage, indices.iter().cloned().collect());
393        unsafe {
394            ctxt.gl.UniformSubroutinesuiv(stage.to_gl_enum(), indices.len() as gl::types::GLsizei, indices.as_ptr() as *const _);
395        }
396    }
397}