From 0e9d34dd9d24e1632b1a5214d54a65a9bfc181bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Mon, 11 Aug 2025 17:03:24 +0200 Subject: [PATCH] Upgrade glycin Tests the beta for GNOME 49 with the loaders in the Flatpak runtime. --- Cargo.lock | 142 ++++++++++++++++-- Cargo.toml | 2 +- build-aux/org.gnome.Fractal.Devel.json | 21 --- .../media/animated_image_paintable.rs | 20 +-- src/utils/media/image/mod.rs | 18 ++- src/utils/media/image/queue.rs | 74 ++------- 6 files changed, 168 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c36614aa..76b5ad7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -259,6 +270,12 @@ dependencies = [ "syn", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.88" @@ -453,6 +470,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bloomfilter" version = "1.0.16" @@ -695,12 +725,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1684,14 +1739,16 @@ dependencies = [ [[package]] name = "glycin" -version = "2.1.1" +version = "3.0.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37cb9e103cb6b8925bf5e8a1cf8a1166797d8aaefaabd03e68cf6ad7443a1baa" +checksum = "0ca9a2584f5315609d3284b8885ebd7f8475b991b2ae77b7fdfb22b723fe3321" dependencies = [ "futures-channel", "futures-util", "gdk4", "gio", + "glib", + "glycin-common", "glycin-utils", "gufo-common", "gufo-exif", @@ -1711,25 +1768,51 @@ dependencies = [ "zbus", ] +[[package]] +name = "glycin-common" +version = "1.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3344437ebbb38f373d52a2636cefdae31689d32042bbed402438d29abe04d9e9" +dependencies = [ + "bitflags 2.9.1", + "gufo-common", + "half", + "memmap2", + "nix 0.29.0", + "paste", + "rmp-serde", + "serde", + "thiserror 2.0.12", + "zerocopy", + "zvariant", +] + [[package]] name = "glycin-utils" -version = "3.0.1" +version = "4.0.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0816d1db00696479cda3cd6c914fb07115982b019dac96555d203c0d5b6d37a" +checksum = "5bb0e781372a461edbf6efddf5fb5ea4b700581773ca6a962cbf9666c8765a46" dependencies = [ + "async-lock", + "bitflags 2.9.1", + "blocking", "env_logger", + "futures-util", + "glycin-common", "gufo-common", + "half", "libc", "libseccomp", "log", "memmap2", "nix 0.29.0", "paste", - "rmp-serde", + "rayon", "serde", "thiserror 2.0.12", "tokio", "zbus", + "zerocopy", ] [[package]] @@ -2073,9 +2156,9 @@ dependencies = [ [[package]] name = "gufo-common" -version = "0.2.0" +version = "1.0.0-beta" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb2b4c040e67e6d6ad954f04693a11864df82bce6c6c468524b6a78bb75581f" +checksum = "6b369fe87840c7f9769e475e65500420072bf040c09b12d00fda83567190ea0f" dependencies = [ "paste", "serde", @@ -2084,9 +2167,9 @@ dependencies = [ [[package]] name = "gufo-exif" -version = "0.2.2" +version = "0.3.0-beta" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1448d72e2458b35dd10c0f148ea913b38c61b649552b22a97fa836ec376189ff" +checksum = "42ed15d1144c2ea214eaf8cb8a83168c9a4dccdbc3b18b889e5610f8cf33128d" dependencies = [ "gufo-common", "thiserror 2.0.12", @@ -2112,6 +2195,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -3866,6 +3959,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4108,6 +4212,26 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "readlock" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index 00c609e3..0f0fa0b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ zeroize = "1" # gtk-rs project and dependents. These usually need to be updated together. adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] } -glycin = { version = "2", default-features = false, features = ["tokio", "gdk4"] } +glycin = { version = "3.0.0-beta.1", default-features = false, features = ["tokio", "gdk4"] } gst = { version = "0.23", package = "gstreamer" } gst_app = { version = "0.23", package = "gstreamer-app" } gst_pbutils = { version = "0.23", package = "gstreamer-pbutils" } diff --git a/build-aux/org.gnome.Fractal.Devel.json b/build-aux/org.gnome.Fractal.Devel.json index c389cea6..b83ee301 100644 --- a/build-aux/org.gnome.Fractal.Devel.json +++ b/build-aux/org.gnome.Fractal.Devel.json @@ -95,27 +95,6 @@ } ] }, - { - "name": "glycin-loaders", - "buildsystem": "meson", - "config-opts": [ - "-Dtests=false", - "-Dlibglycin=false", - "-Dintrospection=false", - "-Dvapi=false", - "-Dcapi_docs=false", - "-Dpython_tests=false" - ], - "sources": [ - { - "type": "git", - "url": "https://gitlab.gnome.org/sophie-h/glycin.git", - "tag": "1.2.2", - "commit": "c7d362287303944721cf583d4d9e9f7721bfa407", - "disable-submodules": true - } - ] - }, { "name": "fractal", "buildsystem": "meson", diff --git a/src/components/media/animated_image_paintable.rs b/src/components/media/animated_image_paintable.rs index ae746e76..75fcabcf 100644 --- a/src/components/media/animated_image_paintable.rs +++ b/src/components/media/animated_image_paintable.rs @@ -6,7 +6,7 @@ use tracing::error; use crate::{ spawn, spawn_tokio, - utils::{CountedRef, File}, + utils::{CountedRef, File, TokioDrop}, }; mod imp { @@ -20,7 +20,7 @@ mod imp { #[derive(Default)] pub struct AnimatedImagePaintable { /// The image loader. - image_loader: OnceCell>>, + image_loader: OnceCell>>, /// The file of the image. file: OnceCell, /// The current frame that is displayed. @@ -49,7 +49,7 @@ mod imp { self.current_frame .borrow() .as_ref() - .map_or_else(|| self.image_loader().info().height, |f| f.height()) + .map_or_else(|| self.image_loader().details().height(), |f| f.height()) .try_into() .unwrap_or(i32::MAX) } @@ -58,7 +58,7 @@ mod imp { self.current_frame .borrow() .as_ref() - .map_or_else(|| self.image_loader().info().width, |f| f.width()) + .map_or_else(|| self.image_loader().details().width(), |f| f.width()) .try_into() .unwrap_or(i32::MAX) } @@ -95,23 +95,23 @@ mod imp { impl AnimatedImagePaintable { /// The image loader. - fn image_loader(&self) -> &Arc> { + fn image_loader(&self) -> &Arc> { self.image_loader .get() - .expect("image loader is initialized") + .expect("image loader should be initialized") } /// Initialize the image. pub(super) fn init( &self, file: File, - image_loader: Arc>, + image_loader: Arc>, first_frame: Arc, ) { - self.file.set(file).expect("file is uninitialized"); + self.file.set(file).expect("file should be uninitialized"); self.image_loader .set(image_loader) - .expect("image loader is uninitialized"); + .expect("image loader should be uninitialized"); self.current_frame.replace(Some(first_frame)); self.update_animation(); @@ -233,7 +233,7 @@ impl AnimatedImagePaintable { /// frame. pub(crate) fn new( file: File, - image_loader: Arc>, + image_loader: Arc>, first_frame: Arc, ) -> Self { let obj = glib::Object::new::(); diff --git a/src/utils/media/image/mod.rs b/src/utils/media/image/mod.rs index 866bda3f..fee8b4d8 100644 --- a/src/utils/media/image/mod.rs +++ b/src/utils/media/image/mod.rs @@ -28,7 +28,10 @@ pub(crate) use queue::{IMAGE_QUEUE, ImageRequestPriority}; use super::{FrameDimensions, MediaFileError}; use crate::{ - DISABLE_GLYCIN_SANDBOX, RUNTIME, components::AnimatedImagePaintable, spawn_tokio, utils::File, + DISABLE_GLYCIN_SANDBOX, RUNTIME, + components::AnimatedImagePaintable, + spawn_tokio, + utils::{File, TokioDrop}, }; /// The maximum dimensions of a thumbnail in the timeline. @@ -65,7 +68,7 @@ const THUMBNAIL_DIMENSIONS_THRESHOLD: u32 = 200; const SUPPORTED_ANIMATED_IMAGE_MIME_TYPES: &[&str] = &["image/gif", "image/png", "image/webp"]; /// Get an image loader for the given file. -async fn image_loader(file: gio::File) -> Result, glycin::ErrorCtx> { +async fn image_loader(file: gio::File) -> Result { let mut loader = glycin::Loader::new(file); if DISABLE_GLYCIN_SANDBOX { @@ -88,11 +91,11 @@ async fn load_image( let image_loader = image_loader(file.as_gfile()).await?; let frame_request = request_dimensions.map(|request| { - let image_info = image_loader.info(); + let image_details = image_loader.details(); let original_dimensions = FrameDimensions { - width: image_info.width, - height: image_info.height, + width: image_details.width(), + height: image_details.height(), }; original_dimensions.to_image_loader_request(request) @@ -104,9 +107,10 @@ async fn load_image( } else { image_loader.next_frame().await? }; + Ok(Image { file, - loader: image_loader.into(), + loader: TokioDrop::new(image_loader).into(), first_frame: first_frame.into(), }) }) @@ -120,7 +124,7 @@ pub(crate) struct Image { /// The file of the image. file: File, /// The image loader. - loader: Arc>, + loader: Arc>, /// The first frame of the image. first_frame: Arc, } diff --git a/src/utils/media/image/queue.rs b/src/utils/media/image/queue.rs index 5d8dc3b3..ca01526c 100644 --- a/src/utils/media/image/queue.rs +++ b/src/utils/media/image/queue.rs @@ -491,14 +491,13 @@ impl IntoFuture for DownloadRequestData { Box::pin(async move { let media = client.media(); - let data = match media.get_media_content(&settings, true).await { - Ok(data) => data, - Err(error) => { - return Err(MediaFileError::from(error)); - } - }; + let data = media + .get_media_content(&settings, true) + .await + .map_err(MediaFileError::from)?; let file = save_data_to_tmp_file(data).await?; + Ok(file) }) } @@ -516,7 +515,7 @@ struct FileRequestData { impl FileRequestData { /// The ID of the image request with this data. fn request_id(&self) -> ImageRequestId { - ImageRequestId::File(self.file.path().expect("file has a path")) + ImageRequestId::File(self.file.path().expect("file should have a path")) } } @@ -535,13 +534,7 @@ impl IntoFuture for FileRequestData { #[derive(Clone)] enum ImageRequestData { /// The data for a download request. - Download { - /// The data to download the image. - download_data: DownloadRequestData, - /// The data to load the image into a paintable, after it was - /// downloaded. - file_data: Option, - }, + Download(DownloadRequestData), /// The data for a file request. File(FileRequestData), } @@ -550,27 +543,10 @@ impl ImageRequestData { /// The ID of the image request with this data. fn request_id(&self) -> ImageRequestId { match self { - ImageRequestData::Download { download_data, .. } => download_data.request_id(), + ImageRequestData::Download(download_data) => download_data.request_id(), ImageRequestData::File(file_data) => file_data.request_id(), } } - - /// The data for the next request with this image request data. - fn into_next_request_data(self) -> DownloadOrFileRequestData { - match self { - Self::Download { - download_data, - file_data, - } => { - if let Some(file_data) = file_data { - file_data.into() - } else { - download_data.into() - } - } - Self::File(file_data) => file_data.into(), - } - } } impl IntoFuture for ImageRequestData { @@ -579,8 +555,8 @@ impl IntoFuture for ImageRequestData { fn into_future(self) -> Self::IntoFuture { Box::pin(async move { - let file_data = match self.into_next_request_data() { - DownloadOrFileRequestData::Download(download_data) => { + let file_data = match self { + Self::Download(download_data) => { let dimensions = download_data.dimensions; // Download the image to a file. @@ -592,11 +568,11 @@ impl IntoFuture for ImageRequestData { } } } - DownloadOrFileRequestData::File(file_data) => file_data, + Self::File(file_data) => file_data, }; // Load the image from the file. - match file_data.clone().await { + match file_data.await { Ok(image) => Ok(image), Err(error) => { warn!("Could not load image from file: {error}"); @@ -608,36 +584,12 @@ impl IntoFuture for ImageRequestData { } impl From for ImageRequestData { - fn from(download_data: DownloadRequestData) -> Self { - Self::Download { - download_data, - file_data: None, - } - } -} - -impl From for ImageRequestData { - fn from(value: FileRequestData) -> Self { - Self::File(value) - } -} - -/// The data of a download request or a file request. -#[derive(Clone)] -enum DownloadOrFileRequestData { - /// The data for a download request. - Download(DownloadRequestData), - /// The data for a file request. - File(FileRequestData), -} - -impl From for DownloadOrFileRequestData { fn from(download_data: DownloadRequestData) -> Self { Self::Download(download_data) } } -impl From for DownloadOrFileRequestData { +impl From for ImageRequestData { fn from(value: FileRequestData) -> Self { Self::File(value) }