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