cyborg/src/lib.rs

149 lines
4.4 KiB
Rust

//! Cyborg is a high-performance, modern, experimental rendering engine written
//! in Rust.
use rayon::prelude::*;
use std::sync::Mutex;
use std::sync::{Arc, RwLock};
use strum::IntoEnumIterator;
pub mod mesh;
pub mod pass;
pub mod phase;
pub mod staging;
use pass::*;
use phase::*;
pub struct Renderer {
device: Arc<wgpu::Device>,
queue: wgpu::Queue,
frames_in_flight: usize,
render_passes: Vec<Box<dyn RenderPassBoxTrait>>,
}
impl Renderer {
pub fn new(device: Arc<wgpu::Device>, queue: wgpu::Queue) -> Self {
Self {
device,
queue,
frames_in_flight: 2,
render_passes: Vec::new(),
}
}
pub fn get_device(&self) -> &Arc<wgpu::Device> {
&self.device
}
pub fn add_pass<T: 'static + RenderPass>(&mut self, pass: T) {
let pass = Arc::new(pass);
self.add_pass_arc(pass);
}
pub fn add_pass_arc<T: 'static + RenderPass>(&mut self, pass: Arc<T>) {
let pass = RenderPassBox::new(pass, self.frames_in_flight);
self.add_pass_box(pass);
}
pub fn add_pass_box(&mut self, pass: Box<dyn RenderPassBoxTrait>) {
self.render_passes.push(pass);
}
pub fn render(
&mut self,
surface: &wgpu::Surface,
format: wgpu::TextureFormat,
) -> Result<(), wgpu::SurfaceError> {
let frame_index = 0;
let phase_passes = multimap::MultiMap::<Phase, usize>::new();
let phase_passes = std::sync::Mutex::new(phase_passes);
self.render_passes
.par_iter_mut()
.enumerate()
.for_each(|(pass_index, rp)| {
let mut phases_buf = Vec::new();
phases_buf.clear();
rp.begin_frame(frame_index, &mut phases_buf);
let mut passes = phase_passes.lock().unwrap();
for phase in phases_buf.into_iter() {
passes.insert(phase, pass_index);
}
});
let phase_passes = phase_passes.into_inner().unwrap();
let viewport = ViewportData;
let output = surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
if let Some(upload) = phase_passes.get_vec(&Phase::Upload) {
upload.iter().for_each(|pass_index| {
let frame_data = IndexedPhaseData {
phase: Phase::Upload,
frame_data: frame_index,
viewport: &viewport,
};
let pass = &self.render_passes[*pass_index];
pass.record_commands(frame_data, &mut encoder);
});
}
let opaque_cmds = Mutex::new(Vec::new());
if let Some(opaque) = phase_passes.get_vec(&Phase::Opaque) {
opaque.par_iter().for_each(|pass_index| {
let pass = &self.render_passes[*pass_index];
let frame_data = IndexedPhaseData {
phase: Phase::Opaque,
frame_data: frame_index,
viewport: &viewport,
};
if let Some(cmd) = pass.record_render(frame_data) {
opaque_cmds.lock().unwrap().push(cmd);
}
})
}
let opaque_cmds = opaque_cmds.into_inner().unwrap();
{
let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
}],
depth_stencil_attachment: None,
});
rp.execute_bundles(opaque_cmds.iter());
}
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
}