Skip to content

Commit

Permalink
Guard against window size exceeding max texture size
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Oct 28, 2023
1 parent 4da90e6 commit 87cb29e
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 22 deletions.
3 changes: 3 additions & 0 deletions crates/kas-core/src/draw/draw_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ impl<DS: DrawSharedImpl> DrawShared for SharedState<DS> {
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);

Expand Down
3 changes: 2 additions & 1 deletion crates/kas-core/src/shell/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ pub trait WindowSurface {
type Shared: kas::draw::DrawSharedImpl;

/// Construct an instance from a window handle
///
/// It is required to call [`WindowSurface::do_resize`] after this.
fn new<W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle>(
shared: &mut Self::Shared,
size: Size,
window: W,
) -> Result<Self>
where
Expand Down
36 changes: 25 additions & 11 deletions crates/kas-core/src/shell/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -93,7 +93,13 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
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();
Expand All @@ -116,18 +122,23 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
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;
Expand Down Expand Up @@ -156,7 +167,10 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
_ => 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();

Expand Down
9 changes: 8 additions & 1 deletion crates/kas-wgpu/src/draw/draw_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ impl<C: CustomPipe> DrawPipe<C> {
};
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)))?;
Expand Down Expand Up @@ -330,6 +333,10 @@ impl<C: CustomPipe> DrawPipe<C> {
impl<C: CustomPipe> DrawSharedImpl for DrawPipe<C> {
type Draw = DrawWindow<C::Window>;

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);
}
Expand Down
15 changes: 6 additions & 9 deletions crates/kas-wgpu/src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,19 +25,15 @@ impl<C: CustomPipe> WindowSurface for Surface<C> {

fn new<W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle>(
shared: &mut Self::Shared,
size: Size,
window: W,
) -> Result<Self, Error> {
let mut draw = shared.new_window();
shared.resize(&mut draw, size);

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
Expand All @@ -46,12 +42,11 @@ impl<C: CustomPipe> WindowSurface for Surface<C> {
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![],
};
surface.configure(&shared.device, &sc_desc);

Ok(Surface {
surface,
sc_desc,
draw,
draw: shared.new_window(),
})
}

Expand All @@ -63,6 +58,8 @@ impl<C: CustomPipe> WindowSurface for Surface<C> {
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);
Expand Down

0 comments on commit 87cb29e

Please sign in to comment.