From 91eed4018aaa8ab6f87e6a2964ce5c0c1147b39c Mon Sep 17 00:00:00 2001 From: LeshaInc Date: Fri, 21 Jul 2023 13:23:03 +0300 Subject: [PATCH] Render diagnostics API improvements --- crates/bevy_core_pipeline/src/bloom/mod.rs | 1 + .../src/core_2d/main_pass_2d_node.rs | 1 + .../src/core_3d/main_opaque_pass_3d_node.rs | 1 + .../core_3d/main_transparent_pass_3d_node.rs | 1 + crates/bevy_core_pipeline/src/prepass/node.rs | 1 + crates/bevy_pbr/src/render/light.rs | 1 + .../internal.rs} | 296 ++---------------- crates/bevy_render/src/diagnostic/mod.rs | 267 ++++++++++++++++ crates/bevy_render/src/lib.rs | 2 +- .../src/render_phase/draw_state.rs | 2 +- .../bevy_render/src/renderer/graph_runner.rs | 2 +- crates/bevy_render/src/renderer/mod.rs | 18 +- examples/3d/3d_scene.rs | 2 +- 13 files changed, 312 insertions(+), 283 deletions(-) rename crates/bevy_render/src/{diagnostics.rs => diagnostic/internal.rs} (69%) create mode 100644 crates/bevy_render/src/diagnostic/mod.rs diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index c8bb99437271d..1f3c1ee0fc08a 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -15,6 +15,7 @@ use bevy_math::UVec2; use bevy_reflect::TypeUuid; use bevy_render::{ camera::ExtractedCamera, + diagnostic::RecordDiagnostics, extract_component::{ ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin, }, diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index 478017dc88df9..87b7a977e0d39 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -5,6 +5,7 @@ use crate::{ use bevy_ecs::prelude::*; use bevy_render::{ camera::ExtractedCamera, + diagnostic::RecordDiagnostics, render_graph::{Node, NodeRunError, RenderGraphContext}, render_phase::RenderPhase, render_resource::{LoadOp, Operations, RenderPassDescriptor}, diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 9508d2dc85d98..52b2a1187dc28 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -7,6 +7,7 @@ use crate::{ use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::ExtractedCamera, + diagnostic::RecordDiagnostics, render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_phase::RenderPhase, render_resource::{ diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index e90fdb1934233..b3be3ac7e4d24 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -2,6 +2,7 @@ use crate::core_3d::Transparent3d; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::ExtractedCamera, + diagnostic::RecordDiagnostics, render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_phase::RenderPhase, render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor}, diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 1d15c680bcb59..aabf64f1fad84 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -3,6 +3,7 @@ use bevy_ecs::query::QueryItem; use bevy_render::render_graph::ViewNode; use bevy_render::{ camera::ExtractedCamera, + diagnostic::RecordDiagnostics, prelude::Color, render_graph::{NodeRunError, RenderGraphContext}, render_phase::RenderPhase, diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index b3fc0be071b12..c9f12e9641e54 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -13,6 +13,7 @@ use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles use bevy_render::{ camera::Camera, color::Color, + diagnostic::RecordDiagnostics, mesh::Mesh, render_asset::RenderAssets, render_graph::{Node, NodeRunError, RenderGraphContext}, diff --git a/crates/bevy_render/src/diagnostics.rs b/crates/bevy_render/src/diagnostic/internal.rs similarity index 69% rename from crates/bevy_render/src/diagnostics.rs rename to crates/bevy_render/src/diagnostic/internal.rs index bad51a6dc6ea5..c1384cef1b92c 100644 --- a/crates/bevy_render/src/diagnostics.rs +++ b/crates/bevy_render/src/diagnostic/internal.rs @@ -1,30 +1,24 @@ use std::{ - borrow::Cow, - hash::{Hash, Hasher}, - marker::PhantomData, ops::Range, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, thread::{self, ThreadId}, + time::Instant, }; -use bevy_app::{App, Plugin, PreUpdate}; - -use bevy_diagnostic::DiagnosticId; use bevy_ecs::system::{Res, ResMut, Resource}; -use bevy_utils::{AHasher, Duration, Instant, Uuid}; +use bevy_utils::Duration; use parking_lot::Mutex; -use smallvec::SmallVec; use wgpu::{ Buffer, BufferDescriptor, BufferUsages, CommandEncoder, ComputePass, Features, MapMode, PipelineStatisticsTypes, QuerySet, QuerySetDescriptor, QueryType, Queue, RenderPass, }; -use crate::RenderApp; +use crate::renderer::RenderDevice; -use super::{RenderDevice, RenderQueue}; +use super::{RecordDiagnostics, RenderDiagnostics, SpanDiagnostics, SpanKind, SpanName, SpanPath}; // buffer offset must be divisible by 256, so this constant must be divisible by 32 (=256/8) const MAX_TIMESTAMP_QUERIES: u32 = 256; @@ -33,134 +27,6 @@ const MAX_PIPELINE_STATISTICS: u32 = 128; const TIMESTAMP_SIZE: u64 = 8; const PIPELINE_STATISTICS_SIZE: u64 = 40; -/// Enables collecting rendering diagnostics into [`RenderDiagnostics`] resource. -/// -/// # Supported platforms -/// Timestamp queries and pipeline statistics are currently supported only on Vulkan and DX12. -/// On other platforms (Metal, WebGPU, WebGL2) only CPU time will be recorded. -#[allow(clippy::doc_markdown)] -#[derive(Default)] -pub struct RenderDiagnosticsPlugin; - -impl Plugin for RenderDiagnosticsPlugin { - fn build(&self, app: &mut App) { - let render_diagnostics_mutex = RenderDiagnosticsMutex::default(); - app.insert_resource(render_diagnostics_mutex.clone()) - .init_resource::() - .add_systems(PreUpdate, sync_render_diagnostics); - - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.insert_resource(render_diagnostics_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(DiagnosticsRecorder::new(device, queue)); - } -} - -/// Resource which stores rendering diagnostics of the most recent frame. -#[derive(Debug, Default, Clone, Resource)] -pub struct RenderDiagnostics(pub Vec); - -/// Diagnostics of a single render span. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct RenderSpanDiagnostics { - /// Path of the span. - pub path: SpanPath, - /// Kind of the span. - pub kind: SpanKind, - /// CPU time spent during the duration of the span. - pub elapsed_cpu: Option, - /// GPU time spent executing commands recorded inside the span. - pub elapsed_gpu: Option, - /// Amount of times the vertex shader is ran. - /// Accounts for the vertex cache when doing indexed rendering. - pub vertex_shader_invocations: Option, - /// Amount of times the clipper is invoked. - /// This is also the amount of triangles output by the vertex shader. - pub clipper_invocations: Option, - /// Amount of primitives that are not culled by the clipper. - /// This is the amount of triangles that are actually on screen and will be rasterized and rendered. - pub clipper_primitives_out: Option, - /// Amount of times the fragment shader is ran. - /// Accounts for fragment shaders running in 2x2 blocks in order to get derivatives. - pub fragment_shader_invocations: Option, - /// Amount of times a compute shader is invoked. - /// This will be equivalent to the dispatch count times the workgroup size. - pub compute_shader_invocations: Option, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum RenderDiagnosticKind { - ElapsedCpu = 0, - ElapsedGpu, - VertexShaderInvocations, - ClipperInvocations, - ClipperPrimitivesOut, - FragmentShaderInvocations, - ComputeShaderInvocations, -} - -/// Kinds of render diagnostic spans. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum SpanKind { - /// An explicit timestamp span. - Timestamp, - /// A [`RenderPass`]. Records timestamps, as well as pipeline statistics. - RenderPass, - /// A [`ComputePass`]. Records timestamps, as well as pipeline statistics. - ComputePass, -} - -#[derive(Debug, Default, Clone)] -pub struct SpanPath { - components: SmallVec<[Cow<'static, str>; 2]>, - hash: u64, -} - -impl SpanPath { - pub fn new(components: impl IntoIterator>) -> SpanPath { - let components: SmallVec<[Cow<'static, str>; 2]> = components.into_iter().collect(); - let mut hasher = AHasher::default(); - components.hash(&mut hasher); - let hash = hasher.finish(); - SpanPath { components, hash } - } - - pub fn components(&self) -> impl Iterator + '_ { - self.components.iter().map(|v| &**v) - } - - pub fn diagnostic_id(&self, kind: RenderDiagnosticKind) -> DiagnosticId { - DiagnosticId(Uuid::from_u64_pair( - 0x6140_553e_4b6a_4400 | u64::from(kind as u8), - self.hash, - )) - } -} - -impl Eq for SpanPath {} - -impl PartialEq for SpanPath { - fn eq(&self, other: &Self) -> bool { - self.hash == other.hash && self.components == other.components - } -} - -impl Hash for SpanPath { - fn hash(&self, state: &mut H) { - self.hash.hash(state); - } -} - /// Records diagnostics into [`QuerySet`]'s keeping track of the mapping between /// spans and indices to the corresponding entries in the [`QuerySet`]. #[derive(Resource)] @@ -207,28 +73,6 @@ impl DiagnosticsRecorder { self.current_frame.get_mut().begin(); } - pub fn begin_time_span( - &self, - encoder: &mut E, - span_name: impl Into>, - ) { - self.current_frame - .lock() - .begin_time_span(encoder, span_name.into()); - } - - pub fn end_time_span(&self, encoder: &mut E) { - self.current_frame.lock().end_time_span(encoder); - } - - pub fn begin_pass_span(&self, pass: &mut P, span_name: impl Into>) { - self.current_frame.lock().begin_pass(pass, span_name.into()); - } - - pub fn end_pass_span(&self, pass: &mut P) { - self.current_frame.lock().end_pass(pass); - } - /// Copies data from [`QuerySet`]'s to a [`Buffer`], after which it can be downloaded to CPU. /// /// Should be called before [`DiagnosticsRecorder::finish_frame`] @@ -260,116 +104,23 @@ impl DiagnosticsRecorder { } } -#[derive(Clone)] -pub struct OptionalDiagnosticRecorder { - recorder: Option>, -} - -impl From> for OptionalDiagnosticRecorder { - fn from(recorder: Option) -> Self { - OptionalDiagnosticRecorder { - recorder: recorder.map(Arc::new), - } - } -} - -impl OptionalDiagnosticRecorder { - pub(crate) fn unwrap(self) -> Option { - self.recorder.map(|v| Arc::try_unwrap(v).ok().unwrap()) - } - - pub fn time_span( - &self, - encoder: &mut E, - span_name: impl Into>, - ) -> TimeSpanScope { - if let Some(recorder) = &self.recorder { - recorder.begin_time_span(encoder, span_name); - TimeSpanScope::new(recorder) - } else { - TimeSpanScope::new_noop() - } - } - - pub fn pass_span( - &self, - pass: &mut P, - span_name: impl Into>, - ) -> PassSpanScope

{ - if let Some(recorder) = &self.recorder { - recorder.begin_pass_span(pass, span_name); - PassSpanScope::new(recorder) - } else { - PassSpanScope::new_noop() - } - } -} - -pub struct TimeSpanScope<'a, E: WriteTimestamp> { - recorder: Option<&'a DiagnosticsRecorder>, - marker: PhantomData, -} - -impl TimeSpanScope<'_, E> { - pub fn new(recorder: &DiagnosticsRecorder) -> TimeSpanScope<'_, E> { - TimeSpanScope { - recorder: Some(recorder), - marker: PhantomData, - } - } - - pub fn new_noop() -> TimeSpanScope<'static, E> { - TimeSpanScope { - recorder: None, - marker: PhantomData, - } - } - - pub fn end(self, encoder: &mut E) { - if let Some(recorder) = &self.recorder { - recorder.current_frame.lock().end_time_span(encoder); - } - std::mem::forget(self) - } -} - -impl Drop for TimeSpanScope<'_, E> { - fn drop(&mut self) { - panic!("TimeSpanScope::end was never called") - } -} - -pub struct PassSpanScope<'a, P: Pass> { - recorder: Option<&'a DiagnosticsRecorder>, - marker: PhantomData

, -} - -impl PassSpanScope<'_, P> { - pub fn new(recorder: &DiagnosticsRecorder) -> PassSpanScope<'_, P> { - PassSpanScope { - recorder: Some(recorder), - marker: PhantomData, - } +impl RecordDiagnostics for DiagnosticsRecorder { + fn begin_time_span(&self, encoder: &mut E, span_name: SpanName) { + self.current_frame + .lock() + .begin_time_span(encoder, span_name); } - pub fn new_noop() -> PassSpanScope<'static, P> { - PassSpanScope { - recorder: None, - marker: PhantomData, - } + fn end_time_span(&self, encoder: &mut E) { + self.current_frame.lock().end_time_span(encoder); } - pub fn end(self, pass: &mut P) { - if let Some(recorder) = &self.recorder { - recorder.current_frame.lock().end_pass(pass); - } - std::mem::forget(self) + fn begin_pass_span(&self, pass: &mut P, span_name: SpanName) { + self.current_frame.lock().begin_pass(pass, span_name); } -} -impl Drop for PassSpanScope<'_, P> { - fn drop(&mut self) { - panic!("PassSpanScope::end was never called") + fn end_pass_span(&self, pass: &mut P) { + self.current_frame.lock().end_pass(pass); } } @@ -394,7 +145,7 @@ struct FrameData { pipeline_statistics_buffer_offset: u64, resolve_buffer: Option, read_buffer: Option, - path_components: Vec>, + path_components: Vec, open_spans: Vec, closed_spans: Vec, is_mapped: Arc, @@ -511,7 +262,7 @@ impl FrameData { } } - fn open_span(&mut self, kind: SpanKind, name: Cow<'static, str>) -> &mut SpanRecord { + fn open_span(&mut self, kind: SpanKind, name: SpanName) -> &mut SpanRecord { let thread_id = thread::current().id(); let parent = self @@ -563,11 +314,11 @@ impl FrameData { self.closed_spans.last_mut().unwrap() } - fn begin_time_span(&mut self, encoder: &mut impl WriteTimestamp, name: Cow<'static, str>) { + fn begin_time_span(&mut self, encoder: &mut impl WriteTimestamp, name: SpanName) { let begin_instant = Instant::now(); let begin_timestamp_index = self.write_timestamp(encoder, false); - let span = self.open_span(SpanKind::Timestamp, name); + let span = self.open_span(SpanKind::Time, name); span.begin_instant = Some(begin_instant); span.begin_timestamp_index = begin_timestamp_index; } @@ -580,7 +331,7 @@ impl FrameData { span.end_instant = Some(Instant::now()); } - fn begin_pass(&mut self, pass: &mut P, name: Cow<'static, str>) { + fn begin_pass(&mut self, pass: &mut P, name: SpanName) { let begin_instant = Instant::now(); let begin_timestamp_index = self.write_timestamp(pass, true); @@ -642,7 +393,7 @@ impl FrameData { let Some(read_buffer) = &self.read_buffer else { // we still have cpu timings, so let's use them - let diagnostics = self.closed_spans.iter().map(|span| RenderSpanDiagnostics { + let diagnostics = self.closed_spans.iter().map(|span| SpanDiagnostics { path: SpanPath::new( self.path_components[span.path_range.clone()] .iter() @@ -706,7 +457,7 @@ impl FrameData { .collect::>(); let diagnostics = self.closed_spans.iter().map(|span| { - let mut diagnostics = RenderSpanDiagnostics { + let mut diagnostics = SpanDiagnostics { path: SpanPath::new( self.path_components[span.path_range.clone()] .iter() @@ -766,12 +517,11 @@ impl FrameData { pub struct RenderDiagnosticsMutex(pub Arc>>); /// Copies fresh [`RenderDiagnostics`] from [`RenderDiagnosticsMutex`]. -pub fn sync_render_diagnostics( +pub fn sync_diagnostics( mutex: Res, mut diagnostics: ResMut, ) { if let Some(v) = mutex.0.lock().take() { - dbg!(&v); *diagnostics = v; } } diff --git a/crates/bevy_render/src/diagnostic/mod.rs b/crates/bevy_render/src/diagnostic/mod.rs new file mode 100644 index 0000000000000..eadb44bbf3f82 --- /dev/null +++ b/crates/bevy_render/src/diagnostic/mod.rs @@ -0,0 +1,267 @@ +pub(crate) mod internal; + +use std::{ + borrow::Cow, + hash::{Hash, Hasher}, + marker::PhantomData, + sync::Arc, + time::Duration, +}; + +use bevy_app::{App, Plugin, PreUpdate}; +use bevy_derive::Deref; +use bevy_diagnostic::DiagnosticId; +use bevy_ecs::system::Resource; +use bevy_utils::{AHasher, Uuid}; +use smallvec::SmallVec; + +use crate::RenderApp; + +use self::internal::{ + sync_diagnostics, DiagnosticsRecorder, Pass, RenderDiagnosticsMutex, WriteTimestamp, +}; + +use super::{RenderDevice, RenderQueue}; + +/// Enables collecting rendering diagnostics into [`RenderDiagnostics`] resource. +/// +/// # Supported platforms +/// Timestamp queries and pipeline statistics are currently supported only on Vulkan and DX12. +/// On other platforms (Metal, WebGPU, WebGL2) only CPU time will be recorded. +#[allow(clippy::doc_markdown)] +#[derive(Default)] +pub struct RenderDiagnosticsPlugin; + +impl Plugin for RenderDiagnosticsPlugin { + fn build(&self, app: &mut App) { + let render_diagnostics_mutex = RenderDiagnosticsMutex::default(); + app.insert_resource(render_diagnostics_mutex.clone()) + .init_resource::() + .add_systems(PreUpdate, sync_diagnostics); + + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.insert_resource(render_diagnostics_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(DiagnosticsRecorder::new(device, queue)); + } +} + +/// Resource which stores rendering diagnostics of the most recent frame. +#[derive(Debug, Default, Clone, Resource)] +pub struct RenderDiagnostics(pub Vec); + +/// Diagnostics of a single render span. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct SpanDiagnostics { + /// Path of the span. + pub path: SpanPath, + /// Kind of the span. + pub kind: SpanKind, + /// CPU time spent during the duration of the span. + pub elapsed_cpu: Option, + /// GPU time spent executing commands recorded inside the span. + pub elapsed_gpu: Option, + /// Amount of times the vertex shader is ran. + /// Accounts for the vertex cache when doing indexed rendering. + pub vertex_shader_invocations: Option, + /// Amount of times the clipper is invoked. + /// This is also the amount of triangles output by the vertex shader. + pub clipper_invocations: Option, + /// Amount of primitives that are not culled by the clipper. + /// This is the amount of triangles that are actually on screen and will be rasterized and rendered. + pub clipper_primitives_out: Option, + /// Amount of times the fragment shader is ran. + /// Accounts for fragment shaders running in 2x2 blocks in order to get derivatives. + pub fragment_shader_invocations: Option, + /// Amount of times a compute shader is invoked. + /// This will be equivalent to the dispatch count times the workgroup size. + pub compute_shader_invocations: Option, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum DiagnosticKind { + ElapsedCpu = 0, + ElapsedGpu, + VertexShaderInvocations, + ClipperInvocations, + ClipperPrimitivesOut, + FragmentShaderInvocations, + ComputeShaderInvocations, +} + +/// Kinds of render diagnostic spans. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum SpanKind { + /// An explicit time span. + /// + /// See also: [`RecordDiagnostic::time_span`] + Time, + /// A [`wgpu::RenderPass`]. Records timestamps, as well as pipeline statistics. + /// + /// See also: [`RecordDiagnostic::pass_span`] + RenderPass, + /// A [`wgpu::ComputePass`]. Records timestamps, as well as pipeline statistics. + /// + /// See also: [`RecordDiagnostic::pass_span`] + ComputePass, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deref)] +pub struct SpanName(pub Cow<'static, str>); + +impl>> From for SpanName { + fn from(value: T) -> Self { + SpanName(value.into()) + } +} + +#[derive(Debug, Default, Clone)] +pub struct SpanPath { + components: SmallVec<[SpanName; 2]>, + hash: u64, +} + +impl SpanPath { + pub fn new(components: impl IntoIterator) -> SpanPath { + let components: SmallVec<[SpanName; 2]> = components.into_iter().collect(); + let mut hasher = AHasher::default(); + components.hash(&mut hasher); + let hash = hasher.finish(); + SpanPath { components, hash } + } + + pub fn components(&self) -> impl Iterator + '_ { + self.components.iter() + } + + pub fn diagnostic_id(&self, kind: DiagnosticKind) -> DiagnosticId { + DiagnosticId(Uuid::from_u64_pair( + 0x6140_553e_4b6a_4400 | u64::from(kind as u8), + self.hash, + )) + } +} + +impl Eq for SpanPath {} + +impl PartialEq for SpanPath { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash && self.components == other.components + } +} + +impl Hash for SpanPath { + fn hash(&self, state: &mut H) { + self.hash.hash(state); + } +} + +pub trait RecordDiagnostics { + fn time_span>( + &self, + encoder: &mut E, + name: N, + ) -> TimeSpanScope<'_, Self, E> { + self.begin_time_span(encoder, name.into()); + TimeSpanScope { + recorder: self, + marker: PhantomData, + } + } + + fn pass_span>( + &self, + pass: &mut P, + name: N, + ) -> PassSpanScope<'_, Self, P> { + self.begin_pass_span(pass, name.into()); + PassSpanScope { + recorder: self, + marker: PhantomData, + } + } + + #[doc(hidden)] + fn begin_time_span(&self, encoder: &mut E, name: SpanName); + + #[doc(hidden)] + fn end_time_span(&self, encoder: &mut E); + + #[doc(hidden)] + fn begin_pass_span(&self, pass: &mut P, name: SpanName); + + #[doc(hidden)] + fn end_pass_span(&self, pass: &mut P); +} + +pub struct TimeSpanScope<'a, R: ?Sized, E> { + recorder: &'a R, + marker: PhantomData, +} + +impl TimeSpanScope<'_, R, E> { + pub fn end(self, encoder: &mut E) { + self.recorder.end_time_span(encoder); + std::mem::forget(self) + } +} + +impl Drop for TimeSpanScope<'_, R, E> { + fn drop(&mut self) { + panic!("TimeSpanScope::end was never called") + } +} + +pub struct PassSpanScope<'a, R: ?Sized, P> { + recorder: &'a R, + marker: PhantomData

, +} + +impl PassSpanScope<'_, R, P> { + pub fn end(self, pass: &mut P) { + self.recorder.end_pass_span(pass); + std::mem::forget(self) + } +} + +impl Drop for PassSpanScope<'_, R, P> { + fn drop(&mut self) { + panic!("PassSpanScope::end was never called") + } +} + +impl RecordDiagnostics for Option> { + fn begin_time_span(&self, encoder: &mut E, name: SpanName) { + if let Some(recorder) = &self { + recorder.begin_time_span(encoder, name); + } + } + + fn end_time_span(&self, encoder: &mut E) { + if let Some(recorder) = &self { + recorder.end_time_span(encoder); + } + } + + fn begin_pass_span(&self, pass: &mut P, name: SpanName) { + if let Some(recorder) = &self { + recorder.begin_pass_span(pass, name); + } + } + + fn end_pass_span(&self, pass: &mut P) { + if let Some(recorder) = &self { + recorder.end_pass_span(pass); + } + } +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 79fd7d67d6892..811cbbb99ff0b 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -7,7 +7,7 @@ extern crate core; pub mod camera; pub mod color; -pub mod diagnostics; +pub mod diagnostic; pub mod extract_component; mod extract_param; pub mod extract_resource; diff --git a/crates/bevy_render/src/render_phase/draw_state.rs b/crates/bevy_render/src/render_phase/draw_state.rs index b1f4da0fe9831..a32866fcfa5cf 100644 --- a/crates/bevy_render/src/render_phase/draw_state.rs +++ b/crates/bevy_render/src/render_phase/draw_state.rs @@ -1,6 +1,6 @@ use crate::{ camera::Viewport, - diagnostics::{Pass, PassKind, WritePipelineStatistics, WriteTimestamp}, + diagnostic::internal::{Pass, PassKind, WritePipelineStatistics, WriteTimestamp}, prelude::Color, render_resource::{ BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId, diff --git a/crates/bevy_render/src/renderer/graph_runner.rs b/crates/bevy_render/src/renderer/graph_runner.rs index cb8efed307933..8eba7fc3eac65 100644 --- a/crates/bevy_render/src/renderer/graph_runner.rs +++ b/crates/bevy_render/src/renderer/graph_runner.rs @@ -9,7 +9,7 @@ use std::{borrow::Cow, collections::VecDeque}; use thiserror::Error; use crate::{ - diagnostics::{DiagnosticsRecorder, RenderDiagnosticsMutex}, + diagnostic::internal::{DiagnosticsRecorder, RenderDiagnosticsMutex}, render_graph::{ Edge, NodeId, NodeRunError, NodeState, RenderGraph, RenderGraphContext, SlotLabel, SlotType, SlotValue, diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 18f8849c9617b..fe7b39157cc14 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -7,7 +7,7 @@ pub use graph_runner::*; pub use render_device::*; use crate::{ - diagnostics::{DiagnosticsRecorder, OptionalDiagnosticRecorder}, + diagnostic::{internal::DiagnosticsRecorder, RecordDiagnostics}, render_graph::RenderGraph, render_phase::TrackedRenderPass, render_resource::RenderPassDescriptor, @@ -302,7 +302,8 @@ pub struct RenderContext { render_device: RenderDevice, command_encoder: Option, command_buffers: Vec, - diagnostics_recorder: OptionalDiagnosticRecorder, + // Stored in an Arc so that the recorder returned from `diagnostic_recorder` won't borrow self + diagnostics_recorder: Option>, } impl RenderContext { @@ -315,7 +316,7 @@ impl RenderContext { render_device, command_encoder: None, command_buffers: Vec::new(), - diagnostics_recorder: diagnostics_recorder.into(), + diagnostics_recorder: diagnostics_recorder.map(Arc::new), } } @@ -324,8 +325,9 @@ impl RenderContext { &self.render_device } - /// Gets the underlying [`OptionalDiagnositcRecorder`] - pub fn diagnostic_recorder(&self) -> OptionalDiagnosticRecorder { + /// Gets the diagnostics recorder, used to track elapsed time and pipeline statistics + /// of various render and compute passes. + pub fn diagnostic_recorder(&self) -> impl RecordDiagnostics { self.diagnostics_recorder.clone() } @@ -378,7 +380,11 @@ impl RenderContext { .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) }); - let mut diagnostics_recorder = self.diagnostics_recorder.unwrap(); + let mut diagnostics_recorder = self.diagnostics_recorder.map(|v| { + Arc::try_unwrap(v) + .ok() + .expect("diagnostic recorder shouldn't be held longer than necessary") + }); if let Some(recorder) = &mut diagnostics_recorder { recorder.resolve(command_encoder); diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index e283880fdfaeb..60f157fc39bbc 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -1,7 +1,7 @@ //! A simple 3D scene with light shining over a cube sitting on a plane. use bevy::prelude::*; -use bevy::render::renderer::diagnostics::{RenderDiagnostics, RenderDiagnosticsPlugin}; +use bevy::render::diagnostic::{RenderDiagnostics, RenderDiagnosticsPlugin}; fn main() { App::new()