1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::core::*;

///
/// A buffer for transferring a set of uniform variables to the shader program
/// (see also [use_uniform_block](crate::core::Program::use_uniform_block)).
///
pub struct UniformBuffer {
    context: Context,
    id: crate::context::Buffer,
    offsets: Vec<usize>,
    data: Vec<f32>,
}

impl UniformBuffer {
    ///
    /// Creates a new uniform buffer with room for a set of variables of varying length defined by the `sizes` argument.
    /// So for example if you create a uniform buffer with `&[3, 1, 4, 16]` as the `sizes` argument, you will have a uniform buffer that has four variables:
    /// The first with 3 elements (a [Vec3]), the second with 1 element (a `f32`), the third with four elements (a [Vec4]) and the last with 16 elements (a [Mat4]).
    /// The variables are initialized to 0.
    ///
    pub fn new(context: &Context, sizes: &[u32]) -> UniformBuffer {
        let id = unsafe { context.create_buffer().expect("Failed creating buffer") };

        let mut offsets = Vec::new();
        let mut length = 0;
        for size in sizes {
            offsets.push(length);
            length += *size as usize;
        }
        let buffer = UniformBuffer {
            context: context.clone(),
            id,
            offsets,
            data: vec![0.0; length as usize],
        };
        buffer.send();
        buffer
    }

    pub(crate) fn bind(&self, id: u32) {
        unsafe {
            self.context
                .bind_buffer_base(crate::context::UNIFORM_BUFFER, id, Some(self.id))
        };
    }

    ///
    /// Update the values of the variable at the given index with the given data.
    ///
    /// # Panic
    /// Will panic if the index is not in the range `[0-max]` where `max` is the length of the `sizes` argument given at construction.
    /// Will panic if the data length does not match the element count of the variable (defined at construction) at the given index.
    ///
    pub fn update(&mut self, index: u32, data: &[f32]) {
        if let Some((offset, length)) = self.offset_length(index as usize) {
            if data.len() != length {
                panic!(
                    "data for element at index {0} has length {1} but a length of {2} was expected",
                    index,
                    data.len(),
                    length,
                );
            }
            self.data
                .splice(offset..offset + length, data.iter().cloned());
            self.send();
        } else {
            panic!(
                "the index {} is outside the expected range [0, {}]",
                index,
                self.offsets.len() - 1
            );
        }
        //TODO: Send to GPU (contextBufferSubData)
    }

    ///
    /// Returns the values of the variable at the given index if inside the range of variables, otherwise `None`.
    ///
    pub fn get(&self, index: u32) -> Option<&[f32]> {
        self.offset_length(index as usize)
            .map(|(offset, length)| &self.data[offset..offset + length])
    }

    fn offset_length(&self, index: usize) -> Option<(usize, usize)> {
        if index >= self.offsets.len() {
            None
        } else {
            let offset = self.offsets[index];
            let length = if index + 1 == self.offsets.len() {
                self.data.len()
            } else {
                self.offsets[index + 1]
            } - offset;
            Some((offset, length))
        }
    }

    fn send(&self) {
        unsafe {
            self.context
                .bind_buffer(crate::context::UNIFORM_BUFFER, Some(self.id));
            self.context.buffer_data_u8_slice(
                crate::context::UNIFORM_BUFFER,
                to_byte_slice(&self.data),
                crate::context::STATIC_DRAW,
            );
            self.context
                .bind_buffer(crate::context::UNIFORM_BUFFER, None);
        }
    }
}

impl Drop for UniformBuffer {
    fn drop(&mut self) {
        unsafe {
            self.context.delete_buffer(self.id);
        }
    }
}