From a746ce965f3099427128b9aa187dadbfb7184860 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Sat, 25 Jan 2025 23:00:36 -0800 Subject: [PATCH] gpu: Expose `FrameBudget` for rendering. Also hook it up to monitor refresh rate. --- all-is-cubes-desktop/src/winit.rs | 17 ++++++++++++++--- all-is-cubes-gpu/src/common.rs | 15 +++++++++++++++ all-is-cubes-gpu/src/in_wgpu/mod.rs | 9 ++++----- all-is-cubes-wasm/src/web_session.rs | 8 ++++++-- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/all-is-cubes-desktop/src/winit.rs b/all-is-cubes-desktop/src/winit.rs index e0ce7968c..d4bfddc28 100644 --- a/all-is-cubes-desktop/src/winit.rs +++ b/all-is-cubes-desktop/src/winit.rs @@ -2,7 +2,7 @@ use std::mem; use std::sync::Arc; -use std::time::Instant; +use std::time::{Duration, Instant}; use winit::event::{DeviceEvent, ElementState, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow}; @@ -11,6 +11,7 @@ use winit::window::{CursorGrabMode, Window}; use all_is_cubes::euclid::{Point2D, Size2D}; use all_is_cubes::listen; use all_is_cubes_gpu::in_wgpu::SurfaceRenderer; +use all_is_cubes_gpu::FrameBudget; use all_is_cubes_render::camera::{self, StandardCameras, Viewport}; use all_is_cubes_ui::apps::InputProcessor; @@ -534,9 +535,19 @@ impl RendererToWinit for SurfaceRenderer { self.cameras() } - fn redraw(&mut self, session: &Session, _window: &Window) { + fn redraw(&mut self, session: &Session, window: &Window) { + let frame_budget = FrameBudget::from_frame_period( + match window + .current_monitor() + .and_then(|m| m.refresh_rate_millihertz()) + { + Some(mhz) => Duration::from_secs_f32(1000.0 / mhz as f32), + None => Duration::from_millis(16), // assume ~60 Hz + }, + ); + let _info = self - .render_frame(session.cursor_result(), |render_info| { + .render_frame(session.cursor_result(), &frame_budget, |render_info| { format!("{}", session.info_text(render_info)) }) .unwrap(); diff --git a/all-is-cubes-gpu/src/common.rs b/all-is-cubes-gpu/src/common.rs index f6d901b08..d1559ffa3 100644 --- a/all-is-cubes-gpu/src/common.rs +++ b/all-is-cubes-gpu/src/common.rs @@ -47,6 +47,21 @@ impl FrameBudget { ui: VERY_LONG, }, }; + + /// Create a [`FrameBudget`] presumed to be a good choice when the period between frames + /// (reciprocal of frame rate) is this long. + pub fn from_frame_period(frame_period: Duration) -> Self { + // The fraction of the frame period not accounted for here is time left for: + // * the simulation, and + // * everything in rendering that is not explicitly budgeted. + // TODO: In principle we should also be negotiating with the simulation. + Self { + update_meshes: Layers { + world: frame_period.mul_f32(0.3), + ui: frame_period.mul_f32(0.2), + }, + } + } } /// A Duration long enough that it is not interesting in questions of rendering, but not diff --git a/all-is-cubes-gpu/src/in_wgpu/mod.rs b/all-is-cubes-gpu/src/in_wgpu/mod.rs index 8161a23b8..1bcc108fc 100644 --- a/all-is-cubes-gpu/src/in_wgpu/mod.rs +++ b/all-is-cubes-gpu/src/in_wgpu/mod.rs @@ -166,13 +166,12 @@ impl SurfaceRenderer { pub fn render_frame( &mut self, cursor_result: Option<&Cursor>, + frame_budget: &FrameBudget, info_text_fn: impl FnOnce(&RenderInfo) -> String, ) -> Result { - let update_info = self.everything.update( - &self.queue, - cursor_result, - &FrameBudget::SIXTY_FPS, // TODO: figure out what we're vsyncing to, instead - )?; + let update_info = self + .everything + .update(&self.queue, cursor_result, frame_budget)?; if self.viewport_dirty.get_and_clear() { // Test because wgpu insists on nonzero values -- we'd rather be inconsistent diff --git a/all-is-cubes-wasm/src/web_session.rs b/all-is-cubes-wasm/src/web_session.rs index 059b51c35..01c00a4ca 100644 --- a/all-is-cubes-wasm/src/web_session.rs +++ b/all-is-cubes-wasm/src/web_session.rs @@ -16,7 +16,7 @@ use web_sys::{ use all_is_cubes::euclid::{Point2D, Vector2D}; use all_is_cubes::listen; use all_is_cubes::universe::{Universe, UniverseStepInfo}; -use all_is_cubes_gpu::in_wgpu; +use all_is_cubes_gpu::{in_wgpu, FrameBudget}; use all_is_cubes_port::file::NonDiskFile; use all_is_cubes_render::camera::{GraphicsOptions, StandardCameras, Viewport}; use all_is_cubes_ui::apps::{CursorIcon, Key}; @@ -419,7 +419,11 @@ impl WebSession { WebRenderer::Wgpu(renderer) => { // note: info text is HTML on web, so no string passed here renderer - .render_frame(inner.session.cursor_result(), |_| String::new()) + .render_frame( + inner.session.cursor_result(), + &FrameBudget::SIXTY_FPS, // TODO: try to estimate real refresh rate + |_| String::new(), + ) .expect("error in render_frame") } };