diff --git a/Cargo.toml b/Cargo.toml index f368fc8c..08774c80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [workspace] members = [ - "crates/example", - "crates/example-wasm", + #"crates/example", + #"crates/example-wasm", "crates/loading-bytes", "crates/renderling", "crates/renderling-shader", - "crates/renderling-gpui", + #"crates/renderling-gpui", "crates/crabslab", "crates/crabslab-derive", ] diff --git a/crates/crabslab/README.md b/crates/crabslab/README.md index 5756011b..3b571126 100644 --- a/crates/crabslab/README.md +++ b/crates/crabslab/README.md @@ -2,33 +2,91 @@ slabcraft for crabs -## what -`crabslab` is a slab implementation focused on marshalling data from CPUs to GPUs. +## What + +`crabslab` is a slab implementation focused on marshalling data between CPUs and GPUs. + +### example +```rust +use crabslab::{CpuSlab, Slab, GrowableSlab, SlabItem, Id}; +use glam::{Vec3, Vec4}; + +#[derive(Debug, Default, SlabItem, PartialEq)] +struct Light { + direction: Vec3, + color: Vec4, + inner_cutoff: f32, + outer_cutoff: f32, + is_on: bool +} + +impl Light { + fn standard() -> Self { + Light { + direction: Vec3::NEG_Z, // pointing down + color: Vec4::ONE, // white + inner_cutoff: 0.5, + outer_cutoff: 2.6, + is_on: true + } + } +} + +fn cpu_code() -> (Id, Vec) { + // Create a new slab on the CPU-side. + // NOTE: For simplicity here we use `Vec` but if we were using `wgpu` + // we could use `crabslab::WgpuBuffer` instead of `Vec`. + // The API is the same. + let light = Light::standard(); + let mut slab = CpuSlab::new(vec![]); + let id = slab.append(&light); + (id, slab.into_inner()) +} + +fn shader_code(light_id: Id, slab: &[u32]) { + let light = slab.read(light_id); + assert_eq!(Light::standard(), light); +} + +let (light_id, slab) = cpu_code(); +// marshalling your data depends on which GPU library you are using... +shader_code(light_id, &slab); +``` + +## But Why? +It's hard to get data onto GPUs in the form you expect. + +To marshall your data correctly you must know about the alignment and sizes of the underlying representation of your data. +This will often surprise you! + +Working with a slab on the other hand, only requires that your types can be written into an array and read from an array. -## why ### Opinion -Working with shaders is much easier using a slab. +Working with _shaders_ is much easier using a slab. + +Shader code can be written in Rust with [`rust-gpu`](https://github.com/EmbarkStudios/rust-gpu), +which will enable you to use this crate on both CPU and GPU code. ### 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 +Specifically, with 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 +## And How `crabslab` includes: * a few traits: - `Slab` - `GrowableSlab` - `SlabItem` -* a derive macro for `SlabItem` -* a few structs for working with various slabs +* a derive macro for `SlabItem` for your structs +* a few new structs for working with slabs - `Id` - `Array` - `Offset` -* a helper struct `CpuSlab` +* a helper struct `CpuSlab` which wraps `Vec` or `WgpuBuffer` * a feature-gated helper for using slabs with `wgpu` - `WgpuBuffer` - [example](src/wgpu_slab.rs#L344) diff --git a/crates/crabslab/src/array.rs b/crates/crabslab/src/array.rs index 9d97af11..f2b7787c 100644 --- a/crates/crabslab/src/array.rs +++ b/crates/crabslab/src/array.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use crate::{id::Id, slab::SlabItem}; +/// Iterator over [`Id`]s in an [`Array`]. #[derive(Clone, Copy)] pub struct ArrayIter { array: Array, diff --git a/crates/crabslab/src/id.rs b/crates/crabslab/src/id.rs index 7ca1444d..795d2827 100644 --- a/crates/crabslab/src/id.rs +++ b/crates/crabslab/src/id.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use crate::{self as crabslab, slab::SlabItem}; +/// `u32` value of an [`Id`] that does not point to any item. pub const ID_NONE: u32 = u32::MAX; /// An identifier that can be used to read or write a type from/into the slab. @@ -144,9 +145,9 @@ impl Id { } } -/// The offset of a field relative a parent's `Id`. +/// The slab offset of a struct's field. /// -/// Offset functions are automatically derived for `SlabItem` structs. +/// Offset functions are automatically derived for [`SlabItem`] structs. /// /// ```rust /// use crabslab::{Id, Offset, Slab, SlabItem}; diff --git a/crates/crabslab/src/lib.rs b/crates/crabslab/src/lib.rs index 22aa6848..72da6487 100644 --- a/crates/crabslab/src/lib.rs +++ b/crates/crabslab/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(target_arch = "spirv", no_std)] //! Creating and crafting a tasty slab of memory. +#![doc = include_str!("../README.md")] mod array; pub use array::*; @@ -16,3 +17,9 @@ mod wgpu_slab; pub use wgpu_slab::*; pub use crabslab_derive::SlabItem; + +#[cfg(doctest)] +pub mod doctest { + #[doc = include_str!("../README.md")] + pub struct ReadmeDoctests; +} diff --git a/crates/crabslab/src/slab.rs b/crates/crabslab/src/slab.rs index 1a90c36d..c236e844 100644 --- a/crates/crabslab/src/slab.rs +++ b/crates/crabslab/src/slab.rs @@ -480,7 +480,7 @@ pub trait GrowableSlab: Slab { /// Preallocate space for one `T` element, but don't write anything to the /// buffer. /// - /// The returned `Id` can be used to write later with [`Self::write`]. + /// The returned `Id` can be used to write later with [`Slab::write`]. /// /// NOTE: This changes the next available buffer index and may change the /// buffer capacity. @@ -494,7 +494,7 @@ pub trait GrowableSlab: Slab { /// the buffer. /// /// This can be used to allocate space for a bunch of elements that get - /// written later with [`Self::write_array`]. + /// written later with [`Slab::write_array`]. /// /// NOTE: This changes the length of the buffer and may change the capacity. fn allocate_array(&mut self, len: usize) -> Array { @@ -528,7 +528,7 @@ pub trait GrowableSlab: Slab { } } -/// A wrapper around a `GrowableSlab` that provides convenience methods for +/// 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 @@ -588,6 +588,11 @@ impl CpuSlab { pub fn new(slab: B) -> Self { Self { slab } } + + /// Consume the [`CpuSlab`], converting it into the underlying buffer. + pub fn into_inner(self) -> B { + self.slab + } } #[cfg(not(target_arch = "spirv"))] diff --git a/crates/crabslab/src/wgpu_slab.rs b/crates/crabslab/src/wgpu_slab.rs index ec7af5f4..3625464f 100644 --- a/crates/crabslab/src/wgpu_slab.rs +++ b/crates/crabslab/src/wgpu_slab.rs @@ -7,8 +7,9 @@ use std::{ use crate::{GrowableSlab, Id, Slab, SlabItem}; use snafu::{IntoError, ResultExt, Snafu}; +/// Errors that can occur when using a [`WgpuBuffer`] as slab storage. #[derive(Debug, Snafu)] -pub enum SlabError { +pub enum WgpuSlabError { #[snafu(display( "Out of capacity. Tried to write {type_is}(slab size={slab_size}) at {index} but capacity \ is {capacity}", @@ -49,8 +50,28 @@ pub enum SlabError { Async { source: wgpu::BufferAsyncError }, } -pub fn print_slab(slab: &[u32], starting_index: usize) { - for (u, i) in slab.iter().zip(starting_index..) { +/// Print the slab's index, binary representation, integer value, and float +/// value. +/// +/// ```rust +/// use crabslab::{print_slab, CpuSlab, GrowableSlab, Slab, SlabItem}; +/// +/// let mut slab = CpuSlab::new(vec![]); +/// slab.append(&42u32); +/// slab.append(&42.0f32); +/// slab.append_array(&[0.0f32, 1.0f32, 2.0f32]); +/// +/// print_slab(slab.as_ref().as_slice(), 0..5); +/// /* stdout: +/// 00: 00000000000000000000000000101010 0000000042 5.9e-44 +/// 01: 01000010001010000000000000000000 1109917696 42.0 +/// 02: 00000000000000000000000000000000 0000000000 0.0 +/// 03: 00111111100000000000000000000000 1065353216 1.0 +/// 04: 01000000000000000000000000000000 1073741824 2.0 +/// */ +/// ``` +pub fn print_slab(slab: &[u32], indices: impl IntoIterator) { + for (u, i) in slab.iter().zip(indices.into_iter()) { println!("{i:02}: {u:032b} {u:010} {:?}", f32::from_bits(*u)); } } @@ -226,12 +247,12 @@ impl WgpuBuffer { #[cfg(feature = "futures-lite")] /// Read from the slab buffer synchronously. - pub fn block_on_read_raw(&self, start: usize, len: usize) -> Result, SlabError> { + pub fn block_on_read_raw(&self, start: usize, len: usize) -> Result, WgpuSlabError> { futures_lite::future::block_on(self.read_raw(start, len)) } /// Read from the slab buffer. - pub async fn read_raw(&self, start: usize, len: usize) -> Result, SlabError> { + pub async fn read_raw(&self, start: usize, len: usize) -> Result, WgpuSlabError> { let byte_offset = start * std::mem::size_of::(); let length = len * std::mem::size_of::(); let output_buffer_size = length as u64; @@ -271,7 +292,7 @@ impl WgpuBuffer { } /// Read from the slab buffer. - pub async fn read_async(&self, id: Id) -> Result { + pub async fn read_async(&self, id: Id) -> Result { let vec = self.read_raw(id.index(), T::slab_size()).await?; let t = Slab::read(vec.as_slice(), Id::::new(0)); Ok(t) diff --git a/crates/renderling/src/stage.rs b/crates/renderling/src/stage.rs index faea6e4b..fbc647be 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -18,7 +18,7 @@ use snafu::Snafu; use crate::{ Atlas, AtlasError, AtlasImage, AtlasImageError, DepthTexture, Device, HdrSurface, Queue, - Skybox, SlabError, + Skybox, WgpuSlabError, }; #[cfg(feature = "gltf")] @@ -33,7 +33,7 @@ pub enum StageError { Atlas { source: AtlasError }, #[snafu(display("{source}"))] - Slab { source: SlabError }, + Slab { source: WgpuSlabError }, } impl From for StageError { @@ -42,8 +42,8 @@ impl From for StageError { } } -impl From for StageError { - fn from(source: SlabError) -> Self { +impl From for StageError { + fn from(source: WgpuSlabError) -> Self { Self::Slab { source } } } @@ -652,7 +652,7 @@ impl Stage { /// into a vector. /// /// This is primarily used for debugging. - pub fn read_slab(&self) -> Result, SlabError> { + pub fn read_slab(&self) -> Result, WgpuSlabError> { // UNWRAP: if we can't acquire the lock we want to panic. self.slab .read() @@ -694,7 +694,7 @@ pub(crate) enum StageDrawStrategy { /// Render the stage. pub fn stage_render( (stage, hdr_frame, depth): (ViewMut, View, View), -) -> Result<(), SlabError> { +) -> Result<(), WgpuSlabError> { let label = Some("stage render"); let pipeline = stage.get_pipeline(); let slab_buffers_bindgroup = stage.get_slab_buffers_bindgroup(); diff --git a/crates/renderling/src/stage/gltf_support.rs b/crates/renderling/src/stage/gltf_support.rs index 740a0f7a..ca940bc7 100644 --- a/crates/renderling/src/stage/gltf_support.rs +++ b/crates/renderling/src/stage/gltf_support.rs @@ -49,11 +49,11 @@ pub enum StageGltfError { MissingCamera { index: usize }, #[snafu(display("{source}"))] - Slab { source: crabslab::SlabError }, + Slab { source: crabslab::WgpuSlabError }, } -impl From for StageGltfError { - fn from(source: crabslab::SlabError) -> Self { +impl From for StageGltfError { + fn from(source: crabslab::WgpuSlabError) -> Self { Self::Slab { source } } } @@ -1309,49 +1309,6 @@ impl<'a> GltfMeshBuilder<'a> { } } -/// Convenience builder for creating a [`GltfDocument`] without having a -/// [`gltf::Document`]. -/// -/// This is useful if you have non-GLTF assets that you want to render. -pub struct GltfDocumentBuilder<'a> { - stage: &'a mut Stage, -} - -impl<'a> GltfDocumentBuilder<'a> { - pub fn new(stage: &'a mut Stage) -> Self { - Self { stage } - } - - pub fn build(self) -> GltfDocument { - let accessors = Array::default(); - let animations = Array::default(); - let buffers = Array::default(); - let cameras = Array::default(); - let materials = Array::default(); - let default_material = Id::NONE; - let meshes = Array::default(); - let nodes = Array::default(); - let scenes = Array::default(); - let skins = Array::default(); - let textures = Array::default(); - let views = Array::default(); - GltfDocument { - accessors, - animations, - buffers, - cameras, - materials, - default_material, - meshes, - nodes, - scenes, - skins, - textures, - views, - } - } -} - #[cfg(test)] mod test { use crate::{ diff --git a/crates/renderling/src/tonemapping.rs b/crates/renderling/src/tonemapping.rs index f246a16c..78c7c6f4 100644 --- a/crates/renderling/src/tonemapping.rs +++ b/crates/renderling/src/tonemapping.rs @@ -1,4 +1,4 @@ -use moongraph::{GraphError, Move, View}; +use moongraph::{GraphError, View}; use crate::{frame::FrameTextureView, Device, HdrSurface, Queue};