diff --git a/Cargo.toml b/Cargo.toml index 7fc42aa..fa9b57d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,9 @@ members = [ "shaders/pathtracer", "shaders/rasterizer", "shaders/tonemap", + "scene", ] +resolver = "2" [workspace.package] version = "0.1.0" @@ -13,6 +15,15 @@ edition = "2021" authors = ["Shashank Singh "] repository = "https://github.com/sshashank124/tsunami" +[workspace.dependencies] +bytemuck = { version = "1.14", features = ["derive"] } +glam = { version = "0.24", default-features = false, features = ["libm"] } +scene = { path = "scene" } +serde = { version = "1.0", default-features = false } +shared = { path = "shared" } +spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu" } +spirv-builder = { git = "https://github.com/EmbarkStudios/rust-gpu" } + [profile.release] opt-level = 3 codegen-units = 16 diff --git a/runner/Cargo.toml b/runner/Cargo.toml index 9f25c8d..68c3533 100644 --- a/runner/Cargo.toml +++ b/runner/Cargo.toml @@ -1,16 +1,19 @@ [package] name = "runner" -version = "0.1.0" +version.workspace = true edition.workspace = true [dependencies] ash = { version = "0.37", features = ["linked"] } -gltf = "1" +bytemuck = { workspace = true } +glam = { workspace = true } gpu-allocator = { version = "0.23", default-features = false, features = [ "vulkan" ] } image = "0.24" -shared = { path = "../shared" } +scene = { workspace = true } +shared = { workspace = true } +spirv-std = { workspace = true } tobj = "4" winit = "0.29" [build-dependencies] -spirv-builder = "0.9" +spirv-builder = { workspace = true } diff --git a/runner/src/app.rs b/runner/src/app.rs index 6ea2862..ca7a332 100644 --- a/runner/src/app.rs +++ b/runner/src/app.rs @@ -9,7 +9,7 @@ use winit::{ }; use crate::{ - data::{camera, gltf_scene::GltfScene}, + data::camera, gpu::{context::Context, Destroy}, input, render, }; @@ -31,10 +31,10 @@ pub struct App { } impl App { - pub fn new(window: &Window, gltf_file: &str) -> Self { + pub fn new(window: &Window, scene_file: &str) -> Self { let mut ctx = Context::init(window); - let scene = GltfScene::load(gltf_file); + let scene = scene::load(scene_file); let camera_controller = camera::Controller::new( scene.info.bounding_box.size() * 1.2 + scene.info.bounding_box.center(), diff --git a/runner/src/data/camera.rs b/runner/src/data/camera.rs index 492a5af..88171a2 100644 --- a/runner/src/data/camera.rs +++ b/runner/src/data/camera.rs @@ -1,4 +1,4 @@ -use shared::{glam, Camera, Transform}; +use shared::{Camera, Transform}; mod conf { pub const Z_NEAR: f32 = 1e-1; diff --git a/runner/src/data/gltf_scene.rs b/runner/src/data/gltf_scene.rs deleted file mode 100644 index 6351829..0000000 --- a/runner/src/data/gltf_scene.rs +++ /dev/null @@ -1,208 +0,0 @@ -use std::collections::HashMap; - -use gltf::{buffer, mesh, scene}; - -use shared::{self, glam}; - -use super::bounding_box::BoundingBox; - -pub struct GltfScene { - pub data: Data, - pub info: Info, -} - -#[derive(Default)] -pub struct Data { - pub indices: Vec, - pub vertices: Vec, - pub materials: Vec, -} - -#[derive(Debug, Default)] -pub struct Info { - pub primitive_infos: Vec, - pub primitive_sizes: Vec, - pub instances: Vec, - pub bounding_box: BoundingBox, -} - -#[derive(Clone, Debug)] -pub struct PrimitiveSize { - pub indices_size: u32, - pub vertices_size: u32, -} - -#[derive(Clone, Debug)] -pub struct Instance { - pub primitive_index: usize, - pub transform: glam::Mat4, -} - -impl GltfScene { - pub fn load(filename: &str) -> Self { - let (document, buffers, _images) = gltf::import(filename).expect("Couldn't import file"); - - let scene = document - .default_scene() - .unwrap_or_else(|| document.scenes().next().expect("No scenes found")); - - let mut info = Info::default(); - let mut data = Data::default(); - - let mut bounding_boxes = Vec::new(); - - // json mesh index -> loaded primitives range - let mut processed_meshes = HashMap::new(); - // json material index -> loaded material index - let mut processed_materials = HashMap::new(); - - scene.nodes().traverse_meshes( - glam::Mat4::IDENTITY, - &mut |mesh: &mesh::Mesh<'_>, transform| { - let primitives_range = processed_meshes - .entry(mesh.index()) - .or_insert_with(|| { - mesh.primitives().for_each(|primitive| { - let (primitive_info, primitive_size) = - data.add_primitive(&primitive, &buffers, &mut processed_materials); - info.primitive_infos.push(primitive_info); - info.primitive_sizes.push(primitive_size); - let bbox = primitive.bounding_box(); - bounding_boxes.push(BoundingBox::new(bbox.min, bbox.max)); - }); - // range of newly added primitive indices - (info.primitive_infos.len() - mesh.primitives().len()) - ..info.primitive_infos.len() - }) - .clone(); - - info.instances - .extend(primitives_range.map(|primitive_index| Instance { - primitive_index, - transform, - })); - }, - ); - - info.bounding_box = info - .instances - .iter() - .map(|instance| bounding_boxes[instance.primitive_index].transform(instance.transform)) - .fold(BoundingBox::default(), BoundingBox::union); - - Self { data, info } - } -} - -impl Data { - fn add_primitive( - &mut self, - primitive: &mesh::Primitive<'_>, - raw_buffers: &[buffer::Data], - processed_materials: &mut HashMap, - ) -> (shared::PrimitiveInfo, PrimitiveSize) { - assert_eq!(primitive.mode(), mesh::Mode::Triangles); - - let reader = primitive.reader(|buffer| Some(&raw_buffers[buffer.index()])); - - let indices = reader.read_indices().expect("No indices found").into_u32(); - let indices_offset = self.indices.len() as _; - self.indices.extend(indices); - let indices_size = self.indices.len() as u32 - indices_offset; - - let positions = reader.read_positions().expect("No positions found"); - let normals = reader.read_normals().map_or_else( - || Box::new(std::iter::repeat_with(Default::default)) as Box<_>, - |nn| Box::new(nn) as Box>, - ); - let tex_coords = reader - .read_tex_coords(0) - .map(mesh::util::ReadTexCoords::into_f32) - .map_or_else( - || Box::new(std::iter::repeat_with(Default::default)) as Box<_>, - |uv| Box::new(uv) as Box>, - ); - - let vertices = positions - .zip(normals) - .zip(tex_coords) - .map(shared::Vertex::from); - let vertices_offset = self.vertices.len() as _; - self.vertices.extend(vertices); - let vertices_size = self.vertices.len() as u32 - vertices_offset; - - let material = primitive.material(); - let material = *processed_materials - .entry(material.index().unwrap_or_default()) - .or_insert_with(|| self.add_material(&material)) as _; - - let primitive_info = shared::PrimitiveInfo { - indices_offset, - vertices_offset, - material, - }; - - let primitive_size = PrimitiveSize { - indices_size, - vertices_size, - }; - - (primitive_info, primitive_size) - } - - fn add_material(&mut self, material: &gltf::Material) -> usize { - let index = self.materials.len(); - - self.materials.push(shared::Material { - color: material.pbr_metallic_roughness().base_color_factor().into(), - emittance: glam::Vec3::from(material.emissive_factor()).extend(1.0), - }); - - index - } -} - -trait Traversable { - fn traverse_meshes( - self, - transform: glam::Mat4, - f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), - ); -} - -impl Traversable for scene::Node<'_> { - fn traverse_meshes( - self, - transform: glam::Mat4, - f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), - ) { - let global_transform = - transform * glam::Mat4::from_cols_array_2d(&self.transform().matrix()); - if let Some(mesh) = self.mesh() { - f(&mesh, global_transform); - } - self.children().traverse_meshes(global_transform, f); - } -} - -macro_rules! impl_traversable { - ($t:ty) => { - impl Traversable for $t { - fn traverse_meshes( - self, - transform: glam::Mat4, - f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), - ) { - self.for_each(|elem| elem.traverse_meshes(transform, f)); - } - } - }; -} -impl_traversable!(scene::iter::Nodes<'_>); -impl_traversable!(scene::iter::Children<'_>); - -impl PrimitiveSize { - pub const fn count(&self) -> u32 { - self.indices_size / 3 - } -} diff --git a/runner/src/data/mod.rs b/runner/src/data/mod.rs index 6eab387..b84959a 100644 --- a/runner/src/data/mod.rs +++ b/runner/src/data/mod.rs @@ -1,3 +1 @@ -pub mod bounding_box; pub mod camera; -pub mod gltf_scene; diff --git a/runner/src/gpu/acceleration_structure.rs b/runner/src/gpu/acceleration_structure.rs index bfd4f6d..eab338b 100644 --- a/runner/src/gpu/acceleration_structure.rs +++ b/runner/src/gpu/acceleration_structure.rs @@ -1,10 +1,10 @@ use std::{ops::Deref, slice}; use ash::vk; -use shared::{self, bytemuck}; + +use shared::{scene, Vertex}; use crate::{ - data::gltf_scene, gpu::{query_pool::QueryPool, scene::Scene, scope::FlushableScope}, util, }; @@ -339,21 +339,21 @@ impl<'a> GeometryInfo<'a> { } fn for_primitive( - scene_info: &'a shared::SceneInfo, - primitive_info: &shared::PrimitiveInfo, - primitive_size: &gltf_scene::PrimitiveSize, + scene: &'a Scene, + primitive_info: &scene::PrimitiveInfo, + primitive_size: &scene::PrimitiveSize, ) -> Self { let triangles = vk::AccelerationStructureGeometryTrianglesDataKHR::builder() .vertex_format(vk::Format::R32G32B32_SFLOAT) - .vertex_stride(std::mem::size_of::() as _) + .vertex_stride(std::mem::size_of::() as _) .max_vertex(primitive_size.vertices_size - 1) .vertex_data(vk::DeviceOrHostAddressConstKHR { - device_address: scene_info.vertices_address - + bytemuck::offset_of!(shared::Vertex, position) as vk::DeviceAddress, + device_address: scene.device_addresses.vertices + + bytemuck::offset_of!(Vertex, position) as vk::DeviceAddress, }) .index_type(vk::IndexType::UINT32) .index_data(vk::DeviceOrHostAddressConstKHR { - device_address: scene_info.indices_address, + device_address: scene.device_addresses.indices, }) .build(); @@ -379,7 +379,7 @@ impl<'a> GeometryInfo<'a> { .iter() .zip(scene.host_info.primitive_sizes.iter()) .map(|(primitive_info, primitive_size)| { - Self::for_primitive(&scene.device_info, primitive_info, primitive_size) + Self::for_primitive(scene, primitive_info, primitive_size) }) .collect() } @@ -388,8 +388,8 @@ impl<'a> GeometryInfo<'a> { impl InstancesInfo { fn for_instances( ctx: &mut Context, - scope: &mut FlushableScope, - instances: &[gltf_scene::Instance], + scope: &FlushableScope, + instances: &[scene::Instance], blases: &[AccelerationStructure], ) -> Self { let instances = Instance::for_instances(instances, blases); @@ -426,7 +426,7 @@ impl InstancesInfo { } impl Instance { - fn for_instance(instance: &gltf_scene::Instance, blases: &[AccelerationStructure]) -> Self { + fn for_instance(instance: &scene::Instance, blases: &[AccelerationStructure]) -> Self { let t = &instance.transform; Self(vk::AccelerationStructureInstanceKHR { transform: vk::TransformMatrixKHR { @@ -450,10 +450,7 @@ impl Instance { }) } - fn for_instances( - instances: &[gltf_scene::Instance], - blases: &[AccelerationStructure], - ) -> Vec { + fn for_instances(instances: &[scene::Instance], blases: &[AccelerationStructure]) -> Vec { instances .iter() .map(|instance| Self::for_instance(instance, blases)) diff --git a/runner/src/gpu/buffer.rs b/runner/src/gpu/buffer.rs index c6c5e24..e2444db 100644 --- a/runner/src/gpu/buffer.rs +++ b/runner/src/gpu/buffer.rs @@ -2,8 +2,6 @@ use std::{ops::Deref, slice}; use ash::vk; -use shared::bytemuck; - use super::{alloc, context::Context, scope::OneshotScope, Destroy}; pub struct Buffer { diff --git a/runner/src/gpu/context/device.rs b/runner/src/gpu/context/device.rs index 4ac7501..c329bad 100644 --- a/runner/src/gpu/context/device.rs +++ b/runner/src/gpu/context/device.rs @@ -73,7 +73,7 @@ impl Device { unsafe { self.device .create_semaphore(&create_info, None) - .unwrap_or_else(|err| panic!("Failed to create `{}` semaphore: {}", name, err)) + .unwrap_or_else(|err| panic!("Failed to create `{name}` semaphore: {err}")) } } @@ -86,7 +86,7 @@ impl Device { unsafe { self.device .create_fence(&create_info, None) - .unwrap_or_else(|err| panic!("Failed to create `{}` fence: {}", name, err)) + .unwrap_or_else(|err| panic!("Failed to create `{name}` fence: {err}")) } } diff --git a/runner/src/gpu/framebuffers.rs b/runner/src/gpu/framebuffers.rs index ce77cac..3f8c791 100644 --- a/runner/src/gpu/framebuffers.rs +++ b/runner/src/gpu/framebuffers.rs @@ -1,11 +1,6 @@ use ash::vk; -use super::{ - context::Context, - image::{format, Image}, - scope::OneshotScope, - Destroy, -}; +use super::{context::Context, image, scope::OneshotScope, Destroy}; pub const CLEAR_VALUES: &[vk::ClearValue] = &[ vk::ClearValue { @@ -21,26 +16,26 @@ pub const CLEAR_VALUES: &[vk::ClearValue] = &[ }, ]; -pub struct Framebuffers { - pub depth: Image<{ format::DEPTH }>, +pub struct Framebuffers { + pub depth: image::Image<{ image::Format::Depth }>, pub framebuffers: Vec, } -impl Framebuffers<{ FORMAT }> { +impl Framebuffers<{ FORMAT }> { pub fn create( ctx: &mut Context, - scope: &mut OneshotScope, + scope: &OneshotScope, name: &str, render_pass: vk::RenderPass, resolution: vk::Extent2D, - colors: &[Image<{ FORMAT }>], + colors: &[image::Image<{ FORMAT }>], ) -> Self { let depth = { let info = vk::ImageCreateInfo { extent: resolution.into(), ..Default::default() }; - Image::create(ctx, scope, &format!("{name} [DEPTH]"), &info, None) + image::Image::create(ctx, scope, &format!("{name} [DEPTH]"), &info, None) }; let framebuffers = colors @@ -65,7 +60,7 @@ impl Framebuffers<{ FORMAT }> { } } -impl Destroy for Framebuffers<{ FORMAT }> { +impl Destroy for Framebuffers<{ FORMAT }> { unsafe fn destroy_with(&mut self, ctx: &mut Context) { self.framebuffers .iter() diff --git a/runner/src/gpu/image.rs b/runner/src/gpu/image.rs index 4ab9c48..9f5de39 100644 --- a/runner/src/gpu/image.rs +++ b/runner/src/gpu/image.rs @@ -1,17 +1,29 @@ -use std::{ops::Deref, slice}; +use std::{marker::ConstParamTy, ops::Deref, slice}; use ash::vk; use super::{alloc, buffer::Buffer, context::Context, scope::OneshotScope, Destroy}; -pub mod format { - pub const HDR: ash::vk::Format = ash::vk::Format::R32G32B32A32_SFLOAT; - pub const COLOR: ash::vk::Format = ash::vk::Format::R8G8B8A8_SRGB; - pub const DEPTH: ash::vk::Format = ash::vk::Format::D32_SFLOAT; - pub const SWAPCHAIN: ash::vk::Format = ash::vk::Format::UNDEFINED; +#[derive(PartialEq, Eq, ConstParamTy)] +pub enum Format { + Hdr, + Color, + Depth, + Swapchain, } -pub struct Image { +impl From for vk::Format { + fn from(format: Format) -> Self { + match format { + Format::Hdr => Self::R32G32B32A32_SFLOAT, + Format::Color => Self::R8G8B8A8_SRGB, + Format::Depth => Self::D32_SFLOAT, + Format::Swapchain => Self::UNDEFINED, + } + } +} + +pub struct Image { pub image: vk::Image, pub view: vk::ImageView, allocation: Option, @@ -23,8 +35,8 @@ pub struct BarrierInfo { access: vk::AccessFlags, } -impl Image { - pub fn new( +impl Image { + pub fn new_of_format( ctx: &Context, image: vk::Image, format: vk::Format, @@ -50,6 +62,10 @@ impl Image { } } + pub fn new(ctx: &Context, image: vk::Image, allocation: Option) -> Self { + Self::new_of_format(ctx, image, FORMAT.into(), allocation) + } + pub fn create( ctx: &mut Context, scope: &OneshotScope, @@ -64,7 +80,7 @@ impl Image { samples: vk::SampleCountFlags::TYPE_1, initial_layout: vk::ImageLayout::UNDEFINED, tiling: vk::ImageTiling::OPTIMAL, - format: FORMAT, + format: FORMAT.into(), usage: Self::usage_flags() | info.usage, ..*info }; @@ -94,7 +110,7 @@ impl Image { .expect("Failed to bind memory"); } - let image = Self::new(ctx, image, FORMAT, Some(allocation)); + let image = Self::new(ctx, image, Some(allocation)); if let Some(to) = to { image.transition_layout(ctx, scope, &BarrierInfo::INIT, to); @@ -145,20 +161,20 @@ impl Image { const fn usage_flags() -> vk::ImageUsageFlags { match FORMAT { - format::DEPTH => vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, + Format::Depth => vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, _ => vk::ImageUsageFlags::SAMPLED, } } const fn aspect_flags() -> vk::ImageAspectFlags { match FORMAT { - format::DEPTH => vk::ImageAspectFlags::DEPTH, + Format::Depth => vk::ImageAspectFlags::DEPTH, _ => vk::ImageAspectFlags::COLOR, } } } -impl Image<{ format::COLOR }> { +impl Image<{ Format::Color }> { pub fn create_from_image( ctx: &mut Context, scope: &mut OneshotScope, @@ -247,7 +263,7 @@ impl BarrierInfo { }; } -impl Destroy for Image { +impl Destroy for Image { unsafe fn destroy_with(&mut self, ctx: &mut Context) { ctx.destroy_image_view(self.view, None); if let Some(allocation) = self.allocation.take() { @@ -259,7 +275,7 @@ impl Destroy for Image { } } -impl Deref for Image { +impl Deref for Image { type Target = vk::Image; fn deref(&self) -> &Self::Target { &self.image diff --git a/runner/src/gpu/model.rs b/runner/src/gpu/model.rs index 6b00fc7..3d26dc7 100644 --- a/runner/src/gpu/model.rs +++ b/runner/src/gpu/model.rs @@ -1,6 +1,6 @@ use super::{ context::Context, - image::{format, Image}, + image::{Format, Image}, scope::OneshotScope, texture::Texture, Destroy, @@ -8,7 +8,7 @@ use super::{ pub struct Model { pub texture_image: image::RgbaImage, - pub texture: Texture<{ format::COLOR }>, + pub texture: Texture<{ Format::Color }>, } impl Model { @@ -34,7 +34,7 @@ impl Model { ctx: &mut Context, scope: &mut OneshotScope, texture_image: &image::RgbaImage, - ) -> Texture<{ format::COLOR }> { + ) -> Texture<{ Format::Color }> { let image = Image::create_from_image(ctx, scope, "Texture", texture_image); Texture::from_image(ctx, image) } diff --git a/runner/src/gpu/scene.rs b/runner/src/gpu/scene.rs index 736d7b0..c8c9657 100644 --- a/runner/src/gpu/scene.rs +++ b/runner/src/gpu/scene.rs @@ -1,50 +1,47 @@ use ash::vk; -use shared::{self, bytemuck}; - use super::{buffer::Buffer, context::Context, scope::OneshotScope, Destroy}; -use crate::data::gltf_scene; pub struct Scene { pub indices: Buffer, pub vertices: Buffer, - pub materials: Buffer, pub primitives: Buffer, - pub host_info: gltf_scene::Info, - pub device_info: shared::SceneInfo, + pub materials: Buffer, + pub host_info: scene::Info, + pub device_addresses: DeviceAddresses, +} + +pub struct DeviceAddresses { + pub indices: u64, + pub vertices: u64, } impl Scene { - pub fn create( - ctx: &mut Context, - scope: &mut OneshotScope, - scene: gltf_scene::GltfScene, - ) -> Self { + pub fn create(ctx: &mut Context, scope: &mut OneshotScope, scene: scene::Scene) -> Self { let (vertices, indices) = Self::init_vertex_index_buffer(ctx, scope, &scene.data); - let materials = Self::init_materials_buffer(ctx, scope, &scene.data); let primitives = Self::init_primitives_buffer(ctx, scope, &scene.info); + let materials = Self::init_materials_buffer(ctx, scope, &scene.data); let host_info = scene.info; - let device_info = shared::SceneInfo { - indices_address: indices.get_device_address(ctx), - vertices_address: vertices.get_device_address(ctx), + let device_addresses = DeviceAddresses { + indices: indices.get_device_address(ctx), + vertices: vertices.get_device_address(ctx), }; Self { indices, vertices, - materials, primitives, - + materials, host_info, - device_info, + device_addresses, } } fn init_vertex_index_buffer( ctx: &mut Context, scope: &mut OneshotScope, - scene: &gltf_scene::Data, + scene: &scene::Data, ) -> (Buffer, Buffer) { let vertices = { let create_info = vk::BufferCreateInfo::builder().usage( @@ -86,7 +83,7 @@ impl Scene { fn init_primitives_buffer( ctx: &mut Context, scope: &mut OneshotScope, - scene: &gltf_scene::Info, + scene: &scene::Info, ) -> Buffer { let create_info = vk::BufferCreateInfo::builder().usage(vk::BufferUsageFlags::STORAGE_BUFFER); @@ -103,7 +100,7 @@ impl Scene { fn init_materials_buffer( ctx: &mut Context, scope: &mut OneshotScope, - scene: &gltf_scene::Data, + scene: &scene::Data, ) -> Buffer { let create_info = vk::BufferCreateInfo::builder().usage(vk::BufferUsageFlags::STORAGE_BUFFER); diff --git a/runner/src/gpu/swapchain.rs b/runner/src/gpu/swapchain.rs index 4f1ee67..c13dde1 100644 --- a/runner/src/gpu/swapchain.rs +++ b/runner/src/gpu/swapchain.rs @@ -2,27 +2,18 @@ use std::slice; use ash::vk; -use crate::gpu::{ - context::Context, - framebuffers::Framebuffers, - image::{format, Image}, - Destroy, -}; +use crate::gpu::{context::Context, framebuffers::Framebuffers, image, Destroy}; use super::scope::OneshotScope; pub struct Swapchain { pub swapchain: vk::SwapchainKHR, - images: Vec>, - pub target: Framebuffers<{ format::SWAPCHAIN }>, + images: Vec>, + pub target: Framebuffers<{ image::Format::Swapchain }>, } impl Swapchain { - pub fn create( - ctx: &mut Context, - scope: &mut OneshotScope, - render_pass: vk::RenderPass, - ) -> Self { + pub fn create(ctx: &mut Context, scope: &OneshotScope, render_pass: vk::RenderPass) -> Self { let create_info = vk::SwapchainCreateInfoKHR::builder() .surface(**ctx.surface) .min_image_count(ctx.surface.config.image_count) @@ -51,7 +42,9 @@ impl Swapchain { .expect("Failed to get swapchain images") } .into_iter() - .map(|image| Image::new(ctx, image, ctx.surface.config.surface_format.format, None)) + .map(|image| { + image::Image::new_of_format(ctx, image, ctx.surface.config.surface_format.format, None) + }) .collect::>(); let target = Framebuffers::create( diff --git a/runner/src/gpu/texture.rs b/runner/src/gpu/texture.rs index c6e4e94..4714701 100644 --- a/runner/src/gpu/texture.rs +++ b/runner/src/gpu/texture.rs @@ -1,20 +1,18 @@ -use ash::vk; +use super::{context::Context, image, sampler::Sampler, Destroy}; -use super::{context::Context, image::Image, sampler::Sampler, Destroy}; - -pub struct Texture { - pub image: Image, +pub struct Texture { + pub image: image::Image, pub sampler: Sampler, } -impl Texture { - pub fn from_image(ctx: &Context, image: Image) -> Self { +impl Texture { + pub fn from_image(ctx: &Context, image: image::Image) -> Self { let sampler = Sampler::create(ctx); Self { image, sampler } } } -impl Destroy for Texture { +impl Destroy for Texture { unsafe fn destroy_with(&mut self, ctx: &mut Context) { self.sampler.destroy_with(ctx); self.image.destroy_with(ctx); diff --git a/runner/src/input/mod.rs b/runner/src/input/mod.rs index 42c2469..d665573 100644 --- a/runner/src/input/mod.rs +++ b/runner/src/input/mod.rs @@ -5,8 +5,6 @@ use winit::{ keyboard::KeyCode, }; -use shared::glam; - #[derive(Default)] pub struct State { keys: HashSet, diff --git a/runner/src/main.rs b/runner/src/main.rs index 471895a..b453df7 100644 --- a/runner/src/main.rs +++ b/runner/src/main.rs @@ -22,12 +22,12 @@ use winit::event_loop::EventLoop; use app::App; fn main() { - let gltf_file = env::args().nth(1).expect("Please specify a gltf file"); + let scene_file = env::args().nth(1).expect("Please specify a scene file"); let event_loop = EventLoop::new().expect("Failed to create event loop"); let window = App::window_builder() .build(&event_loop) .expect("Failed to create window"); - let app = App::new(&window, &gltf_file); + let app = App::new(&window, &scene_file); app.run(event_loop); } diff --git a/runner/src/render/mod.rs b/runner/src/render/mod.rs index 468806b..7644623 100644 --- a/runner/src/render/mod.rs +++ b/runner/src/render/mod.rs @@ -3,11 +3,8 @@ mod sync_state; use std::slice; -use crate::{ - data::gltf_scene::GltfScene, - gpu::{ - context::Context, scope::OneshotScope, swapchain::Swapchain, sync_info::SyncInfo, Destroy, - }, +use crate::gpu::{ + context::Context, scope::OneshotScope, swapchain::Swapchain, sync_info::SyncInfo, Destroy, }; use self::{ @@ -39,18 +36,18 @@ pub enum Error { } impl Renderer { - pub fn create(ctx: &mut Context, scene: GltfScene, camera: shared::Camera) -> Self { + pub fn create(ctx: &mut Context, scene: scene::Scene, camera: shared::Camera) -> Self { let mut common = common::Data::create(ctx, scene); let uniforms = shared::Uniforms { camera }; common.uniforms.update(&uniforms); let pathtracer_pipeline = pathtracer::Pipeline::create(ctx, &common); - let mut init_scope = OneshotScope::begin_on(ctx, ctx.queues.transfer()); + let init_scope = OneshotScope::begin_on(ctx, ctx.queues.transfer()); - let rasterizer_pipeline = rasterizer::Pipeline::create(ctx, &mut init_scope, &common); + let rasterizer_pipeline = rasterizer::Pipeline::create(ctx, &init_scope, &common); let tonemap_pipeline = tonemap::Pipeline::create(ctx, &common); - let swapchain = Swapchain::create(ctx, &mut init_scope, tonemap_pipeline.render_pass); + let swapchain = Swapchain::create(ctx, &init_scope, tonemap_pipeline.render_pass); init_scope.finish(ctx); @@ -141,8 +138,8 @@ impl Renderer { unsafe { self.swapchain.destroy_with(ctx); } - let mut init_scope = OneshotScope::begin_on(ctx, ctx.queues.transfer()); - self.swapchain = Swapchain::create(ctx, &mut init_scope, self.tonemap_pipeline.render_pass); + let init_scope = OneshotScope::begin_on(ctx, ctx.queues.transfer()); + self.swapchain = Swapchain::create(ctx, &init_scope, self.tonemap_pipeline.render_pass); init_scope.finish(ctx); } } diff --git a/runner/src/render/pass/common.rs b/runner/src/render/pass/common.rs index 5887160..72c2bdf 100644 --- a/runner/src/render/pass/common.rs +++ b/runner/src/render/pass/common.rs @@ -2,29 +2,21 @@ use std::slice; use ash::vk; -use crate::{ - data::gltf_scene, - gpu::{ - context::Context, - descriptors::Descriptors, - image::{format, BarrierInfo, Image}, - scene::Scene, - scope::OneshotScope, - uniforms::Uniforms, - Destroy, - }, +use crate::gpu::{ + context::Context, descriptors::Descriptors, image, scene::Scene, scope::OneshotScope, + uniforms::Uniforms, Destroy, }; pub struct Data { pub descriptors: Descriptors, - pub target: Image<{ format::HDR }>, + pub target: image::Image<{ image::Format::Hdr }>, pub resolution: vk::Extent2D, pub uniforms: Uniforms, pub scene: Scene, } impl Data { - pub fn create(ctx: &mut Context, scene: gltf_scene::GltfScene) -> Self { + pub fn create(ctx: &mut Context, scene: scene::Scene) -> Self { let descriptors = Self::create_descriptors(ctx); let resolution = vk::Extent2D { @@ -40,12 +32,12 @@ impl Data { usage: vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::STORAGE, ..Default::default() }; - Image::create( + image::Image::create( ctx, &init_scope, "Intermediate Target", &info, - Some(&BarrierInfo::GENERAL), + Some(&image::BarrierInfo::GENERAL), ) }; diff --git a/runner/src/render/pass/pathtracer.rs b/runner/src/render/pass/pathtracer.rs index 74609c7..aa9e260 100644 --- a/runner/src/render/pass/pathtracer.rs +++ b/runner/src/render/pass/pathtracer.rs @@ -4,7 +4,8 @@ use std::{ }; use ash::vk; -use shared::{bytemuck, PathtracerConstants}; + +use shared::PathtracerConstants; use crate::gpu::{ acceleration_structure::AccelerationStructures, @@ -223,7 +224,7 @@ impl Pipeline { .iter() .copied() .zip(data.descriptors.sets.iter().copied()) - .map(|(a, b)| [a, b]); + .map(<[vk::DescriptorSet; 2]>::from); let pipeline = pipeline::Pipeline::new( ctx, diff --git a/runner/src/render/pass/rasterizer.rs b/runner/src/render/pass/rasterizer.rs index 5127419..c34973e 100644 --- a/runner/src/render/pass/rasterizer.rs +++ b/runner/src/render/pass/rasterizer.rs @@ -1,13 +1,13 @@ use std::slice; use ash::vk; -use shared::{bytemuck, RasterizerConstants, Vertex}; + +use shared::{RasterizerConstants, Vertex}; use crate::gpu::{ context::Context, framebuffers::{self, Framebuffers}, - image::format, - pipeline, + image, pipeline, scope::OneshotScope, sync_info::SyncInfo, Destroy, @@ -25,12 +25,12 @@ pub mod conf { pub struct Pipeline { pub render_pass: vk::RenderPass, - pub target: Framebuffers<{ format::HDR }>, + pub target: Framebuffers<{ image::Format::Hdr }>, pipeline: pipeline::Pipeline<1>, } impl Pipeline { - pub fn create(ctx: &mut Context, scope: &mut OneshotScope, common: &common::Data) -> Self { + pub fn create(ctx: &mut Context, scope: &OneshotScope, common: &common::Data) -> Self { let render_pass = Self::create_render_pass(ctx); let target = Framebuffers::create( @@ -65,7 +65,7 @@ impl Pipeline { fn create_render_pass(ctx: &Context) -> vk::RenderPass { let attachments = [ vk::AttachmentDescription::builder() - .format(format::HDR) + .format(image::Format::Hdr.into()) .samples(vk::SampleCountFlags::TYPE_1) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) @@ -75,7 +75,7 @@ impl Pipeline { .final_layout(vk::ImageLayout::GENERAL) .build(), vk::AttachmentDescription::builder() - .format(format::DEPTH) + .format(image::Format::Depth.into()) .samples(vk::SampleCountFlags::TYPE_1) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::DONT_CARE) @@ -229,7 +229,7 @@ impl Pipeline { fn vertex_binding_info() -> ( [vk::VertexInputBindingDescription; 1], - [vk::VertexInputAttributeDescription; 3], + [vk::VertexInputAttributeDescription; 5], ) { let bindings = [vk::VertexInputBindingDescription { binding: 0, @@ -256,6 +256,18 @@ impl Pipeline { format: vk::Format::R32G32_SFLOAT, offset: bytemuck::offset_of!(Vertex, tex_coord) as _, }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 3, + format: vk::Format::R32_UINT, + offset: bytemuck::offset_of!(Vertex, _pad0) as _, + }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 4, + format: vk::Format::R32_UINT, + offset: bytemuck::offset_of!(Vertex, _pad1) as _, + }, ]; (bindings, attributes) @@ -304,6 +316,7 @@ impl Pipeline { let push_constants = RasterizerConstants { model_transform: instance.transform, material_index: scene_info.primitive_infos[instance.primitive_index].material, + ..Default::default() }; unsafe { diff --git a/runner/src/render/pass/tonemap.rs b/runner/src/render/pass/tonemap.rs index 78fb13a..e9b6b90 100644 --- a/runner/src/render/pass/tonemap.rs +++ b/runner/src/render/pass/tonemap.rs @@ -6,8 +6,7 @@ use crate::gpu::{ context::Context, descriptors::Descriptors, framebuffers::{self, Framebuffers}, - image::{format, Image}, - pipeline, + image, pipeline, sampler::Sampler, sync_info::SyncInfo, Destroy, @@ -25,7 +24,7 @@ mod conf { pub struct Data { descriptors: Descriptors, - input_image: Image<{ format::HDR }>, + input_image: image::Image<{ image::Format::Hdr }>, sampler: Sampler, } @@ -39,7 +38,7 @@ impl Data { pub fn create(ctx: &Context, common: &common::Data) -> Self { let descriptors = Self::create_descriptors(ctx); - let input_image = Image::new(ctx, common.target.image, format::HDR, None); + let input_image = image::Image::new(ctx, common.target.image, None); let data = Self { descriptors, @@ -114,7 +113,7 @@ impl Data { } impl Pipeline { - pub fn create(ctx: &mut Context, common: &common::Data) -> Self { + pub fn create(ctx: &Context, common: &common::Data) -> Self { let data = Data::create(ctx, common); let render_pass = Self::create_render_pass(ctx); @@ -151,7 +150,7 @@ impl Pipeline { .final_layout(vk::ImageLayout::PRESENT_SRC_KHR) .build(), vk::AttachmentDescription::builder() - .format(format::DEPTH) + .format(image::Format::Depth.into()) .samples(vk::SampleCountFlags::TYPE_1) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::DONT_CARE) @@ -295,7 +294,7 @@ impl Pipeline { ctx: &Context, idx: usize, sync_info: &SyncInfo, - output_to: &Framebuffers<{ format::SWAPCHAIN }>, + output_to: &Framebuffers<{ image::Format::Swapchain }>, ) { let commands = self.pipeline.begin_pipeline(ctx, idx); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 33665bf..db64719 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-05-27" +channel = "nightly-2023-09-30" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/scene/Cargo.toml b/scene/Cargo.toml new file mode 100644 index 0000000..8313d3f --- /dev/null +++ b/scene/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "scene" +version.workspace = true +edition.workspace = true + +[dependencies] +flate2 = "1" +glam = { workspace = true, features = ["serde"] } +gltf = "1" +rmp-serde = { version = "1" } +serde = { workspace = true, features = ["derive"] } +shared = { workspace = true } +spirv-std = { workspace = true } diff --git a/scene/src/bin/preprocess.rs b/scene/src/bin/preprocess.rs new file mode 100644 index 0000000..3bcf9fd --- /dev/null +++ b/scene/src/bin/preprocess.rs @@ -0,0 +1,16 @@ +use std::{env, path::Path}; + +use scene::{gltf::Gltf, FileLoader}; + +fn main() { + let filename = env::args().nth(1).expect("No asset filename provided"); + let filepath = Path::new(&filename); + + let scene = if Gltf::can_load(filepath) { + Gltf::load(filepath) + } else { + panic!("No loader found"); + }; + + scene::save(&scene, filepath); +} diff --git a/scene/src/gltf.rs b/scene/src/gltf.rs new file mode 100644 index 0000000..3cb0913 --- /dev/null +++ b/scene/src/gltf.rs @@ -0,0 +1,180 @@ +use std::{collections::HashMap, path::Path}; + +use gltf::{buffer, mesh}; + +use shared::{ + self, + bounding_box::BoundingBox, + scene::{Instance, Material, PrimitiveInfo, PrimitiveSize}, +}; + +use super::{Data, FileLoader, Info, Scene}; + +pub struct Gltf; + +impl FileLoader for Gltf { + const SUPPORTED_EXTENSIONS: &'static [&'static str] = &["gltf", "glb"]; + + fn load(filename: impl AsRef) -> Scene { + let (document, buffers, _images) = gltf::import(filename).expect("Couldn't import file"); + + let scene = document + .default_scene() + .unwrap_or_else(|| document.scenes().next().expect("No scenes found")); + + let mut info = Info::default(); + let mut data = Data::default(); + + let mut bounding_boxes = Vec::new(); + + // json mesh index -> loaded primitives range + let mut processed_meshes = HashMap::new(); + // json material index -> loaded material index + let mut processed_materials = HashMap::new(); + + scene.nodes().traverse_meshes( + glam::Mat4::IDENTITY, + &mut |mesh: &mesh::Mesh<'_>, transform| { + let primitives_range = processed_meshes + .entry(mesh.index()) + .or_insert_with(|| { + mesh.primitives().for_each(|primitive| { + let (primitive_info, primitive_size) = add_primitive( + &mut data, + &primitive, + &buffers, + &mut processed_materials, + ); + info.primitive_infos.push(primitive_info); + info.primitive_sizes.push(primitive_size); + let bbox = primitive.bounding_box(); + bounding_boxes.push(BoundingBox::new(bbox.min, bbox.max)); + }); + // range of newly added primitive indices + (info.primitive_infos.len() - mesh.primitives().len()) + ..info.primitive_infos.len() + }) + .clone(); + + info.instances + .extend(primitives_range.map(|primitive_index| Instance { + primitive_index, + transform, + })); + }, + ); + + info.bounding_box = info + .instances + .iter() + .map(|instance| bounding_boxes[instance.primitive_index].transform(instance.transform)) + .fold(BoundingBox::default(), BoundingBox::union); + + Scene { data, info } + } +} + +fn add_primitive( + data: &mut Data, + primitive: &mesh::Primitive<'_>, + raw_buffers: &[buffer::Data], + processed_materials: &mut HashMap, +) -> (PrimitiveInfo, PrimitiveSize) { + assert_eq!(primitive.mode(), mesh::Mode::Triangles); + + let reader = primitive.reader(|buffer| Some(&raw_buffers[buffer.index()])); + + let indices = reader.read_indices().expect("No indices found").into_u32(); + let indices_offset = data.indices.len() as _; + data.indices.extend(indices); + let indices_size = data.indices.len() as u32 - indices_offset; + + let positions = reader.read_positions().expect("No positions found"); + let normals = reader.read_normals().map_or_else( + || Box::new(std::iter::repeat_with(Default::default)) as Box<_>, + |nn| Box::new(nn) as Box>, + ); + let tex_coords = reader + .read_tex_coords(0) + .map(mesh::util::ReadTexCoords::into_f32) + .map_or_else( + || Box::new(std::iter::repeat_with(Default::default)) as Box<_>, + |uv| Box::new(uv) as Box>, + ); + + let vertices = positions + .zip(normals) + .zip(tex_coords) + .map(shared::Vertex::from); + let vertices_offset = data.vertices.len() as _; + data.vertices.extend(vertices); + let vertices_size = data.vertices.len() as u32 - vertices_offset; + + let material = primitive.material(); + let material = *processed_materials + .entry(material.index().unwrap_or_default()) + .or_insert_with(|| add_material(data, &material)) as _; + + let primitive_info = PrimitiveInfo { + indices_offset, + vertices_offset, + material, + }; + + let primitive_size = PrimitiveSize { + indices_size, + vertices_size, + }; + + (primitive_info, primitive_size) +} + +fn add_material(data: &mut Data, material: &gltf::Material) -> usize { + let index = data.materials.len(); + + data.materials.push(Material { + color: material.pbr_metallic_roughness().base_color_factor().into(), + emittance: glam::Vec3::from(material.emissive_factor()).extend(1.0), + }); + + index +} + +trait Traversable { + fn traverse_meshes( + self, + transform: glam::Mat4, + f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), + ); +} + +impl Traversable for gltf::scene::Node<'_> { + fn traverse_meshes( + self, + transform: glam::Mat4, + f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), + ) { + let global_transform = + transform * glam::Mat4::from_cols_array_2d(&self.transform().matrix()); + if let Some(mesh) = self.mesh() { + f(&mesh, global_transform); + } + self.children().traverse_meshes(global_transform, f); + } +} + +macro_rules! impl_traversable { + ($t:ty) => { + impl Traversable for $t { + fn traverse_meshes( + self, + transform: glam::Mat4, + f: &mut impl FnMut(&mesh::Mesh<'_>, glam::Mat4), + ) { + self.for_each(|elem| elem.traverse_meshes(transform, f)); + } + } + }; +} +impl_traversable!(gltf::scene::iter::Nodes<'_>); +impl_traversable!(gltf::scene::iter::Children<'_>); diff --git a/scene/src/lib.rs b/scene/src/lib.rs new file mode 100644 index 0000000..f4d64d7 --- /dev/null +++ b/scene/src/lib.rs @@ -0,0 +1,72 @@ +pub mod gltf; + +use std::{ + fs::File, + io::{BufReader, BufWriter}, + path::Path, +}; + +use serde::{Deserialize, Serialize}; + +use shared::{ + bounding_box::BoundingBox, + scene::{Instance, Material, PrimitiveInfo, PrimitiveSize}, + Vertex, +}; + +pub trait FileLoader { + const SUPPORTED_EXTENSIONS: &'static [&'static str]; + fn load(filename: impl AsRef) -> Scene; + + fn can_load(filename: impl AsRef) -> bool { + let extension = filename + .as_ref() + .extension() + .and_then(|s| s.to_str()) + .expect("No file extension found"); + Self::SUPPORTED_EXTENSIONS.contains(&extension) + } +} + +#[derive(Deserialize, Serialize)] +pub struct Scene { + pub data: Data, + pub info: Info, +} + +#[derive(Default, Deserialize, Serialize)] +pub struct Data { + pub indices: Vec, + pub vertices: Vec, + pub materials: Vec, +} + +#[derive(Default, Deserialize, Serialize)] +pub struct Info { + pub primitive_infos: Vec, + pub primitive_sizes: Vec, + pub instances: Vec, + pub bounding_box: BoundingBox, +} + +const FILE_EXTENSION: &str = "tsnasset"; + +pub fn load(file: impl AsRef) -> Scene { + let filepath = file.as_ref(); + assert!( + filepath.extension().unwrap_or_default() == FILE_EXTENSION, + "Asset must be preprocessed before loading" + ); + let file = File::open(filepath).expect("Unable to open scene asset file"); + let reader = flate2::bufread::GzDecoder::new(BufReader::new(file)); + rmp_serde::decode::from_read(reader).expect("Failed to load scene asset") +} + +pub fn save(scene: &Scene, file: impl AsRef) { + let output_filename = file.as_ref().with_extension(FILE_EXTENSION); + let output_file = File::create(&output_filename).expect("Unable to open file for writing"); + let mut writer = + flate2::write::GzEncoder::new(BufWriter::new(output_file), flate2::Compression::default()); + rmp_serde::encode::write(&mut writer, &scene).expect("Failed to save processed asset"); + println!("Asset processed and saved to {}", output_filename.display()); +} diff --git a/shaders/pathtracer/Cargo.toml b/shaders/pathtracer/Cargo.toml index f23cfb2..eeb1a79 100644 --- a/shaders/pathtracer/Cargo.toml +++ b/shaders/pathtracer/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "pathtracer" -version = "0.1.0" +version.workspace = true edition.workspace = true [lib] crate-type = ["dylib"] [dependencies] -shared = { path = "../../shared" } +glam = { workspace = true } +shared = { workspace = true } +spirv-std = { workspace = true } diff --git a/shaders/pathtracer/src/lib.rs b/shaders/pathtracer/src/lib.rs index be724f5..0514193 100644 --- a/shaders/pathtracer/src/lib.rs +++ b/shaders/pathtracer/src/lib.rs @@ -1,20 +1,27 @@ -#![cfg_attr(target_arch = "spirv", no_std)] +#![no_std] mod math; mod rand; mod ray; +use glam::{swizzles::Vec4Swizzles, *}; + use math::*; use rand::Rng; -use ray::{Payload, Ray}; -#[allow(unused_imports)] -use shared::spirv_std::num_traits::Float; use shared::{ - glam::*, - spirv_std::{self, *}, - Material, PathtracerConstants, PrimitiveInfo, Uniforms, Vertex, + scene::{Material, PrimitiveInfo}, + PathtracerConstants, Uniforms, Vertex, }; +#[allow(unused_imports)] +use spirv_std::{num_traits::Float, ray_tracing, spirv, Image}; + +const MAX_RECURSE_DEPTH: u32 = 5; +const RAY_FLAGS: ray_tracing::RayFlags = ray_tracing::RayFlags::OPAQUE; +const T_MIN: f32 = 1e-3; +const T_MAX: f32 = 1e+5; +// env light just to see some results (technically not part of the rendering equation) +const ENV_COLOR: Vec3 = Vec3::new(0.01, 0.01, 0.01); pub type OutputImage = Image!(2D, format = rgba32f, sampled = false); @@ -38,7 +45,7 @@ pub fn closest_hit( #[spirv(storage_buffer, descriptor_set = 1, binding = 2)] indices: &[u32], #[spirv(storage_buffer, descriptor_set = 1, binding = 3)] vertices: &[Vertex], #[spirv(storage_buffer, descriptor_set = 1, binding = 4)] primitives: &[PrimitiveInfo], - #[spirv(incoming_ray_payload)] payload: &mut Payload, + #[spirv(incoming_ray_payload)] payload: &mut ray::Payload, ) { let bary = barycentrics(hit_uv); let object_to_world = Mat4::from_cols( @@ -52,18 +59,16 @@ pub fn closest_hit( let primitive = &primitives[primitive_index]; let verts = triangle(indices, vertices, primitive, primitive_id); - let position = Vec3::from(dotv( + let position = dotv( bary, [verts[0].position, verts[1].position, verts[2].position], - )); - let world_position = (object_to_world * position.extend(1.)).truncate(); + ); + let world_position = (object_to_world * position).truncate(); let world_normal = { - let normal = Vec3::from(dotv( - bary, - [verts[0].normal, verts[1].normal, verts[2].normal], - )) - .normalize(); + let normal = dotv(bary, [verts[0].normal, verts[1].normal, verts[2].normal]) + .truncate() + .normalize(); (world_to_object.transpose() * normal).normalize() }; @@ -90,7 +95,7 @@ pub fn closest_hit( let albedo = material.color.truncate(); let emittance = material.emittance.truncate(); - payload.ray = Ray { + payload.ray = ray::Ray { origin: world_position, direction: frame * out_dir, }; @@ -98,11 +103,6 @@ pub fn closest_hit( payload.weight = albedo; } -const MAX_RECURSE_DEPTH: u32 = 5; -const RAY_FLAGS: ray_tracing::RayFlags = ray_tracing::RayFlags::OPAQUE; -const T_MIN: f32 = 1e-3; -const T_MAX: f32 = 1e+5; - #[spirv(ray_generation)] pub fn ray_generation( #[spirv(launch_id)] launch_id: UVec3, @@ -116,7 +116,7 @@ pub fn ray_generation( let ray = { let uv = (launch_id.as_vec3().xy() + 0.5) / launch_size.as_vec3().xy(); let target = (uniforms.camera.proj / (2. * uv - 1.).extend(1.).extend(1.)).truncate(); - Ray { + ray::Ray { origin: (uniforms.camera.view / Vec4::W).truncate(), direction: (uniforms.camera.view / target.normalize().extend(0.)).truncate(), } @@ -126,7 +126,7 @@ pub fn ray_generation( launch_size.x * (launch_size.y * constants.frame + launch_id.y) + launch_id.x, ); - *payload = Payload { + *payload = ray::Payload { ray, rng, ..Default::default() @@ -166,9 +166,7 @@ pub fn ray_generation( } #[spirv(miss)] -pub fn miss(#[spirv(incoming_ray_payload)] out: &mut Payload) { - const ENV_COLOR: Vec3 = Vec3::new(0.05, 0.01, 0.01); - // env light just to see some results (technically not part of the rendering equation) +pub fn miss(#[spirv(incoming_ray_payload)] out: &mut ray::Payload) { out.hit_value = ENV_COLOR; out.depth = MAX_RECURSE_DEPTH; } diff --git a/shaders/pathtracer/src/math.rs b/shaders/pathtracer/src/math.rs index a79b9fa..20991d1 100644 --- a/shaders/pathtracer/src/math.rs +++ b/shaders/pathtracer/src/math.rs @@ -1,6 +1,7 @@ use core::ops::{Add, Mul}; -use shared::{glam::*, PrimitiveInfo, Vertex}; +use glam::*; +use shared::{scene::PrimitiveInfo, Vertex}; pub fn triangle( indices: &[u32], diff --git a/shaders/pathtracer/src/ray.rs b/shaders/pathtracer/src/ray.rs index 942536c..b8b331c 100644 --- a/shaders/pathtracer/src/ray.rs +++ b/shaders/pathtracer/src/ray.rs @@ -1,4 +1,4 @@ -use shared::glam::*; +use glam::*; use super::rand::Rng; diff --git a/shaders/rasterizer/Cargo.toml b/shaders/rasterizer/Cargo.toml index 2b8aa98..a2c61ff 100644 --- a/shaders/rasterizer/Cargo.toml +++ b/shaders/rasterizer/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "rasterizer" -version = "0.1.0" +version.workspace = true edition.workspace = true [lib] crate-type = ["dylib"] [dependencies] -shared = { path = "../../shared" } +glam = { workspace = true } +shared = { workspace = true } +spirv-std = { workspace = true } diff --git a/shaders/rasterizer/src/lib.rs b/shaders/rasterizer/src/lib.rs index 5d3a85b..8ba06a9 100644 --- a/shaders/rasterizer/src/lib.rs +++ b/shaders/rasterizer/src/lib.rs @@ -1,6 +1,9 @@ -#![cfg_attr(target_arch = "spirv", no_std)] +#![no_std] -use shared::{glam::Vec4, spirv_std::spirv, Material, RasterizerConstants, Uniforms, Vertex}; +use glam::Vec4; + +use shared::{scene::Material, RasterizerConstants, Uniforms, Vertex}; +use spirv_std::spirv; #[spirv(vertex)] pub fn vert_main( @@ -9,8 +12,8 @@ pub fn vert_main( vertex: Vertex, #[spirv(position, invariant)] position: &mut Vec4, ) { - *position = uniforms.camera.proj - * (uniforms.camera.view * constants.model_transform * vertex.position.extend(1.0)); + *position = + uniforms.camera.proj * (uniforms.camera.view * constants.model_transform * vertex.position); } #[spirv(fragment)] diff --git a/shaders/tonemap/Cargo.toml b/shaders/tonemap/Cargo.toml index 39a0d84..8d2b4f0 100644 --- a/shaders/tonemap/Cargo.toml +++ b/shaders/tonemap/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "tonemap" -version = "0.1.0" +version.workspace = true edition.workspace = true [lib] crate-type = ["dylib"] [dependencies] -shared = { path = "../../shared" } +glam = { workspace = true } +shared = { workspace = true } +spirv-std = { workspace = true } diff --git a/shaders/tonemap/src/lib.rs b/shaders/tonemap/src/lib.rs index 89ba1ee..d318c0a 100644 --- a/shaders/tonemap/src/lib.rs +++ b/shaders/tonemap/src/lib.rs @@ -1,14 +1,12 @@ -#![cfg_attr(target_arch = "spirv", no_std)] +#![no_std] -use shared::{ +use spirv_std::{ glam::{vec2, Vec2, Vec4}, - spirv_std::{ - image::{Image2d, SampledImage}, - spirv, - }, + image::{Image2d, SampledImage}, + spirv, }; -const GAMMA: f32 = 1.0 / 2.2; +const GAMMA: f32 = 1. / 2.2; #[spirv(vertex)] pub fn vert_main( @@ -17,7 +15,7 @@ pub fn vert_main( uv: &mut Vec2, ) { *uv = vec2(((vertex_index << 1) & 2) as f32, (vertex_index & 2) as f32); - *position = (*uv * 2.0 - 1.0).extend(0.0).extend(1.0); + *position = (2. * *uv - 1.).extend(0.).extend(1.); } #[spirv(fragment)] diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 3dd1aba..97ae355 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -1,8 +1,16 @@ [package] name = "shared" -version = "0.1.0" +version.workspace = true edition.workspace = true [dependencies] -bytemuck = { version = "1.14", features = ["derive"] } -spirv-std = "0.9" +spirv-std = { workspace = true } + +[target.'cfg(target_arch = "spirv")'.dependencies] +glam = { workspace = true } +serde = { workspace = true } + +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +bytemuck = { workspace = true } +glam = { workspace = true, features = ["bytemuck", "serde"] } +serde = { workspace = true, features = ["derive"] } diff --git a/runner/src/data/bounding_box.rs b/shared/src/bounding_box.rs similarity index 84% rename from runner/src/data/bounding_box.rs rename to shared/src/bounding_box.rs index 0f5ba1e..09b10ba 100644 --- a/runner/src/data/bounding_box.rs +++ b/shared/src/bounding_box.rs @@ -1,6 +1,8 @@ -use shared::glam; +#[cfg(not(target_arch = "spirv"))] +use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy)] +#[cfg_attr(not(target_arch = "spirv"), derive(Deserialize, Serialize))] pub struct BoundingBox { pub min: glam::Vec3, pub max: glam::Vec3, diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 2c9581e..4a31316 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,35 +1,40 @@ #![cfg_attr(target_arch = "spirv", no_std)] -use core::ops::{Div, Mul}; +pub mod bounding_box; +pub mod scene; -pub use bytemuck; -pub use spirv_std; -pub use spirv_std::glam; +use core::ops::{Div, Mul}; -use glam::{Mat4, Vec2, Vec3A, Vec4}; +#[cfg(not(target_arch = "spirv"))] +use serde::{Deserialize, Serialize}; #[repr(C)] #[derive(Copy, Clone, Default)] +#[cfg_attr(not(target_arch = "spirv"), derive(bytemuck::Pod, bytemuck::Zeroable))] pub struct RasterizerConstants { - pub model_transform: Mat4, + pub model_transform: glam::Mat4, pub material_index: u32, + pub _pad0: u32, + pub _pad1: u32, + pub _pad2: u32, } -unsafe impl bytemuck::Zeroable for RasterizerConstants {} -unsafe impl bytemuck::Pod for RasterizerConstants {} #[repr(C)] -#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Default)] +#[cfg_attr(not(target_arch = "spirv"), derive(bytemuck::Pod, bytemuck::Zeroable))] pub struct PathtracerConstants { pub frame: u32, } #[repr(C)] -#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Default)] +#[cfg_attr(not(target_arch = "spirv"), derive(bytemuck::Pod, bytemuck::Zeroable))] pub struct Uniforms { pub camera: Camera, } #[repr(C)] -#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Default)] +#[cfg_attr(not(target_arch = "spirv"), derive(bytemuck::Pod, bytemuck::Zeroable))] pub struct Camera { pub view: Transform, pub proj: Transform, @@ -37,53 +42,33 @@ pub struct Camera { #[repr(C)] #[derive(Copy, Clone, Default)] +#[cfg_attr( + not(target_arch = "spirv"), + derive(bytemuck::Pod, bytemuck::Zeroable, Deserialize, Serialize) +)] pub struct Vertex { - pub position: Vec3A, - pub normal: Vec3A, - pub tex_coord: Vec2, + pub position: glam::Vec4, + pub normal: glam::Vec4, + pub tex_coord: glam::Vec2, + pub _pad0: u32, + pub _pad1: u32, } -unsafe impl bytemuck::Zeroable for Vertex {} -unsafe impl bytemuck::Pod for Vertex {} #[repr(C)] #[derive(Copy, Clone, Default)] +#[cfg_attr(not(target_arch = "spirv"), derive(bytemuck::Pod, bytemuck::Zeroable))] pub struct Transform { - pub forward: Mat4, - pub inverse: Mat4, -} -unsafe impl bytemuck::Zeroable for Transform {} -unsafe impl bytemuck::Pod for Transform {} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] -pub struct PrimitiveInfo { - pub indices_offset: u32, - pub vertices_offset: u32, - pub material: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub struct Material { - pub color: Vec4, - pub emittance: Vec4, -} -unsafe impl bytemuck::Zeroable for Material {} -unsafe impl bytemuck::Pod for Material {} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] -pub struct SceneInfo { - pub indices_address: u64, - pub vertices_address: u64, + pub forward: glam::Mat4, + pub inverse: glam::Mat4, } impl Vertex { pub fn new(position: &[f32], normal: &[f32], tex_coord: &[f32]) -> Self { Self { - position: Vec3A::from_slice(position), - normal: Vec3A::from_slice(normal), - tex_coord: Vec2::from_slice(tex_coord), + position: glam::Vec3::from_slice(position).extend(1.0), + normal: glam::Vec3::from_slice(normal).extend(1.0), + tex_coord: glam::Vec2::from_slice(tex_coord), + ..Default::default() } } } @@ -96,14 +81,14 @@ impl From for Vertex { } impl Transform { - pub fn new(mat: Mat4) -> Self { + pub fn new(mat: glam::Mat4) -> Self { Self { forward: mat, inverse: mat.inverse(), } } - pub fn proj(mut mat: Mat4) -> Self { + pub fn proj(mut mat: glam::Mat4) -> Self { mat.y_axis.y *= -1.; Self::new(mat) } @@ -111,9 +96,9 @@ impl Transform { impl Mul for Transform where - Mat4: Mul, + glam::Mat4: Mul, { - type Output = >::Output; + type Output = >::Output; fn mul(self, rhs: T) -> Self::Output { self.forward * rhs } @@ -121,9 +106,9 @@ where impl Div for Transform where - Mat4: Mul, + glam::Mat4: Mul, { - type Output = >::Output; + type Output = >::Output; #[allow(clippy::suspicious_arithmetic_impl)] fn div(self, rhs: T) -> Self::Output { diff --git a/shared/src/scene.rs b/shared/src/scene.rs new file mode 100644 index 0000000..355b0c4 --- /dev/null +++ b/shared/src/scene.rs @@ -0,0 +1,43 @@ +#[cfg(not(target_arch = "spirv"))] +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +#[cfg_attr( + not(target_arch = "spirv"), + derive(bytemuck::Pod, bytemuck::Zeroable, Deserialize, Serialize) +)] +pub struct Material { + pub color: glam::Vec4, + pub emittance: glam::Vec4, +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +#[cfg_attr( + not(target_arch = "spirv"), + derive(bytemuck::Pod, bytemuck::Zeroable, Deserialize, Serialize) +)] +pub struct PrimitiveInfo { + pub indices_offset: u32, + pub vertices_offset: u32, + pub material: u32, +} + +#[cfg_attr(not(target_arch = "spirv"), derive(Deserialize, Serialize))] +pub struct PrimitiveSize { + pub indices_size: u32, + pub vertices_size: u32, +} + +#[cfg_attr(not(target_arch = "spirv"), derive(Deserialize, Serialize))] +pub struct Instance { + pub primitive_index: usize, + pub transform: glam::Mat4, +} + +impl PrimitiveSize { + pub const fn count(&self) -> u32 { + self.indices_size / 3 + } +}