From 097d62fdd021afb1cf50322ce67a555e50fbf59d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 18:03:13 +0000 Subject: [PATCH 1/8] Enable x11 feature by default --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4b513e5e3..62fff4676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ rustdoc-args = ["--cfg", "doc_cfg"] # markdown, resvg. Recommended also: clipboard, yaml (or some config format). minimal = ["wgpu", "winit", "wayland"] # All recommended features for optimal experience -default = ["minimal", "view", "image", "resvg", "clipboard", "markdown", "shaping", "spawn"] +default = ["minimal", "x11", "view", "image", "resvg", "clipboard", "markdown", "shaping", "spawn"] # All standard test target features # NOTE: dynamic is excluded due to linker problems on Windows stable = ["default", "serde", "toml", "yaml", "json", "ron", "macros_log"] From 203c6bf79f6ef59dac3838c96f0e50d69c285ab6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 11:08:42 +0000 Subject: [PATCH 2/8] Update env_logger to 0.11.0 --- Cargo.toml | 2 +- examples/mandlebrot/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62fff4676..5ffeb879b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,7 +133,7 @@ features = ["raster"] [dev-dependencies] chrono = "0.4" -env_logger = "0.10" +env_logger = "0.11" log = "0.4" [workspace] diff --git a/examples/mandlebrot/Cargo.toml b/examples/mandlebrot/Cargo.toml index b5010c2e5..a54fc3ca9 100644 --- a/examples/mandlebrot/Cargo.toml +++ b/examples/mandlebrot/Cargo.toml @@ -11,7 +11,7 @@ publish = false kas = { version = "0.14.1", features = ["wgpu"], path = "../.." } kas-wgpu = { version = "0.14.1", path = "../../crates/kas-wgpu" } chrono = "0.4" -env_logger = "0.10" +env_logger = "0.11" log = "0.4" bytemuck = "1.7.0" From 7d3c70d79c4534d07a983fdc8587926ad8b714f7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 11:11:05 +0000 Subject: [PATCH 3/8] Update cfg_aliases to 0.2.0 --- crates/kas-core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index f592a53f6..38c7cccb2 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -78,7 +78,7 @@ spawn = ["dep:async-global-executor"] unsafe_node = [] [build-dependencies] -cfg_aliases = "0.1.1" +cfg_aliases = "0.2.0" [dependencies] log = "0.4" From db2b58521556d7696e363a1bd2ac0a4abe1dfc5b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 11:14:02 +0000 Subject: [PATCH 4/8] Update usvg/resvg to 0.38.0 --- crates/kas-resvg/Cargo.toml | 4 ++-- crates/kas-resvg/src/svg.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/kas-resvg/Cargo.toml b/crates/kas-resvg/Cargo.toml index 61d014933..da0116882 100644 --- a/crates/kas-resvg/Cargo.toml +++ b/crates/kas-resvg/Cargo.toml @@ -26,8 +26,8 @@ svg = ["dep:resvg", "dep:usvg"] [dependencies] tiny-skia = { version = "0.11.0" } -resvg = { version = "0.36.0", optional = true } -usvg = { version = "0.36.0", optional = true } +resvg = { version = "0.38.0", optional = true } +usvg = { version = "0.38.0", optional = true } once_cell = "1.17.0" thiserror = "1.0.23" diff --git a/crates/kas-resvg/src/svg.rs b/crates/kas-resvg/src/svg.rs index 320421c7c..57f02addd 100644 --- a/crates/kas-resvg/src/svg.rs +++ b/crates/kas-resvg/src/svg.rs @@ -86,11 +86,10 @@ enum State { async fn draw(svg: Source, mut pixmap: Pixmap) -> Pixmap { if let Ok(tree) = svg.tree() { - let tree = resvg::Tree::from_usvg(&tree); let w = f32::conv(pixmap.width()) / tree.size.width(); let h = f32::conv(pixmap.height()) / tree.size.height(); let transform = Transform::from_scale(w, h); - tree.render(transform, &mut pixmap.as_mut()); + resvg::render(&tree, transform, &mut pixmap.as_mut()); } pixmap } From ab22b0d0a0052b90559983056651f66f18d43e60 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 16:33:01 +0000 Subject: [PATCH 5/8] Update raw-window-handle, wgpu --- crates/kas-core/Cargo.toml | 4 ++-- crates/kas-core/src/app/common.rs | 6 +++++- crates/kas-core/src/app/window.rs | 13 ++++++++----- crates/kas-wgpu/Cargo.toml | 6 +----- crates/kas-wgpu/src/draw/draw_pipe.rs | 2 +- crates/kas-wgpu/src/options.rs | 1 - crates/kas-wgpu/src/surface.rs | 13 +++++++++---- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index 38c7cccb2..facb402cc 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -94,7 +94,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" @@ -121,4 +121,4 @@ version = "0.5.0" # used in doc links version = "0.29.2" optional = true default-features = false -features = ["rwh_05"] +features = ["rwh_06"] diff --git a/crates/kas-core/src/app/common.rs b/crates/kas-core/src/app/common.rs index d1ae62a77..4cad490d7 100644 --- a/crates/kas-core/src/app/common.rs +++ b/crates/kas-core/src/app/common.rs @@ -20,6 +20,10 @@ use thiserror::Error; #[non_exhaustive] #[derive(Error, Debug)] pub enum Error { + /// Window-handle error + #[error(transparent)] + Handle(#[from] raw::HandleError), + /// Failure from the graphics sub-system #[error("error from graphics sub-system")] Graphics(Box), @@ -199,7 +203,7 @@ pub trait WindowSurface { /// 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, + W: raw::HasWindowHandle + raw::HasDisplayHandle + Send + Sync, Self: Sized; /// Get current surface size diff --git a/crates/kas-core/src/app/window.rs b/crates/kas-core/src/app/window.rs index f7fb82b3c..299066f2d 100644 --- a/crates/kas-core/src/app/window.rs +++ b/crates/kas-core/src/app/window.rs @@ -158,12 +158,15 @@ impl> Window { ); #[cfg(all(wayland_platform, feature = "clipboard"))] - use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle, WaylandDisplayHandle}; + use raw_window_handle::{HasDisplayHandle, RawDisplayHandle, WaylandDisplayHandle}; #[cfg(all(wayland_platform, feature = "clipboard"))] - let wayland_clipboard = match window.raw_display_handle() { - RawDisplayHandle::Wayland(WaylandDisplayHandle { display, .. }) => { - Some(unsafe { smithay_clipboard::Clipboard::new(display) }) - } + let wayland_clipboard = match window.display_handle() { + Ok(handle) => match handle.as_raw() { + RawDisplayHandle::Wayland(WaylandDisplayHandle { display, .. }) => { + Some(unsafe { smithay_clipboard::Clipboard::new(display.as_ptr()) }) + } + _ => None, + }, _ => None, }; diff --git a/crates/kas-wgpu/Cargo.toml b/crates/kas-wgpu/Cargo.toml index b75623599..f720ed343 100644 --- a/crates/kas-wgpu/Cargo.toml +++ b/crates/kas-wgpu/Cargo.toml @@ -37,10 +37,6 @@ thiserror = "1.0.23" guillotiere = "0.6.0" rustc-hash = "1.0" -# Force patch versions of naga and wgpu-core (otherwise not needed as direct dependencies) -naga = "0.14.1" -wgpu-core = "0.18.1" - [dependencies.kas] # Rename package purely for convenience: version = "0.14.1" @@ -51,7 +47,7 @@ path = "../kas-core" version = "0.6.0" [dependencies.wgpu] -version = "0.18.0" +version = "0.19.1" default-features = false features = ["spirv"] diff --git a/crates/kas-wgpu/src/draw/draw_pipe.rs b/crates/kas-wgpu/src/draw/draw_pipe.rs index 8b21d412c..5ae4bef69 100644 --- a/crates/kas-wgpu/src/draw/draw_pipe.rs +++ b/crates/kas-wgpu/src/draw/draw_pipe.rs @@ -46,7 +46,7 @@ impl DrawPipe { // 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()); + desc.required_limits = desc.required_limits.using_resolution(adapter.limits()); let trace_path = options.wgpu_trace_path.as_deref(); let req = adapter.request_device(&desc, trace_path); diff --git a/crates/kas-wgpu/src/options.rs b/crates/kas-wgpu/src/options.rs index 24f982fd9..f8135af32 100644 --- a/crates/kas-wgpu/src/options.rs +++ b/crates/kas-wgpu/src/options.rs @@ -87,7 +87,6 @@ impl Options { "VULKAN" => Backends::VULKAN, "GL" => Backends::GL, "METAL" => Backends::METAL, - "DX11" => Backends::DX11, "DX12" => Backends::DX12, "BROWSER_WEBGPU" => Backends::BROWSER_WEBGPU, "PRIMARY" => Backends::PRIMARY, diff --git a/crates/kas-wgpu/src/surface.rs b/crates/kas-wgpu/src/surface.rs index 05c42736b..626882ed1 100644 --- a/crates/kas-wgpu/src/surface.rs +++ b/crates/kas-wgpu/src/surface.rs @@ -15,7 +15,7 @@ use std::time::Instant; /// Per-window data pub struct Surface { - surface: wgpu::Surface, + surface: wgpu::Surface<'static>, sc_desc: wgpu::SurfaceConfiguration, draw: DrawWindow, } @@ -25,17 +25,22 @@ impl WindowSurface for Surface { fn new(shared: &mut Self::Shared, window: W) -> Result where - W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle, + W: raw::HasWindowHandle + raw::HasDisplayHandle + Send + Sync, Self: Sized, { - let surface = unsafe { shared.instance.create_surface(&window) } - .map_err(|e| Error::Graphics(Box::new(e)))?; + let surface = unsafe { + // TODO: handle window lifetime safely + let target = wgpu::SurfaceTargetUnsafe::from_window(&window)?; + shared.instance.create_surface_unsafe(target) + } + .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: 0, height: 0, present_mode: wgpu::PresentMode::Fifo, + desired_maximum_frame_latency: 2, // FIXME: current output is for Opaque or PostMultiplied, depending // on window transparency. But we can't pick what we want since only // a sub-set of modes are supported (depending on target). From ff5ef2a03edcdeb87fc92fba95755a58e14ea5fa Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 17:11:56 +0000 Subject: [PATCH 6/8] Add lifetime to Surface --- crates/kas-core/src/app/app.rs | 4 +-- crates/kas-core/src/app/common.rs | 2 +- crates/kas-core/src/app/event_loop.rs | 14 ++++----- crates/kas-core/src/app/mod.rs | 6 ++-- crates/kas-core/src/app/shared.rs | 22 +++++++------- crates/kas-core/src/app/window.rs | 42 +++++++++++++-------------- crates/kas-wgpu/src/lib.rs | 2 +- crates/kas-wgpu/src/surface.rs | 6 ++-- 8 files changed, 49 insertions(+), 49 deletions(-) diff --git a/crates/kas-core/src/app/app.rs b/crates/kas-core/src/app/app.rs index e760d32c0..eed45f472 100644 --- a/crates/kas-core/src/app/app.rs +++ b/crates/kas-core/src/app/app.rs @@ -18,8 +18,8 @@ use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopProxy}; pub struct Application> { el: EventLoop, - windows: Vec>>, - state: AppState, + windows: Vec>>, + state: AppState, } impl_scope! { diff --git a/crates/kas-core/src/app/common.rs b/crates/kas-core/src/app/common.rs index 4cad490d7..f4303a486 100644 --- a/crates/kas-core/src/app/common.rs +++ b/crates/kas-core/src/app/common.rs @@ -185,7 +185,7 @@ pub trait AppGraphicsBuilder { type Shared: DrawSharedImpl; /// Window surface - type Surface: WindowSurface + 'static; + type Surface<'a>: WindowSurface; /// Construct shared state fn build(self) -> Result; diff --git a/crates/kas-core/src/app/event_loop.rs b/crates/kas-core/src/app/event_loop.rs index 2a2baa111..c76dea882 100644 --- a/crates/kas-core/src/app/event_loop.rs +++ b/crates/kas-core/src/app/event_loop.rs @@ -5,8 +5,8 @@ //! Event loop and handling -use super::{AppData, AppState, Pending}; -use super::{ProxyAction, Window, WindowSurface}; +use super::{AppData, AppGraphicsBuilder, AppState, Pending}; +use super::{ProxyAction, Window}; use kas::theme::Theme; use kas::{Action, WindowId}; use std::collections::HashMap; @@ -16,28 +16,28 @@ use winit::event_loop::{ControlFlow, EventLoopWindowTarget}; use winit::window as ww; /// Event-loop data structure (i.e. all run-time state) -pub(super) struct Loop> +pub(super) struct Loop> where T::Window: kas::theme::Window, { /// State is suspended until we receive Event::Resumed suspended: bool, /// Window states - windows: HashMap>>, + windows: HashMap>>, popups: HashMap, /// Translates our WindowId to winit's id_map: HashMap, /// Application state passed from Toolkit - state: AppState, + state: AppState, /// Timer resumes: (time, window identifier) resumes: Vec<(Instant, WindowId)>, } -impl> Loop +impl> Loop where T::Window: kas::theme::Window, { - pub(super) fn new(mut windows: Vec>>, state: AppState) -> Self { + pub(super) fn new(mut windows: Vec>>, state: AppState) -> Self { Loop { suspended: true, windows: windows.drain(..).map(|w| (w.window_id, w)).collect(), diff --git a/crates/kas-core/src/app/mod.rs b/crates/kas-core/src/app/mod.rs index e2814f8bb..70a2d1dce 100644 --- a/crates/kas-core/src/app/mod.rs +++ b/crates/kas-core/src/app/mod.rs @@ -50,11 +50,11 @@ impl AppData for () { #[crate::autoimpl(Debug)] #[cfg(winit)] -enum Pending> { +enum Pending> { AddPopup(WindowId, WindowId, kas::PopupDescriptor), - // NOTE: we don't need S, T here if we construct the Window later. + // NOTE: we don't need G, T here if we construct the Window later. // But this way we can pass a single boxed value. - AddWindow(WindowId, Box>), + AddWindow(WindowId, Box>), CloseWindow(WindowId), Action(kas::Action), } diff --git a/crates/kas-core/src/app/shared.rs b/crates/kas-core/src/app/shared.rs index 08f4dc3ed..b144437f2 100644 --- a/crates/kas-core/src/app/shared.rs +++ b/crates/kas-core/src/app/shared.rs @@ -5,7 +5,7 @@ //! Shared state -use super::{AppData, Error, Pending, Platform, WindowSurface}; +use super::{AppData, AppGraphicsBuilder, Error, Pending, Platform}; use kas::config::Options; use kas::draw::DrawShared; use kas::theme::{Theme, ThemeControl}; @@ -21,27 +21,27 @@ use std::task::Waker; #[cfg(feature = "clipboard")] use arboard::Clipboard; /// Application state used by [`AppShared`] -pub(crate) struct AppSharedState> { +pub(crate) struct AppSharedState> { pub(super) platform: Platform, pub(super) config: Rc>, #[cfg(feature = "clipboard")] clipboard: Option, - pub(super) draw: draw::SharedState, + pub(super) draw: draw::SharedState, pub(super) theme: T, - pub(super) pending: VecDeque>, + pub(super) pending: VecDeque>, pub(super) waker: Waker, window_id: u32, } /// Application state shared by all windows -pub(crate) struct AppState> { - pub(super) shared: AppSharedState, +pub(crate) struct AppState> { + pub(super) shared: AppSharedState, pub(super) data: Data, /// Estimated scale factor (from last window constructed or available screens) options: Options, } -impl> AppState +impl> AppState where T::Window: kas::theme::Window, { @@ -49,7 +49,7 @@ where pub(super) fn new( data: Data, pw: super::PlatformWrapper, - draw_shared: S::Shared, + draw_shared: G::Shared, mut theme: T, options: Options, config: Rc>, @@ -108,7 +108,7 @@ where } } -impl> AppSharedState { +impl> AppSharedState { /// Return the next window identifier /// /// TODO(opt): this should recycle used identifiers since Id does not @@ -207,8 +207,8 @@ pub(crate) trait AppShared { fn waker(&self) -> &std::task::Waker; } -impl> AppShared - for AppSharedState +impl> AppShared + for AppSharedState { fn add_popup(&mut self, parent_id: WindowId, popup: kas::PopupDescriptor) -> WindowId { let id = self.next_window_id(); diff --git a/crates/kas-core/src/app/window.rs b/crates/kas-core/src/app/window.rs index 299066f2d..b64ae1433 100644 --- a/crates/kas-core/src/app/window.rs +++ b/crates/kas-core/src/app/window.rs @@ -7,7 +7,7 @@ use super::common::WindowSurface; use super::shared::{AppSharedState, AppState}; -use super::{AppData, ProxyAction}; +use super::{AppData, AppGraphicsBuilder, ProxyAction}; use kas::cast::{Cast, Conv}; use kas::draw::{color::Rgba, AnimationState, DrawSharedImpl}; use kas::event::{config::WindowConfig, ConfigCx, CursorIcon, EventState}; @@ -24,11 +24,11 @@ use winit::window::WindowBuilder; /// Window fields requiring a frame or surface #[crate::autoimpl(Deref, DerefMut using self.window)] -struct WindowData> { +struct WindowData> { window: winit::window::Window, #[cfg(all(wayland_platform, feature = "clipboard"))] wayland_clipboard: Option, - surface: S, + surface: G::Surface<'static>, /// Frame rate counter frame_count: (Instant, u32), @@ -42,19 +42,19 @@ struct WindowData> { /// Per-window data #[autoimpl(Debug ignore self._data, self.widget, self.ev_state, self.window)] -pub struct Window> { +pub struct Window> { _data: std::marker::PhantomData, pub(super) widget: kas::Window, pub(super) window_id: WindowId, ev_state: EventState, - window: Option>, + window: Option>, } // Public functions, for use by the toolkit -impl> Window { +impl> Window { /// Construct window state (widget) pub(super) fn new( - shared: &AppSharedState, + shared: &AppSharedState, window_id: WindowId, widget: kas::Window, ) -> Self { @@ -71,7 +71,7 @@ impl> Window { /// Open (resume) a window pub(super) fn resume( &mut self, - state: &mut AppState, + state: &mut AppState, elwt: &EventLoopWindowTarget, ) -> super::Result { let time = Instant::now(); @@ -170,7 +170,7 @@ impl> Window { _ => None, }; - let mut surface = S::new(&mut state.shared.draw.draw, &window)?; + let mut surface = G::Surface::new(&mut state.shared.draw.draw, &window)?; if apply_size { surface.do_resize(&mut state.shared.draw.draw, size); } @@ -210,7 +210,7 @@ impl> Window { /// Returns `true` to force polling temporarily. pub(super) fn handle_event( &mut self, - state: &mut AppState, + state: &mut AppState, event: WindowEvent, ) -> bool { let Some(ref mut window) = self.window else { @@ -264,7 +264,7 @@ impl> Window { /// Handle all pending items before event loop sleeps pub(super) fn flush_pending( &mut self, - state: &mut AppState, + state: &mut AppState, ) -> (Action, Option) { let Some(ref window) = self.window else { return (Action::empty(), None); @@ -300,7 +300,7 @@ impl> Window { } /// Handle an action (excludes handling of CLOSE and EXIT) - pub(super) fn handle_action(&mut self, state: &AppState, mut action: Action) { + pub(super) fn handle_action(&mut self, state: &AppState, mut action: Action) { if action.contains(Action::EVENT_CONFIG) { if let Some(ref mut window) = self.window { let scale_factor = window.scale_factor() as f32; @@ -339,7 +339,7 @@ impl> Window { } } - pub(super) fn update_timer(&mut self, state: &mut AppState) -> Option { + pub(super) fn update_timer(&mut self, state: &mut AppState) -> Option { let Some(ref window) = self.window else { return None; }; @@ -355,7 +355,7 @@ impl> Window { pub(super) fn add_popup( &mut self, - state: &mut AppState, + state: &mut AppState, id: WindowId, popup: kas::PopupDescriptor, ) { @@ -374,7 +374,7 @@ impl> Window { self.ev_state.action(Id::ROOT, action); } - pub(super) fn send_close(&mut self, state: &mut AppState, id: WindowId) { + pub(super) fn send_close(&mut self, state: &mut AppState, id: WindowId) { if id == self.window_id { self.ev_state.action(Id::ROOT, Action::CLOSE); } else if let Some(window) = self.window.as_ref() { @@ -390,8 +390,8 @@ impl> Window { } // Internal functions -impl> Window { - fn reconfigure(&mut self, state: &AppState) { +impl> Window { + fn reconfigure(&mut self, state: &AppState) { let time = Instant::now(); let Some(ref mut window) = self.window else { return; @@ -407,7 +407,7 @@ impl> Window { log::trace!(target: "kas_perf::wgpu::window", "reconfigure: {}µs", time.elapsed().as_micros()); } - fn update(&mut self, state: &AppState) { + fn update(&mut self, state: &AppState) { let time = Instant::now(); let Some(ref mut window) = self.window else { return; @@ -420,7 +420,7 @@ impl> Window { log::trace!(target: "kas_perf::wgpu::window", "update: {}µs", time.elapsed().as_micros()); } - fn apply_size(&mut self, state: &AppState, first: bool) { + fn apply_size(&mut self, state: &AppState, first: bool) { let time = Instant::now(); let Some(ref mut window) = self.window else { return; @@ -459,7 +459,7 @@ impl> Window { /// /// Returns an error when drawing is aborted and further event handling may /// be needed before a redraw. - pub(super) fn do_draw(&mut self, state: &mut AppState) -> Result<(), ()> { + pub(super) fn do_draw(&mut self, state: &mut AppState) -> Result<(), ()> { let start = Instant::now(); let Some(ref mut window) = self.window else { return Ok(()); @@ -562,7 +562,7 @@ pub(crate) trait WindowDataErased { fn winit_window(&self) -> Option<&winit::window::Window>; } -impl> WindowDataErased for WindowData { +impl> WindowDataErased for WindowData { fn window_id(&self) -> WindowId { self.window_id } diff --git a/crates/kas-wgpu/src/lib.rs b/crates/kas-wgpu/src/lib.rs index c221cafb9..f802f68e7 100644 --- a/crates/kas-wgpu/src/lib.rs +++ b/crates/kas-wgpu/src/lib.rs @@ -47,7 +47,7 @@ impl AppGraphicsBuilder for WgpuBuilder { type Shared = DrawPipe; - type Surface = surface::Surface; + type Surface<'a> = surface::Surface<'a, CB::Pipe>; fn build(self) -> Result { let mut options = self.options; diff --git a/crates/kas-wgpu/src/surface.rs b/crates/kas-wgpu/src/surface.rs index 626882ed1..27adfa3e2 100644 --- a/crates/kas-wgpu/src/surface.rs +++ b/crates/kas-wgpu/src/surface.rs @@ -14,13 +14,13 @@ use kas::geom::Size; use std::time::Instant; /// Per-window data -pub struct Surface { - surface: wgpu::Surface<'static>, +pub struct Surface<'a, C: CustomPipe> { + surface: wgpu::Surface<'a>, sc_desc: wgpu::SurfaceConfiguration, draw: DrawWindow, } -impl WindowSurface for Surface { +impl<'a, C: CustomPipe> WindowSurface for Surface<'a, C> { type Shared = DrawPipe; fn new(shared: &mut Self::Shared, window: W) -> Result From 34eaf5533bbda8d4729679f942e260ddd6a83c2f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 23 Jan 2024 17:29:31 +0000 Subject: [PATCH 7/8] Safe surface lifetime via Arc --- crates/kas-core/src/app/common.rs | 23 +++++++++++++---------- crates/kas-core/src/app/mod.rs | 4 ++-- crates/kas-core/src/app/window.rs | 7 +++++-- crates/kas-wgpu/src/lib.rs | 12 ++++++++++++ crates/kas-wgpu/src/surface.rs | 24 ++++++++++++------------ 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/crates/kas-core/src/app/common.rs b/crates/kas-core/src/app/common.rs index f4303a486..a569bd5cb 100644 --- a/crates/kas-core/src/app/common.rs +++ b/crates/kas-core/src/app/common.rs @@ -9,7 +9,7 @@ use crate::draw::DrawSharedImpl; use crate::draw::{color::Rgba, DrawIface, WindowCommon}; use crate::geom::Size; use crate::theme::Theme; -use raw_window_handle as raw; +use raw_window_handle as rwh; use std::time::Instant; use thiserror::Error; @@ -22,7 +22,7 @@ use thiserror::Error; pub enum Error { /// Window-handle error #[error(transparent)] - Handle(#[from] raw::HandleError), + Handle(#[from] rwh::HandleError), /// Failure from the graphics sub-system #[error("error from graphics sub-system")] @@ -189,6 +189,17 @@ pub trait AppGraphicsBuilder { /// Construct shared state fn build(self) -> Result; + + /// Construct a window surface + /// + /// It is required to call [`WindowSurface::do_resize`] after this. + fn new_surface<'window, W>( + shared: &mut Self::Shared, + window: W, + ) -> Result> + where + W: rwh::HasWindowHandle + rwh::HasDisplayHandle + Send + Sync + 'window, + Self: Sized; } /// Window graphical surface requirements @@ -198,14 +209,6 @@ pub trait WindowSurface { /// Shared draw state type Shared: kas::draw::DrawSharedImpl; - /// Construct an instance from a window handle - /// - /// It is required to call [`WindowSurface::do_resize`] after this. - fn new(shared: &mut Self::Shared, window: W) -> Result - where - W: raw::HasWindowHandle + raw::HasDisplayHandle + Send + Sync, - Self: Sized; - /// Get current surface size fn size(&self) -> Size; diff --git a/crates/kas-core/src/app/mod.rs b/crates/kas-core/src/app/mod.rs index 70a2d1dce..134e86735 100644 --- a/crates/kas-core/src/app/mod.rs +++ b/crates/kas-core/src/app/mod.rs @@ -71,7 +71,7 @@ enum ProxyAction { #[cfg(test)] mod test { use super::*; - use raw_window_handle as raw; + use raw_window_handle as rwh; use std::time::Instant; struct Draw; @@ -250,7 +250,7 @@ mod test { fn new(_: &mut Self::Shared, _: W) -> Result where - W: raw::HasRawWindowHandle + raw::HasRawDisplayHandle, + W: rwh::HasRawWindowHandle + rwh::HasRawDisplayHandle, Self: Sized, { todo!() diff --git a/crates/kas-core/src/app/window.rs b/crates/kas-core/src/app/window.rs index b64ae1433..3cee764c1 100644 --- a/crates/kas-core/src/app/window.rs +++ b/crates/kas-core/src/app/window.rs @@ -17,6 +17,7 @@ use kas::theme::{DrawCx, SizeCx, ThemeSize}; use kas::theme::{Theme, Window as _}; use kas::{autoimpl, messages::MessageStack, Action, Id, Layout, LayoutExt, Widget, WindowId}; use std::mem::take; +use std::sync::Arc; use std::time::{Duration, Instant}; use winit::event::WindowEvent; use winit::event_loop::EventLoopWindowTarget; @@ -25,7 +26,7 @@ use winit::window::WindowBuilder; /// Window fields requiring a frame or surface #[crate::autoimpl(Deref, DerefMut using self.window)] struct WindowData> { - window: winit::window::Window, + window: Arc, #[cfg(all(wayland_platform, feature = "clipboard"))] wayland_clipboard: Option, surface: G::Surface<'static>, @@ -170,7 +171,9 @@ impl> Window { _ => None, }; - let mut surface = G::Surface::new(&mut state.shared.draw.draw, &window)?; + // NOTE: usage of Arc is inelegant, but avoids lots of unsafe code + let window = Arc::new(window); + let mut surface = G::new_surface(&mut state.shared.draw.draw, window.clone())?; if apply_size { surface.do_resize(&mut state.shared.draw.draw, size); } diff --git a/crates/kas-wgpu/src/lib.rs b/crates/kas-wgpu/src/lib.rs index f802f68e7..04725a9d3 100644 --- a/crates/kas-wgpu/src/lib.rs +++ b/crates/kas-wgpu/src/lib.rs @@ -29,6 +29,7 @@ mod surface; use crate::draw::{CustomPipeBuilder, DrawPipe}; use kas::app::{AppBuilder, AppGraphicsBuilder, Result}; use kas::theme::{FlatTheme, Theme}; +use wgpu::rwh; pub use draw_shaded::{DrawShaded, DrawShadedImpl}; pub use options::Options; @@ -56,6 +57,17 @@ impl AppGraphicsBuilder for WgpuBuilder { } DrawPipe::new(self.custom, &options) } + + fn new_surface<'window, W>( + shared: &mut Self::Shared, + window: W, + ) -> Result> + where + W: rwh::HasWindowHandle + rwh::HasDisplayHandle + Send + Sync + 'window, + Self: Sized, + { + surface::Surface::new(shared, window) + } } impl Default for WgpuBuilder<()> { diff --git a/crates/kas-wgpu/src/surface.rs b/crates/kas-wgpu/src/surface.rs index 27adfa3e2..aa27e8a62 100644 --- a/crates/kas-wgpu/src/surface.rs +++ b/crates/kas-wgpu/src/surface.rs @@ -6,7 +6,7 @@ //! WGPU window surface use crate::draw::{CustomPipe, DrawPipe, DrawWindow}; -use kas::app::{raw_window_handle as raw, Error, WindowSurface}; +use kas::app::{raw_window_handle as rwh, Error, WindowSurface}; use kas::cast::Cast; use kas::draw::color::Rgba; use kas::draw::{DrawIface, DrawSharedImpl, WindowCommon}; @@ -20,20 +20,16 @@ pub struct Surface<'a, C: CustomPipe> { draw: DrawWindow, } -impl<'a, C: CustomPipe> WindowSurface for Surface<'a, C> { - type Shared = DrawPipe; - - fn new(shared: &mut Self::Shared, window: W) -> Result +impl<'a, C: CustomPipe> Surface<'a, C> { + pub fn new(shared: &mut ::Shared, window: W) -> Result where - W: raw::HasWindowHandle + raw::HasDisplayHandle + Send + Sync, + W: rwh::HasWindowHandle + rwh::HasDisplayHandle + Send + Sync + 'a, Self: Sized, { - let surface = unsafe { - // TODO: handle window lifetime safely - let target = wgpu::SurfaceTargetUnsafe::from_window(&window)?; - shared.instance.create_surface_unsafe(target) - } - .map_err(|e| Error::Graphics(Box::new(e)))?; + let surface = 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, @@ -55,6 +51,10 @@ impl<'a, C: CustomPipe> WindowSurface for Surface<'a, C> { draw: shared.new_window(), }) } +} + +impl<'a, C: CustomPipe> WindowSurface for Surface<'a, C> { + type Shared = DrawPipe; fn size(&self) -> Size { Size::new(self.sc_desc.width.cast(), self.sc_desc.height.cast()) From a00c8f35603461b02ba90ce27efc3dc2ef527856 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 24 Jan 2024 09:12:13 +0000 Subject: [PATCH 8/8] Fix tests and examples --- crates/kas-core/src/app/mod.rs | 31 ++++++++++++++++++++++--------- examples/mandlebrot/mandlebrot.rs | 4 ++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/crates/kas-core/src/app/mod.rs b/crates/kas-core/src/app/mod.rs index 134e86735..393ab8fe4 100644 --- a/crates/kas-core/src/app/mod.rs +++ b/crates/kas-core/src/app/mod.rs @@ -248,14 +248,6 @@ mod test { impl WindowSurface for Surface { type Shared = DrawShared; - fn new(_: &mut Self::Shared, _: W) -> Result - where - W: rwh::HasRawWindowHandle + rwh::HasRawDisplayHandle, - Self: Sized, - { - todo!() - } - fn size(&self) -> crate::prelude::Size { todo!() } @@ -280,10 +272,31 @@ mod test { } } + struct AGB; + impl AppGraphicsBuilder for AGB { + type DefaultTheme = crate::theme::SimpleTheme; + + type Shared = DrawShared; + + type Surface<'a> = Surface; + + fn build(self) -> Result { + todo!() + } + + fn new_surface<'window, W>(_: &mut Self::Shared, _: W) -> Result> + where + W: rwh::HasWindowHandle + rwh::HasDisplayHandle + Send + Sync + 'window, + Self: Sized, + { + todo!() + } + } + #[test] fn size_of_pending() { assert_eq!( - std::mem::size_of::>(), + std::mem::size_of::>(), 32 ); } diff --git a/examples/mandlebrot/mandlebrot.rs b/examples/mandlebrot/mandlebrot.rs index ebb2b1456..e44e36bb9 100644 --- a/examples/mandlebrot/mandlebrot.rs +++ b/examples/mandlebrot/mandlebrot.rs @@ -101,8 +101,8 @@ impl CustomPipeBuilder for PipeBuilder { fn device_descriptor() -> wgpu::DeviceDescriptor<'static> { wgpu::DeviceDescriptor { label: None, - features: wgpu::Features::PUSH_CONSTANTS | SHADER_FLOAT64, - limits: wgpu::Limits { + required_features: wgpu::Features::PUSH_CONSTANTS | SHADER_FLOAT64, + required_limits: wgpu::Limits { max_push_constant_size: size_of::().cast(), ..Default::default() },