Merge branch 'imbris/fix-text-offset' into 'master'

Rework scaling to never use the logical size directly from the window as it can end up inconsistent with the size used by the renderer depending on the timing

Closes #1360

See merge request veloren/veloren!2888
This commit is contained in:
Imbris 2021-10-02 13:20:09 +00:00
commit 0bfd2e2c5a
3 changed files with 66 additions and 56 deletions

View File

@ -42,11 +42,12 @@ impl IcedUi {
default_font: Font,
scale_mode: ScaleMode,
) -> Result<Self, Error> {
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<M>, 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 {

View File

@ -118,8 +118,8 @@ pub struct Ui {
interface_locals: UiBoundLocals,
// Consts to specify positions of ingame elements (e.g. Nametags)
ingame_locals: Vec<UiBoundLocals>,
// Window size for updating scaling
window_resized: Option<Vec2<f64>>,
// Whether the window was resized since the last maintain, for updating scaling
window_resized: bool,
// Scale factor changed
scale_factor_changed: Option<f64>,
// 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<Self, Error> {
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

View File

@ -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<f64>,
// Current pixel size of the window
physical_resolution: Vec2<u32>,
// 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<u32>,
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<f64>) -> 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<u32>) -> 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<f64> { self.window_dims / self.scale_factor_logical() }
/// Get logical window size
pub fn logical_resolution(&self) -> Vec2<f64> { self.window_dims }
pub fn scaled_resolution(&self) -> Vec2<f64> {
self.physical_resolution.map(f64::from) / self.scale_factor_physical()
}
// Transform point from logical to scaled coordinates.
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> { point / self.scale_factor_logical() }