fix: ceil integer scale for better text rendering on fractional scaling

This commit is contained in:
drendog 2026-01-19 02:43:52 +01:00
parent c430938bf1
commit 3eea09b2f6
Signed by: dwenya
GPG key ID: 8DD77074645332D0
4 changed files with 121 additions and 22 deletions

View file

@ -10,6 +10,46 @@ pub enum RenderState {
Dirty, Dirty,
} }
#[derive(Debug, Clone, Copy)]
pub struct FractionalScaleConfig {
pub render_scale: f32,
pub render_physical_size: PhysicalSize,
pub logical_width: f32,
pub logical_height: f32,
}
impl FractionalScaleConfig {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub fn new(logical_width: f32, logical_height: f32, scale_factor: f32) -> Self {
let render_scale = Self::render_scale(scale_factor);
Self {
render_scale,
render_physical_size: PhysicalSize::new(
(logical_width * render_scale) as u32,
(logical_height * render_scale) as u32,
),
logical_width,
logical_height,
}
}
#[must_use]
pub fn render_scale(scale_factor: f32) -> f32 {
scale_factor.ceil()
}
pub fn apply_to<W: RenderableWindow + ?Sized>(&self, window: &W) {
window.set_scale_factor(self.render_scale);
window.set_size_with_exact_logical(
self.render_physical_size,
self.logical_width,
self.logical_height,
);
}
}
pub trait RenderableWindow: WindowAdapter { pub trait RenderableWindow: WindowAdapter {
fn render_frame_if_dirty(&self) -> Result<()>; fn render_frame_if_dirty(&self) -> Result<()>;
fn set_scale_factor(&self, scale_factor: f32); fn set_scale_factor(&self, scale_factor: f32);
@ -31,4 +71,16 @@ pub trait RenderableWindow: WindowAdapter {
size: size.to_logical(self.scale_factor()), size: size.to_logical(self.scale_factor()),
}); });
} }
fn set_size_with_exact_logical(
&self,
physical: slint::PhysicalSize,
logical_width: f32,
logical_height: f32,
) {
self.size_cell().set(physical);
self.window().dispatch_event(WindowEvent::Resized {
size: slint::LogicalSize::new(logical_width, logical_height),
});
}
} }

View file

@ -1,7 +1,7 @@
use super::callbacks::{LockCallbackContext, LockCallbackExt, LockPropertyOperationExt}; use super::callbacks::{LockCallbackContext, LockCallbackExt, LockPropertyOperationExt};
use crate::errors::Result; use crate::errors::Result;
use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::rendering::femtovg::main_window::FemtoVGWindow;
use crate::rendering::femtovg::renderable_window::RenderableWindow; use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::rendering::slint_integration::platform::CustomSlintPlatform; use crate::rendering::slint_integration::platform::CustomSlintPlatform;
use crate::wayland::session_lock::lock_surface::LockSurface; use crate::wayland::session_lock::lock_surface::LockSurface;
use crate::wayland::surfaces::component_state::ComponentState; use crate::wayland::surfaces::component_state::ComponentState;
@ -308,7 +308,22 @@ impl ActiveLockSurface {
scale_factor: f32, scale_factor: f32,
) { ) {
match mode { match mode {
LockScalingMode::FractionalWithViewport | LockScalingMode::FractionalOnly => { LockScalingMode::FractionalWithViewport => {
let config = FractionalScaleConfig::new(
dimensions.logical_width() as f32,
dimensions.logical_height() as f32,
scale_factor,
);
info!(
"Lock FractionalWithViewport: render scale {} (from {}), physical {}x{}",
config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(self.window.as_ref());
}
LockScalingMode::FractionalOnly => {
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor); RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
self.window.set_size(WindowSize::Logical(LogicalSize::new( self.window.set_size(WindowSize::Logical(LogicalSize::new(
dimensions.logical_width() as f32, dimensions.logical_width() as f32,

View file

@ -1,6 +1,7 @@
use crate::errors::{LayerShikaError, Result}; use crate::errors::{LayerShikaError, Result};
use crate::rendering::egl::context_factory::RenderContextFactory; use crate::rendering::egl::context_factory::RenderContextFactory;
use crate::rendering::femtovg::{popup_window::PopupWindow, renderable_window::RenderableWindow}; use crate::rendering::femtovg::popup_window::PopupWindow;
use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::wayland::surfaces::display_metrics::{DisplayMetrics, SharedDisplayMetrics}; use crate::wayland::surfaces::display_metrics::{DisplayMetrics, SharedDisplayMetrics};
use layer_shika_domain::dimensions::LogicalSize as DomainLogicalSize; use layer_shika_domain::dimensions::LogicalSize as DomainLogicalSize;
use layer_shika_domain::surface_dimensions::SurfaceDimensions; use layer_shika_domain::surface_dimensions::SurfaceDimensions;
@ -9,7 +10,7 @@ use layer_shika_domain::value_objects::popup_behavior::ConstraintAdjustment;
use layer_shika_domain::value_objects::popup_config::PopupConfig; use layer_shika_domain::value_objects::popup_config::PopupConfig;
use layer_shika_domain::value_objects::popup_position::PopupPosition; use layer_shika_domain::value_objects::popup_position::PopupPosition;
use log::info; use log::info;
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize}; use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize};
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
@ -200,8 +201,9 @@ impl PopupManager {
pub fn update_scale_factor(&self, scale_factor: f32) { pub fn update_scale_factor(&self, scale_factor: f32) {
self.scale_factor.set(scale_factor); self.scale_factor.set(scale_factor);
let render_scale = FractionalScaleConfig::render_scale(scale_factor);
for popup in self.state.borrow().popups.values() { for popup in self.state.borrow().popups.values() {
popup.window.set_scale_factor(scale_factor); popup.window.set_scale_factor(render_scale);
} }
self.mark_all_popups_dirty(); self.mark_all_popups_dirty();
} }
@ -322,11 +324,16 @@ impl PopupManager {
let popup_window = PopupWindow::new_with_callback(renderer, on_close); let popup_window = PopupWindow::new_with_callback(renderer, on_close);
popup_window.set_popup_id(popup_id.to_handle()); popup_window.set_popup_id(popup_id.to_handle());
popup_window.set_scale_factor(scale_factor);
popup_window.set_size(WindowSize::Logical(slint::LogicalSize::new( let config = FractionalScaleConfig::new(params.width, params.height, scale_factor);
params.width, info!(
params.height, "Popup using render scale {} (from {}), render_physical {}x{}",
))); config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(popup_window.as_ref());
let mut state = self.state.borrow_mut(); let mut state = self.state.borrow_mut();
state.popups.insert( state.popups.insert(
@ -489,7 +496,6 @@ impl PopupManager {
ActiveWindow::None ActiveWindow::None
} }
#[allow(clippy::cast_precision_loss)]
pub fn update_scale_for_fractional_scale_object( pub fn update_scale_for_fractional_scale_object(
&self, &self,
fractional_scale_proxy: &WpFractionalScaleV1, fractional_scale_proxy: &WpFractionalScaleV1,
@ -500,8 +506,12 @@ impl PopupManager {
if let Some(popup_key) = self.find_popup_key_by_fractional_scale_id(&fractional_scale_id) { if let Some(popup_key) = self.find_popup_key_by_fractional_scale_id(&fractional_scale_id) {
if let Some(popup_surface) = self.get_popup_window(popup_key) { if let Some(popup_surface) = self.get_popup_window(popup_key) {
let new_scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths); let new_scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths);
info!("Updating popup scale factor to {new_scale_factor} ({scale_120ths}x)"); let render_scale = FractionalScaleConfig::render_scale(new_scale_factor);
popup_surface.set_scale_factor(new_scale_factor); info!(
"Updating popup scale factor to {} (render scale {}, from {}x)",
new_scale_factor, render_scale, scale_120ths
);
popup_surface.set_scale_factor(render_scale);
popup_surface.request_redraw(); popup_surface.request_redraw();
} }
} }

View file

@ -1,5 +1,5 @@
use crate::errors::Result; use crate::errors::Result;
use crate::rendering::femtovg::renderable_window::RenderableWindow; use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::wayland::managed_proxies::{ use crate::wayland::managed_proxies::{
ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport,
}; };
@ -92,12 +92,19 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
) { ) {
match mode { match mode {
ScalingMode::FractionalWithViewport => { ScalingMode::FractionalWithViewport => {
self.window.set_scale_factor(scale_factor); let config = FractionalScaleConfig::new(
self.window dimensions.logical_width() as f32,
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new( dimensions.logical_height() as f32,
dimensions.logical_width() as f32, scale_factor,
dimensions.logical_height() as f32, );
))); info!(
"FractionalWithViewport: render scale {} (from {}), physical {}x{}",
config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(self.window.as_ref());
} }
ScalingMode::FractionalOnly => { ScalingMode::FractionalOnly => {
self.window self.window
@ -154,15 +161,30 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
self.apply_surface_dimensions(dimensions, scale_factor); self.apply_surface_dimensions(dimensions, scale_factor);
} }
#[allow(clippy::cast_precision_loss)]
pub fn apply_surface_dimensions(&mut self, dimensions: SurfaceDimensions, scale_factor: f32) { pub fn apply_surface_dimensions(&mut self, dimensions: SurfaceDimensions, scale_factor: f32) {
let scaling_mode = self.determine_scaling_mode(); let scaling_mode = self.determine_scaling_mode();
let render_physical_size = match scaling_mode {
ScalingMode::FractionalWithViewport => {
FractionalScaleConfig::new(
dimensions.logical_width() as f32,
dimensions.logical_height() as f32,
scale_factor,
)
.render_physical_size
}
_ => dimensions.to_slint_physical_size(),
};
info!( info!(
"Updating window size: logical {}x{}, physical {}x{}, scale {}, buffer_scale {}, mode {:?}", "Updating window size: logical {}x{}, physical {}x{}, render_physical {}x{}, scale {}, buffer_scale {}, mode {:?}",
dimensions.logical_width(), dimensions.logical_width(),
dimensions.logical_height(), dimensions.logical_height(),
dimensions.physical_width(), dimensions.physical_width(),
dimensions.physical_height(), dimensions.physical_height(),
render_physical_size.width,
render_physical_size.height,
scale_factor, scale_factor,
dimensions.buffer_scale(), dimensions.buffer_scale(),
scaling_mode scaling_mode
@ -173,7 +195,7 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
info!("Window physical size: {:?}", self.window.size()); info!("Window physical size: {:?}", self.window.size());
self.size = dimensions.to_slint_physical_size(); self.size = render_physical_size;
self.logical_size = dimensions.to_slint_logical_size(); self.logical_size = dimensions.to_slint_logical_size();
RenderableWindow::request_redraw(self.window.as_ref()); RenderableWindow::request_redraw(self.window.as_ref());
} }