@@ -6,9 +6,9 @@ use std::{
66use super :: {
77 force_fallback_adapter,
88 timestamps:: {
9- GpuTimestampQueries , RENDER_BEGIN_QUERY , RENDER_END_QUERY , SHADOW_BEGIN_QUERY ,
10- SHADOW_END_QUERY , SUBMIT_BEGIN_QUERY , SUBMIT_END_QUERY , SUBMIT_MARKER_COPY_SIZE ,
11- TIMESTAMP_BUFFER_SIZE , TIMESTAMP_QUERY_COUNT ,
9+ raw_timestamp_delta_us , GpuTimestampQueries , RENDER_BEGIN_QUERY , RENDER_END_QUERY ,
10+ SHADOW_BEGIN_QUERY , SHADOW_END_QUERY , SUBMIT_BEGIN_QUERY , SUBMIT_END_QUERY ,
11+ SUBMIT_MARKER_COPY_SIZE , TIMESTAMP_BUFFER_SIZE , TIMESTAMP_QUERY_COUNT ,
1212 } ,
1313} ;
1414
@@ -17,8 +17,18 @@ use super::{
1717pub struct GpuTimestampProbeReport {
1818 /// Whether the selected adapter exposes the timestamp-query feature set.
1919 pub supported : bool ,
20+ /// Adapter name used for the probe.
21+ pub adapter_name : String ,
22+ /// wgpu backend used for the probe.
23+ pub backend : String ,
2024 /// Raw timestamp slots resolved from the GPU query set.
2125 pub raw_queries : [ u64 ; TIMESTAMP_QUERY_COUNT as usize ] ,
26+ /// Resolved shadow-pass GPU timestamp duration.
27+ pub shadow_us : u64 ,
28+ /// Resolved main render-pass GPU timestamp duration.
29+ pub render_us : u64 ,
30+ /// Resolved submit-tail GPU timestamp duration.
31+ pub submit_us : u64 ,
2232}
2333
2434/// Runs a headless wgpu timestamp-query pass pair and resolves the raw query slots.
@@ -30,18 +40,26 @@ async fn probe_gpu_timestamp_queries_async() -> Result<GpuTimestampProbeReport,
3040 let instance = wgpu:: Instance :: default ( ) ;
3141 let adapter = instance
3242 . request_adapter ( & wgpu:: RequestAdapterOptions {
33- power_preference : wgpu:: PowerPreference :: LowPower ,
43+ power_preference : wgpu:: PowerPreference :: HighPerformance ,
3444 compatible_surface : None ,
3545 force_fallback_adapter : force_fallback_adapter ( ) ,
3646 } )
3747 . await
3848 . map_err ( |e| format ! ( "No suitable headless adapter: {e}" ) ) ?;
49+ let adapter_info = adapter. get_info ( ) ;
50+ let adapter_name = adapter_info. name ;
51+ let backend = adapter_info. backend . to_str ( ) . to_string ( ) ;
3952
4053 let features = GpuTimestampQueries :: requested_features ( adapter. features ( ) ) ;
4154 if features. is_empty ( ) {
4255 return Ok ( GpuTimestampProbeReport {
4356 supported : false ,
57+ adapter_name,
58+ backend,
4459 raw_queries : [ 0 ; TIMESTAMP_QUERY_COUNT as usize ] ,
60+ shadow_us : 0 ,
61+ render_us : 0 ,
62+ submit_us : 0 ,
4563 } ) ;
4664 }
4765
@@ -67,6 +85,12 @@ async fn probe_gpu_timestamp_queries_async() -> Result<GpuTimestampProbeReport,
6785 usage : wgpu:: BufferUsages :: QUERY_RESOLVE | wgpu:: BufferUsages :: COPY_SRC ,
6886 mapped_at_creation : false ,
6987 } ) ;
88+ let submit_marker_buffer = device. create_buffer ( & wgpu:: BufferDescriptor {
89+ label : Some ( "goud-gpu-timestamp-probe-submit-marker" ) ,
90+ size : SUBMIT_MARKER_COPY_SIZE ,
91+ usage : wgpu:: BufferUsages :: COPY_SRC ,
92+ mapped_at_creation : false ,
93+ } ) ;
7094 let readback_buffer = device. create_buffer ( & wgpu:: BufferDescriptor {
7195 label : Some ( "goud-gpu-timestamp-probe-readback" ) ,
7296 size : TIMESTAMP_BUFFER_SIZE ,
@@ -88,6 +112,26 @@ async fn probe_gpu_timestamp_queries_async() -> Result<GpuTimestampProbeReport,
88112 "goud-gpu-timestamp-probe-depth" ,
89113 wgpu:: TextureFormat :: Depth32Float ,
90114 ) ;
115+ let shader = create_probe_shader ( & device) ;
116+ let pipeline_layout = device. create_pipeline_layout ( & wgpu:: PipelineLayoutDescriptor {
117+ label : Some ( "goud-gpu-timestamp-probe-pipeline-layout" ) ,
118+ bind_group_layouts : & [ ] ,
119+ immediate_size : 0 ,
120+ } ) ;
121+ let shadow_pipeline = create_probe_pipeline (
122+ & device,
123+ & pipeline_layout,
124+ & shader,
125+ "goud-gpu-timestamp-probe-shadow-pipeline" ,
126+ None ,
127+ ) ;
128+ let render_pipeline = create_probe_pipeline (
129+ & device,
130+ & pipeline_layout,
131+ & shader,
132+ "goud-gpu-timestamp-probe-render-pipeline" ,
133+ Some ( wgpu:: TextureFormat :: Rgba8Unorm ) ,
134+ ) ;
91135
92136 let mut encoder = device. create_command_encoder ( & wgpu:: CommandEncoderDescriptor {
93137 label : Some ( "goud-gpu-timestamp-probe" ) ,
@@ -99,11 +143,26 @@ async fn probe_gpu_timestamp_queries_async() -> Result<GpuTimestampProbeReport,
99143 & shadow_texture. create_view ( & wgpu:: TextureViewDescriptor :: default ( ) ) ,
100144 & color_texture. create_view ( & wgpu:: TextureViewDescriptor :: default ( ) ) ,
101145 & depth_texture. create_view ( & wgpu:: TextureViewDescriptor :: default ( ) ) ,
146+ & shadow_pipeline,
147+ & render_pipeline,
102148 ) ;
103- record_probe_resolve ( & mut encoder, & query_set, & resolve_buffer, & readback_buffer) ;
149+ record_probe_submit_tail (
150+ & mut encoder,
151+ & query_set,
152+ & submit_marker_buffer,
153+ & readback_buffer,
154+ ) ;
155+ queue. submit ( std:: iter:: once ( encoder. finish ( ) ) ) ;
156+
157+ wait_for_submitted_work ( & device, & queue, "timestamp pass" ) ?;
158+
159+ let mut encoder = device. create_command_encoder ( & wgpu:: CommandEncoderDescriptor {
160+ label : Some ( "goud-gpu-timestamp-probe-resolve" ) ,
161+ } ) ;
162+ record_probe_readback ( & mut encoder, & query_set, & resolve_buffer, & readback_buffer) ;
104163 queue. submit ( std:: iter:: once ( encoder. finish ( ) ) ) ;
105164
106- read_probe_queries ( & device, & readback_buffer)
165+ read_probe_queries ( & device, & queue , & readback_buffer, adapter_name , backend )
107166}
108167
109168fn create_probe_texture (
@@ -127,19 +186,86 @@ fn create_probe_texture(
127186 } )
128187}
129188
189+ fn create_probe_shader ( device : & wgpu:: Device ) -> wgpu:: ShaderModule {
190+ device. create_shader_module ( wgpu:: ShaderModuleDescriptor {
191+ label : Some ( "goud-gpu-timestamp-probe-shader" ) ,
192+ source : wgpu:: ShaderSource :: Wgsl (
193+ r#"
194+ @vertex
195+ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
196+ let x = f32(i32(vertex_index) - 1);
197+ let y = select(-1.0, 1.0, vertex_index == 1u);
198+ return vec4<f32>(x, y, 0.0, 1.0);
199+ }
200+
201+ @fragment
202+ fn fs_main() -> @location(0) vec4<f32> {
203+ return vec4<f32>(1.0, 0.25, 0.0, 1.0);
204+ }
205+ "#
206+ . into ( ) ,
207+ ) ,
208+ } )
209+ }
210+
211+ fn create_probe_pipeline (
212+ device : & wgpu:: Device ,
213+ layout : & wgpu:: PipelineLayout ,
214+ shader : & wgpu:: ShaderModule ,
215+ label : & ' static str ,
216+ color_format : Option < wgpu:: TextureFormat > ,
217+ ) -> wgpu:: RenderPipeline {
218+ let color_targets = color_format. map ( |format| {
219+ [ Some ( wgpu:: ColorTargetState {
220+ format,
221+ blend : None ,
222+ write_mask : wgpu:: ColorWrites :: ALL ,
223+ } ) ]
224+ } ) ;
225+ device. create_render_pipeline ( & wgpu:: RenderPipelineDescriptor {
226+ label : Some ( label) ,
227+ layout : Some ( layout) ,
228+ vertex : wgpu:: VertexState {
229+ module : shader,
230+ entry_point : Some ( "vs_main" ) ,
231+ buffers : & [ ] ,
232+ compilation_options : wgpu:: PipelineCompilationOptions :: default ( ) ,
233+ } ,
234+ primitive : wgpu:: PrimitiveState :: default ( ) ,
235+ depth_stencil : Some ( wgpu:: DepthStencilState {
236+ format : wgpu:: TextureFormat :: Depth32Float ,
237+ depth_write_enabled : Some ( true ) ,
238+ depth_compare : Some ( wgpu:: CompareFunction :: Less ) ,
239+ stencil : wgpu:: StencilState :: default ( ) ,
240+ bias : wgpu:: DepthBiasState :: default ( ) ,
241+ } ) ,
242+ multisample : wgpu:: MultisampleState :: default ( ) ,
243+ fragment : color_targets. as_ref ( ) . map ( |targets| wgpu:: FragmentState {
244+ module : shader,
245+ entry_point : Some ( "fs_main" ) ,
246+ targets,
247+ compilation_options : wgpu:: PipelineCompilationOptions :: default ( ) ,
248+ } ) ,
249+ multiview_mask : None ,
250+ cache : None ,
251+ } )
252+ }
253+
130254fn record_probe_passes (
131255 encoder : & mut wgpu:: CommandEncoder ,
132256 query_set : & wgpu:: QuerySet ,
133257 use_render_pass_writes : bool ,
134258 shadow_view : & wgpu:: TextureView ,
135259 color_view : & wgpu:: TextureView ,
136260 depth_view : & wgpu:: TextureView ,
261+ shadow_pipeline : & wgpu:: RenderPipeline ,
262+ render_pipeline : & wgpu:: RenderPipeline ,
137263) {
138264 if !use_render_pass_writes {
139265 encoder. write_timestamp ( query_set, SHADOW_BEGIN_QUERY ) ;
140266 }
141267 {
142- let _shadow_pass = encoder. begin_render_pass ( & wgpu:: RenderPassDescriptor {
268+ let mut shadow_pass = encoder. begin_render_pass ( & wgpu:: RenderPassDescriptor {
143269 label : Some ( "goud-gpu-timestamp-probe-shadow" ) ,
144270 color_attachments : & [ ] ,
145271 depth_stencil_attachment : Some ( wgpu:: RenderPassDepthStencilAttachment {
@@ -158,13 +284,15 @@ fn record_probe_passes(
158284 occlusion_query_set : None ,
159285 multiview_mask : None ,
160286 } ) ;
287+ shadow_pass. set_pipeline ( shadow_pipeline) ;
288+ shadow_pass. draw ( 0 ..3 , 0 ..1 ) ;
161289 }
162290 if !use_render_pass_writes {
163291 encoder. write_timestamp ( query_set, SHADOW_END_QUERY ) ;
164292 encoder. write_timestamp ( query_set, RENDER_BEGIN_QUERY ) ;
165293 }
166294 {
167- let _render_pass = encoder. begin_render_pass ( & wgpu:: RenderPassDescriptor {
295+ let mut render_pass = encoder. begin_render_pass ( & wgpu:: RenderPassDescriptor {
168296 label : Some ( "goud-gpu-timestamp-probe-render" ) ,
169297 color_attachments : & [ Some ( wgpu:: RenderPassColorAttachment {
170298 view : color_view,
@@ -191,34 +319,77 @@ fn record_probe_passes(
191319 occlusion_query_set : None ,
192320 multiview_mask : None ,
193321 } ) ;
322+ render_pass. set_pipeline ( render_pipeline) ;
323+ render_pass. draw ( 0 ..3 , 0 ..1 ) ;
194324 }
195325 if !use_render_pass_writes {
196326 encoder. write_timestamp ( query_set, RENDER_END_QUERY ) ;
197327 }
198328}
199329
200- fn record_probe_resolve (
330+ fn record_probe_submit_tail (
201331 encoder : & mut wgpu:: CommandEncoder ,
202332 query_set : & wgpu:: QuerySet ,
203- resolve_buffer : & wgpu:: Buffer ,
333+ submit_marker_buffer : & wgpu:: Buffer ,
204334 readback_buffer : & wgpu:: Buffer ,
205335) {
206336 encoder. write_timestamp ( query_set, SUBMIT_BEGIN_QUERY ) ;
207337 encoder. copy_buffer_to_buffer (
208- resolve_buffer ,
338+ submit_marker_buffer ,
209339 0 ,
210340 readback_buffer,
211341 0 ,
212342 SUBMIT_MARKER_COPY_SIZE ,
213343 ) ;
214344 encoder. write_timestamp ( query_set, SUBMIT_END_QUERY ) ;
345+ }
346+
347+ fn record_probe_readback (
348+ encoder : & mut wgpu:: CommandEncoder ,
349+ query_set : & wgpu:: QuerySet ,
350+ resolve_buffer : & wgpu:: Buffer ,
351+ readback_buffer : & wgpu:: Buffer ,
352+ ) {
215353 encoder. resolve_query_set ( query_set, 0 ..TIMESTAMP_QUERY_COUNT , resolve_buffer, 0 ) ;
216354 encoder. copy_buffer_to_buffer ( resolve_buffer, 0 , readback_buffer, 0 , TIMESTAMP_BUFFER_SIZE ) ;
217355}
218356
357+ fn wait_for_submitted_work (
358+ device : & wgpu:: Device ,
359+ queue : & wgpu:: Queue ,
360+ label : & ' static str ,
361+ ) -> Result < ( ) , String > {
362+ let ( tx, rx) = mpsc:: channel ( ) ;
363+ queue. on_submitted_work_done ( move || {
364+ let _ = tx. send ( ( ) ) ;
365+ } ) ;
366+
367+ let deadline = Instant :: now ( ) + Duration :: from_secs ( 5 ) ;
368+ loop {
369+ device
370+ . poll ( wgpu:: PollType :: Poll )
371+ . map_err ( |e| format ! ( "Probe {label} poll failed: {e}" ) ) ?;
372+ match rx. try_recv ( ) {
373+ Ok ( ( ) ) => return Ok ( ( ) ) ,
374+ Err ( TryRecvError :: Disconnected ) => {
375+ return Err ( format ! ( "Probe {label} callback disconnected" ) ) ;
376+ }
377+ Err ( TryRecvError :: Empty ) if Instant :: now ( ) >= deadline => {
378+ return Err ( format ! ( "Timed out waiting for timestamp probe {label}" ) ) ;
379+ }
380+ Err ( TryRecvError :: Empty ) => {
381+ std:: thread:: sleep ( Duration :: from_millis ( 10 ) ) ;
382+ }
383+ }
384+ }
385+ }
386+
219387fn read_probe_queries (
220388 device : & wgpu:: Device ,
389+ queue : & wgpu:: Queue ,
221390 readback_buffer : & wgpu:: Buffer ,
391+ adapter_name : String ,
392+ backend : String ,
222393) -> Result < GpuTimestampProbeReport , String > {
223394 let slice = readback_buffer. slice ( ..) ;
224395 let ( tx, rx) = mpsc:: channel ( ) ;
@@ -254,8 +425,26 @@ fn read_probe_queries(
254425 drop ( mapped) ;
255426 readback_buffer. unmap ( ) ;
256427
428+ let timestamp_period_ns = queue. get_timestamp_period ( ) ;
257429 Ok ( GpuTimestampProbeReport {
258430 supported : true ,
431+ adapter_name,
432+ backend,
433+ shadow_us : raw_timestamp_delta_us (
434+ raw_queries[ SHADOW_BEGIN_QUERY as usize ] ,
435+ raw_queries[ SHADOW_END_QUERY as usize ] ,
436+ timestamp_period_ns,
437+ ) ,
438+ render_us : raw_timestamp_delta_us (
439+ raw_queries[ RENDER_BEGIN_QUERY as usize ] ,
440+ raw_queries[ RENDER_END_QUERY as usize ] ,
441+ timestamp_period_ns,
442+ ) ,
443+ submit_us : raw_timestamp_delta_us (
444+ raw_queries[ SUBMIT_BEGIN_QUERY as usize ] ,
445+ raw_queries[ SUBMIT_END_QUERY as usize ] ,
446+ timestamp_period_ns,
447+ ) ,
259448 raw_queries,
260449 } )
261450}
0 commit comments