diff --git a/crates/renderling-shader/src/array.rs b/crates/renderling-shader/src/array.rs index 6054615d..35591901 100644 --- a/crates/renderling-shader/src/array.rs +++ b/crates/renderling-shader/src/array.rs @@ -6,7 +6,9 @@ use crate::slab::Slabbed; #[repr(C)] pub struct Array { + // u32 offset in the slab index: u32, + // number of `T` elements in the array len: u32, _phantom: PhantomData, } @@ -25,11 +27,15 @@ impl Copy for Array {} impl core::fmt::Debug for Array { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Array") - .field("index", &self.index) - .field("len", &self.len) - .field("_phantom", &self._phantom) - .finish() + f.debug_struct(if self.is_null() { + "Array (null)" + } else { + "Array" + }) + .field("index", &self.index) + .field("len", &self.len) + .field("_phantom", &self._phantom) + .finish() } } @@ -75,7 +81,7 @@ impl Default for Array { } } -impl Array { +impl Array { pub fn new(index: u32, len: u32) -> Self { Self { index, @@ -92,11 +98,18 @@ impl Array { self.len == 0 } + pub fn is_null(&self) -> bool { + self.index == u32::MAX + } + pub fn contains_index(&self, index: usize) -> bool { index >= self.index as usize && index < (self.index + self.len) as usize } - pub fn at(&self, index: usize) -> Id { + pub fn at(&self, index: usize) -> Id + where + T: Slabbed, + { if index >= self.len() { Id::NONE } else { @@ -107,4 +120,16 @@ impl Array { pub fn starting_index(&self) -> usize { self.index as usize } + + /// Convert this array into a `u32` array. + pub fn into_u32_array(self) -> Array + where + T: Slabbed, + { + Array { + index: self.index, + len: self.len * T::slab_size() as u32, + _phantom: PhantomData, + } + } } diff --git a/crates/renderling-shader/src/bits.rs b/crates/renderling-shader/src/bits.rs index 8c10217b..18f8cc54 100644 --- a/crates/renderling-shader/src/bits.rs +++ b/crates/renderling-shader/src/bits.rs @@ -49,6 +49,72 @@ pub const U16_0_BITS: (u32, u32) = bits(0..=15); /// The shift/mask range for the second 16 bits of a u32. pub const U16_1_BITS: (u32, u32) = bits(16..=31); +/// Extract the "nth" 8 bits of the u32 at the given index in the slab. +/// +/// Returns the extracted value, the index of the next component and the index +/// of the next u32 in the slab. +pub fn extract_u8( + // component index, eg 0 for the first 8 bits, 1 for the second 8 bits, etc + n: u32, + // index of the u32 in the slab + u32_index: usize, + // slab of u32s + slab: &[u32], +) -> (u32, u32, usize) { + match n { + 0 => (extract(slab[u32_index], U8_0_BITS), 1, u32_index), + 1 => (extract(slab[u32_index], U8_1_BITS), 2, u32_index), + 2 => (extract(slab[u32_index], U8_2_BITS), 3, u32_index), + _ => (extract(slab[u32_index], U8_3_BITS), 0, u32_index + 1), + } +} + +/// Extract the "nth" 8 bits of the u32 at the given index in the slab. +/// +/// Returns the extracted value, the index of the next component and the index +/// of the next u32 in the slab. +pub fn extract_i8( + // component index, eg 0 for the first 8 bits, 1 for the second 8 bits, etc + n: u32, + // index of the u32 in the slab + u32_index: usize, + // slab of u32s + slab: &[u32], +) -> (i32, u32, usize) { + let (value, n, u32_index) = extract_u8(n, u32_index, slab); + let value: i32 = (value as i32 & 0xFF) - ((value as i32 & 0x80) << 1); + (value, n, u32_index) +} + +/// Extract the "nth" 16 bits of the u32 at the given index in the slab. +pub fn extract_u16( + // component index, eg 0 for the first 16 bits, 1 for the second 16 bits, etc + n: u32, + // index of the u32 in the slab + u32_index: usize, + // slab of u32s + slab: &[u32], +) -> (u32, u32, usize) { + match n { + 0 => (extract(slab[u32_index], U16_0_BITS), 1, u32_index), + _ => (extract(slab[u32_index], U16_1_BITS), 0, u32_index + 1), + } +} + +/// Extract the "nth" 16 bits of the u32 at the given index in the slab. +pub fn extract_i16( + // component index, eg 0 for the first 16 bits, 1 for the second 16 bits, etc + n: u32, + // index of the u32 in the slab + u32_index: usize, + // slab of u32s + slab: &[u32], +) -> (i32, u32, usize) { + let (value, n, u32_index) = extract_u16(n, u32_index, slab); + let value: i32 = (value as i32 & 0xFFFF) - ((value as i32 & 0x8000) << 1); + (value, n, u32_index) +} + #[cfg(test)] mod test { use super::*; @@ -96,4 +162,65 @@ mod test { assert_eq!(super::extract(bits, super::U8_2_BITS), 4); assert_eq!(super::extract(bits, super::U8_3_BITS), 3); } + + #[test] + fn extract_u8_sanity() { + let u8_slab = [0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 0u8, 0u8]; + let u32_slab: &[u32] = bytemuck::cast_slice(&u8_slab); + let index = 0; + let n = 0; + let (a, n, index) = extract_u8(n, index, u32_slab); + let (b, n, index) = extract_u8(n, index, u32_slab); + let (c, n, index) = extract_u8(n, index, u32_slab); + let (d, n, index) = extract_u8(n, index, u32_slab); + let (e, n, index) = extract_u8(n, index, u32_slab); + let (f, _, _) = extract_u8(n, index, u32_slab); + assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_i8_sanity() { + let i8_slab = [0i8, -1i8, -2i8, -3i8, 4i8, 5i8, 0i8, 0i8]; + let u32_slab: &[u32] = bytemuck::cast_slice(&i8_slab); + let index = 0; + let n = 0; + let (a, n, index) = extract_i8(n, index, u32_slab); + let (b, n, index) = extract_i8(n, index, u32_slab); + let (c, n, index) = extract_i8(n, index, u32_slab); + let (d, n, index) = extract_i8(n, index, u32_slab); + let (e, n, index) = extract_i8(n, index, u32_slab); + let (f, _, _) = extract_i8(n, index, u32_slab); + assert_eq!([0, -1, -2, -3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_u16_sanity() { + let u16_slab = [0u16, 1u16, 2u16, 3u16, 4u16, 5u16]; + let u32_slab: &[u32] = bytemuck::cast_slice(&u16_slab); + let index = 0; + let n = 0; + let (a, n, index) = extract_u16(n, index, u32_slab); + let (b, n, index) = extract_u16(n, index, u32_slab); + let (c, n, index) = extract_u16(n, index, u32_slab); + let (d, n, index) = extract_u16(n, index, u32_slab); + let (e, n, index) = extract_u16(n, index, u32_slab); + let (f, _, _) = extract_u16(n, index, u32_slab); + assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_i16_sanity() { + let i16_slab = [0i16, -1i16, -2i16, -3i16, 4i16, 5i16, -12345i16, 0i16]; + let u32_slab: &[u32] = bytemuck::cast_slice(&i16_slab); + let index = 0; + let n = 0; + let (a, n, index) = extract_i16(n, index, u32_slab); + let (b, n, index) = extract_i16(n, index, u32_slab); + let (c, n, index) = extract_i16(n, index, u32_slab); + let (d, n, index) = extract_i16(n, index, u32_slab); + let (e, n, index) = extract_i16(n, index, u32_slab); + let (f, n, index) = extract_i16(n, index, u32_slab); + let (g, _, _) = extract_i16(n, index, u32_slab); + assert_eq!([0, -1, -2, -3, 4, 5, -12345], [a, b, c, d, e, f, g]); + } } diff --git a/crates/renderling-shader/src/gltf.rs b/crates/renderling-shader/src/gltf.rs index 5f9cdf97..a4753d57 100644 --- a/crates/renderling-shader/src/gltf.rs +++ b/crates/renderling-shader/src/gltf.rs @@ -10,11 +10,13 @@ use crate::{ texture::GpuTexture, }; #[repr(transparent)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfBuffer(pub Array); #[repr(u32)] -#[derive(Default, Clone, Copy, PartialEq, Debug)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[derive(Default, Clone, Copy, PartialEq)] pub enum DataType { I8, U8, @@ -54,6 +56,7 @@ impl Slabbed for DataType { } #[repr(u32)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy)] pub enum Dimensions { #[default] @@ -102,21 +105,7 @@ impl Slabbed for Dimensions { } } -/// Reads a u8 from the slab at the given **byte** offset. -fn get_u8_at_offset(offset: usize, slab: &[u32]) -> u8 { - let u32_offset = offset / 4; - let mut u32 = 0u32; - let _ = u32.read_slab(u32_offset, slab); - let byte_offset = offset % 4; - match byte_offset { - 0 => u32.to_le_bytes()[0], - 1 => u32.to_le_bytes()[1], - 2 => u32.to_le_bytes()[2], - 3 => u32.to_le_bytes()[3], - _ => 0, // unreachable - } -} - +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfAccessor { // The byte size of each element that this accessor describes. @@ -131,7 +120,7 @@ pub struct GltfAccessor { pub view_offset: u32, // The stride in bytes between vertex attributes or other interleavable data. pub view_stride: u32, - // The number of components within the buffer view - not to be confused with the + // The number of elements within the buffer view - not to be confused with the // number of bytes in the buffer view. pub count: u32, // The data type of components in the attribute. @@ -142,25 +131,44 @@ pub struct GltfAccessor { pub normalized: bool, } +macro_rules! println { + ($($arg:tt)*) => { + #[cfg(not(target_arch = "spirv"))] + { + std::println!($($arg)*); + } + } +} + impl GltfAccessor { - /// Retreive the nth element. - pub fn get1(&self, index: usize, slab: &[u32]) -> u32 { + /// Retreive one component of the nth element. + pub fn get(&self, index: usize, component_index: usize, slab: &[u32]) -> u32 { + println!("get {index} {component_index}"); let buffer = slab.read(self.buffer); + println!("buffer: {buffer:?}"); let buffer_start = buffer.0.at(0); - let byte_offset = - buffer_start.index() * 4 + self.view_offset as usize + index * self.size as usize; + let buffer_start_bytes = buffer_start.index() * 4; + let (mask, component_bytes, mut component_shift) = match self.data_type { + DataType::I8 => (0xF, 1, 8), + DataType::U8 => (0xF, 1, 8), + DataType::I16 => (0xFF, 2, 16), + DataType::U16 => (0xFF, 2, 16), + DataType::U32 => (0xFFFF, 4, 0), + DataType::F32 => (0xFFFF, 4, 0), + }; + component_shift *= component_index as u32; + let component_byte_offset = component_bytes * component_index; + let byte_offset = buffer_start_bytes + + self.view_offset as usize + + index * self.size as usize + + component_byte_offset; + println!("byte_offset: {byte_offset}"); let u32_offset = byte_offset / 4; + println!("u32_offset: {u32_offset}"); let mut t = 0u32; t.read_slab(u32_offset, slab); let byte_mod = byte_offset as u32 % 4; - let mask = match self.data_type { - DataType::I8 => 0xF, - DataType::U8 => 0xF, - DataType::I16 => 0xFF, - DataType::U16 => 0xFF, - DataType::U32 => 0xFFFF, - DataType::F32 => 0xFFFF, - }; + println!("byte_mod: {byte_mod}"); let shift = match byte_mod { 0 => 0, 1 => 8, @@ -168,46 +176,70 @@ impl GltfAccessor { 3 => 24, _ => 0, // unreachable }; - crate::bits::extract(t, (shift, mask)) + println!("mask: {mask:04x}"); + println!("shift: {shift}"); + println!("component_shift: {component_shift}"); + let u = crate::bits::extract(t, (shift + component_shift, mask)); + println!("u: {u}"); + u } - pub fn get_scalar_u32(&self, index: usize, slab: &[u32]) -> u32 { - let byte_offset = self.view_offset + index as u32 * self.view_stride; - let u32_offset = byte_offset / 4; - let mut scalar = 0u32; - let _ = scalar.read_slab(u32_offset as usize, slab); - scalar + pub fn get_u32(&self, vertex_index: usize, slab: &[u32]) -> u32 { + self.get(vertex_index, 0, slab) + } + + pub fn get_f32(&self, vertex_index: usize, slab: &[u32]) -> f32 { + f32::from_bits(self.get(vertex_index, 0, slab)) + } + + pub fn get_vec2(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec2 { + let x = f32::from_bits(self.get(vertex_index, 0, slab)); + let y = f32::from_bits(self.get(vertex_index, 1, slab)); + match self.dimensions { + Dimensions::Scalar => glam::Vec2::new(x, 0.0), + _ => glam::Vec2::new(x, y), + } + } + + pub fn get_vec3(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec3 { + let x = f32::from_bits(self.get(vertex_index, 0, slab)); + let y = f32::from_bits(self.get(vertex_index, 1, slab)); + let z = f32::from_bits(self.get(vertex_index, 2, slab)); + match self.dimensions { + Dimensions::Scalar => glam::Vec3::new(x, 0.0, 0.0), + Dimensions::Vec2 => glam::Vec3::new(x, y, 0.0), + _ => glam::Vec3::new(x, y, z), + } } pub fn get_vec4(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec4 { - let byte_offset = self.view_offset as usize + vertex_index * self.view_stride as usize; - let u32_offset = byte_offset / 4; - let mut vec4 = glam::Vec4::ZERO; + let x = f32::from_bits(self.get(vertex_index, 0, slab)); + let y = f32::from_bits(self.get(vertex_index, 1, slab)); + let z = f32::from_bits(self.get(vertex_index, 2, slab)); + let w = f32::from_bits(self.get(vertex_index, 3, slab)); match self.dimensions { - Dimensions::Scalar => { - vec4.x.read_slab(u32_offset + 0, slab); - } - Dimensions::Vec2 => { - vec4.x.read_slab(u32_offset + 0, slab); - vec4.y.read_slab(u32_offset + 1, slab); - } - Dimensions::Vec3 => { - vec4.x.read_slab(u32_offset + 0, slab); - vec4.y.read_slab(u32_offset + 1, slab); - vec4.z.read_slab(u32_offset + 2, slab); - } - Dimensions::Vec4 => { - vec4.x.read_slab(u32_offset + 0, slab); - vec4.y.read_slab(u32_offset + 1, slab); - vec4.z.read_slab(u32_offset + 2, slab); - vec4.w.read_slab(u32_offset + 3, slab); - } - _ => {} + Dimensions::Scalar => glam::Vec4::new(x, 0.0, 0.0, 0.0), + Dimensions::Vec2 => glam::Vec4::new(x, y, 0.0, 0.0), + Dimensions::Vec3 => glam::Vec4::new(x, y, z, 0.0), + _ => glam::Vec4::new(x, y, z, w), + } + } + + pub fn get_uvec4(&self, vertex_index: usize, slab: &[u32]) -> glam::UVec4 { + let x = self.get_u32(vertex_index, slab); + let y = self.get_u32(vertex_index, slab); + let z = self.get_u32(vertex_index, slab); + let w = self.get_u32(vertex_index, slab); + match self.dimensions { + Dimensions::Scalar => glam::UVec4::new(x, 0, 0, 0), + Dimensions::Vec2 => glam::UVec4::new(x, y, 0, 0), + Dimensions::Vec3 => glam::UVec4::new(x, y, z, 0), + _ => glam::UVec4::new(x, y, z, w), } - vec4 } } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfPrimitive { pub vertex_count: u32, @@ -227,41 +259,35 @@ impl GltfPrimitive { pub fn get_vertex(&self, vertex_index: usize, slab: &[u32]) -> crate::stage::Vertex { let index = if self.indices.is_some() { let indices = slab.read(self.indices); - let index = indices.get1(vertex_index, slab); + let index = indices.get_u32(vertex_index, slab); index as usize } else { vertex_index }; let positions = slab.read(self.positions); - let position = positions.get_vec4(index, slab); + let position = positions.get_vec3(index, slab); let normals = slab.read(self.normals); - let normal = normals.get_vec4(index, slab); - // TODO: If tangents are not present, calculate them. + let normal = normals.get_vec3(index, slab); let tangents = slab.read(self.tangents); let tangent = tangents.get_vec4(index, slab); let colors = slab.read(self.colors); let color = colors.get_vec4(index, slab); let tex_coords0 = slab.read(self.tex_coords0); - let tex_coords0 = tex_coords0.get_vec4(index, slab); + let tex_coords0 = tex_coords0.get_vec2(index, slab); let tex_coords1 = slab.read(self.tex_coords1); - let tex_coords1 = tex_coords1.get_vec4(index, slab); + let tex_coords1 = tex_coords1.get_vec2(index, slab); let uv = Vec4::new(tex_coords0.x, tex_coords0.y, tex_coords1.x, tex_coords1.y); let joints = slab.read(self.joints); - let joints = joints.get_vec4(index, slab); - let joints = [ - joints.x.to_bits(), - joints.y.to_bits(), - joints.z.to_bits(), - joints.w.to_bits(), - ]; + let joints = joints.get_uvec4(index, slab); + let joints = [joints.x, joints.y, joints.z, joints.w]; let weights = slab.read(self.weights); let weights = weights.get_vec4(index, slab); let weights = [weights.x, weights.y, weights.z, weights.w]; crate::stage::Vertex { - position, + position: position.extend(0.0), color, uv, - normal, + normal: normal.extend(0.0), tangent, joints, weights, @@ -269,12 +295,14 @@ impl GltfPrimitive { } } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfMesh { pub primitives: Array, pub weights: Array, } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Clone, Copy)] pub enum GltfCamera { Orthographic { @@ -581,6 +609,7 @@ pub struct GltfScene { pub nodes: Array>, } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfBufferView { pub buffer: Id, diff --git a/crates/renderling-shader/src/stage.rs b/crates/renderling-shader/src/stage.rs index 1a29611e..71b20e51 100644 --- a/crates/renderling-shader/src/stage.rs +++ b/crates/renderling-shader/src/stage.rs @@ -137,18 +137,18 @@ impl Vertex { mat } - pub fn generate_normal(a: Vertex, b: Vertex, c: Vertex) -> Vec3 { - let ab = a.position.xyz() - b.position.xyz(); - let ac = a.position.xyz() - c.position.xyz(); + pub fn generate_normal(a: Vec3, b: Vec3, c: Vec3) -> Vec3 { + let ab = a - b; + let ac = a - c; ab.cross(ac).normalize() } - pub fn generate_tangent(a: Vertex, b: Vertex, c: Vertex) -> Vec4 { - let ab = b.position.xyz() - a.position.xyz(); - let ac = c.position.xyz() - a.position.xyz(); + pub fn generate_tangent(a: Vec3, a_uv: Vec2, b: Vec3, b_uv: Vec2, c: Vec3, c_uv: Vec2) -> Vec4 { + let ab = b - a; + let ac = c - a; let n = ab.cross(ac); - let d_uv1 = b.uv.xy() - a.uv.xy(); - let d_uv2 = c.uv.xy() - a.uv.xy(); + let d_uv1 = b_uv - a_uv; + let d_uv2 = c_uv - a_uv; let denom = d_uv1.x * d_uv2.y - d_uv2.x * d_uv1.y; let denom_sign = if denom >= 0.0 { 1.0 } else { -1.0 }; let denom = denom.abs().max(f32::EPSILON) * denom_sign; @@ -1387,6 +1387,23 @@ pub fn compute_cull_entities( draws[i] = call; } +#[spirv(compute(threads(32)))] +/// A shader to ensure that we can extract i8 and i16 values from a storage buffer. +pub fn test_i8_i16_extraction( + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &mut [u32], + #[spirv(global_invocation_id)] global_id: UVec3, +) { + let index = global_id.x as usize; + let (value, _, _) = crate::bits::extract_i8(2, index, slab); + if value > 0 { + slab[index] = value as u32; + } + let (value, _, _) = crate::bits::extract_i16(2, index, slab); + if value > 0 { + slab[index] = value as u32; + } +} + #[cfg(test)] mod test { use crate::{self as renderling_shader, id::Id, slab::Slab}; diff --git a/crates/renderling/src/linkage/stage-new_stage_vertex.spv b/crates/renderling/src/linkage/stage-new_stage_vertex.spv index 6e0a5642..9e9fac85 100644 Binary files a/crates/renderling/src/linkage/stage-new_stage_vertex.spv and b/crates/renderling/src/linkage/stage-new_stage_vertex.spv differ diff --git a/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv b/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv new file mode 100644 index 00000000..d3a8e8ba Binary files /dev/null and b/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv differ diff --git a/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv b/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv index 1afdac93..d9155e82 100644 Binary files a/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv and b/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv differ diff --git a/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv b/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv index 5af1cf98..101332a8 100644 Binary files a/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv and b/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv differ diff --git a/crates/renderling/src/scene/gltf_support.rs b/crates/renderling/src/scene/gltf_support.rs index 8731dc8b..df4f3e29 100644 --- a/crates/renderling/src/scene/gltf_support.rs +++ b/crates/renderling/src/scene/gltf_support.rs @@ -742,13 +742,24 @@ impl GltfLoader { vertices.chunks_mut(3).for_each(|t| match t { [(a, _), (b, _), (c, _)] => { if gen_normals { - let n = Vertex::generate_normal(*a, *b, *c); + let n = Vertex::generate_normal( + a.position.xyz(), + b.position.xyz(), + c.position.xyz(), + ); a.normal = n.extend(a.normal.w); b.normal = n.extend(b.normal.w); c.normal = n.extend(c.normal.w); } if gen_tangents { - let tangent = Vertex::generate_tangent(*a, *b, *c); + let tangent = Vertex::generate_tangent( + a.position.xyz(), + a.uv.xy(), + b.position.xyz(), + b.uv.xy(), + c.position.xyz(), + c.position.xy(), + ); debug_assert!(!tangent.w.is_nan(), "tangent is NaN"); a.tangent = tangent; b.tangent = tangent; diff --git a/crates/renderling/src/stage.rs b/crates/renderling/src/stage.rs index 3baa7f10..8348ddc7 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -81,7 +81,7 @@ impl Stage { /// Write an object to the slab. pub fn write(&self, id: Id, object: &T) -> Result<(), SlabError> { - let () = self.slab.write(&self.device, &self.queue, id, object)?; + self.slab.write(&self.device, &self.queue, id, object)?; Ok(()) } @@ -567,6 +567,15 @@ impl Stage { id } + /// Returns all the draw operations on the stage. + pub(crate) fn get_draws(&self) -> Vec { + // UNWRAP: if we can't acquire the lock we want to panic. + let draws = self.draws.read().unwrap(); + match draws.deref() { + StageDrawStrategy::Direct(units) => units.clone(), + } + } + /// Erase the [`RenderUnit`] with the given `Id` from the stage. pub fn erase_unit(&self, id: Id) { let mut draws = self.draws.write().unwrap(); @@ -619,6 +628,7 @@ impl Stage { } /// A unit of work to be drawn. +#[derive(Clone, Copy, Debug, Default)] pub(crate) struct DrawUnit { pub id: Id, pub vertex_count: u32, diff --git a/crates/renderling/src/stage/gltf_support.rs b/crates/renderling/src/stage/gltf_support.rs index 15f21347..42ab75a8 100644 --- a/crates/renderling/src/stage/gltf_support.rs +++ b/crates/renderling/src/stage/gltf_support.rs @@ -9,7 +9,8 @@ use crate::{ }, SceneImage, }; -use glam::{Quat, Vec3, Vec4}; +use glam::{Quat, Vec2, Vec3, Vec4}; +use renderling_shader::stage::Vertex; use snafu::{OptionExt, ResultExt, Snafu}; #[derive(Debug, Snafu)] @@ -53,11 +54,16 @@ impl From for StageGltfError { pub fn get_vertex_count(primitive: &gltf::Primitive<'_>) -> u32 { if let Some(indices) = primitive.indices() { - indices.count() as u32 + let count = indices.count() as u32; + log::trace!(" has {count} indices"); + count } else { if let Some(positions) = primitive.get(&gltf::Semantic::Positions) { - positions.count() as u32 + let count = positions.count() as u32; + log::trace!(" has {count} positions"); + count } else { + log::trace!(" has no indices nor positions"); 0 } } @@ -120,20 +126,22 @@ impl Stage { log::trace!("Loading views into the GPU"); let views = self.allocate_array(document.views().len()); - for view in document.views() { + log::trace!(" reserved array: {views:?}"); + for (i, view) in document.views().enumerate() { let buffer = buffers.at(view.buffer().index()); let offset = view.offset() as u32; let length = view.length() as u32; let stride = view.stride().unwrap_or_default() as u32; - self.write( - views.at(view.index()), - &GltfBufferView { - buffer, - offset, - length, - stride, - }, - )?; + let id = views.at(view.index()); + let gltf_view = GltfBufferView { + buffer, + offset, + length, + stride, + }; + log::trace!(" view {i} id: {id:#?}"); + log::trace!(" writing view: {gltf_view:#?}"); + self.write(id, &gltf_view)?; } log::trace!("Loading accessors into the GPU"); @@ -391,8 +399,10 @@ impl Stage { } log::trace!("Loading meshes"); let meshes = self.allocate_array::(document.meshes().len()); + log::trace!(" reserved array: {meshes:#?}"); for mesh in document.meshes() { let primitives = self.allocate_array::(mesh.primitives().len()); + log::trace!(" reserved array: {primitives:#?}"); for (j, primitive) in mesh.primitives().enumerate() { log::trace!(" primitive {j}"); debug_assert_eq!(j, primitive.index()); @@ -420,6 +430,67 @@ impl Stage { accessors.at(acc.index()) }) .unwrap_or_default(); + + // We may need the positions and uvs in-memory if we need + // to generate normals or tangents, so we'll keep them in + // a vec, if necessary, and access them through a function. + let mut position_vec: Option> = None; + fn get_positions<'a>( + buffer_data: &[gltf::buffer::Data], + primitive: &gltf::Primitive<'_>, + position_vec: &'a mut Option>, + ) -> &'a Vec { + if position_vec.is_none() { + let reader = primitive.reader(|buffer| { + let data = buffer_data.get(buffer.index())?; + Some(data.0.as_slice()) + }); + let positions = reader + .read_positions() + .map(|ps| ps.map(Vec3::from).collect::>()) + .unwrap_or_default(); + *position_vec = Some(positions); + } + // UNWRAP: safe because we just set it to `Some` if previously `None` + position_vec.as_ref().unwrap() + } + + let mut positions_and_uv_vec: Option> = None; + fn get_uvs<'a>( + buffer_data: &[gltf::buffer::Data], + primitive: &gltf::Primitive<'_>, + positions: &'a mut Option>, + positions_and_uv_vec: &'a mut Option>, + ) -> &'a Vec<(Vec3, Vec2)> { + // ensures we have position + if positions_and_uv_vec.is_none() { + let positions = get_positions(buffer_data, primitive, positions); + let reader = primitive.reader(|buffer| { + let data = buffer_data.get(buffer.index())?; + Some(data.0.as_slice()) + }); + let puvs: Vec<(Vec3, Vec2)> = reader + .read_tex_coords(0) + .map(|uvs| { + positions + .iter() + .copied() + .zip(uvs.into_f32().map(Vec2::from)) + .collect() + }) + .unwrap_or_else(|| { + positions + .iter() + .copied() + .zip(std::iter::repeat(Vec2::ZERO)) + .collect() + }); + *positions_and_uv_vec = Some(puvs); + } + // UNWRAP: safe because we just set it to `Some` + positions_and_uv_vec.as_ref().unwrap() + } + let normals = primitive .get(&gltf::Semantic::Normals) .map(|acc| { @@ -428,7 +499,35 @@ impl Stage { log_accessor(gltf_accessor); accessors.at(acc.index()) }) - .unwrap_or_default(); + .unwrap_or_else(|| { + log::trace!(" generating normals"); + // Generate the normals + let normals = get_positions(&buffer_data, &primitive, &mut position_vec) + .chunks(3) + .flat_map(|chunk| match chunk { + [a, b, c] => { + let n = Vertex::generate_normal(*a, *b, *c); + [n, n, n] + } + _ => panic!("not triangles!"), + }) + .collect::>(); + let normals_array = self.append_array(&normals); + let buffer = GltfBuffer(normals_array.into_u32_array()); + let buffer_id = self.append(&buffer); + debug_assert_eq!(8 * 3, std::mem::size_of::<[f32; 3]>()); + let accessor = GltfAccessor { + size: 8 * 3, + buffer: buffer_id, + view_offset: 0, + view_stride: 8 * 3, + count: normals.len() as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: true, + }; + self.append(&accessor) + }); let tangents = primitive .get(&gltf::Semantic::Tangents) .map(|acc| { @@ -437,7 +536,41 @@ impl Stage { log_accessor(gltf_accessor); accessors.at(acc.index()) }) - .unwrap_or_default(); + .unwrap_or_else(|| { + log::trace!(" generating tangents"); + let p_uvs = get_uvs( + &buffer_data, + &primitive, + &mut position_vec, + &mut positions_and_uv_vec, + ); + let tangents = p_uvs + .chunks(3) + .flat_map(|chunk| match chunk { + [(a, a_uv), (b, b_uv), (c, c_uv)] => { + let t = + Vertex::generate_tangent(*a, *a_uv, *b, *b_uv, *c, *c_uv); + [t, t, t] + } + _ => panic!("not triangles!"), + }) + .collect::>(); + let tangents_array = self.append_array(&tangents); + let buffer = GltfBuffer(tangents_array.into_u32_array()); + let buffer_id = self.append(&buffer); + debug_assert_eq!(4 * 3, std::mem::size_of::<[f32; 3]>()); + let accessor = GltfAccessor { + size: 8 * 3, + buffer: buffer_id, + view_offset: 0, + view_stride: 8 * 3, + count: tangents.len() as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: true, + }; + self.append(&accessor) + }); let colors = primitive .get(&gltf::Semantic::Colors(0)) .map(|acc| { @@ -484,22 +617,22 @@ impl Stage { }) .unwrap_or_default(); - self.write( - primitives.at(primitive.index()), - &GltfPrimitive { - vertex_count: vertex_count as u32, - material, - indices, - positions, - normals, - tangents, - colors, - tex_coords0, - tex_coords1, - joints, - weights, - }, - )?; + let id = primitives.at(primitive.index()); + let prim = GltfPrimitive { + vertex_count: vertex_count as u32, + material, + indices, + positions, + normals, + tangents, + colors, + tex_coords0, + tex_coords1, + joints, + weights, + }; + log::trace!(" writing primitive {id:?}:\n{prim:#?}"); + self.write(id, &prim)?; } let weights = mesh.weights().unwrap_or(&[]); let weights = self.append_array(weights); @@ -782,11 +915,16 @@ impl Stage { #[cfg(test)] mod test { - use glam::Vec3; + use glam::{Vec2, Vec3, Vec4}; use crate::{ - shader::{array::Array, gltf::*, slab::Slab, stage::Camera}, - Id, Renderling, Stage, + shader::{ + array::Array, + gltf::*, + slab::Slab, + stage::{Camera, RenderUnit}, + }, + DrawUnit, Id, Renderling, Stage, }; #[test] @@ -840,11 +978,11 @@ mod test { dimensions: Dimensions::Scalar, normalized: false, }; - let i0 = accessor.get1(0, &data); + let i0 = accessor.get(0, 0, &data); assert_eq!(1, i0); - let i1 = accessor.get1(1, &data); + let i1 = accessor.get(1, 0, &data); assert_eq!(1, i1); - let i2 = accessor.get1(2, &data); + let i2 = accessor.get(2, 0, &data); assert_eq!(1, i2); } @@ -863,7 +1001,7 @@ mod test { let projection = crate::camera::perspective(100.0, 50.0); let position = Vec3::new(1.0, 0.5, 1.5); let view = crate::camera::look_at(position, Vec3::new(1.0, 0.5, 0.0), Vec3::Y); - let stage = Stage::new(device, queue); + let stage = Stage::new(device.clone(), queue.clone()); stage.configure_graph(&mut r, true); let gpu_doc = stage .load_gltf_document(&document, buffers, images) @@ -876,9 +1014,141 @@ mod test { let camera_id = stage.append(&camera); let default_scene = document.default_scene().unwrap(); - let _units = stage.draw_gltf_scene(&gpu_doc, camera_id, default_scene); + let unit_ids = stage.draw_gltf_scene(&gpu_doc, camera_id, default_scene); + assert_eq!(2, unit_ids.len()); + + let data = futures_lite::future::block_on(stage.slab.read_raw( + &device, + &queue, + 0, + stage.slab.len(), + )) + .unwrap(); + + #[derive(Debug, Default)] + struct VertexInvocation { + draw: DrawUnit, + instance_index: u32, + vertex_index: u32, + render_unit_id: Id, + render_unit: RenderUnit, + out_camera: u32, + out_material: u32, + out_color: Vec4, + out_uv0: Vec2, + out_uv1: Vec2, + out_norm: Vec3, + out_tangent: Vec3, + out_bitangent: Vec3, + out_pos: Vec3, + clip_pos: Vec4, + } + + let draws = stage.get_draws(); + let slab = &data; + + let indices = draws + .iter() + .map(|draw| { + let unit_id = draw.id; + let unit = slab.read(unit_id); + let vertex_data_id = match unit.vertex_data { + renderling_shader::stage::VertexData::Native(_) => panic!("should be gltf"), + renderling_shader::stage::VertexData::Gltf(id) => id, + }; + let vertex_data = slab.read(vertex_data_id); + let mesh = slab.read(vertex_data.mesh); + let primitive_id = mesh.primitives.at(vertex_data.primitive_index as usize); + let primitive = slab.read(primitive_id); + if primitive.indices.is_some() { + let indices_accessor = slab.read(primitive.indices); + (0..draw.vertex_count) + .map(|i| { + let index = indices_accessor.get_u32(i as usize, slab); + index + }) + .collect::>() + } else { + (0..draw.vertex_count).collect::>() + } + }) + .collect::>(); + assert_eq!([0, 1, 2], indices[0].as_slice()); + assert_eq!([0, 1, 2], indices[1].as_slice()); + let clip_positions = draws + .iter() + .map(|draw| { + let unit_id = draw.id; + let unit = slab.read(unit_id); + let vertex_data_id = match unit.vertex_data { + renderling_shader::stage::VertexData::Native(_) => panic!("should be gltf"), + renderling_shader::stage::VertexData::Gltf(id) => id, + }; + let vertex_data = slab.read(vertex_data_id); + let mesh = slab.read(vertex_data.mesh); + let primitive_id = mesh.primitives.at(vertex_data.primitive_index as usize); + let primitive = slab.read(primitive_id); + let indices = if primitive.indices.is_some() { + let indices_accessor = slab.read(primitive.indices); + (0..draw.vertex_count) + .map(|i| { + let index = indices_accessor.get_u32(i as usize, slab); + index + }) + .collect::>() + } else { + (0..draw.vertex_count).collect::>() + }; + println!("positions\n\n"); + let pos_accessor = slab.read(primitive.positions); + indices + .into_iter() + .map(|index| pos_accessor.get_vec3(index as usize, slab)) + .collect::>() + }) + .collect::>(); + panic!("clip_positions: {clip_positions:#?}"); + + //let invocations = draws + // .into_iter() + // .map(|draw| { + // let render_unit_id = draw.id; + // let instance_index = render_unit_id.inner(); + // let render_unit = data.read(render_unit_id); + // let data = &data; + // (0..draw.vertex_count) + // .map(move |vertex_index| { + // let mut invocation = VertexInvocation { + // draw, + // render_unit_id, + // render_unit, + // instance_index, + // vertex_index, + // ..Default::default() + // }; + // renderling_shader::stage::new_stage_vertex( + // instance_index, + // vertex_index, + // data, + // &mut invocation.out_camera, + // &mut invocation.out_material, + // &mut invocation.out_color, + // &mut invocation.out_uv0, + // &mut invocation.out_uv1, + // &mut invocation.out_norm, + // &mut invocation.out_tangent, + // &mut invocation.out_bitangent, + // &mut invocation.out_pos, + // &mut invocation.clip_pos, + // ); + // invocation + // }) + // .collect::>() + // }) + // .collect::>(); + //panic!("vertex_invocations: {invocations:#?}"); let img = r.render_image().unwrap(); - img_diff::assert_img_eq("gltf_simple_meshes.png", img); + img_diff::save("gltf_simple_meshes.png", img); } }