2022-04-04 02:11:38 +00:00
|
|
|
//! Cyborg is a high-performance, modern, experimental rendering engine written
|
|
|
|
//! in Rust.
|
|
|
|
|
2022-04-04 03:56:28 +00:00
|
|
|
use rayon::prelude::*;
|
2022-04-19 23:43:52 +00:00
|
|
|
use std::sync::Arc;
|
2022-05-08 22:25:33 +00:00
|
|
|
use parking_lot::Mutex;
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-23 03:42:33 +00:00
|
|
|
pub mod camera;
|
2022-04-04 03:15:05 +00:00
|
|
|
pub mod pass;
|
2022-04-04 03:56:28 +00:00
|
|
|
pub mod phase;
|
2022-04-28 03:14:58 +00:00
|
|
|
pub mod shader;
|
2022-04-18 03:56:16 +00:00
|
|
|
pub mod staging;
|
2022-05-08 21:52:48 +00:00
|
|
|
pub mod storage;
|
2022-04-25 02:44:51 +00:00
|
|
|
pub mod viewport;
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-23 03:42:33 +00:00
|
|
|
use camera::Camera;
|
2022-04-04 03:15:05 +00:00
|
|
|
use pass::*;
|
2022-04-04 02:31:20 +00:00
|
|
|
use phase::*;
|
2022-04-25 02:44:51 +00:00
|
|
|
use viewport::ViewportImage;
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-23 03:42:33 +00:00
|
|
|
#[repr(C)]
|
|
|
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
2022-04-19 23:43:52 +00:00
|
|
|
pub struct ViewportUniform {
|
2022-04-23 03:42:33 +00:00
|
|
|
pub eye: [f32; 4],
|
|
|
|
pub vp: [[f32; 4]; 4],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ViewportUniform {
|
|
|
|
pub fn from_camera(camera: &impl Camera) -> Self {
|
|
|
|
Self {
|
|
|
|
eye: camera.get_eye(),
|
|
|
|
vp: camera.get_vp(),
|
|
|
|
}
|
|
|
|
}
|
2022-04-19 23:43:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ViewportUniform {
|
|
|
|
pub fn binding_size() -> wgpu::BufferSize {
|
|
|
|
let size = std::mem::size_of::<Self>() as u64;
|
|
|
|
size.try_into().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RenderLayouts {
|
|
|
|
pub device: Arc<wgpu::Device>,
|
|
|
|
pub bind_viewport: wgpu::BindGroupLayout,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderLayouts {
|
|
|
|
pub fn new_arc(device: Arc<wgpu::Device>) -> Arc<Self> {
|
|
|
|
let bind_viewport = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
|
|
label: Some("viewport"),
|
|
|
|
entries: &[wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: 0,
|
|
|
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Buffer {
|
|
|
|
ty: wgpu::BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
|
|
|
min_binding_size: Some(ViewportUniform::binding_size()),
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
Arc::new(Self {
|
|
|
|
device,
|
|
|
|
bind_viewport,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FrameData {
|
|
|
|
pub frame_index: usize,
|
|
|
|
pub bind_viewport: wgpu::BindGroup,
|
|
|
|
pub viewport_uniform: wgpu::Buffer,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FrameData {
|
|
|
|
pub fn new(frame_index: usize, layouts: &RenderLayouts) -> Self {
|
|
|
|
let viewport_uniform = layouts.device.create_buffer(&wgpu::BufferDescriptor {
|
|
|
|
label: Some("viewport"),
|
|
|
|
size: ViewportUniform::binding_size().into(),
|
|
|
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
|
|
mapped_at_creation: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
let bind_viewport = layouts
|
|
|
|
.device
|
|
|
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
|
|
label: Some("viewport"),
|
|
|
|
layout: &layouts.bind_viewport,
|
|
|
|
entries: &[wgpu::BindGroupEntry {
|
|
|
|
binding: 0,
|
|
|
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
|
|
buffer: &viewport_uniform,
|
|
|
|
offset: 0,
|
|
|
|
size: None,
|
|
|
|
}),
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
Self {
|
|
|
|
frame_index,
|
|
|
|
bind_viewport,
|
|
|
|
viewport_uniform,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_phase_data<'a>(
|
|
|
|
&'a self,
|
|
|
|
phase: Phase,
|
|
|
|
viewport_data: &'a ViewportData,
|
|
|
|
) -> IndexedPhaseData<'a> {
|
|
|
|
IndexedPhaseData {
|
|
|
|
phase,
|
|
|
|
frame_data: self.frame_index,
|
|
|
|
viewport_data,
|
|
|
|
bind_viewport: &self.bind_viewport,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 02:11:38 +00:00
|
|
|
pub struct Renderer {
|
2022-04-18 07:59:26 +00:00
|
|
|
device: Arc<wgpu::Device>,
|
2022-04-25 02:44:51 +00:00
|
|
|
queue: Arc<wgpu::Queue>,
|
2022-04-19 23:43:52 +00:00
|
|
|
layouts: Arc<RenderLayouts>,
|
2022-04-18 07:59:26 +00:00
|
|
|
frames_in_flight: usize,
|
2022-04-19 23:43:52 +00:00
|
|
|
frame_datas: Vec<FrameData>,
|
|
|
|
frame_index: usize,
|
2022-04-18 07:59:26 +00:00
|
|
|
render_passes: Vec<Box<dyn RenderPassBoxTrait>>,
|
2022-04-04 02:11:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-18 07:59:26 +00:00
|
|
|
impl Renderer {
|
2022-04-25 02:44:51 +00:00
|
|
|
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
|
2022-04-19 23:43:52 +00:00
|
|
|
let layouts = RenderLayouts::new_arc(device.clone());
|
|
|
|
|
|
|
|
let frames_in_flight = 1;
|
|
|
|
|
|
|
|
let mut frame_datas = Vec::with_capacity(frames_in_flight);
|
|
|
|
for frame_index in 0..frames_in_flight {
|
|
|
|
frame_datas.push(FrameData::new(frame_index, &layouts));
|
|
|
|
}
|
|
|
|
|
2022-04-05 04:21:14 +00:00
|
|
|
Self {
|
2022-04-18 23:40:51 +00:00
|
|
|
device,
|
2022-04-19 23:43:52 +00:00
|
|
|
layouts,
|
2022-04-18 07:59:26 +00:00
|
|
|
queue,
|
2022-04-19 23:43:52 +00:00
|
|
|
frames_in_flight,
|
|
|
|
frame_datas,
|
|
|
|
frame_index: 0,
|
2022-04-05 04:21:14 +00:00
|
|
|
render_passes: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-18 07:59:26 +00:00
|
|
|
pub fn get_device(&self) -> &Arc<wgpu::Device> {
|
|
|
|
&self.device
|
|
|
|
}
|
|
|
|
|
2022-04-19 23:43:52 +00:00
|
|
|
pub fn get_layouts(&self) -> &Arc<RenderLayouts> {
|
|
|
|
&self.layouts
|
|
|
|
}
|
|
|
|
|
2022-04-05 04:21:14 +00:00
|
|
|
pub fn add_pass<T: 'static + RenderPass>(&mut self, pass: T) {
|
2022-04-19 07:48:05 +00:00
|
|
|
let pass = Arc::new(pass);
|
2022-04-05 04:21:14 +00:00
|
|
|
self.add_pass_arc(pass);
|
|
|
|
}
|
|
|
|
|
2022-04-19 07:48:05 +00:00
|
|
|
pub fn add_pass_arc<T: 'static + RenderPass>(&mut self, pass: Arc<T>) {
|
2022-04-05 04:21:14 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-04-25 02:44:51 +00:00
|
|
|
pub fn render<'a>(&mut self, target: &impl ViewportImage<'a>, camera: &impl Camera) {
|
2022-04-19 23:43:52 +00:00
|
|
|
self.frame_index += 1;
|
|
|
|
if self.frame_index >= self.frame_datas.len() {
|
|
|
|
self.frame_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
let frame_index = self.frame_index;
|
|
|
|
let frame_data = &self.frame_datas[frame_index];
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-23 03:42:33 +00:00
|
|
|
let viewport_uniform = ViewportUniform::from_camera(camera);
|
|
|
|
self.queue.write_buffer(
|
|
|
|
&frame_data.viewport_uniform,
|
|
|
|
0,
|
|
|
|
bytemuck::cast_slice(&[viewport_uniform]),
|
|
|
|
);
|
|
|
|
|
2022-04-18 09:54:29 +00:00
|
|
|
let phase_passes = multimap::MultiMap::<Phase, usize>::new();
|
2022-04-04 03:56:28 +00:00
|
|
|
let phase_passes = std::sync::Mutex::new(phase_passes);
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-04 03:56:28 +00:00
|
|
|
self.render_passes
|
|
|
|
.par_iter_mut()
|
|
|
|
.enumerate()
|
|
|
|
.for_each(|(pass_index, rp)| {
|
2022-04-18 09:54:29 +00:00
|
|
|
let mut phases_buf = Vec::new();
|
2022-04-04 03:56:28 +00:00
|
|
|
phases_buf.clear();
|
2022-04-23 06:37:43 +00:00
|
|
|
rp.begin_frame(frame_index, &mut phases_buf, &self.queue);
|
2022-04-18 09:54:29 +00:00
|
|
|
|
|
|
|
let mut passes = phase_passes.lock().unwrap();
|
|
|
|
for phase in phases_buf.into_iter() {
|
|
|
|
passes.insert(phase, pass_index);
|
|
|
|
}
|
2022-04-04 03:56:28 +00:00
|
|
|
});
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-04-04 03:56:28 +00:00
|
|
|
let phase_passes = phase_passes.into_inner().unwrap();
|
2022-04-04 02:11:38 +00:00
|
|
|
let viewport = ViewportData;
|
|
|
|
|
2022-04-18 23:40:51 +00:00
|
|
|
let mut encoder = self
|
|
|
|
.device
|
|
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
|
|
label: Some("Render Encoder"),
|
|
|
|
});
|
|
|
|
|
2022-04-19 02:12:10 +00:00
|
|
|
if let Some(upload) = phase_passes.get_vec(&Phase::Upload) {
|
|
|
|
upload.iter().for_each(|pass_index| {
|
2022-04-19 23:43:52 +00:00
|
|
|
let phase_data = frame_data.make_phase_data(Phase::Upload, &viewport);
|
2022-04-19 02:12:10 +00:00
|
|
|
let pass = &self.render_passes[*pass_index];
|
2022-04-19 23:43:52 +00:00
|
|
|
pass.record_commands(phase_data, &mut encoder);
|
2022-04-19 02:12:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-08 03:48:54 +00:00
|
|
|
if let Some(skinning) = phase_passes.get_vec(&Phase::Skinning) {
|
|
|
|
let mut cmds = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
|
|
label: Some("Skinning Phase"),
|
|
|
|
});
|
|
|
|
|
|
|
|
skinning.iter().for_each(|pass_index| {
|
|
|
|
let phase_data = frame_data.make_phase_data(Phase::Skinning, &viewport);
|
|
|
|
let pass = &self.render_passes[*pass_index];
|
|
|
|
pass.record_compute(phase_data, &mut cmds);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-24 01:56:51 +00:00
|
|
|
let record_render = |phase| {
|
|
|
|
let cmds = Mutex::new(Vec::new());
|
|
|
|
if let Some(passes) = phase_passes.get_vec(&phase) {
|
|
|
|
passes.par_iter().for_each(|pass_index| {
|
|
|
|
let phase_data = frame_data.make_phase_data(phase, &viewport);
|
|
|
|
let pass = &self.render_passes[*pass_index];
|
|
|
|
|
|
|
|
if let Some(cmd) = pass.record_render(phase_data) {
|
2022-05-08 22:25:33 +00:00
|
|
|
cmds.lock().push(cmd);
|
2022-04-24 01:56:51 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-05-08 22:25:33 +00:00
|
|
|
cmds.into_inner()
|
2022-04-24 01:56:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: parallelize each phase's record_render
|
2022-04-25 02:44:51 +00:00
|
|
|
let depth_cmds = record_render(Phase::Depth);
|
2022-04-24 01:56:51 +00:00
|
|
|
let opaque_cmds = record_render(Phase::Opaque);
|
|
|
|
let overlay_cmds = record_render(Phase::Overlay);
|
2022-04-18 23:40:51 +00:00
|
|
|
|
2022-04-19 07:48:05 +00:00
|
|
|
{
|
2022-04-25 02:44:51 +00:00
|
|
|
let target_views = target.get_views();
|
|
|
|
|
2022-04-18 23:40:51 +00:00
|
|
|
let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
|
|
label: Some("Render Pass"),
|
|
|
|
color_attachments: &[wgpu::RenderPassColorAttachment {
|
2022-04-25 02:44:51 +00:00
|
|
|
view: target_views.output,
|
2022-04-18 23:40:51 +00:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
}],
|
2022-04-25 02:44:51 +00:00
|
|
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
|
|
view: target_views.depth,
|
|
|
|
depth_ops: Some(wgpu::Operations {
|
|
|
|
load: wgpu::LoadOp::Clear(1.0),
|
|
|
|
store: true,
|
|
|
|
}),
|
|
|
|
stencil_ops: None,
|
|
|
|
}),
|
2022-04-18 23:40:51 +00:00
|
|
|
});
|
|
|
|
|
2022-04-25 02:44:51 +00:00
|
|
|
rp.execute_bundles(depth_cmds.iter());
|
2022-04-18 23:40:51 +00:00
|
|
|
rp.execute_bundles(opaque_cmds.iter());
|
2022-04-24 01:56:51 +00:00
|
|
|
rp.execute_bundles(overlay_cmds.iter());
|
2022-04-18 23:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.queue.submit(std::iter::once(encoder.finish()));
|
2022-04-04 02:11:38 +00:00
|
|
|
}
|
|
|
|
}
|