2022-04-04 02:11:38 +00:00
|
|
|
//! Cyborg is a high-performance, modern, experimental rendering engine written
|
|
|
|
//! in Rust.
|
2022-05-11 19:00:30 +00:00
|
|
|
//!
|
|
|
|
//! # Features
|
|
|
|
//!
|
|
|
|
//! - `legion`: Enables [Legion](https://github.com/amethyst/legion) integration
|
|
|
|
//! for Cyborg. Disabled by default.
|
2022-04-04 02:11:38 +00:00
|
|
|
|
2022-05-11 15:30:01 +00:00
|
|
|
use parking_lot::Mutex;
|
2022-04-04 03:56:28 +00:00
|
|
|
use rayon::prelude::*;
|
2022-04-19 23:43:52 +00:00
|
|
|
use std::sync::Arc;
|
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-05-11 15:30:01 +00:00
|
|
|
pub mod scene;
|
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-05-11 19:00:30 +00:00
|
|
|
#[cfg(feature = "legion")]
|
|
|
|
pub mod legion;
|
|
|
|
|
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-05-11 15:30:01 +00:00
|
|
|
use viewport::Viewport;
|
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 {
|
2022-05-11 15:30:01 +00:00
|
|
|
pub fn from_camera(camera: &Camera) -> Self {
|
2022-04-23 03:42:33 +00:00
|
|
|
Self {
|
2022-05-11 15:30:01 +00:00
|
|
|
eye: camera.eye,
|
|
|
|
vp: camera.vp,
|
2022-04-23 03:42:33 +00:00
|
|
|
}
|
|
|
|
}
|
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-05-11 15:30:01 +00:00
|
|
|
pub fn get_frames_in_flight(&self) -> usize {
|
|
|
|
self.frames_in_flight
|
2022-04-05 04:21:14 +00:00
|
|
|
}
|
|
|
|
|
2022-05-11 15:30:01 +00:00
|
|
|
pub fn render<'a>(
|
|
|
|
&mut self,
|
|
|
|
passes: &mut [&mut dyn RenderPassBoxTrait],
|
|
|
|
target: &dyn Viewport,
|
|
|
|
camera: &Camera,
|
|
|
|
) {
|
2022-06-29 02:50:45 +00:00
|
|
|
puffin::profile_function!();
|
|
|
|
|
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-05-11 15:30:01 +00:00
|
|
|
passes
|
2022-04-04 03:56:28 +00:00
|
|
|
.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-05-11 15:30:01 +00:00
|
|
|
let pass = &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);
|
2022-05-11 15:30:01 +00:00
|
|
|
let pass = &passes[*pass_index];
|
2022-05-08 03:48:54 +00:00
|
|
|
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());
|
2022-05-11 15:30:01 +00:00
|
|
|
if let Some(phase_passes) = phase_passes.get_vec(&phase) {
|
|
|
|
phase_passes.par_iter().for_each(|pass_index| {
|
2022-04-24 01:56:51 +00:00
|
|
|
let phase_data = frame_data.make_phase_data(phase, &viewport);
|
2022-05-11 15:30:01 +00:00
|
|
|
let pass = &passes[*pass_index];
|
2022-04-24 01:56:51 +00:00
|
|
|
|
|
|
|
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"),
|
2022-09-17 15:34:52 +00:00
|
|
|
color_attachments: &[Some(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 {
|
2022-06-29 02:40:50 +00:00
|
|
|
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
2022-04-18 23:40:51 +00:00
|
|
|
store: true,
|
|
|
|
},
|
2022-09-17 15:34:52 +00:00
|
|
|
})],
|
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
|
|
|
}
|
|
|
|
}
|