From 4cfa6b71fc9f09a4a0213df9aaab722a425ab012 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 30 Sep 2021 21:15:51 -0400 Subject: [PATCH] Rework scaling to never use the logical size directly from the window as it can end up being inconsistent with the rendering target size (depending on timing) --- voxygen/src/ui/ice/mod.rs | 26 +++++++-------- voxygen/src/ui/mod.rs | 29 ++++++++++------- voxygen/src/ui/scale.rs | 67 ++++++++++++++++++++------------------- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index d28ef981d9..7c18378a6e 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -42,11 +42,12 @@ impl IcedUi { default_font: Font, scale_mode: ScaleMode, ) -> Result { - let scale = Scale::new(window, scale_mode, 1.2); + let scale_factor = window.scale_factor(); let renderer = window.renderer_mut(); + let physical_resolution = renderer.resolution(); + let scale = Scale::new(physical_resolution, scale_factor, scale_mode, 1.2); let scaled_resolution = scale.scaled_resolution().map(|e| e as f32); - let physical_resolution = renderer.resolution(); // TODO: examine how much mem fonts take up and reduce clones if significant Ok(Self { @@ -97,16 +98,9 @@ impl IcedUi { use iced::window; match event { // Intercept resizing events - // TODO: examine if we are handling dpi properly here - // ideally these values should be the logical ones - Event::Window(window::Event::Resized { width, height }) => { - if width != 0 && height != 0 { - let new_dims = Vec2::new(width, height); - // TODO maybe use u32 in Scale to be consistent with iced - // Avoid resetting cache if window size didn't change - self.scale_changed |= self.scale.window_resized(new_dims.map(|e| e as f64)); - } - }, + // We check if the resolution of the renderer has changed to determine if a resize has + // occured + Event::Window(window::Event::Resized { .. }) => {}, // Scale cursor movement events // Note: in some cases the scaling could be off if a resized event occured in the same // frame, in practice this shouldn't be an issue @@ -151,9 +145,15 @@ impl IcedUi { clipboard: &mut Clipboard, ) -> (Vec, mouse::Interaction) { span!(_guard, "maintain", "IcedUi::maintain"); + // There could have been a series of resizes that put us back at the original + // resolution. + // Avoid resetting cache if window size didn't actually change. + let resolution_changed = self.scale.surface_resized(renderer.resolution()); + // Handle window resizing, dpi factor change, and scale mode changing - if self.scale_changed { + if self.scale_changed || resolution_changed { self.scale_changed = false; + let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32); self.events .push(Event::Window(iced::window::Event::Resized { diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 72501fb7f0..a7ad28ab68 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -118,8 +118,8 @@ pub struct Ui { interface_locals: UiBoundLocals, // Consts to specify positions of ingame elements (e.g. Nametags) ingame_locals: Vec, - // Window size for updating scaling - window_resized: Option>, + // Whether the window was resized since the last maintain, for updating scaling + window_resized: bool, // Scale factor changed scale_factor_changed: Option, // Used to delay cache resizing until after current frame is drawn @@ -138,12 +138,17 @@ pub struct Ui { impl Ui { pub fn new(window: &mut Window) -> Result { - let scale = Scale::new(window, ScaleMode::Absolute(1.0), 1.0); - let win_dims = scale.scaled_resolution().into_array(); - + let scale_factor = window.scale_factor(); let renderer = window.renderer_mut(); - let physical_resolution = renderer.resolution(); + let scale = Scale::new( + physical_resolution, + scale_factor, + ScaleMode::Absolute(1.0), + 1.0, + ); + + let win_dims = scale.scaled_resolution().into_array(); let mut ui = UiBuilder::new(win_dims).build(); // NOTE: Since we redraw the actual frame each time whether or not the UI needs @@ -176,7 +181,7 @@ impl Ui { model: renderer.create_dynamic_model(100), interface_locals, ingame_locals: Vec::new(), - window_resized: None, + window_resized: false, scale_factor_changed: None, need_cache_resize: false, graphic_replaced: false, @@ -290,7 +295,7 @@ impl Ui { match event.0 { Input::Resize(w, h) => { if w > 0.0 && h > 0.0 { - self.window_resized = Some(Vec2::new(w, h)) + self.window_resized = true; } }, Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch { @@ -342,12 +347,14 @@ impl Ui { }; // Handle window resizing. - let need_resize = if let Some(new_dims) = self.window_resized.take() { + let need_resize = if self.window_resized { + self.window_resized = false; + let surface_resolution = renderer.resolution(); let (old_w, old_h) = self.scale.scaled_resolution().into_tuple(); - self.scale.window_resized(new_dims); + self.scale.surface_resized(surface_resolution); let (w, h) = self.scale.scaled_resolution().into_tuple(); self.ui.handle_event(Input::Resize(w, h)); - self.window_scissor = default_scissor(renderer.resolution()); + self.window_scissor = default_scissor(surface_resolution); // Avoid panic in graphic cache when minimizing. // Avoid resetting cache if window size didn't change diff --git a/voxygen/src/ui/scale.rs b/voxygen/src/ui/scale.rs index a609a6c80c..c3759c3766 100644 --- a/voxygen/src/ui/scale.rs +++ b/voxygen/src/ui/scale.rs @@ -1,4 +1,3 @@ -use crate::window::Window; use serde::{Deserialize, Serialize}; use vek::*; @@ -22,20 +21,23 @@ pub struct Scale { mode: ScaleMode, // Current dpi factor scale_factor: f64, - // Current logical window size - window_dims: Vec2, + // Current pixel size of the window + physical_resolution: Vec2, // TEMP extra_factor: f64, } impl Scale { - pub fn new(window: &Window, mode: ScaleMode, extra_factor: f64) -> Self { - let window_dims = window.logical_size(); - let scale_factor = window.scale_factor(); + pub fn new( + physical_resolution: Vec2, + scale_factor: f64, + mode: ScaleMode, + extra_factor: f64, + ) -> Self { Scale { mode, scale_factor, - window_dims, + physical_resolution, extra_factor, } } @@ -56,36 +58,38 @@ impl Scale { // Get scaling mode transformed to be relative to the window with the same // aspect ratio as the current window pub fn scaling_mode_as_relative(&self) -> ScaleMode { - let scale = self.scale_factor_logical(); - ScaleMode::RelativeToWindow(self.window_dims.map(|e| e / scale)) - } - - /// Calculate factor to transform between logical coordinates and our scaled - /// coordinates. - /// Multiply by scaled coordinates to get the logical coordinates - pub fn scale_factor_logical(&self) -> f64 { - self.extra_factor - * match self.mode { - ScaleMode::Absolute(scale) => scale / self.scale_factor, - ScaleMode::DpiFactor => 1.0, - ScaleMode::RelativeToWindow(dims) => { - (self.window_dims.x / dims.x).min(self.window_dims.y / dims.y) - }, - } + ScaleMode::RelativeToWindow(self.scaled_resolution()) } /// Calculate factor to transform between physical coordinates and our /// scaled coordinates. /// Multiply by scaled coordinates to get the physical coordinates - pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor } + pub fn scale_factor_physical(&self) -> f64 { + self.extra_factor + * match self.mode { + ScaleMode::Absolute(scale) => scale, + ScaleMode::DpiFactor => 1.0 * self.scale_factor, + ScaleMode::RelativeToWindow(dims) => (f64::from(self.physical_resolution.x) + / dims.x) + .min(f64::from(self.physical_resolution.y) / dims.y), + } + } + + /// Calculate factor to transform between logical coordinates and our scaled + /// coordinates. + /// Multiply by scaled coordinates to get the logical coordinates + /// + /// Used to scale coordinates from window events (e.g. the mouse cursor + /// position) + pub fn scale_factor_logical(&self) -> f64 { self.scale_factor_physical() / self.scale_factor } /// Updates window size /// Returns true if the value was changed #[allow(clippy::float_cmp)] - pub fn window_resized(&mut self, new_dims: Vec2) -> bool { - let old_window_dims = self.window_dims; - self.window_dims = new_dims; - old_window_dims != self.window_dims + pub fn surface_resized(&mut self, new_res: Vec2) -> bool { + let old_res = self.physical_resolution; + self.physical_resolution = new_res; + old_res != self.physical_resolution } /// Updates scale factor @@ -98,10 +102,9 @@ impl Scale { } /// Get scaled window size. - pub fn scaled_resolution(&self) -> Vec2 { self.window_dims / self.scale_factor_logical() } - - /// Get logical window size - pub fn logical_resolution(&self) -> Vec2 { self.window_dims } + pub fn scaled_resolution(&self) -> Vec2 { + self.physical_resolution.map(f64::from) / self.scale_factor_physical() + } // Transform point from logical to scaled coordinates. pub fn scale_point(&self, point: Vec2) -> Vec2 { point / self.scale_factor_logical() }