From d16ca5489963f1edc2d872110c8095e721d48fd1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 29 Apr 2024 10:43:28 +0100 Subject: [PATCH 1/7] Update resvg to 0.41 --- crates/kas-resvg/Cargo.toml | 4 ++-- crates/kas-resvg/src/svg.rs | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/kas-resvg/Cargo.toml b/crates/kas-resvg/Cargo.toml index da0116882..fda83edda 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.38.0", optional = true } -usvg = { version = "0.38.0", optional = true } +resvg = { version = "0.41.0", optional = true } +usvg = { version = "0.41.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 7995cadb4..bc32c6952 100644 --- a/crates/kas-resvg/src/svg.rs +++ b/crates/kas-resvg/src/svg.rs @@ -12,7 +12,7 @@ use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::Arc; use tiny_skia::{Pixmap, Transform}; -use usvg::{Tree, TreeParsing}; +use usvg::Tree; /// Load errors #[derive(thiserror::Error, Debug)] @@ -49,12 +49,8 @@ fn load(data: &[u8], resources_dir: Option<&Path>) -> Result image_href_resolver: Default::default(), }; - let mut tree = Tree::from_data(data, &opts)?; - - // Postprocess. TODO: consider using usvg::Tree::postprocess instead: - // do we want to convert text to paths? - tree.calculate_abs_transforms(); - tree.calculate_bounding_boxes(); + let fonts_db = kas::text::fonts::library().read_db(); + let tree = Tree::from_data(data, &opts, fonts_db.db())?; Ok(tree) } @@ -93,8 +89,8 @@ enum State { async fn draw(svg: Source, mut pixmap: Pixmap) -> Pixmap { if let Ok(tree) = svg.tree() { - let w = f32::conv(pixmap.width()) / tree.size.width(); - let h = f32::conv(pixmap.height()) / tree.size.height(); + 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); resvg::render(&tree, transform, &mut pixmap.as_mut()); } @@ -177,7 +173,7 @@ impl_scope! { fn load_source(&mut self, source: Source) -> Result { // Set scaling size. TODO: this is useless if Self::with_size is called after. - let size = source.tree()?.size; + let size = source.tree()?.size(); self.scaling.size = LogicalSize(size.width(), size.height()); self.inner = match std::mem::take(&mut self.inner) { From b7cc51e3eecb15ca70558fb99a40ee7e5f25a914 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 29 Apr 2024 11:01:34 +0100 Subject: [PATCH 2/7] Update image to 0.25.1 --- crates/kas-widgets/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kas-widgets/Cargo.toml b/crates/kas-widgets/Cargo.toml index 681eee81e..5df76f8ca 100644 --- a/crates/kas-widgets/Cargo.toml +++ b/crates/kas-widgets/Cargo.toml @@ -25,7 +25,7 @@ log = "0.4" smallvec = "1.6.1" unicode-segmentation = "1.7" thiserror = "1.0.23" -image = { version = "0.24.1", optional = true } +image = { version = "0.25.1", optional = true } kas-macros = { version = "0.14.1", path = "../kas-macros" } linear-map = "1.2.0" From 4d87266206f257e168d34380c40173eeb01a537d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 2 May 2024 09:45:16 +0100 Subject: [PATCH 3/7] Update winit to 0.30 Regression: always reports Platform::Wayland on Linux --- crates/kas-core/Cargo.toml | 2 +- crates/kas-core/src/app/app.rs | 8 +++---- crates/kas-core/src/app/event_loop.rs | 32 ++++++++++++--------------- crates/kas-core/src/app/shared.rs | 2 +- crates/kas-core/src/app/window.rs | 30 ++++++++++++------------- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index 44f8e41e6..7f19ac7d2 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -121,7 +121,7 @@ version = "0.5.0" # used in doc links [dependencies.winit] # Provides translations for several winit types -version = "0.29.2" +version = "0.30.0" optional = true default-features = false features = ["rwh_06"] diff --git a/crates/kas-core/src/app/app.rs b/crates/kas-core/src/app/app.rs index 6c04b92f0..60cf06a60 100644 --- a/crates/kas-core/src/app/app.rs +++ b/crates/kas-core/src/app/app.rs @@ -13,7 +13,7 @@ use crate::util::warn_about_error; use crate::{impl_scope, Window, WindowId}; use std::cell::{Ref, RefCell, RefMut}; use std::rc::Rc; -use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopProxy}; +use winit::event_loop::{EventLoop, EventLoopProxy}; pub struct Application> { el: EventLoop, @@ -83,7 +83,7 @@ impl_scope! { }); config.borrow_mut().init(); - let el = EventLoopBuilder::with_user_event().build()?; + let el = EventLoop::with_user_event().build()?; let mut draw_shared = self.graphical.build()?; draw_shared.set_raster_config(config.borrow().font.raster()); @@ -237,8 +237,8 @@ impl<'a> PlatformWrapper<'a> { { cfg_if::cfg_if! { if #[cfg(all(feature = "wayland", feature = "x11"))] { - use winit::platform::wayland::EventLoopWindowTargetExtWayland; - return if self.0.is_wayland() { + use winit::platform::wayland::ActiveEventLoopExtWayland; + return if true /*FIXME: self.0.is_wayland()*/ { Platform::Wayland } else { Platform::X11 diff --git a/crates/kas-core/src/app/event_loop.rs b/crates/kas-core/src/app/event_loop.rs index 16b03ac45..ffdc9dc97 100644 --- a/crates/kas-core/src/app/event_loop.rs +++ b/crates/kas-core/src/app/event_loop.rs @@ -12,7 +12,7 @@ use crate::{Action, WindowId}; use std::collections::HashMap; use std::time::Instant; use winit::event::{Event, StartCause}; -use winit::event_loop::{ControlFlow, EventLoopWindowTarget}; +use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::window as ww; /// Event-loop data structure (i.e. all run-time state) @@ -48,15 +48,11 @@ where } } - pub(super) fn handle( - &mut self, - event: Event, - elwt: &EventLoopWindowTarget, - ) { + pub(super) fn handle(&mut self, event: Event, el: &ActiveEventLoop) { match event { Event::NewEvents(cause) => { // MainEventsCleared will reset control_flow (but not when it is Poll) - elwt.set_control_flow(ControlFlow::Wait); + el.set_control_flow(ControlFlow::Wait); match cause { StartCause::ResumeTimeReached { @@ -93,12 +89,12 @@ where } Event::WindowEvent { window_id, event } => { - self.flush_pending(elwt); + self.flush_pending(el); if let Some(id) = self.id_map.get(&window_id) { if let Some(window) = self.windows.get_mut(id) { if window.handle_event(&mut self.state, event) { - elwt.set_control_flow(ControlFlow::Poll); + el.set_control_flow(ControlFlow::Poll); } } } @@ -138,7 +134,7 @@ where Event::Suspended => (), Event::Resumed if self.suspended => { for window in self.windows.values_mut() { - match window.resume(&mut self.state, elwt) { + match window.resume(&mut self.state, el) { Ok(winit_id) => { self.id_map.insert(winit_id, window.window_id); } @@ -152,16 +148,16 @@ where Event::Resumed => (), Event::AboutToWait => { - self.flush_pending(elwt); + self.flush_pending(el); self.resumes.sort_by_key(|item| item.0); if self.windows.is_empty() { - elwt.exit(); - } else if matches!(elwt.control_flow(), ControlFlow::Poll) { + el.exit(); + } else if matches!(el.control_flow(), ControlFlow::Poll) { } else if let Some((instant, _)) = self.resumes.first() { - elwt.set_control_flow(ControlFlow::WaitUntil(*instant)); + el.set_control_flow(ControlFlow::WaitUntil(*instant)); } else { - elwt.set_control_flow(ControlFlow::Wait); + el.set_control_flow(ControlFlow::Wait); }; } @@ -173,7 +169,7 @@ where } } - fn flush_pending(&mut self, elwt: &EventLoopWindowTarget) { + fn flush_pending(&mut self, el: &ActiveEventLoop) { while let Some(pending) = self.state.shared.pending.pop_front() { match pending { Pending::AddPopup(parent_id, id, popup) => { @@ -188,7 +184,7 @@ where Pending::AddWindow(id, mut window) => { log::debug!("Pending: adding window {}", window.widget.title()); if !self.suspended { - match window.resume(&mut self.state, elwt) { + match window.resume(&mut self.state, el) { Ok(winit_id) => { self.id_map.insert(winit_id, id); } @@ -212,7 +208,7 @@ where if action.contains(Action::CLOSE | Action::EXIT) { self.windows.clear(); self.id_map.clear(); - elwt.set_control_flow(ControlFlow::Poll); + el.set_control_flow(ControlFlow::Poll); } else { for (_, window) in self.windows.iter_mut() { window.handle_action(&mut self.state, action); diff --git a/crates/kas-core/src/app/shared.rs b/crates/kas-core/src/app/shared.rs index 8ecff4aa0..80be36ec9 100644 --- a/crates/kas-core/src/app/shared.rs +++ b/crates/kas-core/src/app/shared.rs @@ -217,7 +217,7 @@ impl> AppShared // By far the simplest way to implement this is to let our call // anscestor, event::Loop::handle, do the work. // - // In theory we could pass the EventLoopWindowTarget for *each* event + // In theory we could pass the `ActiveEventLoop` for *each* event // handled to create the winit window here or use statics to generate // errors now, but user code can't do much with this error anyway. 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 cfb564d5a..13ef64227 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, AppGraphicsBuilder, ProxyAction}; +use super::{AppData, AppGraphicsBuilder}; use crate::cast::{Cast, Conv}; use crate::config::WindowConfig; use crate::draw::{color::Rgba, AnimationState, DrawSharedImpl}; @@ -20,8 +20,8 @@ use std::mem::take; use std::sync::Arc; use std::time::{Duration, Instant}; use winit::event::WindowEvent; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::WindowBuilder; +use winit::event_loop::ActiveEventLoop; +use winit::window::WindowAttributes; /// Window fields requiring a frame or surface #[crate::autoimpl(Deref, DerefMut using self.window)] @@ -73,7 +73,7 @@ impl> Window { pub(super) fn resume( &mut self, state: &mut AppState, - elwt: &EventLoopWindowTarget, + el: &ActiveEventLoop, ) -> super::Result { let time = Instant::now(); @@ -104,22 +104,22 @@ impl> Window { .clamp(min_size, max_size) .as_logical(); - let mut builder = WindowBuilder::new().with_inner_size(ideal); + let mut attrs = WindowAttributes::default(); + attrs.inner_size = Some(ideal); + attrs.title = self.widget.title().to_string(); + attrs.visible = false; + attrs.transparent = self.widget.transparent(); + attrs.decorations = self.widget.decorations() == kas::Decorations::Server; + attrs.window_icon = self.widget.icon(); let (restrict_min, restrict_max) = self.widget.restrictions(); if restrict_min { let min = solve_cache.min(true).as_logical(); - builder = builder.with_min_inner_size(min); + attrs.min_inner_size = Some(min); } if restrict_max { - builder = builder.with_max_inner_size(ideal); + attrs.max_inner_size = Some(ideal); } - let window = builder - .with_title(self.widget.title()) - .with_window_icon(self.widget.icon()) - .with_decorations(self.widget.decorations() == kas::Decorations::Server) - .with_transparent(self.widget.transparent()) - .with_visible(false) - .build(elwt)?; + let window = el.create_window(attrs)?; // Now that we have a scale factor, we may need to resize: let scale_factor = window.scale_factor(); @@ -584,7 +584,7 @@ impl> WindowDataErased for WindowData #[inline] fn set_cursor_icon(&self, icon: CursorIcon) { - self.window.set_cursor_icon(icon); + self.window.set_cursor(icon); } #[cfg(winit)] From 19cc240480d7f999a175ab72d432c3d98922d3c8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 2 May 2024 10:03:54 +0100 Subject: [PATCH 4/7] winit: use new ApplicationHandler to run --- crates/kas-core/src/app/app.rs | 4 +- crates/kas-core/src/app/event_loop.rs | 232 ++++++++++++++------------ crates/kas-core/src/app/mod.rs | 2 +- 3 files changed, 124 insertions(+), 114 deletions(-) diff --git a/crates/kas-core/src/app/app.rs b/crates/kas-core/src/app/app.rs index 60cf06a60..05c333fd5 100644 --- a/crates/kas-core/src/app/app.rs +++ b/crates/kas-core/src/app/app.rs @@ -211,8 +211,8 @@ where /// Run the main loop. #[inline] pub fn run(self) -> Result<()> { - let mut el = super::EventLoop::new(self.windows, self.state); - self.el.run(move |event, elwt| el.handle(event, elwt))?; + let mut l = super::Loop::new(self.windows, self.state); + self.el.run_app(&mut l)?; Ok(()) } } diff --git a/crates/kas-core/src/app/event_loop.rs b/crates/kas-core/src/app/event_loop.rs index ffdc9dc97..48d826300 100644 --- a/crates/kas-core/src/app/event_loop.rs +++ b/crates/kas-core/src/app/event_loop.rs @@ -11,7 +11,8 @@ use crate::theme::Theme; use crate::{Action, WindowId}; use std::collections::HashMap; use std::time::Instant; -use winit::event::{Event, StartCause}; +use winit::application::ApplicationHandler; +use winit::event::StartCause; use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::window as ww; @@ -33,139 +34,148 @@ where resumes: Vec<(Instant, WindowId)>, } -impl> Loop +impl ApplicationHandler for Loop where + G: AppGraphicsBuilder, + T: Theme, T::Window: kas::theme::Window, { - pub(super) fn new(mut windows: Vec>>, state: AppState) -> Self { - Loop { - suspended: true, - windows: windows.drain(..).map(|w| (w.window_id, w)).collect(), - popups: Default::default(), - id_map: Default::default(), - state, - resumes: vec![], + fn new_events(&mut self, el: &ActiveEventLoop, cause: StartCause) { + // MainEventsCleared will reset control_flow (but not when it is Poll) + el.set_control_flow(ControlFlow::Wait); + + match cause { + StartCause::ResumeTimeReached { + requested_resume, .. + } => { + let item = self + .resumes + .first() + .cloned() + .unwrap_or_else(|| panic!("timer wakeup without resume")); + assert_eq!(item.0, requested_resume); + log::trace!("Wakeup: timer (window={:?})", item.1); + + let resume = if let Some(w) = self.windows.get_mut(&item.1) { + w.update_timer(&mut self.state) + } else { + // presumably, some window with active timers was removed + None + }; + + if let Some(instant) = resume { + self.resumes[0].0 = instant; + } else { + self.resumes.remove(0); + } + } + StartCause::WaitCancelled { .. } => { + // This event serves no purpose? + // log::debug!("Wakeup: WaitCancelled (ignoring)"); + } + StartCause::Poll => (), + StartCause::Init => (), } } - pub(super) fn handle(&mut self, event: Event, el: &ActiveEventLoop) { + fn user_event(&mut self, _: &ActiveEventLoop, event: ProxyAction) { match event { - Event::NewEvents(cause) => { - // MainEventsCleared will reset control_flow (but not when it is Poll) - el.set_control_flow(ControlFlow::Wait); - - match cause { - StartCause::ResumeTimeReached { - requested_resume, .. - } => { - let item = self - .resumes - .first() - .cloned() - .unwrap_or_else(|| panic!("timer wakeup without resume")); - assert_eq!(item.0, requested_resume); - log::trace!("Wakeup: timer (window={:?})", item.1); - - let resume = if let Some(w) = self.windows.get_mut(&item.1) { - w.update_timer(&mut self.state) - } else { - // presumably, some window with active timers was removed - None - }; - - if let Some(instant) = resume { - self.resumes[0].0 = instant; - } else { - self.resumes.remove(0); - } - } - StartCause::WaitCancelled { .. } => { - // This event serves no purpose? - // log::debug!("Wakeup: WaitCancelled (ignoring)"); - } - StartCause::Poll => (), - StartCause::Init => (), + ProxyAction::Close(id) => { + if let Some(window) = self.windows.get_mut(&id) { + window.send_action(Action::CLOSE); } } - - Event::WindowEvent { window_id, event } => { - self.flush_pending(el); - - if let Some(id) = self.id_map.get(&window_id) { - if let Some(window) = self.windows.get_mut(id) { - if window.handle_event(&mut self.state, event) { - el.set_control_flow(ControlFlow::Poll); - } - } + ProxyAction::CloseAll => { + for window in self.windows.values_mut() { + window.send_action(Action::CLOSE); } } - Event::DeviceEvent { .. } => { - // windows handle local input; we do not handle global input + ProxyAction::Message(msg) => { + let mut stack = crate::messages::MessageStack::new(); + stack.push_erased(msg.into_erased()); + self.state.handle_messages(&mut stack); } - Event::UserEvent(action) => match action { - ProxyAction::Close(id) => { - if let Some(window) = self.windows.get_mut(&id) { - window.send_action(Action::CLOSE); + ProxyAction::WakeAsync => { + // We don't need to do anything: MainEventsCleared will + // automatically be called after, which automatically calls + // window.update(..), which calls EventState::Update. + } + } + } + + fn resumed(&mut self, el: &ActiveEventLoop) { + if self.suspended { + for window in self.windows.values_mut() { + match window.resume(&mut self.state, el) { + Ok(winit_id) => { + self.id_map.insert(winit_id, window.window_id); } - } - ProxyAction::CloseAll => { - for window in self.windows.values_mut() { - window.send_action(Action::CLOSE); + Err(e) => { + log::error!("Unable to create window: {}", e); } } - ProxyAction::Message(msg) => { - let mut stack = crate::messages::MessageStack::new(); - stack.push_erased(msg.into_erased()); - self.state.handle_messages(&mut stack); - } - ProxyAction::WakeAsync => { - // We don't need to do anything: MainEventsCleared will - // automatically be called after, which automatically calls - // window.update(..), which calls EventState::Update. - } - }, - - Event::Suspended if !self.suspended => { - for window in self.windows.values_mut() { - window.suspend(); - } - self.suspended = true; } - Event::Suspended => (), - Event::Resumed if self.suspended => { - for window in self.windows.values_mut() { - match window.resume(&mut self.state, el) { - Ok(winit_id) => { - self.id_map.insert(winit_id, window.window_id); - } - Err(e) => { - log::error!("Unable to create window: {}", e); - } - } + self.suspended = false; + } + } + + fn window_event( + &mut self, + el: &ActiveEventLoop, + window_id: ww::WindowId, + event: winit::event::WindowEvent, + ) { + self.flush_pending(el); + + if let Some(id) = self.id_map.get(&window_id) { + if let Some(window) = self.windows.get_mut(id) { + if window.handle_event(&mut self.state, event) { + el.set_control_flow(ControlFlow::Poll); } - self.suspended = false; } - Event::Resumed => (), + } + } - Event::AboutToWait => { - self.flush_pending(el); - self.resumes.sort_by_key(|item| item.0); + fn about_to_wait(&mut self, el: &ActiveEventLoop) { + self.flush_pending(el); + self.resumes.sort_by_key(|item| item.0); - if self.windows.is_empty() { - el.exit(); - } else if matches!(el.control_flow(), ControlFlow::Poll) { - } else if let Some((instant, _)) = self.resumes.first() { - el.set_control_flow(ControlFlow::WaitUntil(*instant)); - } else { - el.set_control_flow(ControlFlow::Wait); - }; - } + if self.windows.is_empty() { + el.exit(); + } else if matches!(el.control_flow(), ControlFlow::Poll) { + } else if let Some((instant, _)) = self.resumes.first() { + el.set_control_flow(ControlFlow::WaitUntil(*instant)); + } else { + el.set_control_flow(ControlFlow::Wait); + }; + } - Event::LoopExiting => { - self.state.on_exit(); + fn suspended(&mut self, _: &ActiveEventLoop) { + if !self.suspended { + for window in self.windows.values_mut() { + window.suspend(); } + self.suspended = true; + } + } + + fn exiting(&mut self, _: &ActiveEventLoop) { + self.state.on_exit(); + } +} - Event::MemoryWarning => (), // TODO ? +impl> Loop +where + T::Window: kas::theme::Window, +{ + pub(super) fn new(mut windows: Vec>>, state: AppState) -> Self { + Loop { + suspended: true, + windows: windows.drain(..).map(|w| (w.window_id, w)).collect(), + popups: Default::default(), + id_map: Default::default(), + state, + resumes: vec![], } } diff --git a/crates/kas-core/src/app/mod.rs b/crates/kas-core/src/app/mod.rs index e4722d574..18e11e2b3 100644 --- a/crates/kas-core/src/app/mod.rs +++ b/crates/kas-core/src/app/mod.rs @@ -14,7 +14,7 @@ mod common; use crate::messages::MessageStack; #[cfg(winit)] use crate::WindowId; #[cfg(winit)] use app::PlatformWrapper; -#[cfg(winit)] use event_loop::Loop as EventLoop; +#[cfg(winit)] use event_loop::Loop; #[cfg(winit)] pub(crate) use shared::{AppShared, AppState}; #[cfg(winit)] pub(crate) use window::{Window, WindowDataErased}; From 044541a5f547a31960d7c9f11026c421ca7cb548 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 2 May 2024 10:10:17 +0100 Subject: [PATCH 5/7] Update wgpu to 0.20 --- crates/kas-wgpu/Cargo.toml | 2 +- crates/kas-wgpu/src/draw/flat_round.rs | 2 ++ crates/kas-wgpu/src/draw/images.rs | 2 ++ crates/kas-wgpu/src/draw/round_2col.rs | 2 ++ crates/kas-wgpu/src/draw/shaded_round.rs | 2 ++ crates/kas-wgpu/src/draw/shaded_square.rs | 2 ++ crates/kas-wgpu/src/draw/text_pipe.rs | 2 ++ examples/mandlebrot/mandlebrot.rs | 2 ++ 8 files changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/kas-wgpu/Cargo.toml b/crates/kas-wgpu/Cargo.toml index f720ed343..1570d17dc 100644 --- a/crates/kas-wgpu/Cargo.toml +++ b/crates/kas-wgpu/Cargo.toml @@ -47,7 +47,7 @@ path = "../kas-core" version = "0.6.0" [dependencies.wgpu] -version = "0.19.1" +version = "0.20.0" default-features = false features = ["spirv"] diff --git a/crates/kas-wgpu/src/draw/flat_round.rs b/crates/kas-wgpu/src/draw/flat_round.rs index d2af8c6af..16f9c511d 100644 --- a/crates/kas-wgpu/src/draw/flat_round.rs +++ b/crates/kas-wgpu/src/draw/flat_round.rs @@ -68,6 +68,7 @@ impl Pipeline { vertex: wgpu::VertexState { module: &shaders.vert_flat_round, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -94,6 +95,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shaders.frag_flat_round, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/kas-wgpu/src/draw/images.rs b/crates/kas-wgpu/src/draw/images.rs index e38b803d6..7364cd627 100644 --- a/crates/kas-wgpu/src/draw/images.rs +++ b/crates/kas-wgpu/src/draw/images.rs @@ -95,6 +95,7 @@ impl Images { wgpu::VertexState { module: &shaders.vert_image, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, @@ -109,6 +110,7 @@ impl Images { wgpu::FragmentState { module: &shaders.frag_image, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/kas-wgpu/src/draw/round_2col.rs b/crates/kas-wgpu/src/draw/round_2col.rs index faa3cae18..f9d5a38ea 100644 --- a/crates/kas-wgpu/src/draw/round_2col.rs +++ b/crates/kas-wgpu/src/draw/round_2col.rs @@ -57,6 +57,7 @@ impl Pipeline { vertex: wgpu::VertexState { module: &shaders.vert_round_2col, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -82,6 +83,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shaders.frag_round_2col, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/kas-wgpu/src/draw/shaded_round.rs b/crates/kas-wgpu/src/draw/shaded_round.rs index 8792bc3c2..b76776b1b 100644 --- a/crates/kas-wgpu/src/draw/shaded_round.rs +++ b/crates/kas-wgpu/src/draw/shaded_round.rs @@ -63,6 +63,7 @@ impl Pipeline { vertex: wgpu::VertexState { module: &shaders.vert_shaded_round, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -89,6 +90,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shaders.frag_shaded_round, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/kas-wgpu/src/draw/shaded_square.rs b/crates/kas-wgpu/src/draw/shaded_square.rs index 3e6019b6f..a9bac5c33 100644 --- a/crates/kas-wgpu/src/draw/shaded_square.rs +++ b/crates/kas-wgpu/src/draw/shaded_square.rs @@ -50,6 +50,7 @@ impl Pipeline { vertex: wgpu::VertexState { module: &shaders.vert_shaded_square, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -74,6 +75,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shaders.frag_shaded_square, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/kas-wgpu/src/draw/text_pipe.rs b/crates/kas-wgpu/src/draw/text_pipe.rs index ad8ada6e7..a31efcdb4 100644 --- a/crates/kas-wgpu/src/draw/text_pipe.rs +++ b/crates/kas-wgpu/src/draw/text_pipe.rs @@ -96,6 +96,7 @@ impl Pipeline { wgpu::VertexState { module: &shaders.vert_glyph, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, @@ -111,6 +112,7 @@ impl Pipeline { wgpu::FragmentState { module: &shaders.frag_glyph, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: super::RENDER_TEX_FORMAT, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/examples/mandlebrot/mandlebrot.rs b/examples/mandlebrot/mandlebrot.rs index 457f50f44..1cc49af83 100644 --- a/examples/mandlebrot/mandlebrot.rs +++ b/examples/mandlebrot/mandlebrot.rs @@ -132,6 +132,7 @@ impl CustomPipeBuilder for PipeBuilder { vertex: wgpu::VertexState { module: &shaders.vertex, entry_point: "main", + compilation_options: Default::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -152,6 +153,7 @@ impl CustomPipeBuilder for PipeBuilder { fragment: Some(wgpu::FragmentState { module: &shaders.fragment, entry_point: "main", + compilation_options: Default::default(), targets: &[Some(wgpu::ColorTargetState { format: tex_format, blend: None, From 689080f0b15f9120a8ddd595437028b00553ec44 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 20 May 2024 08:42:01 +0100 Subject: [PATCH 6/7] WGPU: change the default choice of backend to PRIMARY --- crates/kas-wgpu/src/options.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/kas-wgpu/src/options.rs b/crates/kas-wgpu/src/options.rs index f8135af32..abd482b28 100644 --- a/crates/kas-wgpu/src/options.rs +++ b/crates/kas-wgpu/src/options.rs @@ -58,6 +58,9 @@ impl Options { /// - `SECONDARY`: any of GL or DX11 /// - `FALLBACK`: force use of fallback (CPU) rendering /// + /// The default backend is `PRIMARY`. Note that secondary backends are less + /// well supported by WGPU, possibly leading to other issues. + /// /// WGPU has an [API tracing] feature for debugging. To use this, ensure the /// `wgpu/trace` feature is enabled and set the output path: /// ```sh @@ -115,7 +118,7 @@ impl Options { pub(crate) fn backend(&self) -> Backends { if self.backends.is_empty() { - Backends::all() + Backends::PRIMARY } else { self.backends } From 18273208d1ba2276c78dc317cc6ef85190e1edf7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 20 May 2024 08:30:22 +0100 Subject: [PATCH 7/7] Revise feature configurations, test targets; remove Harfbuzz --- .github/workflows/test.yml | 12 ++++++------ Cargo.toml | 13 ++++++------- crates/kas-core/Cargo.toml | 13 +++++++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e72fa02b..d5988be95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,15 +28,15 @@ jobs: - name: Build docs run: | cargo doc --all --no-deps - cargo doc --all-features --all --no-deps + cargo doc --features nightly --all --no-deps - name: Test kas-macros run: | cargo test --manifest-path crates/kas-macros/Cargo.toml cargo test --manifest-path crates/kas-macros/Cargo.toml --all-features - name: Test kas-core run: | - cargo test --manifest-path crates/kas-core/Cargo.toml --features winit,x11 - cargo test --manifest-path crates/kas-core/Cargo.toml --all-features + cargo test --manifest-path crates/kas-core/Cargo.toml --features minimal + cargo test --manifest-path crates/kas-core/Cargo.toml --features nightly - name: Test kas-widgets run: | cargo test --manifest-path crates/kas-widgets/Cargo.toml --features kas/winit,kas/wayland @@ -55,9 +55,9 @@ jobs: cargo test --manifest-path crates/kas-dylib/Cargo.toml --features kas-core/winit,kas-core/wayland cargo test --manifest-path crates/kas-dylib/Cargo.toml --all-features --features kas-core/winit,kas-core/x11 - name: Test kas - run: | - cargo test - cargo test --all-features + run: cargo test --features nightly + - name: Test kas (experimental) + run: cargo test --features nightly,experimental - name: Test examples/mandlebrot run: cargo test --manifest-path examples/mandlebrot/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 0de3592d2..5db0748b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,12 +29,13 @@ 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", "x11", "view", "image", "resvg", "clipboard", "markdown", "shaping", "spawn"] +default = ["minimal", "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"] -# Enables "recommended" unstable features -nightly = ["min_spec"] +stable = ["default", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"] +# Enables all "recommended" features for nightly rustc +nightly = ["stable", "min_spec"] +# Additional, less recommendation-worthy features +experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"] # Enable dynamic linking (faster linking via an extra run-time dependency): dynamic = ["dep:kas-dylib"] @@ -66,8 +67,6 @@ markdown = ["kas-core/markdown"] # Enable text shaping shaping = ["kas-core/shaping"] -# Alternative: use Harfbuzz library for shaping -harfbuzz = ["kas-core/harfbuzz"] # Enable serde support (mainly config read/write) serde = ["kas-core/serde"] diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index 7f19ac7d2..b9477285a 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -17,8 +17,15 @@ features = ["stable"] rustdoc-args = ["--cfg", "doc_cfg"] [features] -# All features usable on stable rust -stable = ["winit", "x11", "wayland", "markdown", "yaml", "json", "ron", "shaping", "clipboard", "spawn", "dark-light", "serde"] +# The minimal feature set needed to build basic applications (with assumptions +# about target platforms). +minimal = ["winit", "wayland"] +# All standard test target features +stable = ["minimal", "clipboard", "markdown", "shaping", "spawn", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"] +# Enables all "recommended" features for nightly rustc +nightly = ["stable"] +# Additional, less recommendation-worthy features +experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"] # Use full specialization spec = [] @@ -34,8 +41,6 @@ markdown = ["kas-text/markdown"] # Enable text shaping shaping = ["kas-text/shaping"] -# Alternative: use Harfbuzz library for shaping -harfbuzz = ["kas-text/harfbuzz"] # Enable support for YAML (de)serialisation yaml = ["serde", "dep:serde_yaml"]