commit dbad5cebd3c6842ccc3b88b4496a53e3b6056b1e Author: mars Date: Sun Apr 3 20:11:38 2022 -0600 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c9dff52 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cyborg" +version = "0.1.0" +edition = "2021" + +[dependencies] +strum = { version = "0.24", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f80e21b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,208 @@ +//! Cyborg is a high-performance, modern, experimental rendering engine written +//! in Rust. + +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use strum::IntoEnumIterator; + +#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, strum::EnumIter)] +pub enum PrePhase {} + +#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, strum::EnumIter)] +pub enum ViewportPhase {} + +#[derive(Debug, Default)] +pub struct PhaseList { + pub pre: Vec, + pub viewport: Vec, +} + +impl PhaseList { + pub fn clear(&mut self) { + self.pre.clear(); + self.viewport.clear(); + } +} + +#[derive(Debug, Default)] +pub struct PhaseMultiMap { + pub pre: HashMap>, + pub viewport: HashMap>, +} + +impl PhaseMultiMap { + pub fn insert_multi(&mut self, keys: &PhaseList, val: T) { + for pre in keys.pre.iter() { + Self::insert(&mut self.pre, pre, val); + } + + for viewport in keys.viewport.iter() { + Self::insert(&mut self.viewport, viewport, val); + } + } + + pub fn iter_pre<'a>(&'a self, phase: &'a PrePhase) -> std::slice::Iter<'a, T> { + Self::iter(&self.pre, phase) + } + + pub fn iter_viewport<'a>(&'a self, phase: &'a ViewportPhase) -> std::slice::Iter<'a, T> { + Self::iter(&self.viewport, phase) + } + + fn insert(map: &mut HashMap>, key: &K, val: T) + where + K: std::hash::Hash + Eq + Copy, + { + if let Some(existing) = map.get_mut(key) { + if !existing.contains(&val) { + existing.push(val); + } + } else { + map.insert(*key, vec![val]); + } + } + + fn iter<'a, K>(map: &'a HashMap>, key: &'a K) -> std::slice::Iter<'a, T> + where + K: std::hash::Hash + Eq + Copy, + { + if let Some(vals) = map.get(key) { + vals.iter() + } else { + [].iter() + } + } +} + +pub struct ViewportData; + +pub trait FrameData {} + +pub trait RenderPass { + type FrameData: FrameData; + + fn create_frame_data(&mut self) -> Self::FrameData; + + fn begin_frame(&mut self, data: &mut Self::FrameData, phases: &mut PhaseList); + + fn render_pre( + &self, + phase: PrePhase, + data: &Self::FrameData, + cmds: &mut gpu::RenderBundleEncoder, + ); + + fn render_viewport( + &self, + phase: ViewportPhase, + data: &Self::FrameData, + viewport: &ViewportData, + cmds: &mut gpu::RenderBundleEncoder, + ); +} + +pub struct Renderer { + pub render_passes: Vec>, +} + +impl Renderer { + pub fn render(&mut self) { + let frame_index = 0; + + let mut phase_passes = PhaseMultiMap::::default(); + + let mut phases_buf = PhaseList::default(); + for (pass_index, rp) in self.render_passes.iter_mut().enumerate() { + phases_buf.clear(); + rp.begin_frame(frame_index, &mut phases_buf); + phase_passes.insert_multi(&phases_buf, pass_index); + } + + for phase in PrePhase::iter() { + for pass_index in phase_passes.iter_pre(&phase) { + let pass = &self.render_passes[*pass_index]; + let mut encoder = gpu::RenderBundleEncoder; + pass.render_pre(phase, frame_index, &mut encoder); + } + } + + let viewport = ViewportData; + + for phase in ViewportPhase::iter() { + for pass_index in phase_passes.iter_viewport(&phase) { + let pass = &self.render_passes[*pass_index]; + let mut encoder = gpu::RenderBundleEncoder; + pass.render_viewport(phase, frame_index, &viewport, &mut encoder); + } + } + } +} + +pub trait RenderPassBoxTrait { + fn begin_frame(&mut self, data_index: usize, phases: &mut PhaseList); + + fn render_pre(&self, phase: PrePhase, data_index: usize, cmds: &mut gpu::RenderBundleEncoder); + + fn render_viewport( + &self, + phase: ViewportPhase, + data_index: usize, + viewport: &ViewportData, + cmds: &mut gpu::RenderBundleEncoder, + ); +} + +pub struct RenderPassBox { + render_pass: Arc>, + frame_data: Vec, +} + +impl RenderPassBox { + pub fn new(render_pass: Arc>, frame_num: usize) -> Self { + let frame_data = { + let mut lock = render_pass.write().unwrap(); + (0..frame_num).map(|_| lock.create_frame_data()).collect() + }; + + Self { + render_pass, + frame_data, + } + } +} + +impl RenderPassBoxTrait for RenderPassBox { + fn begin_frame(&mut self, data_index: usize, phases: &mut PhaseList) { + let frame_data = &mut self.frame_data[data_index]; + let mut render_pass = self.render_pass.write().unwrap(); + render_pass.begin_frame(frame_data, phases) + } + + fn render_pre(&self, phase: PrePhase, data_index: usize, cmds: &mut gpu::RenderBundleEncoder) { + let frame_data = &self.frame_data[data_index]; + let render_pass = self.render_pass.read().unwrap(); + render_pass.render_pre(phase, frame_data, cmds) + } + + fn render_viewport( + &self, + phase: ViewportPhase, + data_index: usize, + viewport: &ViewportData, + cmds: &mut gpu::RenderBundleEncoder, + ) { + let frame_data = &self.frame_data[data_index]; + let render_pass = self.render_pass.read().unwrap(); + render_pass.render_viewport(phase, frame_data, viewport, cmds) + } +} + +/// Mock GPU API (like Vulkan, WebGPU, or OpenGL) types for prototyping. +pub mod gpu { + /// Thread-safe GPU command recorder that's later executed in a command buffer. + /// + /// Also known as a: + /// - RenderBundleEncoder in WebGPU + /// - secondary command buffer in Vulkan + pub struct RenderBundleEncoder; +}