use std::{
fmt,
hash::{Hash, Hasher},
ptr::NonNull,
sync::Arc,
};
use web_sys::{WebGl2RenderingContext as GL, WebGlBuffer};
use crate::{
generic::{
ArgumentKind, Automatic, BufferMappedRange, BufferMappedRangeMut, BufferRange, BufferUsage,
DeviceError, Storage, Uniform,
},
RenderCommandEncoder,
};
use super::arguments::ArgumentsField;
struct ShadowBuffer {
offset: usize,
size: usize,
ptr: NonNull<u8>,
}
impl ShadowBuffer {
fn new(offset: usize, size: usize) -> Self {
let ptr = Box::into_raw(Box::<[u8]>::new_zeroed_slice(size));
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut u8) };
ShadowBuffer { offset, size, ptr }
}
unsafe fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.size) }
}
unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.size) }
}
unsafe fn deallocate(self) {
unsafe {
Box::from_raw(std::ptr::slice_from_raw_parts_mut(
self.ptr.as_ptr(),
self.size,
))
};
}
}
struct Inner {
buffer: WebGlBuffer,
size: usize,
usage: BufferUsage,
gl: GL,
shadow: Option<ShadowBuffer>,
}
#[derive(Clone)]
pub struct Buffer {
inner: Arc<Inner>,
}
impl Drop for Inner {
fn drop(&mut self) {
if let Some(shadow) = self.shadow.take() {
unsafe { shadow.deallocate() };
}
self.gl.delete_buffer(Some(&self.buffer));
}
}
impl Buffer {
pub(super) fn new(
buffer: WebGlBuffer,
size: usize,
usage: BufferUsage,
gl: GL,
) -> Self {
Buffer {
inner: Arc::new(Inner {
buffer,
size,
usage,
gl,
shadow: None,
}),
}
}
fn detatched_inner(&mut self) -> &mut Inner {
Arc::get_mut(&mut self.inner).expect("Buffer must be detached to write to it")
}
pub(super) fn webgl(&self) -> &WebGlBuffer {
&self.inner.buffer
}
fn invalidate_range(inner: &mut Inner, offset: usize, size: usize) -> Result<(), DeviceError> {
debug_assert!(
i32::try_from(offset + size).is_ok(),
"Range exceeds i32::MAX"
);
let shadow = inner.shadow.as_mut().expect("Buffer is not mapped");
assert!(
offset >= shadow.offset && offset + size <= shadow.offset + shadow.size,
"Range is out of bounds of the mapped buffer"
);
let offset = offset - shadow.offset;
let shadow_bytes = unsafe { shadow.as_mut_slice() };
inner.gl.bind_buffer(GL::ARRAY_BUFFER, Some(&inner.buffer));
inner.gl.get_buffer_sub_data_with_i32_and_u8_array(
GL::ARRAY_BUFFER,
offset as i32,
&mut shadow_bytes[offset..][..size],
);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, None);
Ok(())
}
pub(crate) fn flush_range(&mut self, offset: usize, size: usize) -> Result<(), DeviceError> {
debug_assert!(
i32::try_from(offset + size).is_ok(),
"Range exceeds i32::MAX"
);
let inner = self.detatched_inner();
let shadow = inner.shadow.as_mut().expect("Buffer is not mapped");
assert!(
offset >= shadow.offset && offset + size <= shadow.offset + shadow.size,
"Range is out of bounds of the mapped buffer"
);
let offset = offset - shadow.offset;
let shadow_bytes = unsafe { shadow.as_slice() };
inner.gl.bind_buffer(GL::ARRAY_BUFFER, Some(&inner.buffer));
inner.gl.buffer_sub_data_with_i32_and_u8_array(
GL::ARRAY_BUFFER,
offset as i32,
&shadow_bytes[offset..][..size],
);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, None);
Ok(())
}
}
impl fmt::Debug for Buffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("Buffer")
.field("handle", &self.inner.buffer)
.field("size", &self.inner.size)
.field("usage", &self.inner.usage)
.finish()
} else {
f.debug_tuple("Buffer").field(&self.inner.buffer).finish()
}
}
}
impl Hash for Buffer {
fn hash<H: Hasher>(&self, state: &mut H) {
std::ptr::hash(&*self.inner, state);
}
}
impl PartialEq for Buffer {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(&*self.inner, &*other.inner)
}
}
impl Eq for Buffer {}
impl crate::traits::Resource for Buffer {}
#[hidden_trait::expose]
impl crate::traits::Buffer for Buffer {
#[inline(always)]
fn size(&self) -> usize {
self.inner.size
}
#[inline(always)]
fn usage(&self) -> BufferUsage {
self.inner.usage
}
#[inline(always)]
fn detached(&self) -> bool {
debug_assert_eq!(Arc::weak_count(&self.inner), 0, "No weak refs allowed");
Arc::strong_count(&self.inner) == 1
}
#[inline(always)]
fn map<R>(&mut self, range: R) -> Result<(), crate::DeviceError>
where
R: crate::generic::BufferRange,
{
let inner = self.detatched_inner();
assert!(inner.shadow.is_none(), "Buffer is already mapped");
let range = range.range(inner.size);
assert!(range.start <= range.end, "Invalid range");
assert!(
i32::try_from(range.end).is_ok(),
"Range end exceeds i32::MAX"
);
inner.shadow = Some(ShadowBuffer::new(range.start, range.end - range.start));
Ok(())
}
fn unmap(&mut self) {
let inner = self.detatched_inner();
if let Some(shadow) = inner.shadow.take() {
unsafe {
shadow.deallocate();
}
}
}
fn read_mapped_range<R>(&mut self, range: R) -> Result<BufferMappedRange<'_>, DeviceError>
where
R: BufferRange,
{
let inner: &mut Inner = self.detatched_inner();
let range = range.range(inner.size);
assert!(range.start <= range.end, "Invalid range");
assert!(
i32::try_from(range.end).is_ok(),
"Range end exceeds i32::MAX"
);
let offset = range.start;
let size = range.end - range.start;
Self::invalidate_range(inner, offset, size);
let shadow = inner.shadow.as_mut().unwrap();
let ptr = unsafe { shadow.ptr.add(offset) };
Ok(BufferMappedRange::new(self, ptr, offset, size))
}
fn write_mapped_range<R>(&mut self, range: R) -> Result<BufferMappedRangeMut<'_>, DeviceError>
where
R: BufferRange,
{
let inner: &mut Inner = self.detatched_inner();
let range = range.range(inner.size);
assert!(range.start <= range.end, "Invalid range");
assert!(
i32::try_from(range.end).is_ok(),
"Range end exceeds i32::MAX"
);
let offset = range.start;
let size = range.end - range.start;
Self::invalidate_range(inner, offset, size);
let shadow = inner.shadow.as_mut().unwrap();
let ptr = unsafe { shadow.ptr.add(offset) };
Ok(BufferMappedRangeMut::new(self, ptr, offset, size))
}
fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), DeviceError> {
let inner: &mut Inner = self.detatched_inner();
assert!(
offset + data.len() <= inner.size,
"Write range exceeds buffer size"
);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, Some(&inner.buffer));
inner
.gl
.buffer_sub_data_with_i32_and_u8_array(GL::ARRAY_BUFFER, offset as i32, data);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, None);
Ok(())
}
fn read(&mut self, offset: usize, data: &mut [u8]) -> Result<(), DeviceError> {
let inner: &mut Inner = self.detatched_inner();
assert!(
offset + data.len() <= inner.size,
"Read range exceeds buffer size"
);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, Some(&inner.buffer));
inner
.gl
.get_buffer_sub_data_with_i32_and_u8_array(GL::ARRAY_BUFFER, offset as i32, data);
inner.gl.bind_buffer(GL::ARRAY_BUFFER, None);
Ok(())
}
}
impl ArgumentsField<Automatic> for Buffer {
const KIND: ArgumentKind = ArgumentKind::UniformBuffer;
const SIZE: usize = 1;
fn bind_vertex(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
encoder.bind_buffer_base(GL::UNIFORM_BUFFER, slot, Some(self.inner.buffer.clone()));
}
fn bind_fragment(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
encoder.bind_buffer_base(GL::UNIFORM_BUFFER, slot, Some(self.inner.buffer.clone()));
}
}
impl ArgumentsField<Uniform> for Buffer {
const KIND: ArgumentKind = ArgumentKind::UniformBuffer;
const SIZE: usize = 1;
fn bind_vertex(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
encoder.bind_buffer_base(GL::UNIFORM_BUFFER, slot, Some(self.inner.buffer.clone()));
}
fn bind_fragment(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
encoder.bind_buffer_base(GL::UNIFORM_BUFFER, slot, Some(self.inner.buffer.clone()));
}
}
impl ArgumentsField<Storage> for Buffer {
const KIND: ArgumentKind = ArgumentKind::StorageBuffer;
const SIZE: usize = 1;
fn bind_vertex(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
unimplemented!("WebGL does not support storage buffers");
}
fn bind_fragment(&self, slot: u32, encoder: &mut RenderCommandEncoder) {
unimplemented!("WebGL does not support storage buffers");
}
}