Profile and preinitialize viewport rendering
This commit is contained in:
@@ -30,7 +30,7 @@ use std::{
|
||||
marker::PhantomData,
|
||||
sync::{Mutex, mpsc},
|
||||
thread,
|
||||
time::Duration,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
const WORLD_AMBIENT_BRIGHTNESS: f32 = 400.0;
|
||||
@@ -43,11 +43,30 @@ enum RenderCommand {
|
||||
textures: Vec<Texture>,
|
||||
background_srgb: [u8; 4],
|
||||
limits: RenderLimits,
|
||||
reply: mpsc::SyncSender<Result<Vec<u8>, RenderError>>,
|
||||
reply: mpsc::SyncSender<Result<RenderedFrame, RenderError>>,
|
||||
},
|
||||
Stop,
|
||||
}
|
||||
|
||||
/// Wall-clock costs of one renderer call. GPU work and readback are included in
|
||||
/// `capture`; the fields before it are CPU-side preparation.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct RenderTimings {
|
||||
pub validation: Duration,
|
||||
pub command_copy: Duration,
|
||||
pub clear_previous_frame: Duration,
|
||||
pub texture_cache_sync: Duration,
|
||||
pub mesh_cache_sync: Duration,
|
||||
pub scene_setup: Duration,
|
||||
pub capture: Duration,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct RenderedFrame {
|
||||
pub rgba: Vec<u8>,
|
||||
pub timings: RenderTimings,
|
||||
}
|
||||
|
||||
/// Reusable renderer facade. Bevy and its ECS stay on a dedicated OS thread.
|
||||
pub struct Renderer {
|
||||
commands: mpsc::SyncSender<RenderCommand>,
|
||||
@@ -110,26 +129,46 @@ impl Renderer {
|
||||
background_srgb: [u8; 4],
|
||||
limits: RenderLimits,
|
||||
) -> Result<Vec<u8>, RenderError> {
|
||||
self.render_profiled(camera, renderables, textures, background_srgb, limits)
|
||||
.map(|frame| frame.rgba)
|
||||
}
|
||||
|
||||
pub fn render_profiled(
|
||||
&self,
|
||||
camera: Camera,
|
||||
renderables: &[Renderable],
|
||||
textures: &[Texture],
|
||||
background_srgb: [u8; 4],
|
||||
limits: RenderLimits,
|
||||
) -> Result<RenderedFrame, RenderError> {
|
||||
let started = Instant::now();
|
||||
validate_scene(camera, renderables, textures, limits)?;
|
||||
let validation = started.elapsed();
|
||||
let started = Instant::now();
|
||||
let (sender, receiver) = mpsc::sync_channel(1);
|
||||
let command = RenderCommand::Frame {
|
||||
camera,
|
||||
renderables: renderables.to_vec(),
|
||||
textures: textures.to_vec(),
|
||||
background_srgb,
|
||||
limits,
|
||||
reply: sender,
|
||||
};
|
||||
let command_copy = started.elapsed();
|
||||
self.commands
|
||||
.try_send(RenderCommand::Frame {
|
||||
camera,
|
||||
renderables: renderables.to_vec(),
|
||||
textures: textures.to_vec(),
|
||||
background_srgb,
|
||||
limits,
|
||||
reply: sender,
|
||||
})
|
||||
.try_send(command)
|
||||
.map_err(|error| match error {
|
||||
mpsc::TrySendError::Full(_) => RenderError::TimedOut,
|
||||
mpsc::TrySendError::Disconnected(_) => {
|
||||
RenderError::Device("renderer thread stopped".into())
|
||||
}
|
||||
})?;
|
||||
receiver
|
||||
let mut frame = receiver
|
||||
.recv_timeout(Duration::from_mins(2))
|
||||
.map_err(|_| RenderError::TimedOut)?
|
||||
.map_err(|_| RenderError::TimedOut)??;
|
||||
frame.timings.validation = validation;
|
||||
frame.timings.command_copy = command_copy;
|
||||
Ok(frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,8 +399,10 @@ impl BevyBackend {
|
||||
textures: &[Texture],
|
||||
background_srgb: [u8; 4],
|
||||
limits: RenderLimits,
|
||||
) -> Result<Vec<u8>, RenderError> {
|
||||
) -> Result<RenderedFrame, RenderError> {
|
||||
let started = Instant::now();
|
||||
self.clear_frame();
|
||||
let clear_previous_frame = started.elapsed();
|
||||
self.apps
|
||||
.main
|
||||
.world_mut()
|
||||
@@ -371,16 +412,21 @@ impl BevyBackend {
|
||||
background_srgb[2],
|
||||
background_srgb[3],
|
||||
)));
|
||||
let started = Instant::now();
|
||||
self.texture_handles = add_textures(
|
||||
self.apps.main.world_mut(),
|
||||
textures,
|
||||
&mut self.texture_cache,
|
||||
);
|
||||
let texture_cache_sync = started.elapsed();
|
||||
let started = Instant::now();
|
||||
let mesh_handles = add_meshes(
|
||||
self.apps.main.world_mut(),
|
||||
renderables,
|
||||
&mut self.mesh_cache,
|
||||
)?;
|
||||
let mesh_cache_sync = started.elapsed();
|
||||
let started = Instant::now();
|
||||
for (renderable, mesh) in renderables.iter().zip(mesh_handles) {
|
||||
let entity = match &renderable.material {
|
||||
Material::BlinnPhong(material) => {
|
||||
@@ -463,7 +509,20 @@ impl BevyBackend {
|
||||
))
|
||||
.id();
|
||||
self.frame_entities.push(light);
|
||||
self.capture(&target)
|
||||
let scene_setup = started.elapsed();
|
||||
let started = Instant::now();
|
||||
let rgba = self.capture(&target)?;
|
||||
Ok(RenderedFrame {
|
||||
rgba,
|
||||
timings: RenderTimings {
|
||||
clear_previous_frame,
|
||||
texture_cache_sync,
|
||||
mesh_cache_sync,
|
||||
scene_setup,
|
||||
capture: started.elapsed(),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn new_target(&mut self, width: u32, height: u32) -> RenderTarget {
|
||||
|
||||
@@ -260,7 +260,7 @@ fn cross(left: [f32; 3], right: [f32; 3]) -> [f32; 3] {
|
||||
#[cfg(feature = "wgpu")]
|
||||
mod gpu;
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub use gpu::Renderer;
|
||||
pub use gpu::{RenderTimings, RenderedFrame, Renderer};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
Reference in New Issue
Block a user