Skip to main content

luminance_webgl/webgl2/
buffer.rs

1//! WebGL2 buffer implementation.
2
3use crate::webgl2::{
4  state::{Bind, WebGL2State},
5  WebGL2,
6};
7use core::fmt;
8use luminance::tess::TessError;
9use std::{
10  cell::RefCell,
11  error,
12  marker::PhantomData,
13  mem,
14  ops::{Deref, DerefMut},
15  rc::Rc,
16  slice,
17};
18use web_sys::{WebGl2RenderingContext, WebGlBuffer};
19
20/// Errors that can occur when dealing with buffers.
21#[derive(Clone, Debug)]
22pub enum BufferError {
23  /// Cannot create the buffer on the backend.
24  CannotCreate,
25}
26
27impl fmt::Display for BufferError {
28  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
29    match self {
30      BufferError::CannotCreate => f.write_str("cannot create buffer on the backend"),
31    }
32  }
33}
34
35impl error::Error for BufferError {}
36
37impl From<BufferError> for TessError {
38  fn from(e: BufferError) -> Self {
39    TessError::cannot_create(e.to_string())
40  }
41}
42
43/// Wrapped WebGL buffer.
44///
45/// Used to drop the buffer.
46#[derive(Clone, Debug)]
47struct BufferWrapper<const TARGET: u32> {
48  handle: WebGlBuffer,
49  state: Rc<RefCell<WebGL2State>>,
50}
51
52impl<const TARGET: u32> Drop for BufferWrapper<TARGET> {
53  fn drop(&mut self) {
54    let mut state = self.state.borrow_mut();
55
56    state.unbind_buffer(&self.handle);
57    state.ctx.delete_buffer(Some(&self.handle));
58  }
59}
60
61/// WebGL buffer.
62#[derive(Clone, Debug)]
63pub struct Buffer<T, const TARGET: u32> {
64  /// A cached version of the GPU buffer; emulate persistent mapping.
65  pub(crate) buf: Vec<T>,
66  gl_buf: BufferWrapper<TARGET>,
67}
68
69impl<T, const TARGET: u32> Buffer<T, TARGET>
70where
71  WebGL2State: BindBuffer<TARGET>,
72{
73  pub(crate) fn from_vec(webgl2: &mut WebGL2, vec: Vec<T>) -> Result<Self, BufferError> {
74    let mut state = webgl2.state.borrow_mut();
75    let len = vec.len();
76
77    let handle = state
78      .create_buffer()
79      .ok_or_else(|| BufferError::CannotCreate)?;
80
81    state.bind_buffer(&handle, Bind::Forced);
82
83    let bytes = mem::size_of::<T>() * len;
84    let data = unsafe { slice::from_raw_parts(vec.as_ptr() as *const _, bytes) };
85    state
86      .ctx
87      .buffer_data_with_u8_array(TARGET, data, WebGl2RenderingContext::STREAM_DRAW);
88
89    let gl_buf = BufferWrapper {
90      handle,
91      state: webgl2.state.clone(),
92    };
93
94    Ok(Buffer { gl_buf, buf: vec })
95  }
96
97  pub(crate) fn handle(&self) -> &WebGlBuffer {
98    &self.gl_buf.handle
99  }
100
101  pub(crate) fn slice_buffer(&self) -> BufferSlice<T> {
102    BufferSlice {
103      handle: &self.gl_buf.handle,
104      ptr: self.buf.as_ptr(),
105      len: self.buf.len(),
106      state: self.gl_buf.state.clone(),
107    }
108  }
109
110  pub(crate) fn slice_buffer_mut(&mut self) -> BufferSliceMut<T, TARGET> {
111    let raw = BufferSliceMutWrapper {
112      handle: &self.gl_buf.handle,
113      ptr: self.buf.as_mut_ptr() as *mut u8,
114      bytes: self.buf.len() * mem::size_of::<T>(),
115      state: self.gl_buf.state.clone(),
116    };
117
118    BufferSliceMut {
119      raw,
120      _phantom: PhantomData,
121    }
122  }
123}
124
125pub struct BufferSlice<'a, T> {
126  handle: &'a WebGlBuffer,
127  ptr: *const T,
128  len: usize,
129  state: Rc<RefCell<WebGL2State>>,
130}
131
132impl<'a> BufferSlice<'a, u8> {
133  /// Transmute to another type.
134  ///
135  /// This method is highly unsafe and should only be used when certain the target type is the
136  /// one actually represented by the raw bytes.
137  pub(crate) unsafe fn transmute<T>(self) -> BufferSlice<'a, T> {
138    let handle = self.handle;
139    let ptr = self.ptr as *const T;
140    let len = self.len / mem::size_of::<T>();
141    let state = self.state;
142
143    BufferSlice {
144      handle,
145      ptr,
146      len,
147      state,
148    }
149  }
150}
151
152impl<T> Deref for BufferSlice<'_, T> {
153  type Target = [T];
154
155  fn deref(&self) -> &Self::Target {
156    unsafe { slice::from_raw_parts(self.ptr, self.len) }
157  }
158}
159
160/// Buffer mutable slice wrapper.
161///
162/// When a buffer is mapped, we are the only owner of it. We can then read or write from/to the
163/// mapped buffer, and then update the GPU buffer on the [`Drop`] implementation.
164pub struct BufferSliceMutWrapper<'a, const TARGET: u32>
165where
166  WebGL2State: BindBuffer<TARGET>,
167{
168  handle: &'a WebGlBuffer,
169  ptr: *mut u8,
170  bytes: usize,
171  state: Rc<RefCell<WebGL2State>>,
172}
173
174impl<const TARGET: u32> Drop for BufferSliceMutWrapper<'_, TARGET>
175where
176  WebGL2State: BindBuffer<TARGET>,
177{
178  fn drop(&mut self) {
179    let mut state = self.state.borrow_mut();
180    let _ = update_webgl_buffer::<TARGET>(&mut state, &self.handle, self.ptr, self.bytes, 0);
181  }
182}
183
184pub struct BufferSliceMut<'a, T, const TARGET: u32>
185where
186  WebGL2State: BindBuffer<TARGET>,
187{
188  raw: BufferSliceMutWrapper<'a, TARGET>,
189  _phantom: PhantomData<T>,
190}
191
192impl<'a, const TARGET: u32> BufferSliceMut<'a, u8, TARGET>
193where
194  WebGL2State: BindBuffer<TARGET>,
195{
196  /// Transmute to another type.
197  ///
198  /// This method is highly unsafe and should only be used when certain the target type is the
199  /// one actually represented by the raw bytes.
200  pub(crate) unsafe fn transmute<T>(self) -> BufferSliceMut<'a, T, TARGET> {
201    BufferSliceMut {
202      raw: self.raw,
203      _phantom: PhantomData,
204    }
205  }
206}
207
208impl<T, const TARGET: u32> Deref for BufferSliceMut<'_, T, TARGET>
209where
210  WebGL2State: BindBuffer<TARGET>,
211{
212  type Target = [T];
213
214  fn deref(&self) -> &Self::Target {
215    unsafe {
216      slice::from_raw_parts(
217        self.raw.ptr as *const T,
218        self.raw.bytes / mem::size_of::<T>(),
219      )
220    }
221  }
222}
223
224impl<T, const TARGET: u32> DerefMut for BufferSliceMut<'_, T, TARGET>
225where
226  WebGL2State: BindBuffer<TARGET>,
227{
228  fn deref_mut(&mut self) -> &mut Self::Target {
229    unsafe {
230      slice::from_raw_parts_mut(self.raw.ptr as *mut T, self.raw.bytes / mem::size_of::<T>())
231    }
232  }
233}
234
235pub trait BindBuffer<const TARGET: u32> {
236  fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind);
237}
238
239impl BindBuffer<{ WebGl2RenderingContext::ARRAY_BUFFER }> for WebGL2State {
240  fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind) {
241    self.bind_array_buffer(Some(handle), bind_mode);
242  }
243}
244
245impl BindBuffer<{ WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER }> for WebGL2State {
246  fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind) {
247    self.bind_element_array_buffer(Some(handle), bind_mode);
248  }
249}
250
251impl BindBuffer<{ WebGl2RenderingContext::UNIFORM_BUFFER }> for WebGL2State {
252  fn bind_buffer(&mut self, handle: &WebGlBuffer, bind: Bind) {
253    self.bind_uniform_buffer(Some(handle), bind);
254  }
255}
256
257/// Update a WebGL buffer by copying an input slice.
258fn update_webgl_buffer<const TARGET: u32>(
259  state: &mut WebGL2State,
260  handle: &WebGlBuffer,
261  data: *const u8,
262  bytes: usize,
263  offset: usize,
264) -> Result<(), BufferError>
265where
266  WebGL2State: BindBuffer<TARGET>,
267{
268  state.bind_buffer(handle, Bind::Cached);
269
270  let data = unsafe { slice::from_raw_parts(data as _, bytes) };
271  state
272    .ctx
273    .buffer_sub_data_with_i32_and_u8_array_and_src_offset(TARGET, offset as _, data, 0);
274
275  Ok(())
276}