From d44db2c05f6204ad7645d2bee7db91e7aa01f47a Mon Sep 17 00:00:00 2001 From: mars Date: Wed, 11 May 2022 13:00:30 -0600 Subject: [PATCH] Initial Legion support --- Cargo.toml | 8 ++++ src/legion.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++++ src/main.rs | 93 +++++++++++++++++++++++++++------------------- src/pass/mesh.rs | 6 ++- src/scene.rs | 1 + 6 files changed, 172 insertions(+), 41 deletions(-) create mode 100644 src/legion.rs diff --git a/Cargo.toml b/Cargo.toml index c234b06..064aef0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,14 @@ strum = { version = "0.24", features = ["derive"] } wgpu = "^0.12" winit = "0.26" +[dependencies.legion] +version = "^0.4" +optional = true + [dependencies.naga] version = "0.8.5" features = ["wgsl-in", "glsl-in", "wgsl-out", "serialize", "deserialize"] + +[[bin]] +name = "cyborg" +required-features = ["legion"] diff --git a/src/legion.rs b/src/legion.rs new file mode 100644 index 0000000..8d3cf0a --- /dev/null +++ b/src/legion.rs @@ -0,0 +1,97 @@ +//! [Legion](https://github.com/amethyst/legion) integration. + +use crate::camera::Camera; +use crate::pass::{self, debug, mesh}; +use crate::scene; +use crate::viewport::{ViewportInfo, WinitViewport}; +use crate::Renderer; +use legion::systems::Builder; +use legion::world::SubWorld; +use legion::*; +use std::sync::Arc; + +/// Initializes the Cyborg renderer within a Legion world. +/// +/// Uses the provided [Builder] to run all rendering code. +/// +/// Required pre-initialized resources: +/// - [Renderer] +/// - [ViewportInfo] (TODO: dynamic viewport targeting) +/// - [mesh::ShaderInfo] +pub fn build_renderer(resources: &mut Resources, builder: &mut Builder) { + let renderer = resources.get_mut::().unwrap(); + let viewport_info = resources.get::().unwrap(); + let mesh_shaders = resources.get::().unwrap(); + + let device = renderer.get_device(); + let layouts = renderer.get_layouts(); + + let mesh_pass = mesh::MeshPass::new( + device.to_owned(), + layouts.to_owned(), + viewport_info.to_owned(), + mesh_shaders.to_owned(), + ); + + let debug_pass = debug::DebugPass::new( + device.to_owned(), + layouts.to_owned(), + viewport_info.to_owned(), + ); + + let frames_in_flight = renderer.get_frames_in_flight(); + let mesh_pass = pass::RenderPassBox::new(Arc::new(mesh_pass), frames_in_flight); + let debug_pass = pass::RenderPassBox::new(Arc::new(debug_pass), frames_in_flight); + + // drop borrowed resources so that new ones can be inserted + drop(renderer); + drop(viewport_info); + drop(mesh_shaders); + + resources.insert(mesh_pass); + resources.insert(debug_pass); + + builder.add_system(draw_transformed_meshes_system()); + builder.add_system(render_system()); + builder.add_system(present_winit_system()); +} + +#[system] +#[read_component(WinitViewport)] +#[read_component(Camera)] +fn render( + world: &mut SubWorld, + #[resource] renderer: &mut Renderer, + #[resource] mesh_pass: &mut pass::RenderPassBox, + #[resource] debug_pass: &mut pass::RenderPassBox, +) { + let target_query: Vec<_> = <(&WinitViewport, &Camera)>::query().iter(world).collect(); + if target_query.len() > 1 { + eprintln!("Cyborg does not currently support more than one viewport at a time!"); + return; + } else if let Some((viewport, camera)) = target_query.get(0) { + let mut passes: Vec<&mut dyn pass::RenderPassBoxTrait> = Vec::new(); + passes.push(mesh_pass); + passes.push(debug_pass); + renderer.render(passes.as_mut_slice(), *viewport, camera); + } +} + +#[system] +fn draw_transformed_meshes( + #[resource] mesh_pass: &pass::RenderPassBox, + world: &mut SubWorld, + query: &mut Query<&scene::TransformedMesh>, +) { + let transformed: Vec<_> = query.iter(world).map(|m| m.clone()).collect(); + mesh_pass + .get_meshes() + .write() + .transformed + .extend_from_slice(transformed.as_slice()); +} + +#[system(for_each)] +fn present_winit(viewport: &mut WinitViewport) { + viewport.present(); +} diff --git a/src/lib.rs b/src/lib.rs index 9fb9bca..86d0918 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ //! Cyborg is a high-performance, modern, experimental rendering engine written //! in Rust. +//! +//! # Features +//! +//! - `legion`: Enables [Legion](https://github.com/amethyst/legion) integration +//! for Cyborg. Disabled by default. use parking_lot::Mutex; use rayon::prelude::*; @@ -14,6 +19,9 @@ pub mod staging; pub mod storage; pub mod viewport; +#[cfg(feature = "legion")] +pub mod legion; + use camera::Camera; use pass::*; use phase::*; diff --git a/src/main.rs b/src/main.rs index 787bd1b..b579ec7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,10 @@ +use cyborg::camera::Camera; +use cyborg::pass::{mesh, RenderPassBox}; use cyborg::shader; use cyborg::storage::mesh::*; -use cyborg::{pass, viewport::*, Renderer}; +use cyborg::viewport::*; +use cyborg::Renderer; +use legion::*; use std::sync::Arc; use winit::{ event::*, @@ -12,16 +16,19 @@ fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let mut viewport = pollster::block_on(WinitViewport::from_window(&window)); - let mut renderer = Renderer::new(viewport.device.clone(), viewport.queue.clone()); + + let mut world = World::new(Default::default()); + let mut resources = Resources::default(); + + let renderer = Renderer::new(viewport.device.clone(), viewport.queue.clone()); + resources.insert(renderer); + + resources.insert(viewport.get_info().to_owned()); let mut flycam = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); let mut is_grabbed = false; - let device = renderer.get_device(); - let layouts = renderer.get_layouts(); - - let shader_store = Arc::new(shader::ShaderStore::new(device.to_owned())); - + let shader_store = Arc::new(shader::ShaderStore::new(viewport.device.clone())); let shaders_dir = std::env::current_dir().unwrap(); let shaders_dir = shaders_dir.join("shaders/"); let shader_watcher = shader::ShaderWatcher::new(shader_store.to_owned(), shaders_dir).unwrap(); @@ -29,43 +36,46 @@ fn main() { let mesh_forward = shader_watcher.add_file("mesh_forward.wgsl").unwrap(); let mesh_skinning = shader_watcher.add_file("mesh_skinning.wgsl").unwrap(); - let mesh_shaders = pass::mesh::ShaderInfo { + let mesh_shaders = mesh::ShaderInfo { store: shader_store.clone(), forward: mesh_forward, skinning: mesh_skinning, }; - let mesh_pass = pass::mesh::MeshPass::new( - device.to_owned(), - layouts.to_owned(), - viewport.get_info(), - mesh_shaders, - ); - let debug_pass = - pass::debug::DebugPass::new(device.to_owned(), layouts.to_owned(), viewport.get_info()); + resources.insert(mesh_shaders); + + let mut render_schedule = Schedule::builder(); + cyborg::legion::build_renderer(&mut resources, &mut render_schedule); + let mut render_schedule = render_schedule.build(); let example_vertices = vec![ - pass::mesh::Vertex { - position: [-0.5, 0.5, 0.0], + mesh::Vertex { + position: [-0.5, 0.0, 0.5], tan_frame: 0, }, - pass::mesh::Vertex { - position: [0.5, 0.5, 0.0], + mesh::Vertex { + position: [0.5, 0.0, 0.5], tan_frame: 0, }, - pass::mesh::Vertex { - position: [0.0, -0.5, 0.0], + mesh::Vertex { + position: [0.0, -0.5, -0.5], + tan_frame: 0, + }, + mesh::Vertex { + position: [0.0, 0.5, -0.5], tan_frame: 0, }, ]; + let mesh_pass = resources.get::>().unwrap(); + let example_vertices = AttrBuffer { id: mesh_pass.get_vertex_attr_id(), count: example_vertices.len(), data: bytemuck::cast_slice(&example_vertices).to_vec(), }; - let example_indices = vec![0u32, 1u32, 2u32]; + let example_indices: Vec = vec![0, 1, 2, 1, 2, 3, 2, 3, 0, 0, 2, 3]; let example_indices = AttrBuffer { id: mesh_pass.get_index_attr_id(), count: example_indices.len(), @@ -77,40 +87,37 @@ fn main() { example_mesh.attributes.push(example_indices); let example_mesh = mesh_pass.get_mesh_pool().load(example_mesh).unwrap(); - let meshes = mesh_pass.get_meshes().to_owned(); + drop(mesh_pass); - let r = 8; - let mut meshes_lock = meshes.write(); + let r = 4; for x in -r..r { for y in -r..r { for z in -r..r { let translation = glam::Vec3::new(x as f32, y as f32, z as f32); let transform = glam::Mat4::from_translation(translation); - meshes_lock.transformed.push(cyborg::scene::TransformedMesh { - transform, - mesh: example_mesh.clone(), - }); + world.push(( + cyborg::scene::TransformedMesh { + transform, + mesh: example_mesh.clone(), + }, + (), + )); } } } - drop(meshes_lock); - let frames_in_flight = renderer.get_frames_in_flight(); - let mut mesh_pass = pass::RenderPassBox::new(Arc::new(mesh_pass), frames_in_flight); - let mut debug_pass = pass::RenderPassBox::new(Arc::new(debug_pass), frames_in_flight); + let viewport_entity = world.push((viewport, flycam.get_camera())); event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { + let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); + let viewport = viewport_entry.get_component_mut::().unwrap(); match viewport.acquire() { Err(wgpu::SurfaceError::Lost) => viewport.resize(viewport.size), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(e) => eprintln!("error: {:?}", e), Ok(_) => { - let mut passes: Vec<&mut dyn pass::RenderPassBoxTrait> = Vec::new(); - passes.push(&mut mesh_pass); - passes.push(&mut debug_pass); - renderer.render(passes.as_mut_slice(), &viewport, &flycam.get_camera()); - viewport.present(); + render_schedule.execute(&mut world, &mut resources); } } } @@ -118,6 +125,10 @@ fn main() { shader_watcher.watch(); flycam.update(); window.request_redraw(); + + let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); + let camera = viewport_entry.get_component_mut::().unwrap(); + let _ = std::mem::replace(camera, flycam.get_camera()); } Event::DeviceEvent { ref event, .. } => match event { DeviceEvent::MouseMotion { delta } => { @@ -163,9 +174,13 @@ fn main() { } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { + let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); + let viewport = viewport_entry.get_component_mut::().unwrap(); viewport.resize(*physical_size); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); + let viewport = viewport_entry.get_component_mut::().unwrap(); viewport.resize(**new_inner_size); } _ => {} diff --git a/src/pass/mesh.rs b/src/pass/mesh.rs index 72793b6..8efe848 100644 --- a/src/pass/mesh.rs +++ b/src/pass/mesh.rs @@ -306,10 +306,10 @@ impl RenderPass for MeshPass { data.groups.clear(); data.skinning_uniforms.clear(); - let meshes_read = self.meshes.read(); + let mut meshes_lock = self.meshes.write(); let mesh_bindings = self .mesh_pool - .iter_meshes(self.mesh_layout_id, meshes_read.transformed.iter(), |i| { + .iter_meshes(self.mesh_layout_id, meshes_lock.transformed.iter(), |i| { &i.mesh }) .unwrap(); @@ -398,6 +398,8 @@ impl RenderPass for MeshPass { data.skinned_vertices.reserve(skinned_cursor); data.skinned_vertices.write(&queue); data.skinning_uniforms.write(&queue); + + meshes_lock.transformed.clear(); } fn record_commands(&self, data: PhaseData<&FrameData>, cmds: &mut wgpu::CommandEncoder) { diff --git a/src/scene.rs b/src/scene.rs index d543c4c..ab73522 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -7,6 +7,7 @@ use crate::storage::mesh::MeshHandle; use parking_lot::RwLock; use std::sync::Arc; +#[derive(Clone, Debug)] pub struct TransformedMesh { pub transform: glam::Mat4, pub mesh: MeshHandle,