solstice/
buffer.rs

1use super::BufferKey;
2
3/// Used to inform the implementation of how it should be bound.
4#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
5pub enum BufferType {
6    Vertex,
7    Index,
8}
9
10impl From<BufferType> for u32 {
11    fn from(ty: BufferType) -> Self {
12        match ty {
13            BufferType::Vertex => glow::ARRAY_BUFFER,
14            BufferType::Index => glow::ELEMENT_ARRAY_BUFFER,
15        }
16    }
17}
18
19/// Used to hint to the implementation how frequently the user will be changing the buffer's data.
20/// * `Static`: The user will set the data once.
21/// * `Dynamic`: The user will set the data occasionally.
22/// * `Stream`: The user will be changing the data after every use. Or almost every use.
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24pub enum Usage {
25    Stream,
26    Static,
27    Dynamic,
28}
29
30impl Usage {
31    pub fn to_gl(self) -> u32 {
32        match self {
33            Usage::Stream => glow::STREAM_DRAW,
34            Usage::Static => glow::STATIC_DRAW,
35            Usage::Dynamic => glow::DYNAMIC_DRAW,
36        }
37    }
38}
39
40/// A memory map between a CPU and GPU buffer.
41///
42/// This implementation, while safe, only operates on bytes to better mirror GPU buffers. It is best
43/// used through a [`Mesh`](solstice::mesh::Mesh) to provide information on how the data is laid out
44/// internally and allow the use of more types and structures.
45///
46/// This buffer is not resizable. All operations are sized in bytes.
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct Buffer {
49    size: usize,
50    handle: BufferKey,
51    buffer_type: BufferType,
52    usage: Usage,
53}
54
55impl Buffer {
56    /// Constructs an empty buffer of `size` bytes.
57    pub fn new(
58        ctx: &mut super::Context,
59        size: usize,
60        buffer_type: BufferType,
61        usage: Usage,
62    ) -> Result<Self, super::GraphicsError> {
63        let handle = ctx.new_buffer(size, buffer_type, usage, None)?;
64        Ok(Self {
65            size,
66            handle,
67            buffer_type,
68            usage,
69        })
70    }
71
72    /// Constructs a buffer of the size and contents of the passed in the Vec.
73    pub fn with_data(
74        ctx: &mut super::Context,
75        data: &[u8],
76        buffer_type: BufferType,
77        usage: Usage,
78    ) -> Result<Self, super::GraphicsError> {
79        let size = data.len();
80        let handle = ctx.new_buffer(size, buffer_type, usage, Some(data))?;
81        Ok(Self {
82            size,
83            handle,
84            buffer_type,
85            usage,
86        })
87    }
88
89    /// Returns an identifier that can be used with the graphics context to retrieve the raw GPU
90    /// buffer handle.
91    pub fn handle(&self) -> BufferKey {
92        self.handle
93    }
94
95    /// The buffer's capacity/size. Since it's not resizable these concepts are the same.
96    pub fn size(&self) -> usize {
97        self.size
98    }
99
100    /// The buffer's type.
101    pub fn buffer_type(&self) -> BufferType {
102        self.buffer_type
103    }
104
105    /// The buffer's usage.
106    pub fn usage(&self) -> Usage {
107        self.usage
108    }
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq)]
112pub struct ModifiedRange<D> {
113    pub offset: D,
114    pub size: D,
115}
116
117#[derive(Debug, PartialEq)]
118pub struct Mapped<T, D: ndarray::Dimension> {
119    pub(crate) inner: T,
120    pub(crate) memory_map: ndarray::Array<u8, D>,
121    pub(crate) modified_range: Option<ModifiedRange<D>>,
122}
123
124impl<T, D> Mapped<T, D>
125where
126    D: ndarray::Dimension,
127{
128    pub fn with_shape<S>(inner: T, shape: S) -> Self
129    where
130        S: ndarray::ShapeBuilder<Dim = D>,
131    {
132        Self {
133            inner,
134            memory_map: ndarray::Array::default(shape),
135            modified_range: None,
136        }
137    }
138
139    pub fn inner(&self) -> &T {
140        &self.inner
141    }
142
143    pub fn memory_map(&self) -> &[u8] {
144        self.memory_map.as_slice_memory_order().unwrap()
145    }
146}
147
148impl<T> Mapped<T, ndarray::Ix1> {
149    pub fn from_vec(inner: T, vec: Vec<u8>) -> Self {
150        Self {
151            inner,
152            memory_map: vec.into(),
153            modified_range: None,
154        }
155    }
156
157    /// Write new data into the buffer and adjust it's dirty range accordingly.
158    ///
159    /// This function will panic if the buffer overflows.
160    pub fn write(&mut self, data: &[u8], offset: usize) {
161        self.memory_map.as_slice_memory_order_mut().unwrap()[offset..(offset + data.len())]
162            .copy_from_slice(data);
163        self.set_modified_range(offset, data.len());
164    }
165
166    pub fn modified_range(&self) -> Option<ModifiedRange<usize>> {
167        self.modified_range.map(|range| ModifiedRange {
168            offset: range.offset[0],
169            size: range.size[0],
170        })
171    }
172
173    /// Sets the dirty range of this buffer. This marks how much of the data is synced to the GPU
174    /// when this buffer is unmapped.
175    fn set_modified_range(&mut self, offset: usize, modified_size: usize) {
176        let range = self.modified_range.get_or_insert(ModifiedRange {
177            offset: ndarray::Ix1(0),
178            size: ndarray::Ix1(0),
179        });
180        // We're being conservative right now by internally marking the whole range
181        // from the start of section a to the end of section b as modified if both
182        // a and b are marked as modified.
183        let old_range_end = range.offset + range.size;
184        range.offset = ndarray::Ix1(std::cmp::min(range.offset[0], offset));
185
186        let new_range_end = std::cmp::max(offset + modified_size, old_range_end[0]);
187        range.size = ndarray::Ix1(new_range_end - range.offset[0]);
188    }
189}
190
191pub type MappedBuffer = Mapped<Buffer, ndarray::Ix1>;
192impl MappedBuffer {
193    pub fn with_buffer(
194        ctx: &mut super::Context,
195        size: usize,
196        buffer_type: BufferType,
197        usage: Usage,
198    ) -> Result<Self, super::GraphicsError> {
199        let inner = Buffer::new(ctx, size, buffer_type, usage)?;
200        let memory_map = ndarray::Array1::from(vec![0u8; inner.size()]);
201        Ok(Self {
202            inner,
203            memory_map,
204            modified_range: None,
205        })
206    }
207
208    pub fn unmap(&mut self, ctx: &mut super::Context) {
209        ctx.unmap_buffer(self);
210        self.modified_range = None;
211    }
212}
213
214// pub trait MappedBufferTrait<I> {
215//     type UnmappedBuffer;
216//
217//     fn set(&mut self, index: I, v: u8);
218//     fn get(&self, index: I) -> Option<&u8>;
219//     fn unmap(&mut self, ctx: &mut super::Context) -> &Self::UnmappedBuffer;
220// }
221//
222// struct MappedBuffer<D> {
223//     inner: Buffer,
224//     memory_map: ndarray::Array<u8, D>
225// }
226//
227// impl<I> MappedBufferTrait<I> for MappedBuffer<ndarray::Ix1> where I: ndarray::NdIndex<ndarray::Ix1> {
228//     type UnmappedBuffer = Buffer;
229//
230//     fn set(&mut self, index: I, v: u8) {
231//         self.memory_map[index] = v;
232//     }
233//
234//     fn get(&self, index: I) -> Option<&u8> {
235//         self.memory_map.get(index)
236//     }
237//
238//     fn unmap(&mut self, ctx: &mut super::Context) -> &Self::UnmappedBuffer {
239//         ctx.unmap_buffer(&mut self.inner);
240//         &self.inner
241//     }
242// }
243
244// impl<I, D> MappedBufferTrait<I> for MappedBuffer<D> where D: ndarray::Dimension, I: ndarray::NdIndex<D> {
245//     fn set(&mut self, index: I, v: u8) {
246//         self.memory_map[index] = v;
247//     }
248//
249//     fn get(&self, index: I) -> Option<&u8> {
250//         self.memory_map.get(index)
251//     }
252// }