diff --git a/crates/kas-core/src/draw/draw_shared.rs b/crates/kas-core/src/draw/draw_shared.rs index 469c57488..3d9fe20bd 100644 --- a/crates/kas-core/src/draw/draw_shared.rs +++ b/crates/kas-core/src/draw/draw_shared.rs @@ -149,6 +149,9 @@ impl DrawShared for SharedState { pub trait DrawSharedImpl: Any { type Draw: DrawImpl; + /// Get the maximum 2D texture size + fn max_texture_dimension_2d(&self) -> u32; + /// Set font raster config fn set_raster_config(&mut self, config: &RasterConfig); diff --git a/crates/kas-core/src/shell/common.rs b/crates/kas-core/src/shell/common.rs index 09e510666..d23c808bd 100644 --- a/crates/kas-core/src/shell/common.rs +++ b/crates/kas-core/src/shell/common.rs @@ -195,12 +195,11 @@ pub trait WindowSurface { type Shared: kas::draw::DrawSharedImpl; /// Construct an instance from a window handle - fn new( - shared: &mut Self::Shared, - size: Size, - window: W, - ) -> Result + /// + /// It is required to call [`WindowSurface::do_resize`] after this. + fn new(shared: &mut Self::Shared, window: W) -> Result where + W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle, Self: Sized; /// Get current surface size diff --git a/crates/kas-core/src/shell/mod.rs b/crates/kas-core/src/shell/mod.rs index 104fceedb..c57cf7bb4 100644 --- a/crates/kas-core/src/shell/mod.rs +++ b/crates/kas-core/src/shell/mod.rs @@ -153,6 +153,10 @@ mod test { impl crate::draw::DrawSharedImpl for DrawShared { type Draw = Draw; + fn max_texture_dimension_2d(&self) -> u32 { + todo!() + } + fn set_raster_config(&mut self, _: &crate::theme::RasterConfig) { todo!() } @@ -225,12 +229,9 @@ mod test { impl WindowSurface for Surface { type Shared = DrawShared; - fn new( - _: &mut Self::Shared, - _: crate::prelude::Size, - _: W, - ) -> Result + fn new(_: &mut Self::Shared, _: W) -> Result where + W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle, Self: Sized, { todo!() diff --git a/crates/kas-core/src/shell/window.rs b/crates/kas-core/src/shell/window.rs index 82394b086..0560af74c 100644 --- a/crates/kas-core/src/shell/window.rs +++ b/crates/kas-core/src/shell/window.rs @@ -8,8 +8,8 @@ use super::common::WindowSurface; use super::shared::{SharedState, ShellShared}; use super::ProxyAction; -use kas::cast::Cast; -use kas::draw::{color::Rgba, AnimationState}; +use kas::cast::{Cast, Conv}; +use kas::draw::{color::Rgba, AnimationState, DrawSharedImpl}; use kas::event::{config::WindowConfig, ConfigCx, CursorIcon, EventState}; use kas::geom::{Coord, Rect, Size}; use kas::layout::SolveCache; @@ -93,7 +93,13 @@ impl> Window { let mut solve_cache = SolveCache::find_constraints(node, sizer); // Opening a zero-size window causes a crash, so force at least 1x1: - let ideal = solve_cache.ideal(true).max(Size(1, 1)).as_logical(); + let min_size = Size(1, 1); + let max_size = Size::splat(shared.shell.draw.draw.max_texture_dimension_2d().cast()); + + let ideal = solve_cache + .ideal(true) + .clamp(min_size, max_size) + .as_logical(); let mut builder = WindowBuilder::new().with_inner_size(ideal); let (restrict_min, restrict_max) = self.widget.restrictions(); @@ -116,18 +122,23 @@ impl> Window { let scale_factor = window.scale_factor(); let apply_size; if scale_factor != 1.0 { - let scale_factor = scale_factor as f32; - shared - .shell - .theme - .update_window(&mut theme_window, scale_factor); + let sf32 = scale_factor as f32; + shared.shell.theme.update_window(&mut theme_window, sf32); let dpem = theme_window.size().dpem(); - self.ev_state.update_config(scale_factor, dpem); + self.ev_state.update_config(sf32, dpem); let node = self.widget.as_node(&shared.data); let sizer = SizeCx::new(theme_window.size()); solve_cache = SolveCache::find_constraints(node, sizer); - let ideal = solve_cache.ideal(true).max(Size(1, 1)).as_physical(); + // NOTE: we would use .as_physical(), but we need to ensure rounding + // doesn't result in anything exceeding max_size which can happen + // otherwise (default rounding mode is to nearest, away from zero). + let ideal = solve_cache.ideal(true).max(min_size); + let ub = (f64::conv(max_size.0) / scale_factor).floor(); + let w = ub.min(f64::conv(ideal.0) / scale_factor); + let h = ub.min(f64::conv(ideal.1) / scale_factor); + let ideal = winit::dpi::LogicalSize::new(w, h); + if let Some(size) = window.request_inner_size(ideal) { debug_assert_eq!(size, window.inner_size()); apply_size = true; @@ -156,7 +167,10 @@ impl> Window { _ => None, }; - let surface = S::new(&mut shared.shell.draw.draw, size, &window)?; + let mut surface = S::new(&mut shared.shell.draw.draw, &window)?; + if apply_size { + surface.do_resize(&mut shared.shell.draw.draw, size); + } let winit_id = window.id(); diff --git a/crates/kas-wgpu/src/draw/draw_pipe.rs b/crates/kas-wgpu/src/draw/draw_pipe.rs index c65070c8e..f373ee4c7 100644 --- a/crates/kas-wgpu/src/draw/draw_pipe.rs +++ b/crates/kas-wgpu/src/draw/draw_pipe.rs @@ -47,7 +47,10 @@ impl DrawPipe { }; log::info!("Using graphics adapter: {}", adapter.get_info().name); - let desc = CB::device_descriptor(); + // Use adapter texture size limits to support the largest window surface possible + let mut desc = CB::device_descriptor(); + desc.limits = desc.limits.using_resolution(adapter.limits()); + let trace_path = options.wgpu_trace_path.as_deref(); let req = adapter.request_device(&desc, trace_path); let (device, queue) = block_on(req).map_err(|e| Error::Graphics(Box::new(e)))?; @@ -330,6 +333,10 @@ impl DrawPipe { impl DrawSharedImpl for DrawPipe { type Draw = DrawWindow; + fn max_texture_dimension_2d(&self) -> u32 { + self.device.limits().max_texture_dimension_2d + } + fn set_raster_config(&mut self, config: &RasterConfig) { self.text.set_raster_config(config); } diff --git a/crates/kas-wgpu/src/surface.rs b/crates/kas-wgpu/src/surface.rs index 20afd7739..8e68dfb49 100644 --- a/crates/kas-wgpu/src/surface.rs +++ b/crates/kas-wgpu/src/surface.rs @@ -8,7 +8,7 @@ use crate::draw::{CustomPipe, DrawPipe, DrawWindow}; use kas::cast::Cast; use kas::draw::color::Rgba; -use kas::draw::{DrawIface, WindowCommon}; +use kas::draw::{DrawIface, DrawSharedImpl, WindowCommon}; use kas::geom::Size; use kas::shell::{raw_window_handle as raw, Error, WindowSurface}; use std::time::Instant; @@ -23,21 +23,18 @@ pub struct Surface { impl WindowSurface for Surface { type Shared = DrawPipe; - fn new( - shared: &mut Self::Shared, - size: Size, - window: W, - ) -> Result { - let mut draw = shared.new_window(); - shared.resize(&mut draw, size); - + fn new(shared: &mut Self::Shared, window: W) -> Result + where + W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle, + Self: Sized, + { let surface = unsafe { shared.instance.create_surface(&window) } .map_err(|e| Error::Graphics(Box::new(e)))?; let sc_desc = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: crate::draw::RENDER_TEX_FORMAT, - width: size.0.cast(), - height: size.1.cast(), + width: 0, + height: 0, present_mode: wgpu::PresentMode::Fifo, // FIXME: current output is for Opaque or PostMultiplied, depending // on window transparency. But we can't pick what we want since only @@ -46,12 +43,11 @@ impl WindowSurface for Surface { alpha_mode: wgpu::CompositeAlphaMode::Auto, view_formats: vec![], }; - surface.configure(&shared.device, &sc_desc); Ok(Surface { surface, sc_desc, - draw, + draw: shared.new_window(), }) } @@ -63,6 +59,8 @@ impl WindowSurface for Surface { if size == self.size() { return false; } + let size = size.min(Size::splat(shared.max_texture_dimension_2d().cast())); + let time = Instant::now(); shared.resize(&mut self.draw, size);