diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7f47e90c67495..d886d45fd19ce 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -34,7 +34,7 @@ pub mod prelude { color::Color, mesh::{morph::MorphWeights, shape, Mesh}, render_resource::Shader, - renderer::RenderStatistics, + renderer::{RenderPassStatistics, RenderStatistics, RenderStatisticsPlugin}, spatial_bundle::SpatialBundle, texture::{Image, ImagePlugin}, view::{ComputedVisibility, Msaa, Visibility, VisibilityBundle}, @@ -44,10 +44,7 @@ pub mod prelude { use bevy_window::{PrimaryWindow, RawHandleWrapper}; use globals::GlobalsPlugin; -use renderer::{ - sync_render_statistics, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, - RenderStatistics, RenderStatisticsMutex, StatisticsRecorder, -}; +use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}; use wgpu::Instance; use crate::{ @@ -58,7 +55,7 @@ use crate::{ settings::WgpuSettings, view::{ViewPlugin, WindowRenderPlugin}, }; -use bevy_app::{App, AppLabel, Plugin, PreUpdate, SubApp}; +use bevy_app::{App, AppLabel, Plugin, SubApp}; use bevy_asset::{AddAsset, AssetServer}; use bevy_ecs::{prelude::*, schedule::ScheduleLabel, system::SystemState}; use bevy_utils::tracing::debug; @@ -274,18 +271,11 @@ impl Plugin for RenderPlugin { let mut extract_schedule = Schedule::new(); extract_schedule.set_apply_final_deferred(false); - let render_statistics = RenderStatisticsMutex::default(); - - app.insert_resource(render_statistics.clone()) - .add_systems(PreUpdate, sync_render_statistics) - .init_resource::(); - render_app .add_schedule(ExtractSchedule, extract_schedule) .add_schedule(Render, Render::base_schedule()) .init_resource::() .insert_resource(app.world.resource::().clone()) - .insert_resource(render_statistics) .add_systems(ExtractSchedule, PipelineCache::extract_shaders) .add_systems( Render, @@ -380,7 +370,6 @@ impl Plugin for RenderPlugin { render_app .insert_resource(RenderInstance(instance)) .insert_resource(PipelineCache::new(device.clone())) - .insert_resource(StatisticsRecorder::new(&device, &queue)) .insert_resource(device) .insert_resource(queue) .insert_resource(render_adapter) diff --git a/crates/bevy_render/src/renderer/graph_runner.rs b/crates/bevy_render/src/renderer/graph_runner.rs index fe21a4879761b..28cab6535a168 100644 --- a/crates/bevy_render/src/renderer/graph_runner.rs +++ b/crates/bevy_render/src/renderer/graph_runner.rs @@ -57,12 +57,14 @@ impl RenderGraphRunner { pub fn run( graph: &RenderGraph, render_device: RenderDevice, - mut statistics_recorder: StatisticsRecorder, + mut statistics_recorder: Option, queue: &wgpu::Queue, world: &World, finalizer: impl FnOnce(&mut wgpu::CommandEncoder), - ) -> Result { - statistics_recorder.begin_frame(); + ) -> Result, RenderGraphRunnerError> { + if let Some(recorder) = &mut statistics_recorder { + recorder.begin_frame(); + } let mut render_context = RenderContext::new(render_device, statistics_recorder); Self::run_graph(graph, None, &mut render_context, world, &[], None)?; @@ -78,10 +80,12 @@ impl RenderGraphRunner { (render_device, statistics_recorder) }; - let render_statistics_mutex = world.resource::().0.clone(); - statistics_recorder.finish_frame(&render_device, move |statistics| { - *render_statistics_mutex.lock() = Some(statistics); - }); + if let Some(recorder) = &mut statistics_recorder { + let render_statistics_mutex = world.resource::().0.clone(); + recorder.finish_frame(&render_device, move |statistics| { + *render_statistics_mutex.lock() = Some(statistics); + }); + } Ok(statistics_recorder) } diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index ef7781b90f069..03750457e4ae2 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -29,7 +29,7 @@ pub fn render_system(world: &mut World) { graph.update(world); }); - let statistics_recorder = world.remove_resource::().unwrap(); + let statistics_recorder = world.remove_resource::(); let graph = world.resource::(); let render_device = world.resource::(); @@ -45,9 +45,10 @@ pub fn render_system(world: &mut World) { crate::view::screenshot::submit_screenshot_commands(world, encoder); }, ) { - Ok(statistics_recorder) => { + Ok(Some(statistics_recorder)) => { world.insert_resource(statistics_recorder); } + Ok(None) => {} Err(e) => { error!("Error running render graph:"); { @@ -302,12 +303,15 @@ pub struct RenderContext { render_device: RenderDevice, command_encoder: Option, command_buffers: Vec, - statistics_recorder: StatisticsRecorder, + statistics_recorder: Option, } impl RenderContext { /// Creates a new [`RenderContext`] from a [`RenderDevice`]. - pub fn new(render_device: RenderDevice, statistics_recorder: StatisticsRecorder) -> Self { + pub fn new( + render_device: RenderDevice, + statistics_recorder: Option, + ) -> Self { Self { render_device, command_encoder: None, @@ -340,8 +344,13 @@ impl RenderContext { self.render_device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) }); - let render_pass = - MeasuredRenderPass::new(command_encoder, &mut self.statistics_recorder, descriptor); + + let render_pass = MeasuredRenderPass::new( + command_encoder, + self.statistics_recorder.as_mut(), + descriptor, + ); + TrackedRenderPass::new(&self.render_device, render_pass) } @@ -358,13 +367,15 @@ impl RenderContext { /// Finalizes the queue and returns the queue of [`CommandBuffer`]s. /// /// When the render statistics become available, `statistics_callback` will be invoked. - pub fn finish(mut self) -> (Vec, RenderDevice, StatisticsRecorder) { + pub fn finish(mut self) -> (Vec, RenderDevice, Option) { let command_encoder = self.command_encoder.get_or_insert_with(|| { self.render_device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) }); - self.statistics_recorder.resolve(command_encoder); + if let Some(recorder) = &mut self.statistics_recorder { + recorder.resolve(command_encoder); + } self.flush_encoder(); diff --git a/crates/bevy_render/src/renderer/statistics.rs b/crates/bevy_render/src/renderer/statistics.rs index 269ca22fe6924..82cc1c5c6d613 100644 --- a/crates/bevy_render/src/renderer/statistics.rs +++ b/crates/bevy_render/src/renderer/statistics.rs @@ -3,6 +3,7 @@ use std::sync::{ Arc, }; +use bevy_app::{App, Plugin, PreUpdate}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_utils::{Duration, HashMap, Instant}; @@ -13,7 +14,9 @@ use wgpu::{ RenderPassDescriptor, }; -use super::RenderDevice; +use crate::RenderApp; + +use super::{RenderDevice, RenderQueue}; // buffer offset must be divisible by 256, so this constant must be divisible by 32 (=256/8) const MAX_TIMESTAMP_QUERIES: u32 = 256; @@ -22,6 +25,33 @@ const MAX_PIPELINE_STATISTICS: u32 = 128; const TIMESTAMP_SIZE: u64 = 8; const PIPELINE_STATISTICS_SIZE: u64 = 40; +/// Enables collecting render pass statistics into [`RenderStatistics`] resource. +#[derive(Default)] +pub struct RenderStatisticsPlugin; + +impl Plugin for RenderStatisticsPlugin { + fn build(&self, app: &mut App) { + let render_statistics_mutex = RenderStatisticsMutex::default(); + app.insert_resource(render_statistics_mutex.clone()) + .init_resource::() + .add_systems(PreUpdate, sync_render_statistics); + + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.insert_resource(render_statistics_mutex); + } + } + + fn finish(&self, app: &mut App) { + let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + let device = render_app.world.resource::(); + let queue = render_app.world.resource::(); + render_app.insert_resource(StatisticsRecorder::new(device, queue)); + } +} + /// Resource which stores statistics for each render pass. #[derive(Debug, Default, Clone, Resource)] pub struct RenderStatistics(pub HashMap); @@ -102,8 +132,6 @@ impl StatisticsRecorder { } } - dbg!(self.finished_frames.len() + self.submitted_frames.len() + 1); - self.current_frame.begin(); } @@ -415,7 +443,7 @@ pub struct MeasuredRenderPass<'a> { #[deref] render_pass: RenderPass<'a>, name: Option, - recorder: &'a mut StatisticsRecorder, + recorder: Option<&'a mut StatisticsRecorder>, } impl MeasuredRenderPass<'_> { @@ -424,13 +452,17 @@ impl MeasuredRenderPass<'_> { /// [`RenderPassDescriptor`] must have a label, otherwise no statistics will be recorded. pub fn new<'a>( encoder: &'a mut CommandEncoder, - recorder: &'a mut StatisticsRecorder, + mut recorder: Option<&'a mut StatisticsRecorder>, desc: RenderPassDescriptor<'a, '_>, ) -> MeasuredRenderPass<'a> { - let name = desc.label.map(|v| v.to_owned()); + // copy label only if recording is enabled + let name = recorder + .as_ref() + .and_then(|_| desc.label.map(|v| v.to_owned())); + let mut render_pass = encoder.begin_render_pass(&desc); - if let Some(name) = &name { + if let (Some(recorder), Some(name)) = (&mut recorder, &name) { recorder.begin_render_pass(&mut render_pass, name); } @@ -448,8 +480,8 @@ impl Drop for MeasuredRenderPass<'_> { return; } - if let Some(name) = &self.name { - self.recorder.end_render_pass(&mut self.render_pass, name); + if let (Some(recorder), Some(name)) = (&mut self.recorder, &self.name) { + recorder.end_render_pass(&mut self.render_pass, name); } } } diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index d3e936f7cd82d..e8e0b8f3f752a 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -5,6 +5,7 @@ use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) + .add_plugins(RenderStatisticsPlugin) .add_systems(Startup, setup) .add_systems(Update, print_render_statistics) .run();