#![warn(missing_docs)]
use crate::core::{
algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4},
array_as_u8_slice,
arrayvec::ArrayVec,
color::Color,
value_as_u8_slice,
};
pub trait ByteStorage {
fn reset(&mut self);
fn bytes(&self) -> &[u8];
fn bytes_count(&self) -> usize;
fn write_bytes(&mut self, bytes: &[u8]);
fn write_zeros(&mut self, count: usize);
fn push_padding(&mut self, alignment: usize) {
debug_assert!(alignment.is_power_of_two());
let bytes_count = self.bytes_count();
let remainder = (alignment - 1) & bytes_count;
if remainder > 0 {
let padding = alignment - remainder;
self.write_zeros(padding);
}
}
}
impl<const N: usize> ByteStorage for ArrayVec<u8, N> {
fn reset(&mut self) {
self.clear();
}
fn bytes(&self) -> &[u8] {
self.as_slice()
}
fn bytes_count(&self) -> usize {
self.len()
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.try_extend_from_slice(bytes).unwrap()
}
fn write_zeros(&mut self, count: usize) {
let old_len = self.len();
let new_len = old_len + count;
assert!(new_len <= self.capacity());
unsafe {
self.set_len(new_len);
std::ptr::write_bytes(self.as_mut_ptr().add(old_len), 0, count)
}
}
}
impl ByteStorage for Vec<u8> {
fn reset(&mut self) {
self.clear();
}
fn bytes(&self) -> &[u8] {
self.as_slice()
}
fn bytes_count(&self) -> usize {
self.len()
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.extend_from_slice(bytes)
}
#[allow(clippy::uninit_vec)]
fn write_zeros(&mut self, count: usize) {
let old_len = self.len();
let new_len = old_len + count;
self.reserve(count);
unsafe {
self.set_len(new_len);
std::ptr::write_bytes(self.as_mut_ptr().add(old_len), 0, count)
}
}
}
#[derive(Default)]
pub struct UniformBuffer<S: ByteStorage> {
storage: S,
}
pub type StaticUniformBuffer<const N: usize> = UniformBuffer<ArrayVec<u8, N>>;
pub type DynamicUniformBuffer = UniformBuffer<Vec<u8>>;
pub trait Std140 {
fn write(&self, dest: &mut dyn ByteStorage);
}
macro_rules! default_write_impl {
($alignment:expr) => {
fn write(&self, dest: &mut dyn ByteStorage) {
dest.push_padding($alignment);
dest.write_bytes(value_as_u8_slice(self))
}
};
}
impl Std140 for f32 {
default_write_impl!(4);
}
impl Std140 for u32 {
default_write_impl!(4);
}
impl Std140 for i32 {
default_write_impl!(4);
}
impl Std140 for Vector2<f32> {
default_write_impl!(8);
}
impl Std140 for Vector3<f32> {
default_write_impl!(16);
}
impl Std140 for Vector4<f32> {
default_write_impl!(16);
}
impl Std140 for Matrix4<f32> {
default_write_impl!(16);
}
impl Std140 for Matrix3<f32> {
fn write(&self, dest: &mut dyn ByteStorage) {
dest.push_padding(16);
for row in (self as &dyn AsRef<[[f32; 3]; 3]>).as_ref() {
dest.write_bytes(array_as_u8_slice(row));
dest.write_bytes(&[0; size_of::<f32>()]);
}
}
}
impl Std140 for Matrix2<f32> {
fn write(&self, dest: &mut dyn ByteStorage) {
dest.push_padding(16);
for row in (self as &dyn AsRef<[[f32; 2]; 2]>).as_ref() {
dest.write_bytes(array_as_u8_slice(row));
dest.write_bytes(&[0; 2 * size_of::<f32>()]);
}
}
}
impl Std140 for Color {
fn write(&self, dest: &mut dyn ByteStorage) {
dest.push_padding(16);
let frgba = self.as_frgba();
dest.write_bytes(value_as_u8_slice(&frgba));
}
}
impl Std140 for bool {
fn write(&self, dest: &mut dyn ByteStorage) {
dest.push_padding(4);
let integer = if *self { 1 } else { 0 };
dest.write_bytes(value_as_u8_slice(&integer));
}
}
fn write_array(arr: &[impl Std140], dest: &mut dyn ByteStorage) {
for item in arr {
dest.push_padding(16);
item.write(dest);
dest.push_padding(16);
}
}
impl<T: Std140, const N: usize> Std140 for [T; N] {
fn write(&self, dest: &mut dyn ByteStorage) {
write_array(self, dest)
}
}
impl<T: Std140> Std140 for [T] {
fn write(&self, dest: &mut dyn ByteStorage) {
write_array(self, dest)
}
}
impl<S> UniformBuffer<S>
where
S: ByteStorage,
{
pub fn new() -> Self
where
S: Default,
{
Self {
storage: S::default(),
}
}
pub fn with_storage(storage: S) -> Self {
Self { storage }
}
pub fn clear(&mut self) {
self.storage.reset();
}
pub fn len(&self) -> usize {
self.storage.bytes_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn push_padding(&mut self, alignment: usize) {
self.storage.push_padding(alignment);
}
pub fn push<T>(&mut self, value: &T) -> &mut Self
where
T: Std140 + ?Sized,
{
value.write(&mut self.storage);
self
}
pub fn with<T>(mut self, value: &T) -> Self
where
T: Std140 + ?Sized,
{
self.push(value);
self
}
fn push_array_element<T: Std140>(&mut self, item: &T) {
self.push_padding(16);
item.write(&mut self.storage);
self.push_padding(16);
}
pub fn push_slice_with_max_size<T: Std140 + Default>(
&mut self,
slice: &[T],
max_len: usize,
) -> &mut Self {
let len = slice.len();
if !slice.is_empty() {
let end = max_len.min(len);
self.push(&slice[0..end]);
}
let remainder = max_len.saturating_sub(len);
let item = T::default();
for _ in 0..remainder {
self.push_array_element(&item);
}
self
}
pub fn with_slice_with_max_size<T: Std140 + Default>(
mut self,
slice: &[T],
max_len: usize,
) -> Self {
self.push_slice_with_max_size(slice, max_len);
self
}
pub fn storage(&self) -> &S {
&self.storage
}
pub fn finish(mut self) -> S {
self.push_padding(16);
self.storage
}
pub fn next_write_aligned_position(&self, alignment: usize) -> usize {
let position = self.storage.bytes_count();
let remainder = (alignment - 1) & position;
if remainder > 0 {
let padding = alignment - remainder;
position + padding
} else {
position
}
}
pub fn write_bytes_with_alignment(&mut self, bytes: &[u8], alignment: usize) -> usize {
self.push_padding(alignment);
let data_location = self.storage.bytes_count();
self.storage.write_bytes(bytes);
data_location
}
}
#[cfg(test)]
mod test {
use crate::{
core::algebra::{Matrix3, Vector3, Vector4},
uniform::DynamicUniformBuffer,
};
use fyrox_core::transmute_slice;
#[test]
fn test_uniform_buffer() {
let mut buffer = DynamicUniformBuffer::default();
buffer.push(&123.321);
assert_eq!(buffer.len(), 4);
buffer.push(&Vector3::new(1.0, 2.0, 3.0));
assert_eq!(buffer.len(), 28);
buffer.push(&Vector4::new(1.0, 2.0, 3.0, 4.0));
assert_eq!(buffer.len(), 48);
buffer.push(&Matrix3::default());
assert_eq!(buffer.len(), 96);
buffer.push(&123.0);
assert_eq!(buffer.len(), 100);
buffer.push(&[1.0, 2.0, 3.0, 4.0]);
assert_eq!(buffer.len(), 176);
buffer.push(&[1.0, 2.0, 3.0]);
assert_eq!(buffer.len(), 224);
buffer.push(&[1.0, 2.0]);
assert_eq!(buffer.len(), 256);
let bytes = buffer.finish();
assert_eq!(bytes.len(), 256);
}
#[test]
fn test_uniform_buffer_mixed_alignment() {
let mut buffer = DynamicUniformBuffer::default();
buffer.push(&Vector3::repeat(1.0));
assert_eq!(buffer.len(), 12);
buffer.push(&1.0);
assert_eq!(buffer.len(), 16);
}
#[test]
fn test_push_with_max_len() {
let mut buffer = DynamicUniformBuffer::default();
buffer.push_slice_with_max_size(&[1.0, 2.0, 3.0, 4.0], 6);
let floats: &[f32] = transmute_slice(buffer.storage().as_slice());
assert_eq!(
floats,
&[
1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ]
);
buffer.clear();
buffer.push_slice_with_max_size(&[1.0, 2.0], 1);
let floats: &[f32] = transmute_slice(buffer.storage().as_slice());
assert_eq!(floats, &[1.0, 0.0, 0.0, 0.0,]);
}
}