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