Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WGPU update, improved window creation #417

Merged
merged 9 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
toolchain: [stable]
include:
- os: ubuntu-latest
toolchain: "1.66.0"
toolchain: "1.67.0"
- os: ubuntu-latest
toolchain: beta

Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ ron = { version = "0.8.0", package = "ron", optional = true }
toml = { version = "0.8.2", package = "toml", optional = true }
num_enum = "0.7.0"
dark-light = { version = "1.0", optional = true }
raw-window-handle = "0.5.0"
raw-window-handle = "0.6.0"
async-global-executor = { version = "2.3.1", optional = true }
cfg-if = "1.0.0"
smol_str = "0.2.0"
Expand All @@ -123,4 +123,4 @@ version = "0.5.0" # used in doc links
version = "0.29.2"
optional = true
default-features = false
features = ["rwh_05"]
features = ["rwh_06"]
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
14 changes: 8 additions & 6 deletions crates/kas-core/src/shell/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::draw::{color::Rgba, DrawIface, WindowCommon};
use crate::geom::Size;
use crate::theme::Theme;
use raw_window_handle as raw;
use std::time::Instant;
use thiserror::Error;

/// Possible failures from constructing a [`Shell`](super::Shell)
Expand Down Expand Up @@ -194,12 +195,11 @@ pub trait WindowSurface {
type Shared: kas::draw::DrawSharedImpl;

/// Construct an instance from a window handle
fn new<W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle>(
shared: &mut Self::Shared,
size: Size,
window: W,
) -> Result<Self>
///
/// It is required to call [`WindowSurface::do_resize`] after this.
fn new<W>(shared: &mut Self::Shared, window: W) -> Result<Self>
where
W: raw::HasWindowHandle + raw::HasDisplayHandle,
Self: Sized;

/// Get current surface size
Expand All @@ -220,5 +220,7 @@ pub trait WindowSurface {
fn common_mut(&mut self) -> &mut WindowCommon;

/// Present frame
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba);
///
/// Return time at which render finishes
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> Instant;
}
14 changes: 8 additions & 6 deletions crates/kas-core/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ enum ProxyAction {
#[cfg(test)]
mod test {
use super::*;
use std::time::Instant;

struct Draw;
impl crate::draw::DrawImpl for Draw {
Expand Down Expand Up @@ -152,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!()
}
Expand Down Expand Up @@ -224,12 +229,9 @@ mod test {
impl WindowSurface for Surface {
type Shared = DrawShared;

fn new<W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle>(
_: &mut Self::Shared,
_: crate::prelude::Size,
_: W,
) -> Result<Self>
fn new<W>(_: &mut Self::Shared, _: W) -> Result<Self>
where
W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle,
Self: Sized,
{
todo!()
Expand All @@ -254,7 +256,7 @@ mod test {
todo!()
}

fn present(&mut self, _: &mut Self::Shared, _: crate::draw::color::Rgba) {
fn present(&mut self, _: &mut Self::Shared, _: crate::draw::color::Rgba) -> Instant {
todo!()
}
}
Expand Down
2 changes: 0 additions & 2 deletions crates/kas-core/src/shell/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ pub(crate) struct SharedState<Data: AppData, S: WindowSurface, T: Theme<S::Share
pub(super) shell: ShellShared<Data, S, T>,
pub(super) data: Data,
/// Estimated scale factor (from last window constructed or available screens)
pub(super) scale_factor: f64,
options: Options,
}

Expand Down Expand Up @@ -82,7 +81,6 @@ where
window_id: 0,
},
data,
scale_factor: pw.guess_scale_factor(),
options,
})
}
Expand Down
11 changes: 0 additions & 11 deletions crates/kas-core/src/shell/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,17 +262,6 @@ impl<'a> PlatformWrapper<'a> {
// Otherwise platform is unsupported!
}

/// Guess scale factor of first window
pub(super) fn guess_scale_factor(&self) -> f64 {
if let Some(mon) = self.0.primary_monitor() {
return mon.scale_factor();
}
if let Some(mon) = self.0.available_monitors().next() {
return mon.scale_factor();
}
1.0
}

/// Create a waker
///
/// This waker may be used by a [`Future`](std::future::Future) to revive
Expand Down
111 changes: 65 additions & 46 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 @@ -76,19 +76,11 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
) -> super::Result<winit::window::WindowId> {
let time = Instant::now();

// Wayland only supports windows constructed via logical size
let use_logical_size = shared.shell.platform.is_wayland();

let scale_factor = if use_logical_size {
1.0
} else {
shared.scale_factor as f32
};

let mut theme_window = shared.shell.theme.new_window(scale_factor);
// We cannot reliably determine the scale factor before window creation.
// A factor of 1.0 lets us estimate the size requirements (logical).
let mut theme_window = shared.shell.theme.new_window(1.0);
let dpem = theme_window.size().dpem();

self.ev_state.update_config(scale_factor, dpem);
self.ev_state.update_config(1.0, dpem);
self.ev_state.full_configure(
theme_window.size(),
self.window_id,
Expand All @@ -101,20 +93,18 @@ 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));
let ideal = match use_logical_size {
false => ideal.as_physical(),
true => ideal.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();
if restrict_min {
let min = solve_cache.min(true);
let min = match use_logical_size {
false => min.as_physical(),
true => min.as_logical(),
};
let min = solve_cache.min(true).as_logical();
builder = builder.with_min_inner_size(min);
}
if restrict_max {
Expand All @@ -125,40 +115,63 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
.with_window_icon(self.widget.icon())
.with_decorations(self.widget.decorations() == kas::Decorations::Server)
.with_transparent(self.widget.transparent())
.with_visible(false)
.build(elwt)?;

// Now that we have a scale factor, we may need to resize:
let scale_factor = window.scale_factor();
shared.scale_factor = scale_factor;
let apply_size;
if scale_factor != 1.0 {
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(sf32, dpem);
let node = self.widget.as_node(&shared.data);
let sizer = SizeCx::new(theme_window.size());
solve_cache = SolveCache::find_constraints(node, sizer);

// 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;
} else {
// We will receive WindowEvent::Resized
apply_size = false;
}
} else {
apply_size = true;
}

let size: Size = window.inner_size().cast();
log::info!(
"new: constructed with physical size {:?}, scale factor {}",
size,
scale_factor
);

// Now that we have a scale factor, we may need to resize:
if use_logical_size && scale_factor != 1.0 {
let scale_factor = scale_factor as f32;
shared
.shell
.theme
.update_window(&mut theme_window, scale_factor);
let dpem = theme_window.size().dpem();
self.ev_state.update_config(scale_factor, dpem);
solve_cache.invalidate_rule_cache();
}

#[cfg(all(wayland_platform, feature = "clipboard"))]
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle, WaylandDisplayHandle};
use raw_window_handle::{HasDisplayHandle, RawDisplayHandle, WaylandDisplayHandle};
// TODO: this shouldn't unwrap
#[cfg(all(wayland_platform, feature = "clipboard"))]
let wayland_clipboard = match window.raw_display_handle() {
let wayland_clipboard = match window.display_handle().unwrap().as_raw() {
RawDisplayHandle::Wayland(WaylandDisplayHandle { display, .. }) => {
Some(unsafe { smithay_clipboard::Clipboard::new(display) })
Some(unsafe { smithay_clipboard::Clipboard::new(display.as_ptr()) })
}
_ => 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 All @@ -176,7 +189,9 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
queued_frame_time: Some(time),
});

self.apply_size(shared, true);
if apply_size {
self.apply_size(shared, true);
}

log::trace!(target: "kas_perf::wgpu::window", "resume: {}µs", time.elapsed().as_micros());
Ok(winit_id)
Expand All @@ -202,7 +217,6 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
match event {
WindowEvent::Moved(_) | WindowEvent::Destroyed => false,
WindowEvent::Resized(size) => {
// TODO: maybe enqueue to allow skipping of obsolete resizes
if window
.surface
.do_resize(&mut shared.shell.draw.draw, size.cast())
Expand All @@ -213,14 +227,16 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
// Note: API allows us to set new window size here.
shared.scale_factor = scale_factor;
let scale_factor = scale_factor as f32;
shared
.shell
.theme
.update_window(&mut window.theme_window, scale_factor);
let dpem = window.theme_window.size().dpem();
self.ev_state.update_config(scale_factor, dpem);

// NOTE: we could try resizing here in case the window is too
// small due to non-linear scaling, but it appears unnecessary.
window.solve_cache.invalidate_rule_cache();
false
}
Expand Down Expand Up @@ -418,6 +434,7 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
}
self.widget.resize_popups(&mut cx, &shared.data);

// Size restrictions may have changed due to content or size (line wrapping)
let (restrict_min, restrict_max) = self.widget.restrictions();
if restrict_min {
let min = window.solve_cache.min(true).as_physical();
Expand All @@ -428,6 +445,7 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
window.set_max_inner_size(Some(ideal));
};

window.set_visible(true);
window.request_redraw();
log::trace!(
target: "kas_perf::wgpu::window",
Expand Down Expand Up @@ -477,18 +495,19 @@ impl<A: AppData, S: WindowSurface, T: Theme<S::Shared>> Window<A, S, T> {
} else {
shared.shell.theme.clear_color()
};
window
let time3 = window
.surface
.present(&mut shared.shell.draw.draw, clear_color);

let text_dur_micros = take(&mut window.surface.common_mut().dur_text);
let end = Instant::now();
log::trace!(
target: "kas_perf::wgpu::window",
"do_draw: {}µs ({}μs widgets, {}µs text, {}µs render)",
"do_draw: {}μs ({}μs widgets, {}μs text, {}μs render, {}μs present)",
(end - start).as_micros(),
(time2 - start).as_micros(),
text_dur_micros.as_micros(),
(time3 - time2).as_micros(),
(end - time2).as_micros()
);

Expand Down
7 changes: 7 additions & 0 deletions crates/kas-resvg/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,13 @@ impl_scope! {
State::Rendering(source) |
State::Ready(source, _) => State::Ready(source, pixmap),
};

let own_size: (u32, u32) = self.core.rect.size.cast();
if size != own_size {
if let Some(fut) = self.inner.resize(size) {
cx.push_spawn(self.id(), fut);
}
}
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion crates/kas-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ raster = ["kas-text/raster"]
bytemuck = "1.7.0"
futures-lite = "1.12"
log = "0.4"
wgpu = { version = "0.17.0", features = ["spirv"] }
thiserror = "1.0.23"
guillotiere = "0.6.0"
rustc-hash = "1.0"
Expand All @@ -43,5 +42,11 @@ path = "../kas-core"
[dependencies.kas-text]
version = "0.6.0"

[dependencies.wgpu]
version = "0.18.0"
features = ["spirv"]
git = "https://github.com/gfx-rs/wgpu.git"
rev = "1dc5347b141f98392fa012ae3416901faf4249a5"

[build-dependencies]
glob = "0.3"
Loading