diff --git a/src/buffer/dynamic.rs b/src/buffer/dynamic.rs new file mode 100644 index 00000000000..d866d4750a2 --- /dev/null +++ b/src/buffer/dynamic.rs @@ -0,0 +1,572 @@ +use backend::Facade; +use context::CommandContext; +use context::Context; +use version::Version; +use CapabilitiesSource; +use ContextExt; +use gl; +use std::error::Error; +use std::{fmt, mem, ptr}; +use std::cell::Cell; +use std::marker::PhantomData; +use std::rc::Rc; +use std::os::raw::c_void; +use std::ops::{Deref, DerefMut, Range}; +use GlObject; +use TransformFeedbackSessionExt; + +use buffer::{BufferType, BufferCreationError, CopyError, BufferAnySlice}; +use vertex::TransformFeedbackSession; +use vertex_array_object::VertexAttributesSystem; + +use version::Api; + +use buffer::raw; +use buffer::ArrayContent; +use buffer::Content; +use buffer::CopyTo; +use buffer::Create; +use buffer::Invalidate; +use buffer::Storage; + +/// +pub struct DynamicBuffer where T: Content { + marker: PhantomData, + + context: Rc, + + /// OpenGL identifier ; can't be zero. + id: gl::types::GLuint, + + /// Type of buffer. + ty: BufferType, + + /// The flag used when creating the buffer (eg. `STREAM_DRAW`, `STATIC_READ`, etc.). + usage: gl::types::GLenum, + + /// Size in bytes of the buffer. + size: usize, + + /// True if the buffer is currently mapped with something else than persistent mapping. + /// + /// The purpose of this flag is to detect if the user mem::forgets the `Mapping` object. + mapped: Cell, + + /// ID of the draw call where the buffer was last written as an SSBO. + latest_shader_write: Cell, +} + +impl DynamicBuffer where T: Content { + /// Builds a new buffer containing the given data. The size of the buffer is equal to the size + /// of the data. + pub fn new(facade: &F, data: &T, ty: BufferType, mode: BufferMode) + -> Result<$ty, BufferCreationError> + where F: Facade + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of_val(data); + let id = try!(unsafe { create_buffer(&mut ctxt, size, Some(data), ty, gl::STATIC_DRAW) }); + + Ok(DynamicBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + usage: gl::STATIC_DRAW, + size: size, + mapped: Cell::new(false), + latest_shader_write: Cell::new(0), + }) + } + + pub fn empty(facade: &F, ty: BufferType) -> Result, BufferCreationError> + where F: Facade, T: Sized, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of::(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty, gl::STATIC_DRAW) }); + + Ok(DynamicBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + usage: gl::STATIC_DRAW, + size: size, + mapped: Cell::new(false), + latest_shader_write: Cell::new(0), + }) + } + + pub fn empty_array(facade: &F, len: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: ArrayContent + { + let mut ctxt = facade.get_context().make_current(); + + let size = len * ::element_size(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty, gl::STATIC_DRAW) }); + + Ok(DynamicBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + usage: gl::STATIC_DRAW, + size: size, + mapped: Cell::new(false), + latest_shader_write: Cell::new(0), + }) + } + + pub fn empty_unsized(facade: &F, size: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty, gl::STATIC_DRAW) }); + + Ok(DynamicBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + usage: gl::STATIC_DRAW, + size: size, + mapped: Cell::new(false), + latest_shader_write: Cell::new(0), + }) + } + + /// Copies the content of the buffer to another buffer. + /// + /// # Panic + /// + /// Panics if `T` is unsized and the other buffer is too small. + /// + pub fn copy_to<'a, S>(&self, target: S) -> Result<(), CopyError> + where S: Into<$slice_ty<'a, T>>, T: 'a + { + let target = target.into(); + let alloc = self.alloc.as_ref().unwrap(); + + try!(alloc.copy_to(0 .. self.get_size(), &target.alloc, target.get_offset_bytes())); + + if let Some(inserter) = self.as_slice().add_fence() { + let mut ctxt = alloc.get_context().make_current(); + inserter.insert(&mut ctxt); + } + + if let Some(inserter) = target.add_fence() { + let mut ctxt = alloc.get_context().make_current(); + inserter.insert(&mut ctxt); + } + + Ok(()) + } + + /// Uploads data in the buffer. + /// + /// The data must fit inside the buffer. + /// + /// # Panic + /// + /// Panics if `T` is unsized and the data is not the same size as the buffer. + /// + pub fn upload(&self, data: &T) { + if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_direct_state_access { + ctxt.gl.NamedBufferData(self.id, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _, self.usage) + + } else if ctxt.extensions.gl_ext_direct_state_access { + ctxt.gl.NamedBufferDataEXT(self.id, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _, self.usage) + + } else if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferData(bind, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _, self.usage); + + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferDataARB(bind, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _, self.usage); + + } else { + unreachable!(); + } + } + + /// Implementation of the `upload` function meant to called by the `upload` functions + /// in the slices. + /// + /// # Panic + /// + /// Panics if `offset_bytes` is out of range or the data is too large to fit in the buffer. + /// + /// # Safety + /// + /// Type safety is not enforced. This is mostly the equivalent of `std::ptr::write`. + pub unsafe fn upload_impl(&self, offset_bytes: usize, data: &D) + where D: Content + { + assert!(offset_bytes + mem::size_of_val(data) <= self.size); + assert!(offset_bytes < self.size); + + let mut ctxt = self.context.make_current(); + self.barrier_for_buffer_update(&mut ctxt); + + let invalidate_all = offset_bytes == 0 && mem::size_of_val(data) == self.size; + + self.assert_unmapped(&mut ctxt); + self.assert_not_transform_feedback(&mut ctxt); + + if invalidate_all && (ctxt.version >= &Version(Api::Gl, 4, 3) || + ctxt.extensions.gl_arb_invalidate_subdata) + { + ctxt.gl.InvalidateBufferData(self.id); + } + + if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_direct_state_access { + ctxt.gl.NamedBufferSubData(self.id, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _) + + } else if ctxt.extensions.gl_ext_direct_state_access { + ctxt.gl.NamedBufferSubDataEXT(self.id, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _) + + } else if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferSubData(bind, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _); + + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferSubDataARB(bind, offset_bytes as gl::types::GLintptr, + mem::size_of_val(data) as gl::types::GLsizeiptr, + data.to_void_ptr() as *const _); + + } else { + unreachable!(); + } + } + + /// Invalidates the content of the buffer. The data becomes undefined. + /// + /// This is done by calling `glBufferData` with a null pointer for the data. + /// + /// # Panic + /// + /// Panics if out of range. + /// + pub fn invalidate(&self) { + let flags = match self.creation_mode { + BufferMode::Default | BufferMode::Immutable => gl::STATIC_DRAW, + BufferMode::Persistent | BufferMode::Dynamic => gl::DYNAMIC_DRAW, + }; + + if ctxt.version >= &Version(Api::Gl, 1, 5) || ctxt.version >= &Version(Api::GlEs, 2, 0) { + unsafe { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferData(bind, size as gl::types::GLsizeiptr, + ptr::null(), flags); + } + + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + unsafe { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.BufferDataARB(bind, size as gl::types::GLsizeiptr, + ptr::null(), flags); + } + + } else { + unreachable!(); + } + } + + /// Returns a read and write mapping in memory of the content of the buffer. + /// + /// # Panic + /// + /// Panicks if the `bytes_range` is not aligned to a mappable slice. + /// + /// # Unsafety + /// + /// If the buffer uses persistent mapping, the caller of this function must handle + /// synchronization. + /// + #[inline] + pub fn map(&mut self, bytes_range: Range) + -> Mapping where D: Content + { + self.map_impl(bytes_range, true, true) + } + + fn map_impl(&mut self, bytes_range: Range, read: bool, write: bool) + -> MappingImpl + where D: Content + { + let data = { + let mut ctxt = self.context.make_current(); + + let ptr = { + self.assert_unmapped(&mut ctxt); + self.assert_not_transform_feedback(&mut ctxt); + self.barrier_for_buffer_update(&mut ctxt); + let ptr = map_buffer(&mut ctxt, self.id, self.ty, bytes_range.clone(), + read, write) + .expect("Buffer mapping is not supported by the backend"); + self.mapped.set(true); + ptr + }; + + match Content::ref_from_ptr(ptr, bytes_range.end - bytes_range.start) { + Some(data) => data, + None => { + unmap_buffer(&mut ctxt, self.id, self.ty); + panic!("Wrong bytes range"); + } + } + }; + + Mapping { + buffer: self, + data: data, + needs_flushing: write, + } + } + + /// Reads the content of the buffer. + #[inline] + pub fn read(&self) -> Result { + self.read_impl::(0 .. self.size()) + } + + /// Implementation of the `read` function. Takes a range as parameter so that it can be called + /// as well from the `read` functions implemented on slices. + /// + /// # Panic + /// + /// Panicks if `range` is out of range. + /// + /// # Unsafe + /// + /// The caller must make sure that the content within `range` corresponds to an object of + /// type `D`. + unsafe fn read_impl(&self, range: Range) -> Result + where D: Content + { + let mut ctxt = self.context.make_current(); + + if ctxt.state.lost_context { + return Err(ReadError::ContextLost); + } + + self.assert_unmapped(&mut ctxt); + self.barrier_for_buffer_update(&mut ctxt); + + ::read(size_to_read, |output| { + if ctxt.version >= &Version(Api::Gl, 4, 5) { + ctxt.gl.GetNamedBufferSubData(self.id, range.start as gl::types::GLintptr, + size_to_read as gl::types::GLsizeiptr, + output as *mut _ as *mut _); + Ok(()) + + } else if ctxt.version >= &Version(Api::Gl, 1, 5) { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.GetBufferSubData(bind, range.start as gl::types::GLintptr, + size_to_read as gl::types::GLsizeiptr, + output as *mut _ as *mut _); + Ok(()) + + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + let bind = bind_buffer(&mut ctxt, self.id, self.ty); + ctxt.gl.GetBufferSubDataARB(bind, range.start as gl::types::GLintptr, + size_to_read as gl::types::GLsizeiptr, + output as *mut _ as *mut _); + Ok(()) + + } else if ctxt.version >= &Version(Api::GlEs, 1, 0) { + Err(ReadError::NotSupported); + + } else { + unreachable!() + } + }) + } + + /// Copies data from this buffer to another one. + /// + /// With persistent-mapped buffers you must create a sync fence *after* this operation. + /// + /// # Panic + /// + /// Panics if the offset/sizes are out of range. + /// + pub fn copy_to(&self, range: Range, target: &Alloc, dest_offset: usize) + -> Result<(), CopyError> + { + // TODO: read+write manually + // TODO: check that the other buffer belongs to the same context + + assert!(range.end >= range.start); + assert!(range.end <= self.size); + assert!(dest_offset + range.end - range.start <= target.size); + + let mut ctxt = self.context.make_current(); + + unsafe { + copy_buffer(&mut ctxt, self.id, range.start, target.id, dest_offset, + range.end - range.start) + } + } +} + +buffers_base!(DynamicBuffer, DynamicBufferSlice, DynamicBufferMutSlice); + +impl<'a, T: ?Sized> DynamicBufferSlice<'a, T> where T: Content { + + /// Uploads data in the buffer. + /// + /// The data must fit inside the buffer. + /// + /// # Panic + /// + /// Panics if `T` is unsized and the data is not the same size as the buffer. + /// + pub fn upload(&self, data: &T) { + } +} + +impl<'a, T: ?Sized> DynamicBufferMutSlice<'a, T> where T: Content { + pub fn map(&mut self) -> Mapping { + self.buffer.map_impl(self.bytes_start .. self.bytes_end, true, true) + } +} + +/// A mapping of a buffer for reading and writing. +pub struct Mapping<'a, T: ?Sized> { + context: Rc, + id: gl::types::GLuint, + ty: BufferType, + data: *mut D, + marker: PhantomData<&'a mut T>, + is_mapped: &'a mut Cell, +} + +unsafe impl<'a, T: ?Sized> Sync for Mapping<'a, T> where T: Send + Sync {} + +impl<'a, T: ?Sized> Deref for Mapping<'a, T> where T: Content { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &*data + } +} + +impl<'a, T: ?Sized> DerefMut for Mapping<'a, T> where T: Content { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *data } + } +} + +impl<'a, T: ?Sized> Drop for Mapping<'a, T> { + fn drop(&mut self) { + let mut ctxt = self.context.make_current(); + + unsafe { + raw::flush_range(&mut ctxt, self.id, self.ty, 0 .. mem::size_of_val(&*data)); + raw::unmap_buffer(&mut ctxt, self.id, self.ty); + } + + self.is_mapped.set(false); + } +} + +/// Creates a new buffer. +/// +/// # Panic +/// +/// Panics if `mem::size_of_val(&data) != size`. +unsafe fn create_buffer(mut ctxt: &mut CommandContext, size: usize, data: Option<&D>, + ty: BufferType, usage: gl::types::GLenum) + -> Result + where D: Content +{ + if !raw::is_buffer_type_supported(ctxt, ty) { + return Err(BufferCreationError::BufferTypeNotSupported); + } + + if let Some(data) = data { + assert!(mem::size_of_val(data) == size); + } + + // creating the id of the buffer + let id = raw::create_buffer_name(ctxt); + + // raw pointer to data + let data_ptr = if let Some(data) = data { + if size == 0 { // if the size is `0` we pass `1` instead (see below), + ptr::null() // so it's important to have `null` here + } else { + data.to_void_ptr() + } + } else { + ptr::null() + }; + + // if the `size` is 0 bytes then we use 1 instead, otherwise nvidia drivers complain + // note that according to glium the size of the buffer will remain 0 + let size = match size { + 0 => 1, + a => a + }; + + // will store the actual size of the buffer so that we can compare it with the expected size + let mut obtained_size: gl::types::GLint = mem::uninitialized(); + + if ctxt.version >= &Version(Api::Gl, 1, 5) || ctxt.version >= &Version(Api::GlEs, 2, 0) { + let bind = bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferData(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, usage); + ctxt.gl.GetBufferParameteriv(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + let bind = bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferDataARB(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, usage); + ctxt.gl.GetBufferParameterivARB(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else { + unreachable!(); + } + + if size != obtained_size as usize { + if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + ctxt.gl.DeleteBuffers(1, [id].as_ptr()); + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + ctxt.gl.DeleteBuffersARB(1, [id].as_ptr()); + } else { + unreachable!(); + } + + return Err(BufferCreationError::OutOfMemory); + } + + Ok(id) +} diff --git a/src/buffer/immutable.rs b/src/buffer/immutable.rs new file mode 100644 index 00000000000..05f603eda2a --- /dev/null +++ b/src/buffer/immutable.rs @@ -0,0 +1,355 @@ +use backend::Facade; +use context::CommandContext; +use context::Context; +use version::Version; +use CapabilitiesSource; +use ContextExt; +use gl; +use std::error::Error; +use std::{fmt, mem, ptr}; +use std::cell::Cell; +use std::marker::PhantomData; +use std::rc::Rc; +use std::ops::{Deref, DerefMut, Range}; +use GlObject; +use TransformFeedbackSessionExt; + +use buffer::{BufferType, BufferCreationError, CopyError, BufferAnySlice}; +use vertex::TransformFeedbackSession; +use vertex_array_object::VertexAttributesSystem; + +use version::Api; + +use buffer::raw; +use buffer::ArrayContent; +use buffer::Content; +use buffer::CopyTo; +use buffer::Create; +use buffer::Invalidate; +use buffer::Storage; + +/// A buffer whose content is inaccessible from the CPU. In other words, whose content is +/// inaccessible from your application. +/// +/// The fact that the data is not accessible often gives a performance boost, for two reasons: +/// +/// 1) The driver doesn't have to worry about synchronization, as everything that touches the +/// buffer executes serially. +/// 2) The data can be stored in memory that is inaccessible from the CPU. The GPU can usually read +/// faster from this kind of memory. +/// +/// There is no way to modify an immutable buffer's content, except by copying data from another +/// buffer or by issuing rendering commands. +pub struct ImmutableBuffer where T: Content { + marker: PhantomData, + + context: Rc, + + /// OpenGL identifier ; can't be zero. + id: gl::types::GLuint, + + /// Type of buffer. + ty: BufferType, + + /// Size in bytes of the buffer. + size: usize, +} + +impl ImmutableBuffer where T: Content { + pub fn new(facade: &F, data: &T, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of_val(data); + let id = try!(unsafe { create_buffer(&mut ctxt, size, Some(data), ty) }); + + Ok(ImmutableBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + }) + } + + pub fn empty(facade: &F, ty: BufferType) -> Result, BufferCreationError> + where F: Facade, T: Sized, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of::(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(ImmutableBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + }) + } + + pub fn empty_array(facade: &F, len: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: ArrayContent + { + let mut ctxt = facade.get_context().make_current(); + + let size = len * ::element_size(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(ImmutableBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + }) + } + + pub fn empty_unsized(facade: &F, size: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + let id = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(ImmutableBuffer { + marker: PhantomData, + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + }) + } + + /// See the `CopyTo` trait. + pub fn copy_to(&self, target: &S) -> Result<(), CopyError> + where S: Storage + { + // TODO: check that the other buffer belongs to the same context + let mut ctxt = self.context.make_current(); + + unimplemented!() + /*unsafe { + raw::copy_buffer(&mut ctxt, self.id, range.start, target.id, dest_offset, + range.end - range.start) + }*/ + } + + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferData(self.id); + } + } + } +} + +impl Drop for ImmutableBuffer where T: Content { + fn drop(&mut self) { + unsafe { + let mut ctxt = self.context.make_current(); + unimplemented!();//self.assert_not_transform_feedback(&mut ctxt); // TODO: + VertexAttributesSystem::purge_buffer(&mut ctxt, self.id); + raw::destroy_buffer(&mut ctxt, self.id); + } + } +} + +impl<'a, T: ?Sized> ImmutableBufferSlice<'a, T> where T: Content { + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferSubData(self.buffer, self.bytes_start as gl::types::GLintptr, + self.size() as gl::types::GLsizeiptr); + } + } + } +} + +impl<'a, T: ?Sized> ImmutableBufferMutSlice<'a, T> where T: Content { + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferSubData(self.buffer, self.bytes_start as gl::types::GLintptr, + self.size() as gl::types::GLsizeiptr); + } + } + } +} + +impl Storage for ImmutableBuffer where T: Content { + type Content = T; + + fn as_slice_any(&self) -> BufferAnySlice { + unimplemented!() + } + + #[inline] + fn size(&self) -> usize { + self.size + } +} + +impl Invalidate for ImmutableBuffer where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl<'a, T: ?Sized + 'a> Invalidate for ImmutableBufferSlice<'a, T> where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl<'a, T: ?Sized + 'a> Invalidate for ImmutableBufferMutSlice<'a, T> where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl Create for ImmutableBuffer where T: Content { + #[inline] + fn new(facade: &F, data: &T, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade + { + ImmutableBuffer::new(facade, data, ty) + } + + #[inline] + fn empty(facade: &F, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + ImmutableBuffer::empty(facade, ty) + } + + #[inline] + fn empty_array(facade: &F, len: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: ArrayContent + { + ImmutableBuffer::empty_array(facade, len, ty) + } + + #[inline] + fn empty_unsized(facade: &F, size: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + ImmutableBuffer::empty_unsized(facade, size, ty) + } +} + +impl CopyTo for ImmutableBuffer where T: Content { + #[inline] + fn copy_to(&self, target: &S) -> Result<(), CopyError> + where S: Storage + { + self.copy_to(target) + } +} + +buffers_base!(ImmutableBuffer, ImmutableBufferSlice, ImmutableBufferMutSlice); + +/// Creates a new buffer. +/// +/// # Panic +/// +/// Panics if `mem::size_of_val(&data) != size`. +unsafe fn create_buffer(mut ctxt: &mut CommandContext, size: usize, data: Option<&D>, + ty: BufferType) -> Result + where D: Content +{ + if !raw::is_buffer_type_supported(ctxt, ty) { + return Err(BufferCreationError::BufferTypeNotSupported); + } + + if let Some(data) = data { + assert!(mem::size_of_val(data) == size); + } + + // creating the id of the buffer + let id = raw::create_buffer_name(ctxt); + + // raw pointer to data + let data_ptr = if let Some(data) = data { + if size == 0 { // if the size is `0` we pass `1` instead (see below), + ptr::null() // so it's important to have `null` here + } else { + data.to_void_ptr() + } + } else { + ptr::null() + }; + + // if the `size` is 0 bytes then we use 1 instead, otherwise nvidia drivers complain + // note that according to glium the size of the buffer will remain 0 + let size = match size { + 0 => 1, + a => a + }; + + // will store the actual size of the buffer so that we can compare it with the expected size + let mut obtained_size: gl::types::GLint = mem::uninitialized(); + + if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_direct_state_access { + ctxt.gl.NamedBufferStorage(id, size as gl::types::GLsizeiptr, data_ptr as *const _, 0); + ctxt.gl.GetNamedBufferParameteriv(id, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.extensions.gl_arb_buffer_storage && + ctxt.extensions.gl_ext_direct_state_access + { + ctxt.gl.NamedBufferStorageEXT(id, size as gl::types::GLsizeiptr, data_ptr as *const _, 0); + ctxt.gl.GetNamedBufferParameterivEXT(id, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.version >= &Version(Api::Gl, 4, 4) || + ctxt.extensions.gl_arb_buffer_storage + { + let bind = raw::bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferStorage(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, 0); + ctxt.gl.GetBufferParameteriv(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.extensions.gl_ext_buffer_storage { + let bind = raw::bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferStorageEXT(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, 0); + ctxt.gl.GetBufferParameteriv(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else { + // FIXME: return error instead + panic!() + } + + if size != obtained_size as usize { + if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + ctxt.gl.DeleteBuffers(1, [id].as_ptr()); + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + ctxt.gl.DeleteBuffersARB(1, [id].as_ptr()); + } else { + unreachable!(); + } + + return Err(BufferCreationError::OutOfMemory); + } + + Ok(id) +} diff --git a/src/buffer/lock.rs b/src/buffer/lock.rs new file mode 100644 index 00000000000..5c769821fbf --- /dev/null +++ b/src/buffer/lock.rs @@ -0,0 +1,16 @@ +use sync::SyncFence; + +pub struct GlobalFence { + inner: T, + fence: Option, +} + +impl GlobalFence { + #[inline] + pub fn new(inner: T) -> GlobalFence { + GlobalFence { + inner: inner, + fence: None, + } + } +} diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 8c1d6ceda61..28e9305e215 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -5,24 +5,26 @@ //! //! # Buffers management in glium //! -//! There are three levels of abstraction in glium: -//! -//! - An `Alloc` corresponds to an OpenGL buffer object and is unsafe to use. -//! This type is not public. -//! - A `Buffer` wraps around an `Alloc` and provides safety by handling the data type and fences. -//! - The `VertexBuffer`, `IndexBuffer`, `UniformBuffer`, `PixelBuffer`, etc. types are -//! abstractions over a `Buffer` indicating their specific purpose. They implement `Deref` -//! for the `Buffer`. These types are in the `vertex`, `index`, etc. modules. +//! There are three types of buffers in glium: +//! +//! - Dynamic buffers, which are the buffers that you usually use by default. All OpenGL tutorials +//! that you find on the web use these kinds of buffers. +//! - Immutable buffers, whose content is inaccessible from the CPU. They usually offer slightly +//! better performances when the buffer's content never needs to be modified. +//! - Persistent buffers (short for "persistent-mapped buffers"). They are more difficult to use +//! but offer the best control over what's happening and are ideal when you are often modifying +//! the content of the buffer. //! //! # Unsized types //! //! In order to put some data in a buffer, it must implement the `Content` trait. This trait is //! automatically implemented on all `Sized` types and on slices (like `[u8]`). This means that -//! you can create a `Buffer` (if `Foo` is sized) or a `Buffer<[u8]>` for example without -//! worrying about it. +//! you can for example create a `Buffer` (if `Foo` is sized) or a `Buffer<[u8]>` without +//! having to worry about this. //! -//! However unsized structs don't automatically implement this trait and you must call the -//! `implement_buffer_content!` macro on them. You must then use the `empty_unsized` constructor. +//! However if you create an unsized struct, it doesn't automatically implement this trait. You can +//! solve this by manually calling the `implement_buffer_content!` macro on them. You must then +//! use the `empty_unsized` constructor on buffers in order to store their content. //! //! ```no_run //! # #[macro_use] extern crate glium; fn main() { @@ -37,7 +39,7 @@ //! //! // creates a buffer of 64 bytes, which thus holds 8 f32s //! let mut buffer = glium::buffer::Buffer::::empty_unsized(&display, BufferType::UniformBuffer, -//! 64, BufferMode::Default).unwrap(); +//! 64).unwrap(); //! //! // you can then write to it like you normally would //! buffer.map().data[4] = 2.1; @@ -53,8 +55,8 @@ use texture::Texture1dDataSink; pub use self::view::{Buffer, BufferSlice, BufferMutSlice}; pub use self::view::{DynamicBuffer, DynamicBufferSlice, DynamicBufferMutSlice}; -pub use self::view::{ImmutableBuffer, ImmutableBufferSlice, ImmutableBufferMutSlice}; -pub use self::view::{PersistentBuffer, PersistentBufferSlice, PersistentBufferMutSlice}; +pub use self::immutable::{ImmutableBuffer, ImmutableBufferSlice, ImmutableBufferMutSlice}; +pub use self::persistent::{PersistentBuffer, PersistentBufferSlice, PersistentBufferMutSlice}; pub use self::view::{BufferAny, BufferAnySlice}; pub use self::alloc::{Mapping, WriteMapping, ReadMapping, ReadError}; pub use self::alloc::{is_buffer_read_supported}; @@ -79,8 +81,537 @@ use std::slice; use backend::Facade; +macro_rules! buffers_base { + ($buffer:ident, $slice:ident, $mut_slice:ident) => ( + impl $buffer where T: Content { + /// Returns the size in bytes of the buffer. + #[inline] + pub fn size(&self) -> usize { + ::buffer::Storage::size(self) + } + + /// Returns the number of elements in this buffer. + /// + /// This method is only available if the content is an array. + #[inline] + pub fn len(&self) -> usize where T: ArrayContent { + self.size() / ::element_size() + } + + /// Builds a subslice of this slice. Returns `None` if out of range. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + /// + /// # Example + /// + /// ```no_run + /// # let buffer: glium::buffer::ImmutableBuffer<[u8]> = unsafe { ::std::mem::uninitialized() }; + /// // let buffer: glium::buffer::ImmutableBuffer<[u8]>; + /// let slice = buffer.slice(0 .. 8).unwrap(); + /// slice.invalidate(); // invalidates only elements 0 to 8 + /// ``` + #[inline] + pub fn slice(&self, range: R) -> Option<$slice> + where T: ArrayContent, + R: ::utils::range::RangeArgument + { + self.as_slice().slice(range) + } + + /// Builds a subslice of this slice. Returns `None` if out of range. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn slice_mut(&mut self, range: R) -> Option<$mut_slice> + where T: ArrayContent, + R: ::utils::range::RangeArgument + { + self.as_mut_slice().slice(range) + } + + /// Builds a slice that contains an element from inside the buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + /// + /// # Example + /// + /// ```no_run + /// #[derive(Copy, Clone)] + /// struct BufferContent { + /// value1: u16, + /// value2: u16, + /// } + /// # let buffer: glium::buffer::BufferSlice = + /// # unsafe { std::mem::uninitialized() }; + /// let slice = unsafe { buffer.slice_custom(|content| &content.value2) }; + /// ``` + /// + /// # Safety + /// + /// The object whose reference is passed to the closure is uninitialized. Therefore you + /// **must not** access the content of the object. + /// + /// You **must** return a reference to an element from the parameter. The closure **must not** + /// panic. + #[inline] + pub unsafe fn slice_custom(&self, f: F) -> $slice + where F: for<'r> FnOnce(&'r T) -> &'r R, + R: Content + { + self.as_slice().slice_custom(f) + } + + /// Same as `slice_custom` but returns a mutable slice. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub unsafe fn slice_custom_mut(&mut self, f: F) -> $mut_slice + where F: for<'r> FnOnce(&'r T) -> &'r R, + R: Content + { + self.as_mut_slice().slice_custom(f) + } + + /// Builds a slice representing the whole buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn as_slice(&self) -> $slice { + $slice { + context: &self.context, + buffer: self.id, + bytes_start: 0, + bytes_end: self.size(), + marker: ::std::marker::PhantomData, + } + } + + /// Builds a slice representing the whole buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn as_mut_slice(&mut self) -> $mut_slice { + let size = self.size(); + + $mut_slice { + context: &mut self.context, + buffer: self.id, + bytes_start: 0, + bytes_end: size, + marker: ::std::marker::PhantomData, + } + } + } + + impl ::std::fmt::Debug for $buffer where T: Content { + #[inline] + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + write!(fmt, "OpenGL buffer (len: {} bytes)", self.size()) + } + } + + #[derive(Copy, Clone)] + pub struct $slice<'a, T: ?Sized + 'a> where T: Content { + context: &'a Rc, + buffer: gl::types::GLuint, + bytes_start: usize, + bytes_end: usize, + marker: ::std::marker::PhantomData, + } + + // TODO: we need this because PhantomData requires T: Sized + impl<'a, T: ?Sized> ::std::fmt::Debug for $slice<'a, T> where T: Content { + #[inline] + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + write!(fmt, "OpenGL buffer slice (len: {} bytes)", self.size()) + } + } + + impl<'a, T: ?Sized> $slice<'a, T> where T: Content + 'a { + /// Returns the size in bytes of this slice. + #[inline] + pub fn size(&self) -> usize { + self.bytes_end - self.bytes_start + } + + /// Builds a slice that contains an element from inside the buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + /// + /// # Example + /// + /// ```no_run + /// #[derive(Copy, Clone)] + /// struct BufferContent { + /// value1: u16, + /// value2: u16, + /// } + /// # let buffer: glium::buffer::BufferSlice = + /// # unsafe { std::mem::uninitialized() }; + /// let slice = unsafe { buffer.slice_custom(|content| &content.value2) }; + /// ``` + /// + /// # Safety + /// + /// The object whose reference is passed to the closure is uninitialized. Therefore you + /// **must not** access the content of the object. + /// + /// You **must** return a reference to an element from the parameter. The closure **must not** + /// panic. + #[inline] + pub unsafe fn slice_custom(&self, f: F) -> $slice<'a, R> + where F: for<'r> FnOnce(&'r T) -> &'r R, + R: Content + { + let data: &T = mem::zeroed(); + let result = f(data); + let size = mem::size_of_val(result); + let result = result as *const R as *const () as usize; + + assert!(result <= self.size()); + assert!(result + size <= self.size()); + + $slice { + context: self.context, + buffer: self.buffer, + bytes_start: self.bytes_start + result, + bytes_end: self.bytes_start + result + size, + marker: ::std::marker::PhantomData, + } + } + + /// Builds a slice-any containing the whole subbuffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn as_slice_any(&self) -> BufferAnySlice<'a> { + unimplemented!() + /*BufferAnySlice { + alloc: self.alloc, + bytes_start: self.bytes_start, + bytes_end: self.bytes_end, + elements_size: ::get_elements_size(), + }*/ + } + + /// Returns the number of elements in this slice. + #[inline] + pub fn len(&self) -> usize where T: ArrayContent { + (self.bytes_end - self.bytes_start) / ::element_size() + } + + /// Builds a subslice of this slice. Returns `None` if out of range. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn slice(&self, range: R) -> Option<$slice<'a, T>> + where T: ArrayContent, + R: ::utils::range::RangeArgument + { + if range.start().map_or(0, |e| *e) > self.len() || range.end().map_or(0, |e| *e) > self.len() { + return None; + } + + unimplemented!() + /*Some($slice { + buffer: self.buffer, + bytes_start: self.bytes_start + range.start().map_or(0, |e| *e) * ::element_size(), + bytes_end: self.bytes_start + range.end().map_or(self.len(), |e| *e) * ::element_size(), + })*/ + } + } + + pub struct $mut_slice<'a, T: ?Sized + 'a> where T: Content { + context: &'a mut Rc, + buffer: gl::types::GLuint, + bytes_start: usize, + bytes_end: usize, + marker: ::std::marker::PhantomData, + } + + // TODO: we need this because PhantomData requires T: Sized + impl<'a, T: ?Sized> ::std::fmt::Debug for $mut_slice<'a, T> where T: Content { + #[inline] + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + write!(fmt, "OpenGL buffer slice (len: {} bytes)", self.size()) + } + } + + impl<'a, T: ?Sized> $mut_slice<'a, T> where T: Content + 'a { + /// Returns the size in bytes of this slice. + #[inline] + pub fn size(&self) -> usize { + self.bytes_end - self.bytes_start + } + + /// Builds a slice that contains an element from inside the buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + /// + /// # Example + /// + /// ```no_run + /// #[derive(Copy, Clone)] + /// struct BufferContent { + /// value1: u16, + /// value2: u16, + /// } + /// # let buffer: glium::buffer::BufferSlice = + /// # unsafe { std::mem::uninitialized() }; + /// let slice = unsafe { buffer.slice_custom(|content| &content.value2) }; + /// ``` + /// + /// # Safety + /// + /// The object whose reference is passed to the closure is uninitialized. Therefore you + /// **must not** access the content of the object. + /// + /// You **must** return a reference to an element from the parameter. The closure **must not** + /// panic. + #[inline] + pub unsafe fn slice_custom(self, f: F) -> $mut_slice<'a, R> + where F: for<'r> FnOnce(&'r T) -> &'r R, + R: Content + { + let data: &T = mem::zeroed(); + let result = f(data); + let size = mem::size_of_val(result); + let result = result as *const R as *const () as usize; + + assert!(result <= self.size()); + assert!(result + size <= self.size()); + + unimplemented!() + /*$mut_slice { + buffer: self.buffer, + bytes_start: self.bytes_start + result, + bytes_end: self.bytes_start + result + size, + }*/ + } + + /// Builds a slice-any containing the whole subbuffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn as_slice_any(self) -> BufferAnySlice<'a> { + unimplemented!() + /*BufferAnySlice { + alloc: self.alloc, + bytes_start: self.bytes_start, + bytes_end: self.bytes_end, + elements_size: ::get_elements_size(), + }*/ + } + + /// Returns the number of elements in this slice. + #[inline] + pub fn len(&self) -> usize where T: ArrayContent { + (self.bytes_end - self.bytes_start) / ::element_size() + } + + /// Builds a subslice of this slice. Returns `None` if out of range. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// OpenGL is performed. + #[inline] + pub fn slice(self, range: R) -> Option<$mut_slice<'a, T>> + where T: ArrayContent, + R: ::utils::range::RangeArgument + { + if range.start().map_or(0, |e| *e) > self.len() || range.end().map_or(0, |e| *e) > self.len() { + return None; + } + + let new_start = self.bytes_start + range.start().map_or(0, |e| *e) * ::element_size(); + let new_end = self.bytes_start + range.end().map_or(self.len(), |e| *e) * ::element_size(); + + Some($mut_slice { + context: self.context, + buffer: self.buffer, + bytes_start: new_start, + bytes_end: new_end, + marker: ::std::marker::PhantomData, + }) + } + } + + impl<'a, T> ::buffer::Slice for &'a $buffer where T: Content { + type Slice = $slice<'a, T>; + + fn as_slice(self) -> $slice<'a, T> { + self.as_slice() + } + + fn slice(self, range: R) -> Option<$slice<'a, T>> + where T: ::buffer::ArrayContent, + R: ::utils::range::RangeArgument + { + self.slice(range) + } + } + + impl<'a, T> ::buffer::Slice for &'a mut $buffer where T: Content { + type Slice = $slice<'a, T>; + + fn as_slice(self) -> $slice<'a, T> { + self.as_slice() + } + + fn slice(self, range: R) -> Option<$slice<'a, T>> + where T: ::buffer::ArrayContent, + R: ::utils::range::RangeArgument + { + self.slice(range) + } + } + + impl<'a, T> ::buffer::SliceMut for &'a mut $buffer where T: Content { + type SliceMut = $mut_slice<'a, T>; + + fn as_mut_slice(self) -> $mut_slice<'a, T> { + self.as_mut_slice() + } + + fn slice_mut(self, range: R) -> Option<$mut_slice<'a, T>> + where T: ::buffer::ArrayContent, + R: ::utils::range::RangeArgument + { + self.slice_mut(range) + } + } + + impl<'a, T, R: ?Sized> ::buffer::SliceCustom for &'a $buffer + where T: Content, R: Content + { + type Slice = $slice<'a, R>; + + #[inline] + unsafe fn slice_custom(self, f: F) -> Self::Slice + where F: for<'r> FnOnce(&'r Self::Content) -> &'r R, Self: Sized + { + self.slice_custom(f) + } + } + + impl<'a, T, R: ?Sized> ::buffer::SliceCustom for &'a mut $buffer + where T: Content, R: Content + { + type Slice = $slice<'a, R>; + + #[inline] + unsafe fn slice_custom(self, f: F) -> Self::Slice + where F: for<'r> FnOnce(&'r Self::Content) -> &'r R, Self: Sized + { + self.slice_custom(f) + } + } + + impl<'a, T, R: ?Sized> ::buffer::SliceCustomMut for &'a mut $buffer + where T: Content, R: Content + { + type SliceMut = $mut_slice<'a, R>; + + #[inline] + unsafe fn slice_custom_mut(self, f: F) -> Self::SliceMut + where F: for<'r> FnOnce(&'r Self::Content) -> &'r R, Self: Sized + { + self.slice_custom_mut(f) + } + } + + impl<'a, T: ?Sized> From<&'a $buffer> for ::buffer::BufferAnySlice<'a> + where T: ::buffer::Content + { + #[inline] + fn from(buf: &'a $buffer) -> ::buffer::BufferAnySlice<'a> { + buf.as_slice_any() + } + } + + impl<'a, T: ?Sized> From<$slice<'a, T>> for ::buffer::BufferAnySlice<'a> + where T: ::buffer::Content + { + #[inline] + fn from(buf: $slice<'a, T>) -> ::buffer::BufferAnySlice<'a> { + buf.as_slice_any() + } + } + + impl<'a, T: ?Sized> Storage for &'a $buffer where T: Content { + type Content = T; + + #[inline] + fn size(&self) -> usize { + (*self).size() + } + + #[inline] + fn as_slice_any(&self) -> BufferAnySlice { + (*self).as_slice_any() + } + } + + impl<'a, T: ?Sized> Storage for &'a mut $buffer where T: Content { + type Content = T; + + #[inline] + fn size(&self) -> usize { + (*self).size() + } + + #[inline] + fn as_slice_any(&self) -> BufferAnySlice { + (*self).as_slice_any() + } + } + + impl<'a, T: ?Sized> Storage for $slice<'a, T> where T: Content { + type Content = T; + + #[inline] + fn size(&self) -> usize { + self.bytes_end - self.bytes_start + } + + #[inline] + fn as_slice_any(&self) -> BufferAnySlice { + self.as_slice_any() + } + } + + impl<'a, T: ?Sized> Storage for $mut_slice<'a, T> where T: Content { + type Content = T; + + #[inline] + fn size(&self) -> usize { + self.bytes_end - self.bytes_start + } + + #[inline] + fn as_slice_any(&self) -> BufferAnySlice { + self.as_slice_any() + } + } + ); +} + mod alloc; +//mod dynamic; mod fences; +mod immutable; +//mod lock; +mod persistent; mod raw; mod view; diff --git a/src/buffer/persistent.rs b/src/buffer/persistent.rs new file mode 100644 index 00000000000..28bc8e3c434 --- /dev/null +++ b/src/buffer/persistent.rs @@ -0,0 +1,387 @@ +use backend::Facade; +use context::CommandContext; +use context::Context; +use version::Version; +use CapabilitiesSource; +use ContextExt; +use gl; +use std::error::Error; +use std::{fmt, mem, ptr}; +use std::cell::Cell; +use std::marker::PhantomData; +use std::rc::Rc; +use std::os::raw::c_void; +use std::ops::{Deref, DerefMut, Range}; +use GlObject; +use TransformFeedbackSessionExt; + +use buffer::{BufferType, BufferCreationError, CopyError, BufferAnySlice}; +use vertex::TransformFeedbackSession; +use vertex_array_object::VertexAttributesSystem; + +use version::Api; + +use buffer::raw; +use buffer::ArrayContent; +use buffer::Content; +use buffer::CopyTo; +use buffer::Create; +use buffer::Invalidate; +use buffer::Storage; + +/// A buffer whose content is always accessible from the CPU. +/// +/// Care must be taken that the application doesn't read the content of the buffer while the GPU +/// is writing it, or writes to the buffer while the GPU is reading or writing it. Therefore +/// accessing the content of the buffer is marked as `unsafe`. +/// +/// In order to safely access the content of the buffer, it is highly recommended to wrap around +/// the `PersistentBuffer` with one of the locking strategies: TODO INSERT LIST. +pub struct PersistentBuffer where T: Content { + context: Rc, + + /// OpenGL identifier ; can't be zero. + id: gl::types::GLuint, + + /// Type of buffer. + ty: BufferType, + + /// Size in bytes of the buffer. + size: usize, + + /// Pointer to the content of the buffer. + mapping: *mut T, +} + +impl PersistentBuffer where T: Content { + pub fn new(facade: &F, data: &T, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of_val(data); + let (id, ptr) = try!(unsafe { create_buffer(&mut ctxt, size, Some(data), ty) }); + + Ok(PersistentBuffer { + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + mapping: T::ref_from_ptr(ptr as *mut _, size).expect("Error while creating PMB"), + }) + } + + pub fn empty(facade: &F, ty: BufferType) -> Result, BufferCreationError> + where F: Facade, T: Sized, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + + let size = mem::size_of::(); + let (id, ptr) = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(PersistentBuffer { + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + mapping: T::ref_from_ptr(ptr as *mut _, size).expect("Error while creating PMB"), + }) + } + + pub fn empty_array(facade: &F, len: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: ArrayContent + { + let mut ctxt = facade.get_context().make_current(); + + let size = len * ::element_size(); + let (id, ptr) = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(PersistentBuffer { + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + mapping: T::ref_from_ptr(ptr as *mut _, size).expect("Error while creating PMB"), + }) + } + + pub fn empty_unsized(facade: &F, size: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + let mut ctxt = facade.get_context().make_current(); + let (id, ptr) = try!(unsafe { create_buffer::<()>(&mut ctxt, size, None, ty) }); + + Ok(PersistentBuffer { + context: facade.get_context().clone(), + id: id, + ty: ty, + size: size, + mapping: T::ref_from_ptr(ptr as *mut _, size).expect("Error while creating PMB"), + }) + } + + /// Accesses the content of the buffer. + #[inline] + pub unsafe fn access<'a>(&'a mut self) -> &'a mut T { + &mut *self.mapping + } + + /// See the `CopyTo` trait. + pub fn copy_to(&self, target: &S) -> Result<(), CopyError> + where S: Storage + { + // TODO: check that the other buffer belongs to the same context + let mut ctxt = self.context.make_current(); + + unimplemented!() + /*unsafe { + raw::copy_buffer(&mut ctxt, self.id, range.start, target.id, dest_offset, + range.end - range.start) + }*/ + } + + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferData(self.id); + } + } + } +} + +impl Drop for PersistentBuffer where T: Content { + fn drop(&mut self) { + unsafe { + let mut ctxt = self.context.make_current(); + unimplemented!();//self.assert_not_transform_feedback(&mut ctxt); // TODO: + VertexAttributesSystem::purge_buffer(&mut ctxt, self.id); + raw::destroy_buffer(&mut ctxt, self.id); + } + } +} + +impl<'a, T: ?Sized> PersistentBufferSlice<'a, T> where T: Content { + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferSubData(self.buffer, self.bytes_start as gl::types::GLintptr, + self.size() as gl::types::GLsizeiptr); + } + } + } +} + +impl<'a, T: ?Sized> PersistentBufferMutSlice<'a, T> where T: Content { + /// See the `Invalidate` trait. + pub fn invalidate(&self) { + let mut ctxt = self.context.make_current(); + + if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.extensions.gl_arb_invalidate_subdata { + unsafe { + ctxt.gl.InvalidateBufferSubData(self.buffer, self.bytes_start as gl::types::GLintptr, + self.size() as gl::types::GLsizeiptr); + } + } + } +} + +// TODO: should this be implemented? what if `gpu_access` is called and then the buffer is passed to +// a lock constructor? +impl Storage for PersistentBuffer where T: Content { + type Content = T; + + fn as_slice_any(&self) -> BufferAnySlice { + unimplemented!() + } + + #[inline] + fn size(&self) -> usize { + self.size + } +} + +impl Invalidate for PersistentBuffer where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl<'a, T: ?Sized + 'a> Invalidate for PersistentBufferSlice<'a, T> where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl<'a, T: ?Sized + 'a> Invalidate for PersistentBufferMutSlice<'a, T> where T: Content { + #[inline] + fn invalidate(&self) { + self.invalidate() + } +} + +impl Create for PersistentBuffer where T: Content { + #[inline] + fn new(facade: &F, data: &T, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade + { + PersistentBuffer::new(facade, data, ty) + } + + #[inline] + fn empty(facade: &F, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + PersistentBuffer::empty(facade, ty) + } + + #[inline] + fn empty_array(facade: &F, len: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: ArrayContent + { + PersistentBuffer::empty_array(facade, len, ty) + } + + #[inline] + fn empty_unsized(facade: &F, size: usize, ty: BufferType) + -> Result, BufferCreationError> + where F: Facade, T: Copy + { + PersistentBuffer::empty_unsized(facade, size, ty) + } +} + +impl CopyTo for PersistentBuffer where T: Content { + #[inline] + fn copy_to(&self, target: &S) -> Result<(), CopyError> + where S: Storage + { + self.copy_to(target) + } +} + +buffers_base!(PersistentBuffer, PersistentBufferSlice, PersistentBufferMutSlice); + +/// Creates a new buffer. +/// +/// # Panic +/// +/// Panics if `mem::size_of_val(&data) != size`. +unsafe fn create_buffer(mut ctxt: &mut CommandContext, size: usize, data: Option<&D>, + ty: BufferType) -> Result<(gl::types::GLuint, + *mut c_void), BufferCreationError> + where D: Content +{ + if !raw::is_buffer_type_supported(ctxt, ty) { + return Err(BufferCreationError::BufferTypeNotSupported); + } + + if let Some(data) = data { + assert!(mem::size_of_val(data) == size); + } + + // creating the id of the buffer + let id = raw::create_buffer_name(ctxt); + + // raw pointer to data + let data_ptr = if let Some(data) = data { + if size == 0 { // if the size is `0` we pass `1` instead (see below), + ptr::null() // so it's important to have `null` here + } else { + data.to_void_ptr() + } + } else { + ptr::null() + }; + + // if the `size` is 0 bytes then we use 1 instead, otherwise nvidia drivers complain + // note that according to glium the size of the buffer will remain 0 + let size = match size { + 0 => 1, + a => a + }; + + let flags = gl::MAP_PERSISTENT_BIT | gl::MAP_READ_BIT | gl::MAP_WRITE_BIT; + + // will store the actual size of the buffer so that we can compare it with the expected size + let mut obtained_size: gl::types::GLint = mem::uninitialized(); + + if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_direct_state_access { + ctxt.gl.NamedBufferStorage(id, size as gl::types::GLsizeiptr, data_ptr as *const _, flags); + ctxt.gl.GetNamedBufferParameteriv(id, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.extensions.gl_arb_buffer_storage && + ctxt.extensions.gl_ext_direct_state_access + { + ctxt.gl.NamedBufferStorageEXT(id, size as gl::types::GLsizeiptr, + data_ptr as *const _, flags); + ctxt.gl.GetNamedBufferParameterivEXT(id, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.version >= &Version(Api::Gl, 4, 4) || + ctxt.extensions.gl_arb_buffer_storage + { + let bind = raw::bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferStorage(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, flags); + ctxt.gl.GetBufferParameteriv(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else if ctxt.extensions.gl_ext_buffer_storage { + let bind = raw::bind_buffer(&mut ctxt, id, ty); + ctxt.gl.BufferStorageEXT(bind, size as gl::types::GLsizeiptr, data_ptr as *const _, flags); + ctxt.gl.GetBufferParameteriv(bind, gl::BUFFER_SIZE, &mut obtained_size); + + } else { + // FIXME: return error instead + panic!() + } + + if size != obtained_size as usize { + if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + ctxt.gl.DeleteBuffers(1, [id].as_ptr()); + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + ctxt.gl.DeleteBuffersARB(1, [id].as_ptr()); + } else { + unreachable!(); + } + + return Err(BufferCreationError::OutOfMemory); + } + + let ptr = if ctxt.version >= &Version(Api::Gl, 4, 5) { + ctxt.gl.MapNamedBufferRange(id, 0, size as gl::types::GLsizeiptr, + gl::MAP_READ_BIT | gl::MAP_WRITE_BIT | + gl::MAP_PERSISTENT_BIT | gl::MAP_FLUSH_EXPLICIT_BIT) + + } else if ctxt.version >= &Version(Api::Gl, 3, 0) || + ctxt.extensions.gl_arb_map_buffer_range + { + let bind = raw::bind_buffer(&mut ctxt, id, ty); + ctxt.gl.MapBufferRange(bind, 0, size as gl::types::GLsizeiptr, + gl::MAP_READ_BIT | gl::MAP_WRITE_BIT | + gl::MAP_PERSISTENT_BIT | gl::MAP_FLUSH_EXPLICIT_BIT) + } else { + // already checked above + unreachable!(); + }; + + if ptr.is_null() { + let error = ::get_gl_error(ctxt); + panic!("glMapBufferRange returned null (error: {:?})", error); + } + + Ok((id, ptr)) +} diff --git a/src/buffer/raw.rs b/src/buffer/raw.rs index 191f56a0c48..95355678dfb 100644 --- a/src/buffer/raw.rs +++ b/src/buffer/raw.rs @@ -76,6 +76,26 @@ pub fn is_buffer_type_supported(ctxt: &mut CommandContext, ty: BufferType) -> bo } } +/// Reserves a new identifier for a buffer. +#[inline] +pub unsafe fn create_buffer_name(ctxt: &mut CommandContext) -> gl::types::GLuint { + let mut id: gl::types::GLuint = mem::uninitialized(); + + if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_direct_state_access { + ctxt.gl.CreateBuffers(1, &mut id); + } else if ctxt.version >= &Version(Api::Gl, 1, 5) || + ctxt.version >= &Version(Api::GlEs, 2, 0) + { + ctxt.gl.GenBuffers(1, &mut id); + } else if ctxt.extensions.gl_arb_vertex_buffer_object { + ctxt.gl.GenBuffersARB(1, &mut id); + } else { + unreachable!(); + } + + id +} + /// Binds a buffer of the given type, and returns the GLenum of the bind point. /// `id` can be 0. /// diff --git a/src/buffer/view.rs b/src/buffer/view.rs index e14c6ba3cfe..71e29ba940b 100644 --- a/src/buffer/view.rs +++ b/src/buffer/view.rs @@ -50,8 +50,6 @@ pub use self::DynamicBufferMutSlice as BufferMutSlice; pub struct DynamicBuffer where T: Content { // TODO: this `Option` is here because we have a destructor and need to be able to move out alloc: Option, - // TODO: this `Option` is here because we have a destructor and need to be able to move out - fence: Option, marker: PhantomData, } @@ -59,8 +57,6 @@ pub struct DynamicBuffer where T: Content { pub struct ImmutableBuffer where T: Content { // TODO: this `Option` is here because we have a destructor and need to be able to move out alloc: Option, - // TODO: this `Option` is here because we have a destructor and need to be able to move out - fence: Option, marker: PhantomData, } @@ -68,8 +64,6 @@ pub struct ImmutableBuffer where T: Content { pub struct PersistentBuffer where T: Content { // TODO: this `Option` is here because we have a destructor and need to be able to move out alloc: Option, - // TODO: this `Option` is here because we have a destructor and need to be able to move out - fence: Option, marker: PhantomData, } @@ -86,7 +80,6 @@ macro_rules! impl_buffer_base { .map(|buffer| { $ty { alloc: Some(buffer), - fence: Some(Fences::new()), marker: PhantomData, } }) @@ -102,7 +95,6 @@ macro_rules! impl_buffer_base { .map(|buffer| { $ty { alloc: Some(buffer), - fence: Some(Fences::new()), marker: PhantomData, } }) @@ -202,7 +194,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc.as_ref().unwrap(), bytes_start: 0, bytes_end: self.get_size(), - fence: self.fence.as_ref().unwrap(), marker: PhantomData, } } @@ -219,7 +210,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc.as_mut().unwrap(), bytes_start: 0, bytes_end: size, - fence: self.fence.as_ref().unwrap(), marker: PhantomData, } } @@ -236,7 +226,6 @@ macro_rules! impl_buffer_base { bytes_start: 0, bytes_end: self.get_size(), elements_size: ::get_elements_size(), - fence: self.fence.as_ref().unwrap(), } } } @@ -335,7 +324,6 @@ macro_rules! impl_buffer_base { .map(|buffer| { $ty { alloc: Some(buffer), - fence: Some(Fences::new()), marker: PhantomData, } }) @@ -476,7 +464,6 @@ macro_rules! impl_buffer_base { .map(|buffer| { $ty { alloc: Some(buffer), - fence: Some(Fences::new()), marker: PhantomData, } }) @@ -492,7 +479,6 @@ macro_rules! impl_buffer_base { .map(|buffer| { $ty { alloc: Some(buffer), - fence: Some(Fences::new()), marker: PhantomData, } }) @@ -530,15 +516,6 @@ macro_rules! impl_buffer_base { } } - impl Drop for $ty where T: Content { - #[inline] - fn drop(&mut self) { - if let (Some(alloc), Some(mut fence)) = (self.alloc.take(), self.fence.take()) { - fence.clean(&mut alloc.get_context().make_current()); - } - } - } - impl BufferExt for $ty where T: Content { #[inline] fn get_offset_bytes(&self) -> usize { @@ -639,7 +616,6 @@ macro_rules! impl_buffer_base { alloc: &'a Alloc, bytes_start: usize, bytes_end: usize, - fence: &'a Fences, marker: PhantomData<&'a T>, } @@ -724,7 +700,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc, bytes_start: self.bytes_start + result, bytes_end: self.bytes_start + result + size, - fence: self.fence, marker: PhantomData, } } @@ -740,7 +715,6 @@ macro_rules! impl_buffer_base { bytes_start: self.bytes_start, bytes_end: self.bytes_end, elements_size: ::get_elements_size(), - fence: self.fence, } } } @@ -766,7 +740,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc, bytes_start: self.bytes_start + range.start().map_or(0, |e| *e) * mem::size_of::(), bytes_end: self.bytes_start + range.end().map_or(self.len(), |e| *e) * mem::size_of::(), - fence: self.fence, marker: PhantomData, }) } @@ -786,7 +759,6 @@ macro_rules! impl_buffer_base { alloc: s.alloc, bytes_start: s.bytes_start, bytes_end: s.bytes_end, - fence: s.fence, marker: PhantomData, } } @@ -809,11 +781,7 @@ macro_rules! impl_buffer_base { impl<'a, T: ?Sized> BufferSliceExt<'a> for $slice_ty<'a, T> where T: Content { #[inline] fn add_fence(&self) -> Option> { - if !self.alloc.uses_persistent_mapping() { - return None; - } - - Some(self.fence.inserter(self.bytes_start .. self.bytes_end)) + unimplemented!() } } @@ -904,7 +872,6 @@ macro_rules! impl_buffer_base { alloc: &'a mut Alloc, bytes_start: usize, bytes_end: usize, - fence: &'a Fences, marker: PhantomData, } @@ -983,7 +950,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc, bytes_start: self.bytes_start + result, bytes_end: self.bytes_start + result + size, - fence: self.fence, marker: PhantomData, } } @@ -999,7 +965,6 @@ macro_rules! impl_buffer_base { bytes_start: self.bytes_start, bytes_end: self.bytes_end, elements_size: ::get_elements_size(), - fence: self.fence, } } } @@ -1026,7 +991,6 @@ macro_rules! impl_buffer_base { alloc: self.alloc, bytes_start: self.bytes_start + range.start().map_or(0, |e| *e) * mem::size_of::(), bytes_end: self.bytes_start + range.end().map_or(len, |e| *e) * mem::size_of::(), - fence: self.fence, marker: PhantomData, }) } @@ -1035,11 +999,7 @@ macro_rules! impl_buffer_base { impl<'a, T: ?Sized> BufferSliceExt<'a> for $slice_mut_ty<'a, T> where T: Content { #[inline] fn add_fence(&self) -> Option> { - if !self.alloc.uses_persistent_mapping() { - return None; - } - - Some(self.fence.inserter(self.bytes_start .. self.bytes_end)) + unimplemented!() } } @@ -1115,8 +1075,6 @@ impl DynamicBuffer where T: Content { /// - For other types, calls `glMapBuffer` or `glMapSubBuffer`. /// pub fn map(&mut self) -> Mapping { - self.fence.as_ref().unwrap().wait(&mut self.alloc.as_ref().unwrap().get_context().make_current(), - 0 .. self.get_size()); let size = self.get_size(); unsafe { self.alloc.as_mut().unwrap().map(0 .. size) } } @@ -1283,8 +1241,6 @@ impl<'a, T: ?Sized> DynamicBufferMutSlice<'a, T> where T: Content { /// - For other types, calls `glMapBuffer` or `glMapSubBuffer`. /// pub fn map(&mut self) -> Mapping { - self.fence.wait(&mut self.alloc.get_context().make_current(), - self.bytes_start .. self.bytes_end); unsafe { self.alloc.map(self.bytes_start .. self.bytes_end) } } } @@ -1335,7 +1291,6 @@ pub struct BufferAny { alloc: Alloc, size: usize, elements_size: usize, - fence: Fences, } impl BufferAny { @@ -1347,7 +1302,6 @@ impl BufferAny { bytes_start: 0, bytes_end: self.size, elements_size: self.elements_size, - fence: &self.fence, } } @@ -1398,7 +1352,6 @@ impl BufferAny { #[inline] pub unsafe fn read(&self) -> Result where T: Content { // TODO: add check - self.fence.wait(&mut self.alloc.get_context().make_current(), 0 .. self.get_size()); self.alloc.read::(0 .. self.get_size()) } } @@ -1412,18 +1365,10 @@ impl From> for BufferAny where T: Content + Send + 'static alloc: buffer.alloc.take().unwrap(), size: size, elements_size: ::get_elements_size(), - fence: buffer.fence.take().unwrap(), } } } -impl Drop for BufferAny { - #[inline] - fn drop(&mut self) { - self.fence.clean(&mut self.alloc.get_context().make_current()); - } -} - impl fmt::Debug for BufferAny { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -1520,7 +1465,6 @@ pub struct BufferAnySlice<'a> { bytes_start: usize, bytes_end: usize, elements_size: usize, - fence: &'a Fences, } impl<'a> BufferAnySlice<'a> { @@ -1576,11 +1520,7 @@ impl<'a> fmt::Debug for BufferAnySlice<'a> { impl<'a> BufferSliceExt<'a> for BufferAnySlice<'a> { #[inline] fn add_fence(&self) -> Option> { - if !self.alloc.uses_persistent_mapping() { - return None; - } - - Some(self.fence.inserter(self.bytes_start .. self.bytes_end)) + unimplemented!() } }