Skip to content

Commit

Permalink
feat: slab tutorial examples
Browse files Browse the repository at this point in the history
  • Loading branch information
schell committed Nov 28, 2023
1 parent 6c052fc commit d4f9755
Show file tree
Hide file tree
Showing 36 changed files with 2,173 additions and 240 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"crates/example",
"crates/example-debugging",
"crates/example-wasm",
"crates/loading-bytes",
"crates/renderling",
Expand All @@ -16,6 +17,7 @@ resolver = "2"

[workspace.dependencies]
bytemuck = { version = "1.13.0", features = ["derive"] }
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"
Expand Down
2 changes: 2 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Just pro-cons on tech choices and little things I don't want to forget whil impl
* it's Rust
- using cargo and Rust module system
- expressions!
- type checking!
- editor tooling!

## cons / limititions

Expand Down
2 changes: 1 addition & 1 deletion crates/example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ renderling-gpui = { path = "../renderling-gpui" }
anyhow = "^1.0"
clap = { version = "^4.3", features = ["derive"] }
env_logger = "0.10.0"
futures-lite = "^1.13"
futures-lite = {workspace=true}
icosahedron = "^0.1"
instant = "^0.1"
loading-bytes = { path = "../loading-bytes" }
Expand Down
19 changes: 4 additions & 15 deletions crates/renderling-shader/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl<T: Slabbed> Slabbed for Array<T> {
}

fn read_slab(&mut self, index: usize, slab: &[u32]) -> usize {
if index + Self::slab_size() >= slab.len() {
if index + Self::slab_size() > slab.len() {
index
} else {
let index = self.index.read_slab(index, slab);
Expand All @@ -44,7 +44,7 @@ impl<T: Slabbed> Slabbed for Array<T> {
}

fn write_slab(&self, index: usize, slab: &mut [u32]) -> usize {
if index + Self::slab_size() >= slab.len() {
if index + Self::slab_size() > slab.len() {
index
} else {
let index = self.index.write_slab(index, slab);
Expand Down Expand Up @@ -72,6 +72,7 @@ impl<T: Slabbed> Array<T> {
_phantom: PhantomData,
}
}

pub fn len(&self) -> usize {
self.len as usize
}
Expand All @@ -88,19 +89,7 @@ impl<T: Slabbed> Array<T> {
if index >= self.len() {
Id::NONE
} else {
Id::new(self.index + index as u32)
Id::new(self.index + (T::slab_size() * index) as u32)
}
}
}

impl<T: Slabbed> Array<T> {
fn slab_size() -> usize {
2
}

pub fn read(&self, item: &mut T, item_index: usize, slab: &[u32]) {
let size = T::slab_size();
let start = self.index as usize + size * item_index;
let _ = item.read_slab(start, slab);
}
}
15 changes: 10 additions & 5 deletions crates/renderling-shader/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ impl<T> Default for Id<T> {
}
}

#[cfg(not(target_arch = "spirv"))]
impl<T> std::fmt::Debug for Id<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("Id<{}>", std::any::type_name::<T>()))
.field(&self.0)
impl<T> core::fmt::Debug for Id<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Id")
.field("type", &core::any::type_name::<T>())
.field("index", &self.0)
.finish()
}
}
Expand Down Expand Up @@ -122,6 +122,11 @@ impl<T> Id<T> {
self.0 as usize
}

/// The raw u32 value of this id.
pub fn inner(&self) -> u32 {
self.0
}

pub fn is_none(&self) -> bool {
*self == Id::NONE
}
Expand Down
13 changes: 5 additions & 8 deletions crates/renderling-shader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

use core::ops::Mul;

use glam::{Vec4Swizzles, Vec3, Quat};
use spirv_std::num_traits::Zero;
use glam::{Quat, Vec3, Vec4Swizzles};
#[cfg(target_arch = "spirv")]
use spirv_std::num_traits::Float;
use spirv_std::num_traits::Zero;

pub mod array;
pub mod bits;
Expand All @@ -16,10 +16,11 @@ pub mod debug;
pub mod id;
pub mod math;
pub mod pbr;
pub mod stage;
pub mod skybox;
pub mod slab;
pub mod stage;
pub mod tonemapping;
pub mod tutorial;
pub mod ui;

/// Additional methods for vector types.
Expand Down Expand Up @@ -138,11 +139,7 @@ impl IsMatrix for glam::Mat4 {
return srt_id();
}

let det_sign = if det >= 0.0 {
1.0
} else {
-1.0
};
let det_sign = if det >= 0.0 { 1.0 } else { -1.0 };

let scale = glam::Vec3::new(
self.x_axis.length() * det_sign,
Expand Down
104 changes: 104 additions & 0 deletions crates/renderling-shader/src/slab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,27 @@ impl<T: core::any::Any> Slabbed for PhantomData<T> {
}

pub trait Slab {
/// Return the number of u32 elements in the slab.
fn len(&self) -> usize;

/// Returns whether the slab may contain the value with the given id.
fn contains<T: Slabbed>(&self, id: Id<T>) -> bool {
id.index() + T::slab_size() <= self.len()
}

/// Read the type from the slab using the Id as the index.
fn read<T: Slabbed + Default>(&self, id: Id<T>) -> T;

#[cfg(not(target_arch = "spirv"))]
fn read_vec<T: Slabbed + Default>(&self, array: crate::array::Array<T>) -> Vec<T> {
let mut vec = Vec::with_capacity(array.len());
for i in 0..array.len() {
let id = array.at(i);
vec.push(self.read(id));
}
vec
}

/// Write the type into the slab at the index.
///
/// Return the next index, or the same index if writing would overlap the slab.
Expand All @@ -372,6 +390,10 @@ pub trait Slab {
}

impl Slab for [u32] {
fn len(&self) -> usize {
self.len()
}

fn read<T: Slabbed + Default>(&self, id: Id<T>) -> T {
let mut t = T::default();
let _ = t.read_slab(id.index(), self);
Expand All @@ -391,8 +413,31 @@ impl Slab for [u32] {
}
}

#[cfg(not(target_arch = "spirv"))]
impl Slab for Vec<u32> {
fn len(&self) -> usize {
self.len()
}

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

fn write<T: Slabbed + Default>(&mut self, t: &T, index: usize) -> usize {
self.as_mut_slice().write(t, index)
}

fn write_slice<T: Slabbed + Default>(&mut self, t: &[T], index: usize) -> usize {
self.as_mut_slice().write_slice(t, index)
}
}

#[cfg(test)]
mod test {
use glam::Vec4;

use crate::{array::Array, stage::Vertex};

use super::*;

#[test]
Expand All @@ -402,5 +447,64 @@ mod test {
slab.write(&666, 1);
let t = slab.read(Id::<[u32; 2]>::new(0));
assert_eq!([42, 666], t);
let t: Vec<u32> = slab.read_vec(Array::new(0, 2));
assert_eq!([42, 666], t[..]);
slab.write_slice(&[1, 2, 3, 4], 2);
let t: Vec<u32> = slab.read_vec(Array::new(2, 4));
assert_eq!([1, 2, 3, 4], t[..]);
slab.write_slice(&[[1.0, 2.0, 3.0, 4.0], [5.5, 6.5, 7.5, 8.5]], 0);

let arr = Array::<[f32; 4]>::new(0, 2);
assert_eq!(Id::new(0), arr.at(0));
assert_eq!(Id::new(4), arr.at(1));
assert_eq!([1.0, 2.0, 3.0, 4.0], slab.read(arr.at(0)));
assert_eq!([5.5, 6.5, 7.5, 8.5], slab.read(arr.at(1)));

let geometry = vec![
Vertex {
position: Vec4::new(0.5, -0.5, 0.0, 1.0),
color: Vec4::new(1.0, 0.0, 0.0, 1.0),
..Default::default()
},
Vertex {
position: Vec4::new(0.0, 0.5, 0.0, 1.0),
color: Vec4::new(0.0, 1.0, 0.0, 1.0),
..Default::default()
},
Vertex {
position: Vec4::new(-0.5, -0.5, 0.0, 1.0),
color: Vec4::new(0.0, 0.0, 1.0, 1.0),
..Default::default()
},
Vertex {
position: Vec4::new(-1.0, 1.0, 0.0, 1.0),
color: Vec4::new(1.0, 0.0, 0.0, 1.0),
..Default::default()
},
Vertex {
position: Vec4::new(-1.0, 0.0, 0.0, 1.0),
color: Vec4::new(0.0, 1.0, 0.0, 1.0),
..Default::default()
},
Vertex {
position: Vec4::new(0.0, 1.0, 0.0, 1.0),
color: Vec4::new(0.0, 0.0, 1.0, 1.0),
..Default::default()
},
];
let geometry_slab_size = Vertex::slab_size() * geometry.len();
let mut slab = vec![0u32; geometry_slab_size + Array::<Vertex>::slab_size()];
let index = 0usize;
let vertices = Array::<Vertex>::new(index as u32, geometry.len() as u32);
let index = slab.write_slice(&geometry, index);
assert_eq!(geometry_slab_size, index);
let vertices_id = Id::<Array<Vertex>>::from(index);
let index = slab.write(&vertices, index);
assert_eq!(geometry_slab_size + Array::<Vertex>::slab_size(), index);
assert_eq!(Vertex::slab_size() * 6, vertices_id.index());
assert!(slab.contains(vertices_id),);

let array = slab.read(vertices_id);
assert_eq!(vertices, array);
}
}
51 changes: 31 additions & 20 deletions crates/renderling-shader/src/stage.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Types used to store and update an entire 3d scene on the GPU.
//! Types used to store and update an entire scene on the GPU.
//!
//! This is roughly what the [vulkan guide](https://vkguide.dev/docs/gpudriven)
//! calls "gpu driven rendering".
Expand Down Expand Up @@ -897,10 +897,8 @@ pub struct StageLegend {
/// transformations.
#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]
#[repr(C)]
#[derive(Default, Clone, Copy, PartialEq, Slabbed)]
#[derive(Clone, Copy, PartialEq, Slabbed)]
pub struct RenderUnit {
// Points to an index in the `RenderUnit` slab.
pub id: Id<RenderUnit>,
// Points to an array of `Vertex` in the stage's slab.
pub vertices: Array<Vertex>,
// Points to a `PbrMaterial` in the stage's slab.
Expand All @@ -913,14 +911,26 @@ pub struct RenderUnit {
pub scale: Vec3,
}

impl Default for RenderUnit {
fn default() -> Self {
Self {
vertices: Default::default(),
material: Default::default(),
camera: Default::default(),
position: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
}
}
}

#[spirv(vertex)]
pub fn new_stage_vertex(
// Which render unit are we rendering
#[spirv(instance_index)] instance_index: u32,
// Which vertex within the render unit are we rendering
#[spirv(vertex_index)] vertex_index: u32,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] unit_slab: &[u32],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] stage_slab: &[u32],
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &[u32],
#[spirv(flat)] out_camera: &mut u32,
#[spirv(flat)] out_material: &mut u32,
out_color: &mut Vec4,
Expand All @@ -934,8 +944,8 @@ pub fn new_stage_vertex(
#[spirv(position)] gl_pos: &mut Vec4,
) {
let unit_id: Id<RenderUnit> = Id::from(instance_index);
let unit = unit_slab.read(unit_id);
let vertex = stage_slab.read(unit.vertices.at(vertex_index as usize));
let unit = slab.read(unit_id);
let vertex = slab.read(unit.vertices.at(vertex_index as usize));
let model_matrix =
Mat4::from_scale_rotation_translation(unit.scale, unit.rotation, unit.position);
*out_material = unit.material.into();
Expand All @@ -957,30 +967,31 @@ pub fn new_stage_vertex(
*out_norm = normal_w;
let view_pos = model_matrix * vertex.position.xyz().extend(1.0);
*out_pos = view_pos.xyz();
let camera = stage_slab.read(unit.camera);
let camera = slab.read(unit.camera);
*out_camera = unit.camera.into();
*gl_pos = camera.projection * camera.view * view_pos;
}

#[allow(clippy::too_many_arguments)]
#[spirv(fragment)]
/// Scene fragment shader.
pub fn stage_fragment(
atlas: &Image2d,
atlas_sampler: &Sampler,
#[spirv(descriptor_set = 1, binding = 0)] atlas: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] atlas_sampler: &Sampler,

irradiance: &Cubemap,
irradiance_sampler: &Sampler,
#[spirv(descriptor_set = 1, binding = 2)] irradiance: &Cubemap,
#[spirv(descriptor_set = 1, binding = 3)] irradiance_sampler: &Sampler,

prefiltered: &Cubemap,
prefiltered_sampler: &Sampler,
#[spirv(descriptor_set = 1, binding = 4)] prefiltered: &Cubemap,
#[spirv(descriptor_set = 1, binding = 5)] prefiltered_sampler: &Sampler,

brdf: &Image2d,
brdf_sampler: &Sampler,
#[spirv(descriptor_set = 1, binding = 6)] brdf: &Image2d,
#[spirv(descriptor_set = 1, binding = 7)] brdf_sampler: &Sampler,

slab: &[u32],
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &[u32],

in_camera: u32,
in_material: u32,
#[spirv(flat)] in_camera: u32,
#[spirv(flat)] in_material: u32,
in_color: Vec4,
in_uv0: Vec2,
in_uv1: Vec2,
Expand Down
Loading

0 comments on commit d4f9755

Please sign in to comment.