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)

This commit is contained in:
Imbris 2021-09-30 21:15:51 -04:00
parent ca4e9a3436
commit e6e5219104
3 changed files with 66 additions and 56 deletions

View File

@ -42,11 +42,12 @@ impl IcedUi {
default_font: Font, default_font: Font,
scale_mode: ScaleMode, scale_mode: ScaleMode,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let scale = Scale::new(window, scale_mode, 1.2); let scale_factor = window.scale_factor();
let renderer = window.renderer_mut(); 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 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 // TODO: examine how much mem fonts take up and reduce clones if significant
Ok(Self { Ok(Self {
@ -97,16 +98,9 @@ impl IcedUi {
use iced::window; use iced::window;
match event { match event {
// Intercept resizing events // Intercept resizing events
// TODO: examine if we are handling dpi properly here // We check if the resolution of the renderer has changed to determine if a resize has
// ideally these values should be the logical ones // occured
Event::Window(window::Event::Resized { width, height }) => { Event::Window(window::Event::Resized { .. }) => {},
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));
}
},
// Scale cursor movement events // Scale cursor movement events
// Note: in some cases the scaling could be off if a resized event occured in the same // 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 // frame, in practice this shouldn't be an issue
@ -151,9 +145,15 @@ impl IcedUi {
clipboard: &mut Clipboard, clipboard: &mut Clipboard,
) -> (Vec<M>, mouse::Interaction) { ) -> (Vec<M>, mouse::Interaction) {
span!(_guard, "maintain", "IcedUi::maintain"); 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 // Handle window resizing, dpi factor change, and scale mode changing
if self.scale_changed { if self.scale_changed || resolution_changed {
self.scale_changed = false; self.scale_changed = false;
let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32); let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32);
self.events self.events
.push(Event::Window(iced::window::Event::Resized { .push(Event::Window(iced::window::Event::Resized {

View File

@ -118,8 +118,8 @@ pub struct Ui {
interface_locals: UiBoundLocals, interface_locals: UiBoundLocals,
// Consts to specify positions of ingame elements (e.g. Nametags) // Consts to specify positions of ingame elements (e.g. Nametags)
ingame_locals: Vec<UiBoundLocals>, ingame_locals: Vec<UiBoundLocals>,
// Window size for updating scaling // Whether the window was resized since the last maintain, for updating scaling
window_resized: Option<Vec2<f64>>, window_resized: bool,
// Scale factor changed // Scale factor changed
scale_factor_changed: Option<f64>, scale_factor_changed: Option<f64>,
// Used to delay cache resizing until after current frame is drawn // Used to delay cache resizing until after current frame is drawn
@ -138,12 +138,17 @@ pub struct Ui {
impl Ui { impl Ui {
pub fn new(window: &mut Window) -> Result<Self, Error> { pub fn new(window: &mut Window) -> Result<Self, Error> {
let scale = Scale::new(window, ScaleMode::Absolute(1.0), 1.0); let scale_factor = window.scale_factor();
let win_dims = scale.scaled_resolution().into_array();
let renderer = window.renderer_mut(); let renderer = window.renderer_mut();
let physical_resolution = renderer.resolution(); 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(); let mut ui = UiBuilder::new(win_dims).build();
// NOTE: Since we redraw the actual frame each time whether or not the UI needs // 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), model: renderer.create_dynamic_model(100),
interface_locals, interface_locals,
ingame_locals: Vec::new(), ingame_locals: Vec::new(),
window_resized: None, window_resized: false,
scale_factor_changed: None, scale_factor_changed: None,
need_cache_resize: false, need_cache_resize: false,
graphic_replaced: false, graphic_replaced: false,
@ -290,7 +295,7 @@ impl Ui {
match event.0 { match event.0 {
Input::Resize(w, h) => { Input::Resize(w, h) => {
if w > 0.0 && h > 0.0 { 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 { Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch {
@ -342,12 +347,14 @@ impl Ui {
}; };
// Handle window resizing. // 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(); 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(); let (w, h) = self.scale.scaled_resolution().into_tuple();
self.ui.handle_event(Input::Resize(w, h)); 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 panic in graphic cache when minimizing.
// Avoid resetting cache if window size didn't change // Avoid resetting cache if window size didn't change

View File

@ -1,4 +1,3 @@
use crate::window::Window;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use vek::*; use vek::*;
@ -22,20 +21,23 @@ pub struct Scale {
mode: ScaleMode, mode: ScaleMode,
// Current dpi factor // Current dpi factor
scale_factor: f64, scale_factor: f64,
// Current logical window size // Current pixel size of the window
window_dims: Vec2<f64>, physical_resolution: Vec2<u32>,
// TEMP // TEMP
extra_factor: f64, extra_factor: f64,
} }
impl Scale { impl Scale {
pub fn new(window: &Window, mode: ScaleMode, extra_factor: f64) -> Self { pub fn new(
let window_dims = window.logical_size(); physical_resolution: Vec2<u32>,
let scale_factor = window.scale_factor(); scale_factor: f64,
mode: ScaleMode,
extra_factor: f64,
) -> Self {
Scale { Scale {
mode, mode,
scale_factor, scale_factor,
window_dims, physical_resolution,
extra_factor, extra_factor,
} }
} }
@ -56,36 +58,38 @@ impl Scale {
// Get scaling mode transformed to be relative to the window with the same // Get scaling mode transformed to be relative to the window with the same
// aspect ratio as the current window // aspect ratio as the current window
pub fn scaling_mode_as_relative(&self) -> ScaleMode { pub fn scaling_mode_as_relative(&self) -> ScaleMode {
let scale = self.scale_factor_logical(); ScaleMode::RelativeToWindow(self.scaled_resolution())
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)
},
}
} }
/// Calculate factor to transform between physical coordinates and our /// Calculate factor to transform between physical coordinates and our
/// scaled coordinates. /// scaled coordinates.
/// Multiply by scaled coordinates to get the physical 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 /// Updates window size
/// Returns true if the value was changed /// Returns true if the value was changed
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
pub fn window_resized(&mut self, new_dims: Vec2<f64>) -> bool { pub fn surface_resized(&mut self, new_res: Vec2<u32>) -> bool {
let old_window_dims = self.window_dims; let old_res = self.physical_resolution;
self.window_dims = new_dims; self.physical_resolution = new_res;
old_window_dims != self.window_dims old_res != self.physical_resolution
} }
/// Updates scale factor /// Updates scale factor
@ -98,10 +102,9 @@ impl Scale {
} }
/// Get scaled window size. /// Get scaled window size.
pub fn scaled_resolution(&self) -> Vec2<f64> { self.window_dims / self.scale_factor_logical() } pub fn scaled_resolution(&self) -> Vec2<f64> {
self.physical_resolution.map(f64::from) / self.scale_factor_physical()
/// Get logical window size }
pub fn logical_resolution(&self) -> Vec2<f64> { self.window_dims }
// Transform point from logical to scaled coordinates. // Transform point from logical to scaled coordinates.
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> { point / self.scale_factor_logical() } pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> { point / self.scale_factor_logical() }