diff --git a/Cargo.lock b/Cargo.lock index f88634fd456..d39724e6e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,6 +1239,7 @@ dependencies = [ "puffin", "thiserror", "type-map", + "web-time", "wgpu", "winit", ] diff --git a/Cargo.toml b/Cargo.toml index 993751bffdd..977c1d9a39d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ glow = "0.13" puffin = "0.18" raw-window-handle = "0.6.0" thiserror = "1.0.37" +web-time = "0.2" # Timekeeping for native and web wgpu = { version = "0.19.1", default-features = false, features = [ # Make the renderer `Sync` even on wasm32, because it makes the code simpler: "fragile-send-sync-non-atomic-wasm", diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 56847b92124..6c317447951 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -644,7 +644,7 @@ impl WgpuWinitRunning { let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point); let screenshot_requested = std::mem::take(&mut viewport.screenshot_requested); - let screenshot = painter.paint_and_update_textures( + let (_vsync_secs, screenshot) = painter.paint_and_update_textures( viewport_id, pixels_per_point, app.clear_color(&egui_ctx.style().visuals), diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index c598669eb2a..b653238e82f 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -45,6 +45,7 @@ bytemuck = "1.7" log = { version = "0.4", features = ["std"] } thiserror.workspace = true type-map = "0.5.0" +web-time.workspace = true wgpu = { workspace = true, features = ["wgsl"] } #! ### Optional dependencies diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index ca292212a15..12e8e25ad06 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -504,7 +504,10 @@ impl Painter { }) } - // Returns a vector with the frame's pixel data if it was requested. + /// Returns two things: + /// + /// The approximate number of seconds spent on vsync-waiting (if any), + /// and the captures captured screenshot if it was requested. pub fn paint_and_update_textures( &mut self, viewport_id: ViewportId, @@ -513,29 +516,16 @@ impl Painter { clipped_primitives: &[epaint::ClippedPrimitive], textures_delta: &epaint::textures::TexturesDelta, capture: bool, - ) -> Option { + ) -> (f32, Option) { crate::profile_function!(); - let render_state = self.render_state.as_mut()?; - let surface_state = self.surfaces.get(&viewport_id)?; + let mut vsync_sec = 0.0; - let output_frame = { - crate::profile_scope!("get_current_texture"); - // This is what vsync-waiting happens, at least on Mac. - surface_state.surface.get_current_texture() + let Some(render_state) = self.render_state.as_mut() else { + return (vsync_sec, None); }; - - let output_frame = match output_frame { - Ok(frame) => frame, - Err(err) => match (*self.configuration.on_surface_error)(err) { - SurfaceErrorAction::RecreateSurface => { - Self::configure_surface(surface_state, render_state, &self.configuration); - return None; - } - SurfaceErrorAction::SkipFrame => { - return None; - } - }, + let Some(surface_state) = self.surfaces.get(&viewport_id) else { + return (vsync_sec, None); }; let mut encoder = @@ -580,6 +570,28 @@ impl Painter { } }; + let output_frame = { + crate::profile_scope!("get_current_texture"); + // This is what vsync-waiting happens on my Mac. + let start = web_time::Instant::now(); + let output_frame = surface_state.surface.get_current_texture(); + vsync_sec += start.elapsed().as_secs_f32(); + output_frame + }; + + let output_frame = match output_frame { + Ok(frame) => frame, + Err(err) => match (*self.configuration.on_surface_error)(err) { + SurfaceErrorAction::RecreateSurface => { + Self::configure_surface(surface_state, render_state, &self.configuration); + return (vsync_sec, None); + } + SurfaceErrorAction::SkipFrame => { + return (vsync_sec, None); + } + }, + }; + { let renderer = render_state.renderer.read(); let frame_view = if capture { @@ -589,8 +601,11 @@ impl Painter { render_state, ); self.screen_capture_state - .as_ref()? - .texture + .as_ref() + .map_or_else( + || &output_frame.texture, + |capture_state| &capture_state.texture, + ) .create_view(&wgpu::TextureViewDescriptor::default()) } else { output_frame @@ -654,23 +669,33 @@ impl Painter { // Submit the commands: both the main buffer and user-defined ones. { crate::profile_scope!("Queue::submit"); + // wgpu doesn't document where vsync can happen. Maybe here? + let start = web_time::Instant::now(); render_state .queue .submit(user_cmd_bufs.into_iter().chain([encoded])); + vsync_sec += start.elapsed().as_secs_f32(); }; let screenshot = if capture { - let screen_capture_state = self.screen_capture_state.as_ref()?; - Self::read_screen_rgba(screen_capture_state, render_state, &output_frame) + self.screen_capture_state + .as_ref() + .and_then(|screen_capture_state| { + Self::read_screen_rgba(screen_capture_state, render_state, &output_frame) + }) } else { None }; { crate::profile_scope!("present"); + // wgpu doesn't document where vsync can happen. Maybe here? + let start = web_time::Instant::now(); output_frame.present(); + vsync_sec += start.elapsed().as_secs_f32(); } - screenshot + + (vsync_sec, screenshot) } pub fn gc_viewports(&mut self, active_viewports: &ViewportIdSet) { diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 1324e8091a3..bd629e663e8 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -60,7 +60,7 @@ egui = { version = "0.25.0", path = "../egui", default-features = false, feature ] } log = { version = "0.4", features = ["std"] } raw-window-handle.workspace = true -web-time = { version = "0.2" } # We use web-time so we can (maybe) compile for web +web-time.workspace = true winit = { workspace = true, default-features = false, features = ["rwh_06"] } #! ### Optional dependencies