Skip to content

Commit

Permalink
Buffer improvements (#511)
Browse files Browse the repository at this point in the history
* InstanceBuffer and VertexBuffer generic argument

* fill_subset

* ElementBuffer generic argument

* ElementBuffer::fill_subset

* Fix order of mesh and instance transform
  • Loading branch information
asny authored Nov 21, 2024
1 parent bed312f commit 1b3dd57
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 352 deletions.
36 changes: 22 additions & 14 deletions src/core/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//! Different types of buffers used for sending data (primarily geometry data) to the GPU.
//!
mod element_buffer;
use std::marker::PhantomData;

#[doc(inline)]
pub use element_buffer::*;

Expand Down Expand Up @@ -40,36 +42,32 @@ impl<T: BufferDataType + PrimitiveDataType> BufferDataType for [T; 4] {}

impl BufferDataType for Quat {}

struct Buffer {
struct Buffer<T: BufferDataType> {
context: Context,
id: crate::context::Buffer,
attribute_count: u32,
data_type: u32,
data_size: u32,
normalized: bool,
_d: PhantomData<T>,
}

impl Buffer {
impl<T: BufferDataType> Buffer<T> {
pub fn new(context: &Context) -> Self {
Self {
context: context.clone(),
id: unsafe { context.create_buffer().expect("Failed creating buffer") },
attribute_count: 0,
data_type: 0,
data_size: 0,
normalized: false,
_d: PhantomData,
}
}

pub fn new_with_data<T: BufferDataType>(context: &Context, data: &[T]) -> Self {
pub fn new_with_data(context: &Context, data: &[T]) -> Self {
let mut buffer = Self::new(context);
if !data.is_empty() {
buffer.fill(data);
}
buffer
}

pub fn fill<T: BufferDataType>(&mut self, data: &[T]) {
pub fn fill(&mut self, data: &[T]) {
self.bind();
unsafe {
self.context.buffer_data_u8_slice(
Expand All @@ -84,9 +82,19 @@ impl Buffer {
self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
}
self.attribute_count = data.len() as u32;
self.data_type = T::data_type();
self.data_size = T::size();
self.normalized = T::normalized();
}

pub fn fill_subset(&mut self, offset: u32, data: &[T]) {
self.bind();
unsafe {
self.context.buffer_sub_data_u8_slice(
crate::context::ARRAY_BUFFER,
offset as i32,
to_byte_slice(data),
);
self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
}
self.attribute_count = (offset as u32 + data.len() as u32).max(self.attribute_count);
}

pub fn attribute_count(&self) -> u32 {
Expand All @@ -101,7 +109,7 @@ impl Buffer {
}
}

impl Drop for Buffer {
impl<T: BufferDataType> Drop for Buffer<T> {
fn drop(&mut self) {
unsafe {
self.context.delete_buffer(self.id);
Expand Down
50 changes: 33 additions & 17 deletions src/core/buffer/element_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::marker::PhantomData;

use crate::core::*;

/// The basic data type used for each index in an element buffer.
Expand Down Expand Up @@ -28,14 +30,14 @@ impl ElementBufferDataType for u32 {
/// The three indices refer to three places in a set of [VertexBuffer] where the data (position, normal etc.) is found for the three vertices of the triangle.
/// See for example [Program::draw_elements] to use this for drawing.
///
pub struct ElementBuffer {
pub struct ElementBuffer<T: ElementBufferDataType> {
context: Context,
id: crate::context::Buffer,
count: usize,
data_type: u32,
count: u32,
_d: PhantomData<T>,
}

impl ElementBuffer {
impl<T: ElementBufferDataType> ElementBuffer<T> {
///
/// Creates a new empty element buffer.
///
Expand All @@ -45,14 +47,14 @@ impl ElementBuffer {
context: context.clone(),
id,
count: 0,
data_type: 0,
_d: PhantomData,
}
}

///
/// Creates a new element buffer and fills it with the given indices which must be divisable by 3.
///
pub fn new_with_data<T: ElementBufferDataType>(context: &Context, data: &[T]) -> Self {
pub fn new_with_data(context: &Context, data: &[T]) -> Self {
let mut buffer = Self::new(context);
if !data.is_empty() {
buffer.fill(data);
Expand All @@ -62,33 +64,51 @@ impl ElementBuffer {

///
/// Fills the buffer with the given indices which must be divisable by 3.
/// This function will resize the buffer to have the same size as the indices array, if that is not desired, use [fill_subset](Self::fill_subset) instead.
///
pub fn fill<T: ElementBufferDataType>(&mut self, data: &[T]) {
pub fn fill(&mut self, indices: &[T]) {
self.bind();
unsafe {
self.context.buffer_data_u8_slice(
crate::context::ELEMENT_ARRAY_BUFFER,
to_byte_slice(data),
to_byte_slice(indices),
crate::context::STATIC_DRAW,
);
self.context
.bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
}
self.count = data.len();
self.data_type = T::data_type();
self.count = indices.len() as u32;
}

///
/// Fills the buffer with the given indices starting at the given offset.
/// This will increase the size of the buffer if there's not enough room. Otherwise, the size will remain unchanged.
///
pub fn fill_subset(&mut self, offset: u32, indices: &[T]) {
self.bind();
unsafe {
self.context.buffer_sub_data_u8_slice(
crate::context::ELEMENT_ARRAY_BUFFER,
offset as i32,
to_byte_slice(indices),
);
self.context
.bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
}
self.count = (offset as u32 + indices.len() as u32).max(self.count);
}

///
/// The number of values in the buffer.
///
pub fn count(&self) -> usize {
pub fn count(&self) -> u32 {
self.count
}

///
/// The number of triangles in the buffer.
///
pub fn triangle_count(&self) -> usize {
pub fn triangle_count(&self) -> u32 {
self.count / 3
}

Expand All @@ -98,13 +118,9 @@ impl ElementBuffer {
.bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, Some(self.id));
}
}

pub(crate) fn data_type(&self) -> u32 {
self.data_type
}
}

impl Drop for ElementBuffer {
impl<T: ElementBufferDataType> Drop for ElementBuffer<T> {
fn drop(&mut self) {
unsafe {
self.context.delete_buffer(self.id);
Expand Down
33 changes: 15 additions & 18 deletions src/core/buffer/instance_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::core::*;
/// A buffer containing per instance data.
/// To send this data to a shader, use the [Program::use_instance_attribute] method.
///
pub struct InstanceBuffer {
buffer: Buffer,
pub struct InstanceBuffer<T: BufferDataType> {
buffer: Buffer<T>,
}

impl InstanceBuffer {
impl<T: BufferDataType> InstanceBuffer<T> {
///
/// Creates a new empty instance buffer.
///
Expand All @@ -23,7 +23,7 @@ impl InstanceBuffer {
/// Creates a new instance buffer and fills it with the given data. The data should be in the same format as specified in the shader.
/// As an example, if specified as `vec3` in the shader it needs to be specified as an array of `Vector3<T>` where `T` is a primitive type that implements [BufferDataType], for example can be f16 or f32.
///
pub fn new_with_data<T: BufferDataType>(context: &Context, data: &[T]) -> Self {
pub fn new_with_data(context: &Context, data: &[T]) -> Self {
Self {
buffer: Buffer::new_with_data(context, data),
}
Expand All @@ -32,16 +32,25 @@ impl InstanceBuffer {
///
/// Fills the instance buffer with the given data. The data should be in the same format as specified in the shader.
/// As an example, if specified as `vec3` in the shader it needs to be specified as an array of `Vector3<T>` where `T` is a primitive type that implements [BufferDataType], for example can be f16 or f32.
/// This function will resize the buffer to have the same size as the data, if that is not desired, use [fill_subset](Self::fill_subset) instead.
///
pub fn fill<T: BufferDataType>(&mut self, data: &[T]) {
pub fn fill(&mut self, data: &[T]) {
self.buffer.fill(data)
}

///
/// Fills the vertex buffer with the given data starting at the given offset.
/// This will increase the size of the buffer if there's not enough room. Otherwise, the size will remain unchanged.
///
pub fn fill_subset(&mut self, offset: u32, data: &[T]) {
self.buffer.fill_subset(offset, data);
}

///
/// The number of values in the buffer.
///
pub fn count(&self) -> u32 {
self.buffer.attribute_count() * self.buffer.data_size
self.buffer.attribute_count() * T::size()
}

///
Expand All @@ -54,16 +63,4 @@ impl InstanceBuffer {
pub(in crate::core) fn bind(&self) {
self.buffer.bind();
}

pub(in crate::core) fn data_type(&self) -> u32 {
self.buffer.data_type
}

pub(in crate::core) fn data_size(&self) -> u32 {
self.buffer.data_size
}

pub(in crate::core) fn normalized(&self) -> bool {
self.buffer.normalized
}
}
33 changes: 15 additions & 18 deletions src/core/buffer/vertex_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::core::*;
/// A buffer containing per vertex data, for example positions, normals, uv coordinates or colors.
/// To send this data to a shader, use the [Program::use_vertex_attribute] method.
///
pub struct VertexBuffer {
buffer: Buffer,
pub struct VertexBuffer<T: BufferDataType> {
buffer: Buffer<T>,
}

impl VertexBuffer {
impl<T: BufferDataType> VertexBuffer<T> {
///
/// Creates a new empty vertex buffer.
///
Expand All @@ -23,7 +23,7 @@ impl VertexBuffer {
/// Creates a new vertex buffer and fills it with the given data. The data should be in the same format as specified in the shader.
/// As an example, if specified as `vec3` in the shader it needs to be specified as an array of `Vector3<T>` where `T` is a primitive type that implements [BufferDataType], for example can be f16 or f32.
///
pub fn new_with_data<T: BufferDataType>(context: &Context, data: &[T]) -> Self {
pub fn new_with_data(context: &Context, data: &[T]) -> Self {
Self {
buffer: Buffer::new_with_data(context, data),
}
Expand All @@ -32,16 +32,25 @@ impl VertexBuffer {
///
/// Fills the vertex buffer with the given data. The data should be in the same format as specified in the shader.
/// As an example, if specified as `vec3` in the shader it needs to be specified as an array of `Vector3<T>` where `T` is a primitive type that implements [BufferDataType], for example can be f16 or f32.
/// This function will resize the buffer to have the same size as the data, if that is not desired, use [fill_subset](Self::fill_subset) instead.
///
pub fn fill<T: BufferDataType>(&mut self, data: &[T]) {
pub fn fill(&mut self, data: &[T]) {
self.buffer.fill(data);
}

///
/// Fills the vertex buffer with the given data starting at the given offset.
/// This will increase the size of the buffer if there's not enough room. Otherwise, the size will remain unchanged.
///
pub fn fill_subset(&mut self, offset: u32, data: &[T]) {
self.buffer.fill_subset(offset, data);
}

///
/// The number of values in the buffer.
///
pub fn count(&self) -> u32 {
self.buffer.attribute_count() * self.buffer.data_size
self.buffer.attribute_count() * T::size()
}

///
Expand All @@ -54,16 +63,4 @@ impl VertexBuffer {
pub(in crate::core) fn bind(&self) {
self.buffer.bind();
}

pub(in crate::core) fn data_type(&self) -> u32 {
self.buffer.data_type
}

pub(in crate::core) fn data_size(&self) -> u32 {
self.buffer.data_size
}

pub(in crate::core) fn normalized(&self) -> bool {
self.buffer.normalized
}
}
Loading

0 comments on commit 1b3dd57

Please sign in to comment.