149 lines
4.4 KiB
Rust
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(())
|
|
}
|
|
}
|