diff options
feat: paralllelism, memory overcommitment to save on repeated mallocs
| -rw-r--r-- | Cargo.lock | 52 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | src/main.rs | 104 | ||||
| -rw-r--r-- | src/xcursor.rs | 23 |
4 files changed, 140 insertions, 46 deletions
@@ -30,6 +30,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" name = "batacli" version = "0.1.0" dependencies = [ + "rayon", "resvg", "serde", "serde_json", @@ -92,12 +93,43 @@ dependencies = [ ] [[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 = "data-url" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] name = "fdeflate" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -274,6 +306,26 @@ dependencies = [ ] [[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] name = "resvg" version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +rayon = "1.11.0" resvg = "0.44.0" serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.133" @@ -14,3 +15,9 @@ usvg = "0.44.0" lto = "fat" codegen-units = 1 strip = "symbols" + +[features] +default = ["overalloc"] + +# Potentially over-allocate xcursor vectors to gain performance +overalloc = [] diff --git a/src/main.rs b/src/main.rs index fed8090..912d0cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ -use std::{fs, io::Result, os::unix}; +use std::{fs, io::Result, os::unix, sync::{Arc, RwLock}}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; use tiny_skia::Pixmap; use xcursor::{ImageInfo, XCursorEncoder}; @@ -102,40 +103,65 @@ fn process_xcursor( desired_sizes: Vec<u16>, time_scale: u32, ) -> Vec<u8> { - let mut image_info_vec: Vec<ImageInfo> = Vec::new(); - for cursor_frame in cursor.frames { - for desired_size in &desired_sizes { - let svg_data = apply_colours( - resize_cursvg(&cursor_frame.svg, desired_size), - &desired_colours, - ); - let pixmap = vector_to_pixmap(svg_data.as_bytes()); - - let pixels = { - let pixels = pixmap.pixels(); - let mut otherpixels: Vec<u8> = Vec::new(); - for elem in pixels.iter() { - otherpixels.push(elem.red()); - otherpixels.push(elem.green()); - otherpixels.push(elem.blue()); - otherpixels.push(elem.alpha()); + let size = cursor.frames.len() * desired_sizes.len(); + let image_info_vec: Arc<RwLock<Vec<ImageInfo>>> = Arc::new(RwLock::new(vec![ImageInfo { + r#type: 0, + subtype: 0, + width: 0, + height: 0, + xhot: 0, + yhot: 0, + data: vec![], + delay: 0, + };size])); + let desired_colours = Arc::new(desired_colours); + let desired_sizes = Arc::new(desired_sizes); + rayon::scope(|s| { + let mut i=0; + for cursor_frame in cursor.frames { + let ouridx = i; + i+=1; + let image_info_vec = image_info_vec.clone(); + let desired_colours = desired_colours.clone(); + let desired_sizes = desired_sizes.clone(); + s.spawn(move |_| { + for desired_size in &desired_sizes.to_vec() { + let svg_data = apply_colours( + resize_cursvg(&cursor_frame.svg, desired_size), + &desired_colours, + ); + let pixmap = vector_to_pixmap(svg_data.as_bytes()); + + let pixels = { + let pixels = pixmap.pixels(); + let mut otherpixels: Vec<u8> = Vec::new(); + for elem in pixels.iter() { + otherpixels.push(elem.red()); + otherpixels.push(elem.green()); + otherpixels.push(elem.blue()); + otherpixels.push(elem.alpha()); + } + otherpixels + }; + let image_info = ImageInfo { + r#type: *desired_size as u32, + subtype: *desired_size as u32, + width: pixmap.width().try_into().unwrap(), + height: pixmap.height().try_into().unwrap(), + xhot: (cursor.hot.x * pixmap.width() / 256).try_into().unwrap(), + yhot: (cursor.hot.y * pixmap.height() / 256).try_into().unwrap(), + data: pixels.try_into().unwrap(), + delay: (time_scale / cursor_frame.delay).try_into().unwrap(), + }; + { + let mut image_info_vec = image_info_vec.write().unwrap(); + image_info_vec[ouridx] = image_info; + } } - otherpixels - }; - let image_info = ImageInfo { - r#type: *desired_size as u32, - subtype: *desired_size as u32, - width: pixmap.width().try_into().unwrap(), - height: pixmap.height().try_into().unwrap(), - xhot: (cursor.hot.x * pixmap.width() / 256).try_into().unwrap(), - yhot: (cursor.hot.y * pixmap.height() / 256).try_into().unwrap(), - data: pixels.try_into().unwrap(), - delay: (time_scale / cursor_frame.delay).try_into().unwrap(), - }; - image_info_vec.push(image_info); + }); } - } - let mut encoder = XCursorEncoder::new(image_info_vec); + }); + let mut encoder = XCursorEncoder::new(image_info_vec.read().unwrap().to_vec()); encoder.pack() } @@ -150,7 +176,7 @@ fn main() -> Result<()> { fs::create_dir("xcursor-out")?; for style in cursor_style { fs::create_dir(format!("xcursor-out/{}", style.kind))?; - for cursor in style.cursors { + let c: usize = style.cursors.par_iter().map(|cursor|{ println!("xcursor-out/{}/{}", style.kind, cursor.name.clone()); // shove these files in a dir named `cursors` inside the theme dir // the theme dir should have these 2 files in it: @@ -185,14 +211,16 @@ fn main() -> Result<()> { ], 750, ), - )?; - for alias in cursor.aliases { + ).unwrap(); + for alias in cursor.aliases.clone() { unix::fs::symlink( cursor.name.clone(), format!("xcursor-out/{}/{}", style.kind, alias), - )?; + ).unwrap(); } - } + 1 + }).sum(); + println!("Generated {c} cursors"); } Ok(()) diff --git a/src/xcursor.rs b/src/xcursor.rs index ff6c3d6..8d329bf 100644 --- a/src/xcursor.rs +++ b/src/xcursor.rs @@ -31,11 +31,14 @@ impl XCursorEncoder { } pub fn pack(&mut self) -> Vec<u8> { - fn insert_bytes(data: &mut Vec<u8>, new_data: impl IntoIterator<Item = u8>) { - for int in new_data { - data.push(int); - } + fn insert_bytes(data: &mut Vec<u8>, new_data: &[u8]) { + data.extend_from_slice(new_data); } + // fn insert_iterated(data: &mut Vec<u8>, new_data: impl IntoIterator<Item = u8>) { + // for int in new_data { + // data.push(int); + // } + // } fn insert_int(data: &mut Vec<u8>, int: u32) { data.push((int & 0xff) as u8); data.push(((int >> 8) & 0xff) as u8); @@ -44,11 +47,15 @@ impl XCursorEncoder { } let mut data: Vec<u8> = Vec::new(); + #[cfg(feature="overalloc")] + data.reserve( + 4194304 + ); // File Header { // MAGIC string ("Xcur") - insert_bytes(&mut data, MAGIC); + insert_bytes(&mut data, &MAGIC); // CARD32 bytes in this header insert_int(&mut data, 16); // CARD32 file version @@ -62,7 +69,7 @@ impl XCursorEncoder { let mut img_idx: usize = 0; for img in self.images.clone().into_iter() { // Some header - insert_bytes(&mut data, IMAGE_HEADER); + insert_bytes(&mut data, &IMAGE_HEADER); // CARD32 type-specific label - size for images insert_int(&mut data, img.r#type); // CARD32 absolute byte position of table in file @@ -78,7 +85,7 @@ impl XCursorEncoder { // Header Size (36) insert_int(&mut data, 36); // Image Type - insert_bytes(&mut data, IMAGE_HEADER); + insert_bytes(&mut data, &IMAGE_HEADER); // Subtype, for nominal size insert_int(&mut data, img.subtype); // Version @@ -92,7 +99,7 @@ impl XCursorEncoder { // Milliseconds till next frame insert_int(&mut data, img.delay); // Raw image data - insert_bytes(&mut data, img.data); + insert_bytes(&mut data, &img.data); } } |