//! Render pass data structures and interfaces. use crate::phase::Phase; use std::sync::Arc; pub mod debug; pub mod mesh; /// Viewport data shared by all passes, once the [ViewportPhase] group is /// entered. pub struct ViewportData; /// Data passed to each render pass's recording phases. pub struct PhaseData<'a, T> { pub phase: Phase, pub frame_data: T, pub viewport_data: &'a ViewportData, pub bind_viewport: &'a wgpu::BindGroup, } pub type IndexedPhaseData<'a> = PhaseData<'a, usize>; /// The render pass interface trait. /// /// Render passes are persistent structures that share GPU resources, user data, /// and more across both phases and frames. /// /// Rendering functions record their GPU commands using the [gpu::RenderBundleEncoder] /// passed to them. pub trait RenderPass: Send + Sync { /// A structure that contains a pass's per-frame data. type FrameData: Send + Sync; /// Gets a short name for this pass. fn get_name(&self) -> &str { std::any::type_name::() } /// Sets up a new instance of [Self::FrameData]. /// /// Called when a render pass is added to [crate::Renderer]. fn create_frame_data(&self) -> Self::FrameData; /// Initializes this frame's [Self::FrameData], and queries the pass for /// which phases to execute. /// /// This is the only opportunity the render pass has to mutate this frame's /// data this frame, so all setup for future phases should be done here. /// /// The render pass is also given access to the [queue][wgpu::Queue] this /// frame will be submitted on, and can be used to transfer image or buffer /// data to the GPU. fn begin_frame(&self, data: &mut Self::FrameData, phases: &mut Vec, queue: &wgpu::Queue); fn record_commands( &self, _data: PhaseData<&Self::FrameData>, _cmds: &mut wgpu::CommandEncoder, ) { } fn record_compute<'a>( &'a self, _data: PhaseData<&'a Self::FrameData>, _cmds: &mut wgpu::ComputePass<'a>, ) { } fn record_render(&self, _data: PhaseData<&Self::FrameData>) -> Option { None } } /// The interface trait for [RenderPassBox], allowing use of a statically-sized /// [Box] for storage. /// /// Functions on this trait map one-to-one with [RenderPass], except that /// frame data is passed by index and not by reference. pub trait RenderPassBoxTrait: Send + Sync { fn begin_frame(&mut self, data_index: usize, phases: &mut Vec, queue: &wgpu::Queue); fn record_commands(&self, data: IndexedPhaseData, cmds: &mut wgpu::CommandEncoder); fn record_compute<'a>(&'a self, data: IndexedPhaseData<'a>, cmds: &mut wgpu::ComputePass<'a>); fn record_render(&self, data: IndexedPhaseData) -> Option; } /// A container for a reference-counted render pass instance and its frame data. pub struct RenderPassBox { render_pass: Arc, frame_data: Vec, } impl RenderPassBox { /// Creates a new boxed render pass. /// /// Calls to [RenderPassBoxTrait] functions with frame indices greater /// than or equal to `frame_num` are out-of-bounds and will panic. pub fn new(render_pass: Arc, frame_num: usize) -> Self { let frame_data = { (0..frame_num) .map(|_| render_pass.create_frame_data()) .collect() }; Self { render_pass, frame_data, } } /// Same as [Self::new], but creates a boxed [RenderPassBoxTrait] in a /// generic-friendly interface. pub fn new_boxed(render_pass: Arc, frame_num: usize) -> Box { Box::new(Self::new(render_pass, frame_num)) } } impl RenderPassBox { fn get_frame_data<'a>( &'a self, index: IndexedPhaseData<'a>, ) -> PhaseData<'a, &'a T::FrameData> { let PhaseData { phase, frame_data, viewport_data, bind_viewport, } = index; let frame_data = self.frame_data.get(frame_data).unwrap(); PhaseData { phase, frame_data, viewport_data, bind_viewport, } } } impl std::ops::Deref for RenderPassBox { type Target = T; fn deref(&self) -> &T { self.render_pass.as_ref() } } impl RenderPassBoxTrait for RenderPassBox { fn begin_frame(&mut self, data_index: usize, phases: &mut Vec, queue: &wgpu::Queue) { let rp = &self.render_pass; let name = rp.get_name(); puffin::profile_scope!("RenderPass::begin_frame(...)", name); let frame_data = &mut self.frame_data[data_index]; rp.begin_frame(frame_data, phases, queue) } fn record_commands(&self, data: IndexedPhaseData, cmds: &mut wgpu::CommandEncoder) { let rp = &self.render_pass; let name = rp.get_name(); let scope = format!("{} (phase: {:?})", name, data.phase); puffin::profile_scope!("RenderPass::record_commands(...)", scope); let frame_data = self.get_frame_data(data); rp.record_commands(frame_data, cmds) } fn record_compute<'a>(&'a self, data: IndexedPhaseData<'a>, cmds: &mut wgpu::ComputePass<'a>) { let rp = &self.render_pass; let name = rp.get_name(); let scope = format!("{} (phase: {:?})", name, data.phase); puffin::profile_scope!("RenderPass::record_compute(...)", scope); let frame_data = self.get_frame_data(data); rp.record_compute(frame_data, cmds) } fn record_render(&self, data: IndexedPhaseData) -> Option { let rp = &self.render_pass; let name = rp.get_name(); let scope = format!("{} (phase: {:?})", name, data.phase); puffin::profile_scope!("RenderPass::record_render(...)", scope); let frame_data = self.get_frame_data(data); rp.record_render(frame_data) } }