Skip to content

Commit

Permalink
WIP part out crabslab, remove non-slab shaders, remove bloom (to be r…
Browse files Browse the repository at this point in the history
…eimplemented later)
  • Loading branch information
schell committed Dec 28, 2023
1 parent ac64a57 commit 0108d78
Show file tree
Hide file tree
Showing 51 changed files with 541 additions and 2,542 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ futures-lite = "1.13"
gltf = { git = 'https://github.com/gltf-rs/gltf.git', features = ["KHR_lights_punctual", "KHR_materials_unlit", "KHR_materials_emissive_strength", "extras"] }
image = "0.24"
log = "0.4"
glam = "0.24.2"
glam = { version = "0.24.2", default-features = false }
snafu = "0.7"
winit = { version = "0.27" }
wgpu = { version = "0.17" }
10 changes: 8 additions & 2 deletions crates/crabslab/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["wgpu", "glam", "dep:futures-lite"]
default = ["wgpu", "glam", "futures-lite"]
futures-lite = ["dep:futures-lite"]
glam = ["dep:glam"]
wgpu = ["dep:wgpu", "dep:bytemuck", "dep:snafu", "dep:async-channel", "dep:log"]

[dependencies]
async-channel = {workspace=true, optional=true}
bytemuck = {workspace=true, optional=true}
futures-lite = {workspace=true, optional=true}
glam = {workspace=true, optional=true}
log = {workspace=true, optional=true}
crabslab-derive = { version = "0.1.0", path = "../crabslab-derive" }
snafu = {workspace=true, optional=true}
wgpu = {workspace=true, optional=true}

[target.'cfg(not(target_arch = "spirv"))'.dependencies]
glam = { workspace = true, features = ["std"], optional = true }

[target.'cfg(target_arch = "spirv")'.dependencies]
glam = { version = "0.24.2", default-features = false, features = ["libm"], optional = true }
9 changes: 9 additions & 0 deletions crates/crabslab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
### Opinion
Working with shaders is much easier using a slab.

### rust-gpu
This crate was made to work with [`rust-gpu`](https://github.com/EmbarkStudios/rust-gpu/).
Specifically, using this crate it is possible to pack your types into a buffer on the CPU
and then read your types from the slab on the GPU (in Rust).

### Other no-std platforms
Even though this crate was written with `rust-gpu` in mind, it should work in other `no-std`
contexts.

## how
`crabslab` includes:
* a few traits:
Expand Down
10 changes: 6 additions & 4 deletions crates/crabslab/src/array.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! A slab-allocated array.
use core::marker::PhantomData;

use crate::id::Id;
use crate::slab::SlabItem;
use crate::{id::Id, slab::SlabItem};

#[derive(Clone, Copy)]
pub struct ArrayIter<T> {
Expand Down Expand Up @@ -60,9 +59,12 @@ impl<T> From<Id<T>> for Array<T> {
impl<T> core::fmt::Debug for Array<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.is_null() {
f.write_fmt(format_args!("Array<{}>(null)", core::any::type_name::<T>()))
f.write_fmt(core::format_args!(
"Array<{}>(null)",
core::any::type_name::<T>()
))
} else {
f.write_fmt(format_args!(
f.write_fmt(core::format_args!(
"Array<{}>({}, {})",
core::any::type_name::<T>(),
self.index,
Expand Down
7 changes: 5 additions & 2 deletions crates/crabslab/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ impl<T> Default for Id<T> {
impl<T> core::fmt::Debug for Id<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.is_none() {
f.write_fmt(format_args!("Id<{}>(null)", &core::any::type_name::<T>(),))
f.write_fmt(core::format_args!(
"Id<{}>(null)",
&core::any::type_name::<T>(),
))
} else {
f.write_fmt(format_args!(
f.write_fmt(core::format_args!(
"Id<{}>({})",
&core::any::type_name::<T>(),
&self.0
Expand Down
1 change: 1 addition & 0 deletions crates/crabslab/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(target_arch = "spirv", no_std)]
//! Creating and crafting a tasty slab of memory.
mod array;
Expand Down
137 changes: 69 additions & 68 deletions crates/crabslab/src/slab.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Slab traits.
use core::marker::PhantomData;
use core::{default::Default, marker::PhantomData};
pub use crabslab_derive::SlabItem;

use crate::{array::Array, id::Id};
Expand Down Expand Up @@ -457,73 +457,12 @@ pub trait GrowableSlab: Slab {
///
/// Returns the previous length.
fn increment_len(&mut self, n: usize) -> usize;
}

/// A wrapper around a `GrowableSlab` that provides convenience methods for
/// working with CPU-side slabs.
///
/// Working with slabs on the CPU is much more convenient because the underlying
/// buffer `B` is often a growable type, like `Vec<u32>`. This wrapper provides
/// methods for appending to the end of the buffer with automatic resizing and
/// for preallocating space for elements that will be written later.
pub struct CpuSlab<B> {
slab: B,
}

impl<B> AsRef<B> for CpuSlab<B> {
fn as_ref(&self) -> &B {
&self.slab
}
}

impl<B> AsMut<B> for CpuSlab<B> {
fn as_mut(&mut self) -> &mut B {
&mut self.slab
}
}

impl<B: Slab> Slab for CpuSlab<B> {
fn len(&self) -> usize {
self.slab.len()
}

fn read<T: SlabItem + Default>(&self, id: Id<T>) -> T {
self.slab.read(id)
}

fn write_indexed<T: SlabItem>(&mut self, t: &T, index: usize) -> usize {
self.slab.write_indexed(t, index)
}

fn write_indexed_slice<T: SlabItem>(&mut self, t: &[T], index: usize) -> usize {
self.slab.write_indexed_slice(t, index)
}
}

impl<B: GrowableSlab> GrowableSlab for CpuSlab<B> {
fn capacity(&self) -> usize {
self.slab.capacity()
}

fn reserve_capacity(&mut self, capacity: usize) {
self.slab.reserve_capacity(capacity);
}

fn increment_len(&mut self, n: usize) -> usize {
self.slab.increment_len(n)
}
}

impl<B: GrowableSlab> CpuSlab<B> {
/// Create a new `SlabBuffer` with the given slab.
pub fn new(slab: B) -> Self {
Self { slab }
}

/// Expands the slab to fit the given number of `T`s, if necessary.
fn maybe_expand_to_fit<T: SlabItem>(&mut self, len: usize) {
let size = T::slab_size();
let capacity = self.slab.capacity();
let capacity = self.capacity();
//log::trace!(
// "append_slice: {size} * {ts_len} + {len} ({}) >= {capacity}",
// size * ts_len + len
Expand All @@ -545,7 +484,7 @@ impl<B: GrowableSlab> CpuSlab<B> {
///
/// NOTE: This changes the next available buffer index and may change the
/// buffer capacity.
pub fn allocate<T: SlabItem>(&mut self) -> Id<T> {
fn allocate<T: SlabItem>(&mut self) -> Id<T> {
self.maybe_expand_to_fit::<T>(1);
let index = self.increment_len(T::slab_size());
Id::from(index)
Expand All @@ -558,7 +497,7 @@ impl<B: GrowableSlab> CpuSlab<B> {
/// written later with [`Self::write_array`].
///
/// NOTE: This changes the length of the buffer and may change the capacity.
pub fn allocate_array<T: SlabItem>(&mut self, len: usize) -> Array<T> {
fn allocate_array<T: SlabItem>(&mut self, len: usize) -> Array<T> {
if len == 0 {
return Array::default();
}
Expand All @@ -570,25 +509,87 @@ impl<B: GrowableSlab> CpuSlab<B> {
/// Append to the end of the buffer.
///
/// Returns the `Id` of the written element.
pub fn append<T: SlabItem + Default>(&mut self, t: &T) -> Id<T> {
fn append<T: SlabItem + Default>(&mut self, t: &T) -> Id<T> {
let id = self.allocate::<T>();
// IGNORED: safe because we just allocated the id
let _ = self.slab.write(id, t);
let _ = self.write(id, t);
id
}

/// Append a slice to the end of the buffer, resizing if necessary
/// and returning a slabbed array.
///
/// Returns the `Array` of the written elements.
pub fn append_array<T: SlabItem + Default>(&mut self, ts: &[T]) -> Array<T> {
fn append_array<T: SlabItem + Default>(&mut self, ts: &[T]) -> Array<T> {
let array = self.allocate_array::<T>(ts.len());
// IGNORED: safe because we just allocated the array
let _ = self.write_array(array, ts);
array
}
}

/// A wrapper around a `GrowableSlab` that provides convenience methods for
/// working with CPU-side slabs.
///
/// Working with slabs on the CPU is much more convenient because the underlying
/// buffer `B` is often a growable type, like `Vec<u32>`. This wrapper provides
/// methods for appending to the end of the buffer with automatic resizing and
/// for preallocating space for elements that will be written later.
pub struct CpuSlab<B> {
slab: B,
}

impl<B> AsRef<B> for CpuSlab<B> {
fn as_ref(&self) -> &B {
&self.slab
}
}

impl<B> AsMut<B> for CpuSlab<B> {
fn as_mut(&mut self) -> &mut B {
&mut self.slab
}
}

impl<B: Slab> Slab for CpuSlab<B> {
fn len(&self) -> usize {
self.slab.len()
}

fn read<T: SlabItem + Default>(&self, id: Id<T>) -> T {
self.slab.read(id)
}

fn write_indexed<T: SlabItem>(&mut self, t: &T, index: usize) -> usize {
self.slab.write_indexed(t, index)
}

fn write_indexed_slice<T: SlabItem>(&mut self, t: &[T], index: usize) -> usize {
self.slab.write_indexed_slice(t, index)
}
}

impl<B: GrowableSlab> GrowableSlab for CpuSlab<B> {
fn capacity(&self) -> usize {
self.slab.capacity()
}

fn reserve_capacity(&mut self, capacity: usize) {
self.slab.reserve_capacity(capacity);
}

fn increment_len(&mut self, n: usize) -> usize {
self.slab.increment_len(n)
}
}

impl<B: GrowableSlab> CpuSlab<B> {
/// Create a new `SlabBuffer` with the given slab.
pub fn new(slab: B) -> Self {
Self { slab }
}
}

#[cfg(not(target_arch = "spirv"))]
impl GrowableSlab for Vec<u32> {
fn capacity(&self) -> usize {
Expand Down
34 changes: 13 additions & 21 deletions crates/crabslab/src/wgpu_slab.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! CPU side of slab storage using `wgpu`.
use std::{
ops::Deref,
sync::{atomic::AtomicUsize, Arc, RwLock},
sync::{atomic::AtomicUsize, Arc},
};

use crate::{GrowableSlab, Id, Slab, SlabItem};
Expand Down Expand Up @@ -56,19 +56,16 @@ pub fn print_slab(slab: &[u32], starting_index: usize) {
}

/// A slab buffer used by the stage to store heterogeneous objects.
///
/// A clone of a buffer is a reference to the same buffer.
#[derive(Clone)]
pub struct WgpuBuffer {
pub(crate) buffer: Arc<RwLock<wgpu::Buffer>>,
pub(crate) buffer: wgpu::Buffer,
device: Arc<wgpu::Device>,
queue: Arc<wgpu::Queue>,
// The number of u32 elements currently stored in the buffer.
//
// This is the next index to write into.
len: Arc<AtomicUsize>,
len: AtomicUsize,
// The total number of u32 elements that can be stored in the buffer.
capacity: Arc<AtomicUsize>,
capacity: AtomicUsize,
}

impl Slab for WgpuBuffer {
Expand Down Expand Up @@ -103,8 +100,7 @@ impl Slab for WgpuBuffer {
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
self.queue.write_buffer(
// UNWRAP: if we can't lock we want to panic
&self.buffer.read().unwrap(),
&self.buffer,
byte_offset as u64,
bytemuck::cast_slice(bytes.as_slice()),
);
Expand Down Expand Up @@ -137,8 +133,7 @@ impl Slab for WgpuBuffer {
let _ = u32_data.write_indexed_slice(t, 0);
let byte_offset = index * std::mem::size_of::<u32>();
self.queue.write_buffer(
// UNWRAP: if we can't lock we want to panic
&self.buffer.read().unwrap(),
&self.buffer,
byte_offset as u64,
bytemuck::cast_slice(&u32_data),
);
Expand All @@ -161,21 +156,20 @@ impl GrowableSlab for WgpuBuffer {
if new_capacity > capacity {
log::trace!("resizing buffer from {capacity} to {new_capacity}");
let len = self.len();
let mut buffer = self.buffer.write().unwrap();
let new_buffer = Self::new_buffer(&self.device, new_capacity, buffer.usage());
let new_buffer = Self::new_buffer(&self.device, new_capacity, self.buffer.usage());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
// UNWRAP: if we can't lock we want to panic
encoder.copy_buffer_to_buffer(
&buffer,
&self.buffer,
0,
&new_buffer,
0,
(len * std::mem::size_of::<u32>()) as u64,
);
self.queue.submit(std::iter::once(encoder.finish()));
*buffer = new_buffer;
self.buffer = new_buffer;
self.capacity
.store(new_capacity, std::sync::atomic::Ordering::Relaxed);
}
Expand Down Expand Up @@ -222,7 +216,7 @@ impl WgpuBuffer {
let device = device.into();
let queue = queue.into();
Self {
buffer: RwLock::new(Self::new_buffer(&device, capacity, usage)).into(),
buffer: Self::new_buffer(&device, capacity, usage),
len: AtomicUsize::new(0).into(),
capacity: AtomicUsize::new(capacity).into(),
device,
Expand Down Expand Up @@ -256,8 +250,7 @@ impl WgpuBuffer {
output_buffer_size:{output_buffer_size}",
);
encoder.copy_buffer_to_buffer(
// UNWRAP: if we can't lock we want to panic
&self.buffer.read().unwrap(),
&self.buffer,
byte_offset as u64,
&output_buffer,
0,
Expand Down Expand Up @@ -285,9 +278,8 @@ impl WgpuBuffer {
}

/// Get the underlying buffer.
pub fn get_buffer(&self) -> impl Deref<Target = wgpu::Buffer> + '_ {
// UNWRAP: if we can't lock we want to panic
self.buffer.read().unwrap()
pub fn get_buffer(&self) -> &wgpu::Buffer {
&self.buffer
}
}

Expand Down
Loading

0 comments on commit 0108d78

Please sign in to comment.