Postpone call to get_current_texture (#3914)

This should help slightly with CPU/GPU parallelism when vsync is on.

I also return the time spent on vsync, which can help users figure out
how much CPU wall-time was used on non-vsync stuff
This commit is contained in:
Emil Ernerfeldt 2024-01-29 18:06:21 +01:00 committed by GitHub
parent 1d1b07c6b2
commit 21253d844e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 55 additions and 27 deletions

1
Cargo.lock generated
View File

@ -1239,6 +1239,7 @@ dependencies = [
"puffin",
"thiserror",
"type-map",
"web-time",
"wgpu",
"winit",
]

View File

@ -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",

View File

@ -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),

View File

@ -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

View File

@ -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<epaint::ColorImage> {
) -> (f32, Option<epaint::ColorImage>) {
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) {

View File

@ -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