wgpu_profiler/scope.rs
1//! Scope types that wrap a `wgpu` encoder/pass and start a scope on creation. In most cases, they
2//! then allow automatically ending the scope on drop.
3
4use crate::{GpuProfiler, GpuProfilerQuery, ProfilerCommandRecorder};
5
6/// Scope that takes a (mutable) reference to the encoder/pass.
7///
8/// Calls [`GpuProfiler::end_query()`] on drop.
9pub struct Scope<'a, Recorder: ProfilerCommandRecorder> {
10 pub profiler: &'a GpuProfiler,
11 pub recorder: &'a mut Recorder,
12 pub scope: Option<GpuProfilerQuery>,
13}
14
15impl<R: ProfilerCommandRecorder> Drop for Scope<'_, R> {
16 #[inline]
17 fn drop(&mut self) {
18 if let Some(scope) = self.scope.take() {
19 self.profiler.end_query(self.recorder, scope);
20 }
21 }
22}
23
24/// Scope that takes ownership of the encoder/pass.
25///
26/// Calls [`GpuProfiler::end_query()`] on drop.
27pub struct OwningScope<'a, Recorder: ProfilerCommandRecorder> {
28 pub profiler: &'a GpuProfiler,
29 pub recorder: Recorder,
30 pub scope: Option<GpuProfilerQuery>,
31}
32
33impl<R: ProfilerCommandRecorder> Drop for OwningScope<'_, R> {
34 #[inline]
35 fn drop(&mut self) {
36 if let Some(scope) = self.scope.take() {
37 self.profiler.end_query(&mut self.recorder, scope);
38 }
39 }
40}
41
42/// Scope that takes ownership of the encoder/pass.
43///
44/// Does NOT call [`GpuProfiler::end_query()`] on drop.
45/// This construct is just for completeness in cases where working with scopes is preferred but one can't rely on the Drop call in the right place.
46/// This is useful when the owned value needs to be recovered after the end of the scope.
47/// In particular, to submit a [`wgpu::CommandEncoder`] to a queue, ownership of the encoder is necessary.
48pub struct ManualOwningScope<'a, Recorder: ProfilerCommandRecorder> {
49 pub profiler: &'a GpuProfiler,
50 pub recorder: Recorder,
51 pub scope: Option<GpuProfilerQuery>,
52}
53
54impl<R: ProfilerCommandRecorder> ManualOwningScope<'_, R> {
55 /// Ends the scope allowing the extraction of the owned [`ProfilerCommandRecorder`].
56 #[track_caller]
57 #[inline]
58 pub fn end_query(mut self) -> R {
59 // Can't fail since creation implies begin_query.
60 self.profiler
61 .end_query(&mut self.recorder, self.scope.take().unwrap());
62 self.recorder
63 }
64}
65
66/// Most implementation code of the different scope types is exactly the same.
67///
68/// This macro allows to avoid code duplication.
69/// Another way of achieving this are extension traits, but this would mean that a user has to
70/// import the extension trait to use all methods of the scope types which I found a bit annoying.
71macro_rules! impl_scope_ext {
72 ($scope:ident, $recorder_type:ty) => {
73 impl<'a, R: ProfilerCommandRecorder> $scope<'a, R> {
74 /// Starts a new profiler scope nested within this one.
75 #[must_use]
76 #[track_caller]
77 #[inline]
78 pub fn scope(
79 &mut self,
80 label: impl Into<String>,
81 device: &wgpu::Device,
82 ) -> Scope<'_, R> {
83 let recorder: &mut R = &mut self.recorder;
84 let scope = self
85 .profiler
86 .begin_query(label, recorder, device)
87 .with_parent(self.scope.as_ref());
88 Scope {
89 profiler: self.profiler,
90 recorder,
91 scope: Some(scope),
92 }
93 }
94 }
95
96 impl<'a> $scope<'a, wgpu::CommandEncoder> {
97 /// Start a render pass wrapped in a [`OwningScope`].
98 ///
99 /// Ignores passed `wgpu::RenderPassDescriptor::timestamp_writes` and replaces it with
100 /// `timestamp_writes` managed by `GpuProfiler` if profiling is enabled.
101 ///
102 /// This also sets the `wgpu::RenderPassDescriptor::label` if it's `None` (default).
103 ///
104 /// Note that in order to take measurements, this requires the [`wgpu::Features::TIMESTAMP_QUERY`] feature.
105 /// [`wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] & [`wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES`] are not required.
106 #[track_caller]
107 pub fn scoped_render_pass<'b>(
108 &'b mut self,
109 label: impl Into<String>,
110 device: &wgpu::Device,
111 pass_descriptor: wgpu::RenderPassDescriptor<'_>,
112 ) -> OwningScope<'b, wgpu::RenderPass<'b>> {
113 let child_scope = self
114 .profiler
115 .begin_pass_query(label, &mut self.recorder, device)
116 .with_parent(self.scope.as_ref());
117 let render_pass = self
118 .recorder
119 .begin_render_pass(&wgpu::RenderPassDescriptor {
120 timestamp_writes: child_scope.render_pass_timestamp_writes(),
121 label: pass_descriptor.label.or(Some(&child_scope.label)),
122 ..pass_descriptor
123 });
124
125 OwningScope {
126 profiler: self.profiler,
127 recorder: render_pass,
128 scope: Some(child_scope),
129 }
130 }
131
132 /// Start a compute pass wrapped in a [`OwningScope`].
133 ///
134 /// Uses passed label both for profiler scope and compute pass label.
135 /// `timestamp_writes` managed by `GpuProfiler` if profiling is enabled.
136 ///
137 /// Note that in order to take measurements, this requires the [`wgpu::Features::TIMESTAMP_QUERY`] feature.
138 /// [`wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] & [`wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES`] are not required.
139 #[track_caller]
140 pub fn scoped_compute_pass<'b>(
141 &'b mut self,
142 label: impl Into<String>,
143 device: &wgpu::Device,
144 ) -> OwningScope<'b, wgpu::ComputePass<'b>> {
145 let child_scope = self
146 .profiler
147 .begin_pass_query(label, &mut self.recorder, device)
148 .with_parent(self.scope.as_ref());
149
150 let render_pass = self
151 .recorder
152 .begin_compute_pass(&wgpu::ComputePassDescriptor {
153 label: Some(&child_scope.label),
154 timestamp_writes: child_scope.compute_pass_timestamp_writes(),
155 });
156
157 OwningScope {
158 profiler: self.profiler,
159 recorder: render_pass,
160 scope: Some(child_scope),
161 }
162 }
163 }
164
165 impl<'a, R: ProfilerCommandRecorder> std::ops::Deref for $scope<'a, R> {
166 type Target = R;
167
168 #[inline]
169 fn deref(&self) -> &Self::Target {
170 &self.recorder
171 }
172 }
173
174 impl<'a, R: ProfilerCommandRecorder> std::ops::DerefMut for $scope<'a, R> {
175 #[inline]
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 &mut self.recorder
178 }
179 }
180 };
181}
182
183impl_scope_ext!(Scope, &'a mut R);
184impl_scope_ext!(OwningScope, R);
185impl_scope_ext!(ManualOwningScope, R);