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 @@
-## 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};