bevy_render/diagnostic/
mod.rs1pub(crate) mod internal;
6
7use alloc::{borrow::Cow, sync::Arc};
8use core::marker::PhantomData;
9
10use bevy_app::{App, Plugin, PreUpdate};
11
12use crate::RenderApp;
13
14use self::internal::{
15 sync_diagnostics, DiagnosticsRecorder, Pass, RenderDiagnosticsMutex, WriteTimestamp,
16};
17
18use super::{RenderDevice, RenderQueue};
19
20#[allow(clippy::doc_markdown)]
47#[derive(Default)]
48pub struct RenderDiagnosticsPlugin;
49
50impl Plugin for RenderDiagnosticsPlugin {
51 fn build(&self, app: &mut App) {
52 let render_diagnostics_mutex = RenderDiagnosticsMutex::default();
53 app.insert_resource(render_diagnostics_mutex.clone())
54 .add_systems(PreUpdate, sync_diagnostics);
55
56 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
57 render_app.insert_resource(render_diagnostics_mutex);
58 }
59 }
60
61 fn finish(&self, app: &mut App) {
62 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
63 return;
64 };
65
66 let device = render_app.world().resource::<RenderDevice>();
67 let queue = render_app.world().resource::<RenderQueue>();
68 render_app.insert_resource(DiagnosticsRecorder::new(device, queue));
69 }
70}
71
72pub trait RecordDiagnostics: Send + Sync {
74 fn time_span<E, N>(&self, encoder: &mut E, name: N) -> TimeSpanGuard<'_, Self, E>
78 where
79 E: WriteTimestamp,
80 N: Into<Cow<'static, str>>,
81 {
82 self.begin_time_span(encoder, name.into());
83 TimeSpanGuard {
84 recorder: self,
85 marker: PhantomData,
86 }
87 }
88
89 fn pass_span<P, N>(&self, pass: &mut P, name: N) -> PassSpanGuard<'_, Self, P>
94 where
95 P: Pass,
96 N: Into<Cow<'static, str>>,
97 {
98 self.begin_pass_span(pass, name.into());
99 PassSpanGuard {
100 recorder: self,
101 marker: PhantomData,
102 }
103 }
104
105 #[doc(hidden)]
106 fn begin_time_span<E: WriteTimestamp>(&self, encoder: &mut E, name: Cow<'static, str>);
107
108 #[doc(hidden)]
109 fn end_time_span<E: WriteTimestamp>(&self, encoder: &mut E);
110
111 #[doc(hidden)]
112 fn begin_pass_span<P: Pass>(&self, pass: &mut P, name: Cow<'static, str>);
113
114 #[doc(hidden)]
115 fn end_pass_span<P: Pass>(&self, pass: &mut P);
116}
117
118pub struct TimeSpanGuard<'a, R: ?Sized, E> {
122 recorder: &'a R,
123 marker: PhantomData<E>,
124}
125
126impl<R: RecordDiagnostics + ?Sized, E: WriteTimestamp> TimeSpanGuard<'_, R, E> {
127 pub fn end(self, encoder: &mut E) {
129 self.recorder.end_time_span(encoder);
130 core::mem::forget(self);
131 }
132}
133
134impl<R: ?Sized, E> Drop for TimeSpanGuard<'_, R, E> {
135 fn drop(&mut self) {
136 panic!("TimeSpanScope::end was never called")
137 }
138}
139
140pub struct PassSpanGuard<'a, R: ?Sized, P> {
144 recorder: &'a R,
145 marker: PhantomData<P>,
146}
147
148impl<R: RecordDiagnostics + ?Sized, P: Pass> PassSpanGuard<'_, R, P> {
149 pub fn end(self, pass: &mut P) {
151 self.recorder.end_pass_span(pass);
152 core::mem::forget(self);
153 }
154}
155
156impl<R: ?Sized, P> Drop for PassSpanGuard<'_, R, P> {
157 fn drop(&mut self) {
158 panic!("PassSpanScope::end was never called")
159 }
160}
161
162impl<T: RecordDiagnostics> RecordDiagnostics for Option<Arc<T>> {
163 fn begin_time_span<E: WriteTimestamp>(&self, encoder: &mut E, name: Cow<'static, str>) {
164 if let Some(recorder) = &self {
165 recorder.begin_time_span(encoder, name);
166 }
167 }
168
169 fn end_time_span<E: WriteTimestamp>(&self, encoder: &mut E) {
170 if let Some(recorder) = &self {
171 recorder.end_time_span(encoder);
172 }
173 }
174
175 fn begin_pass_span<P: Pass>(&self, pass: &mut P, name: Cow<'static, str>) {
176 if let Some(recorder) = &self {
177 recorder.begin_pass_span(pass, name);
178 }
179 }
180
181 fn end_pass_span<P: Pass>(&self, pass: &mut P) {
182 if let Some(recorder) = &self {
183 recorder.end_pass_span(pass);
184 }
185 }
186}