Skip to content

Commit

Permalink
updates to window size management to improve high DPI support
Browse files Browse the repository at this point in the history
* values in the `WindowDescriptor` and the `set_resolution` call are now "requested" sizes
* once the window is created, the actual width and height are learned along with the scale factor
* `width` and `height` return the actual size or the requested size if the window has not been created yet
* add some documentation for the window size APIs
* update a handfull of examples to change the window size of f32
  • Loading branch information
Nathan Jeffords authored and blunted2night committed Dec 12, 2020
1 parent c35105d commit e242233
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ impl Node for WindowTextureNode {
render_resource_context.remove_texture(old_texture);
}

self.descriptor.size.width = window.physical_width();
self.descriptor.size.height = window.physical_height();
self.descriptor.size.width = window.physical_width().unwrap();
self.descriptor.size.height = window.physical_height().unwrap();
let texture_resource = render_resource_context.create_texture(self.descriptor);
output.set(WINDOW_TEXTURE, RenderResourceId::Texture(texture_resource));
}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_wgpu/src/wgpu_type_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,8 @@ impl WgpuFrom<&Window> for wgpu::SwapChainDescriptor {
wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: TextureFormat::default().wgpu_into(),
width: window.physical_width(),
height: window.physical_height(),
width: window.physical_width().unwrap(),
height: window.physical_height().unwrap(),
present_mode: if window.vsync() {
wgpu::PresentMode::Mailbox
} else {
Expand Down
176 changes: 144 additions & 32 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,57 @@ impl Default for WindowId {
}
}

/// The requested size of the window client area in logical pixels
#[derive(Debug, Clone, Copy)]
struct RequestedSize {
width: f32,
height: f32,
}

/// The actual size of the window client area in physical pixels, and the
/// scaling factory needed to convert to logical pixels
#[derive(Debug, Clone, Copy)]
struct ActualSize {
physical_width: u32,
physical_height: u32,
scale_factor: f64,
}

impl ActualSize {
fn logical_width(&self) -> f32 {
(self.physical_width as f64 / self.scale_factor) as f32
}

fn logical_height(&self) -> f32 {
(self.physical_height as f64 / self.scale_factor) as f32
}
}

/// An operating system window that can present content and receive user input.
///
/// ## Window Sizes
///
/// There are three sizes associated with a window. The physical size which is
/// the height and width in physical pixels on the monitor. The logical size
/// which is the physical size scaled by an operating system provided factor to
/// account for monitors with differing pixel densities or user preference. And
/// the requested size, measured in logical pixels, which is the value submitted
/// to the API when creating the window, or requesting that it be resized.
///
/// The actual size, in logical pixels, of the window may not match the
/// requested size due to operating system limits on the window size, or the
/// quantization of the logical size when converting the physical size to the
/// logical size through the scaling factor.
///
/// Prior to window creation, the actual size of the window cannot be known. If
/// an application requires exact values at startup, it should process the
/// [WindowResized](crate::WindowResized) event. The first one of which will be
/// delivered during the first frame.
#[derive(Debug)]
pub struct Window {
id: WindowId,
physical_width: u32,
physical_height: u32,
requested_size: RequestedSize,
actual_size: Option<ActualSize>,
title: String,
vsync: bool,
resizable: bool,
Expand All @@ -48,7 +94,6 @@ pub struct Window {
#[cfg(target_arch = "wasm32")]
pub canvas: Option<String>,
command_queue: Vec<WindowCommand>,
scale_factor: f64,
}

#[derive(Debug)]
Expand All @@ -61,8 +106,7 @@ pub enum WindowCommand {
title: String,
},
SetResolution {
physical_width: u32,
physical_height: u32,
resolution: (f32, f32),
},
SetVsync {
vsync: bool,
Expand Down Expand Up @@ -103,8 +147,11 @@ impl Window {
pub fn new(id: WindowId, window_descriptor: &WindowDescriptor) -> Self {
Window {
id,
physical_height: window_descriptor.height,
physical_width: window_descriptor.width,
requested_size: RequestedSize {
width: window_descriptor.width,
height: window_descriptor.height,
},
actual_size: None,
title: window_descriptor.title.clone(),
vsync: window_descriptor.vsync,
resizable: window_descriptor.resizable,
Expand All @@ -116,7 +163,6 @@ impl Window {
#[cfg(target_arch = "wasm32")]
canvas: window_descriptor.canvas.clone(),
command_queue: Vec::new(),
scale_factor: 1.0,
}
}

Expand All @@ -125,24 +171,68 @@ impl Window {
self.id
}

/// The current logical width of the window's client area.
///
/// If the window has not been created yet, this will return
/// the [requested width](Window::requested_width) instead of
/// the [actual width](Window::logical_width).
#[inline]
pub fn width(&self) -> f32 {
(self.physical_width as f64 / self.scale_factor) as f32
self.logical_width().unwrap_or(self.requested_size.width)
}

/// The current logical height of the window's client area.
///
/// If the window has not been created yet, this will return
/// the [requested height](Window::requested_height) instead of
/// the [actual height](Window::logical_height).
#[inline]
pub fn height(&self) -> f32 {
(self.physical_height as f64 / self.scale_factor) as f32
self.logical_height().unwrap_or(self.requested_size.height)
}

/// The requested window client area width in logical pixels from window
/// creation or the last call to [set_resolution](Window::set_resolution).
///
/// This may differ from the actual width depending on OS size limits and
/// the scaling factor for high DPI monitors.
#[inline]
pub fn requested_width(&self) -> f32 {
self.requested_size.width
}

/// The requested window client area height in logical pixels from window
/// creation or the last call to [set_resolution](Window::set_resolution).
///
/// This may differ from the actual width depending on OS size limits and
/// the scaling factor for high DPI monitors.
#[inline]
pub fn requested_height(&self) -> f32 {
self.requested_size.height
}

/// The window's client area width in logical pixels if it has been created.
#[inline]
pub fn physical_width(&self) -> u32 {
self.physical_width
pub fn logical_width(&self) -> Option<f32> {
self.actual_size.map(|s| s.logical_width())
}

/// The window's client area width in logical pixels if it has been created.
#[inline]
pub fn physical_height(&self) -> u32 {
self.physical_height
pub fn logical_height(&self) -> Option<f32> {
self.actual_size.map(|s| s.logical_height())
}

/// The window's client area width in physical pixels if it has been created.
#[inline]
pub fn physical_width(&self) -> Option<u32> {
self.actual_size.map(|s| s.physical_width)
}

/// The window's client area height in physical pixels if it has been created.
#[inline]
pub fn physical_height(&self) -> Option<u32> {
self.actual_size.map(|s| s.physical_height)
}

#[inline]
Expand All @@ -151,31 +241,51 @@ impl Window {
.push(WindowCommand::SetMaximized { maximized });
}

/// Request the OS to resize the window such the the client area matches the
/// specified width and height.
pub fn set_resolution(&mut self, width: f32, height: f32) {
self.physical_width = (width as f64 * self.scale_factor) as u32;
self.physical_height = (height as f64 * self.scale_factor) as u32;
self.requested_size = RequestedSize { width, height };
self.command_queue.push(WindowCommand::SetResolution {
physical_width: self.physical_width,
physical_height: self.physical_height,
resolution: (self.requested_size.width, self.requested_size.height),
});
}

#[allow(missing_docs)]
#[inline]
pub fn update_physical_size_from_backend(&mut self, width: u32, height: u32) {
self.physical_width = width;
self.physical_height = height;
pub fn update_actual_size_and_scale_from_backend(
&mut self,
width: u32,
height: u32,
scale: f64,
) {
self.actual_size = Some(ActualSize {
physical_width: width,
physical_height: height,
scale_factor: scale,
});
}

#[allow(missing_docs)]
#[inline]
pub fn update_scale_factor_from_backend(&mut self, scale_factor: f64) {
self.scale_factor = scale_factor;
}

pub fn update_actual_size_from_backend(
&mut self,
width: u32,
height: u32,
) -> Option<(f32, f32)> {
self.actual_size.as_mut().map(|size| {
size.physical_width = width;
size.physical_height = height;

(size.logical_width(), size.logical_height())
})
}

/// The ratio of physical pixels to logical pixels if window has been created.
///
/// `physical_pixels = logical_pixels * scale_factor`
#[inline]
pub fn scale_factor(&self) -> f64 {
self.scale_factor
pub fn scale_factor(&self) -> Option<f64> {
self.actual_size.map(|s| s.scale_factor)
}

#[inline]
Expand Down Expand Up @@ -267,9 +377,11 @@ impl Window {

pub fn set_mode(&mut self, mode: WindowMode) {
self.mode = mode;
let width = self.requested_size.width as u32;
let height = self.requested_size.height as u32;
self.command_queue.push(WindowCommand::SetWindowMode {
mode,
resolution: (self.physical_width, self.physical_height),
resolution: (width, height),
});
}

Expand All @@ -281,8 +393,8 @@ impl Window {

#[derive(Debug, Clone)]
pub struct WindowDescriptor {
pub width: u32,
pub height: u32,
pub width: f32,
pub height: f32,
pub title: String,
pub vsync: bool,
pub resizable: bool,
Expand All @@ -298,8 +410,8 @@ impl Default for WindowDescriptor {
fn default() -> Self {
WindowDescriptor {
title: "bevy".to_string(),
width: 1280,
height: 720,
width: 1280.,
height: 720.,
vsync: true,
resizable: true,
decorations: true,
Expand Down
42 changes: 25 additions & 17 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,12 @@ fn change_window(_: &mut World, resources: &mut Resources) {
window.set_title(&title);
}
bevy_window::WindowCommand::SetResolution {
physical_width,
physical_height,
resolution: (logical_width, logical_height),
} => {
let window = winit_windows.get_window(id).unwrap();
window.set_inner_size(winit::dpi::PhysicalSize::new(
physical_width,
physical_height,
window.set_inner_size(winit::dpi::LogicalSize::new(
logical_width,
logical_height,
));
}
bevy_window::WindowCommand::SetVsync { .. } => (),
Expand Down Expand Up @@ -204,15 +203,21 @@ pub fn winit_runner(mut app: App) {
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
window.update_physical_size_from_backend(size.width, size.height);

let mut resize_events =
app.resources.get_mut::<Events<WindowResized>>().unwrap();
resize_events.send(WindowResized {
id: window_id,
height: window.height(),
width: window.width(),
});
if let Some((width, height)) =
window.update_actual_size_from_backend(size.width, size.height)
{
let mut resize_events =
app.resources.get_mut::<Events<WindowResized>>().unwrap();
resize_events.send(WindowResized {
id: window_id,
width,
height,
});
} else {
// this should not occur as we shouldn't receive resize
// events prior to window creation completing and
// learning of the scale factor
}
}
WindowEvent::CloseRequested => {
let mut window_close_requested_events = app
Expand Down Expand Up @@ -309,7 +314,8 @@ pub fn winit_runner(mut app: App) {

// FIXME?: On Android window start is top while on PC/Linux/OSX on bottom
if cfg!(target_os = "android") {
let window_height = windows.get_primary().unwrap().height();
let window_height =
windows.get_primary().unwrap().logical_height().unwrap();
location.y = window_height - location.y;
}
touch_input_events.send(converters::convert_touch_input(touch, location));
Expand All @@ -336,11 +342,13 @@ pub fn winit_runner(mut app: App) {
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
window.update_physical_size_from_backend(
window.update_actual_size_and_scale_from_backend(
new_inner_size.width,
new_inner_size.height,
scale_factor,
);
window.update_scale_factor_from_backend(scale_factor);
// should we send a resize event to indicate the change in
// logical size?
}
WindowEvent::Focused(focused) => {
let mut focused_events =
Expand Down
Loading

0 comments on commit e242233

Please sign in to comment.