This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

⚡️ Parallelize rasterization when encoding videos! (#87)

+264 -58
+161 -2
Cargo.lock
··· 110 110 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 111 111 112 112 [[package]] 113 + name = "ascii" 114 + version = "1.1.0" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" 117 + 118 + [[package]] 113 119 name = "assert_no_alloc" 114 120 version = "1.1.2" 115 121 source = "git+https://github.com/robbert-vdh/rust-assert-no-alloc.git?branch=feature%2Fnested-permit-forbid#a6fb4f62b9624715291e320ea5f0f70e73b035cf" ··· 528 534 checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 529 535 530 536 [[package]] 537 + name = "channels-console" 538 + version = "0.3.1" 539 + source = "registry+https://github.com/rust-lang/crates.io-index" 540 + checksum = "3e0dd3082cea05e3d0bda6db8f68bcb1ed6601f628cfa6e4626e21b7cd578707" 541 + dependencies = [ 542 + "cfg-if 1.0.4", 543 + "crossbeam-channel", 544 + "eyre", 545 + "futures-util", 546 + "prettytable-rs", 547 + "serde", 548 + "serde_json", 549 + "tiny_http", 550 + ] 551 + 552 + [[package]] 531 553 name = "chrono" 532 554 version = "0.4.42" 533 555 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 539 561 "wasm-bindgen", 540 562 "windows-link 0.2.1", 541 563 ] 564 + 565 + [[package]] 566 + name = "chunked_transfer" 567 + version = "1.5.0" 568 + source = "registry+https://github.com/rust-lang/crates.io-index" 569 + checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" 542 570 543 571 [[package]] 544 572 name = "clap-sys" ··· 587 615 "encode_unicode", 588 616 "libc", 589 617 "once_cell", 590 - "unicode-width", 618 + "unicode-width 0.2.2", 591 619 "windows-sys 0.61.2", 592 620 ] 593 621 ··· 757 785 ] 758 786 759 787 [[package]] 788 + name = "dirs-next" 789 + version = "2.0.0" 790 + source = "registry+https://github.com/rust-lang/crates.io-index" 791 + checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 792 + dependencies = [ 793 + "cfg-if 1.0.4", 794 + "dirs-sys-next", 795 + ] 796 + 797 + [[package]] 798 + name = "dirs-sys-next" 799 + version = "0.1.2" 800 + source = "registry+https://github.com/rust-lang/crates.io-index" 801 + checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 802 + dependencies = [ 803 + "libc", 804 + "redox_users", 805 + "winapi 0.3.9", 806 + ] 807 + 808 + [[package]] 760 809 name = "dispatch2" 761 810 version = "0.3.0" 762 811 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 902 951 ] 903 952 904 953 [[package]] 954 + name = "eyre" 955 + version = "0.6.12" 956 + source = "registry+https://github.com/rust-lang/crates.io-index" 957 + checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 958 + dependencies = [ 959 + "indenter", 960 + "once_cell", 961 + ] 962 + 963 + [[package]] 905 964 name = "fake-simd" 906 965 version = "0.1.2" 907 966 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1033 1092 ] 1034 1093 1035 1094 [[package]] 1095 + name = "futures-macro" 1096 + version = "0.3.31" 1097 + source = "registry+https://github.com/rust-lang/crates.io-index" 1098 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1099 + dependencies = [ 1100 + "proc-macro2", 1101 + "quote", 1102 + "syn 2.0.110", 1103 + ] 1104 + 1105 + [[package]] 1036 1106 name = "futures-task" 1037 1107 version = "0.3.31" 1038 1108 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1045 1115 checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 1046 1116 dependencies = [ 1047 1117 "futures-core", 1118 + "futures-macro", 1048 1119 "futures-task", 1049 1120 "pin-project-lite", 1050 1121 "pin-utils", 1122 + "slab", 1051 1123 ] 1052 1124 1053 1125 [[package]] ··· 1397 1469 checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" 1398 1470 1399 1471 [[package]] 1472 + name = "indenter" 1473 + version = "0.3.4" 1474 + source = "registry+https://github.com/rust-lang/crates.io-index" 1475 + checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" 1476 + 1477 + [[package]] 1400 1478 name = "indexmap" 1401 1479 version = "2.12.0" 1402 1480 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1414 1492 dependencies = [ 1415 1493 "console 0.16.1", 1416 1494 "portable-atomic", 1417 - "unicode-width", 1495 + "unicode-width 0.2.2", 1418 1496 "unit-prefix", 1419 1497 "web-time", 1420 1498 ] ··· 1440 1518 ] 1441 1519 1442 1520 [[package]] 1521 + name = "is-terminal" 1522 + version = "0.4.17" 1523 + source = "registry+https://github.com/rust-lang/crates.io-index" 1524 + checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" 1525 + dependencies = [ 1526 + "hermit-abi 0.5.2", 1527 + "libc", 1528 + "windows-sys 0.61.2", 1529 + ] 1530 + 1531 + [[package]] 1443 1532 name = "is_terminal_polyfill" 1444 1533 version = "1.70.2" 1445 1534 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1525 1614 ] 1526 1615 1527 1616 [[package]] 1617 + name = "lazy_static" 1618 + version = "1.5.0" 1619 + source = "registry+https://github.com/rust-lang/crates.io-index" 1620 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1621 + 1622 + [[package]] 1528 1623 name = "lazycell" 1529 1624 version = "1.3.0" 1530 1625 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1541 1636 version = "0.2.15" 1542 1637 source = "registry+https://github.com/rust-lang/crates.io-index" 1543 1638 checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 1639 + 1640 + [[package]] 1641 + name = "libredox" 1642 + version = "0.1.10" 1643 + source = "registry+https://github.com/rust-lang/crates.io-index" 1644 + checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" 1645 + dependencies = [ 1646 + "bitflags 2.10.0", 1647 + "libc", 1648 + ] 1544 1649 1545 1650 [[package]] 1546 1651 name = "linux-raw-sys" ··· 2158 2263 ] 2159 2264 2160 2265 [[package]] 2266 + name = "prettytable-rs" 2267 + version = "0.10.0" 2268 + source = "registry+https://github.com/rust-lang/crates.io-index" 2269 + checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" 2270 + dependencies = [ 2271 + "encode_unicode", 2272 + "is-terminal", 2273 + "lazy_static", 2274 + "term", 2275 + "unicode-width 0.1.14", 2276 + ] 2277 + 2278 + [[package]] 2161 2279 name = "proc-macro-crate" 2162 2280 version = "3.4.0" 2163 2281 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2350 2468 ] 2351 2469 2352 2470 [[package]] 2471 + name = "redox_users" 2472 + version = "0.4.6" 2473 + source = "registry+https://github.com/rust-lang/crates.io-index" 2474 + checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 2475 + dependencies = [ 2476 + "getrandom 0.2.16", 2477 + "libredox", 2478 + "thiserror 1.0.69", 2479 + ] 2480 + 2481 + [[package]] 2353 2482 name = "reflink" 2354 2483 version = "0.1.3" 2355 2484 source = "git+https://github.com/nicokoch/reflink.git?rev=e8d93b465f5d9ad340cd052b64bbc77b8ee107e2#e8d93b465f5d9ad340cd052b64bbc77b8ee107e2" ··· 2701 2830 "anyhow", 2702 2831 "axum", 2703 2832 "backtrace", 2833 + "channels-console", 2704 2834 "chrono", 2705 2835 "console 0.16.1", 2706 2836 "easing-function", ··· 2951 3081 ] 2952 3082 2953 3083 [[package]] 3084 + name = "term" 3085 + version = "0.7.0" 3086 + source = "registry+https://github.com/rust-lang/crates.io-index" 3087 + checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 3088 + dependencies = [ 3089 + "dirs-next", 3090 + "rustversion", 3091 + "winapi 0.3.9", 3092 + ] 3093 + 3094 + [[package]] 2954 3095 name = "termcolor" 2955 3096 version = "1.4.1" 2956 3097 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3056 3197 "arrayref", 3057 3198 "bytemuck", 3058 3199 "strict-num", 3200 + ] 3201 + 3202 + [[package]] 3203 + name = "tiny_http" 3204 + version = "0.12.0" 3205 + source = "registry+https://github.com/rust-lang/crates.io-index" 3206 + checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" 3207 + dependencies = [ 3208 + "ascii", 3209 + "chunked_transfer", 3210 + "httpdate", 3211 + "log", 3059 3212 ] 3060 3213 3061 3214 [[package]] ··· 3337 3490 version = "0.1.0" 3338 3491 source = "registry+https://github.com/rust-lang/crates.io-index" 3339 3492 checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" 3493 + 3494 + [[package]] 3495 + name = "unicode-width" 3496 + version = "0.1.14" 3497 + source = "registry+https://github.com/rust-lang/crates.io-index" 3498 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 3340 3499 3341 3500 [[package]] 3342 3501 name = "unicode-width"
+3 -1
Cargo.toml
··· 25 25 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 26 26 27 27 [features] 28 - default = ["vst", "video", "video-server"] 28 + default = ["vst", "video", "video-server", "channels-console"] 29 29 vst = [ 30 30 "rand/thread_rng", 31 31 "dep:env_logger", ··· 43 43 ] 44 44 video-server = ["dep:axum"] 45 45 serde-wasm-bindgen = ["dep:serde-wasm-bindgen"] 46 + channels-console = ["dep:channels-console"] 46 47 47 48 [dependencies] 48 49 nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", features = [ ··· 97 98 quick-xml = "0.38.3" 98 99 num = "0.4.3" 99 100 serde-wasm-bindgen = { version = "0.6.5", optional = true } 101 + channels-console = { version = "0.3.1", optional = true } 100 102 101 103 102 104 [dev-dependencies]
+49 -27
src/video/encoders/ffmpeg.rs
··· 6 6 }; 7 7 use anyhow::{Result, anyhow}; 8 8 use measure_time::debug_time; 9 - use std::{fs::File, io::Write, path::PathBuf, sync::Arc}; 9 + use rayon::prelude::*; 10 + use std::{fs::File, io::Write, ops::ControlFlow, path::PathBuf, sync::Arc}; 10 11 11 12 pub struct FFMpegEncoder { 12 - pixmap: tiny_skia::Pixmap, 13 + progress: indicatif::ProgressBar, 13 14 process: std::process::Child, 15 + output_size: (u32, u32), 14 16 fontdb: Option<Arc<resvg::usvg::fontdb::Database>>, 15 17 destination: PathBuf, 16 18 } ··· 77 79 Ok(FFMpegEncoder { 78 80 destination: output_path.clone(), 79 81 fontdb: self.initial_canvas.fontdb.clone(), 80 - pixmap: create_pixmap(width, height), 82 + output_size: (width, height), 83 + progress: self.progress_bars.encoding.clone(), 81 84 process: command 82 85 .spawn() 83 86 .map_err(|e| anyhow!("Could not run {commandline}: {e:?}",))?, ··· 90 93 "FFMpeg".into() 91 94 } 92 95 93 - fn encode_frame(&mut self, output: EngineOutput) -> Result<()> { 94 - if let EngineOutput::Frame { svg, dimensions } = output { 95 - // TODO prendre width et height sur la node svg au lieu de devoir donnner un canvas initial (la grid size peut changer depuis l'initial canvas) 96 - debug_time!("encode_frame"); 97 - // Make sure that width and height are divisible by 2, as the encoder requires it 96 + fn encode_frames( 97 + &mut self, 98 + outputs: Vec<EngineOutput>, 99 + ) -> Result<ControlFlow<()>> { 100 + let (width, height) = self.output_size; 98 101 99 - // let pixmap = svg_to_pixmap(width, height, &svg.to_string())?; 100 - paint_svg_on_pixmap( 101 - self.pixmap.as_mut(), 102 - &svg.to_string(), 103 - dimensions, 104 - &self.fontdb, 105 - )?; 102 + let mut rasterizations: Vec<_> = outputs 103 + .par_iter() 104 + .filter_map(|output| match output { 105 + EngineOutput::Finished => None, 106 + EngineOutput::Frame { index, size, svg } => Some({ 107 + debug_time!("encode_frame"); 108 + // Make sure that width and height are divisible by 2, as the encoder requires it 106 109 107 - // Send frame 108 - self.process 109 - .stdin 110 - .as_mut() 111 - .unwrap() 112 - .write_all(&self.pixmap.data())?; 110 + let mut pixmap = create_pixmap(width, height); 113 111 114 - // let frame = 115 - // canvas.pixmap_to_hwc_frame((width as usize, height as usize), &pixmap)?; 116 - // Ok(encoder.encode(&frame, timestamp)?) 117 - Ok(()) 118 - } else { 119 - Ok(()) 112 + match paint_svg_on_pixmap( 113 + pixmap.as_mut(), 114 + &svg.to_string(), 115 + *size, 116 + &self.fontdb, 117 + ) { 118 + Ok(..) => Some(Ok((index, pixmap.data().to_vec()))), 119 + Err(e) => Some(Err(e)), 120 + } 121 + }), 122 + }) 123 + .collect(); 124 + 125 + rasterizations.sort_by_cached_key(|r| match r { 126 + Some(Ok((index, _))) => **index, 127 + _ => 0, 128 + }); 129 + 130 + for rasterization in rasterizations { 131 + match rasterization { 132 + None => return Ok(ControlFlow::Break(())), 133 + Some(Ok((index, data))) => { 134 + self.process.stdin.as_mut().unwrap().write_all(&data)?; 135 + self.progress.inc(1); 136 + self.progress.set_message(format!("{index}th frame",)); 137 + } 138 + Some(Err(e)) => return Err(e), 139 + } 120 140 } 141 + 142 + Ok(ControlFlow::Continue(())) 121 143 } 122 144 123 145 fn finish(&mut self) -> Result<()> {
+6 -1
src/video/encoders/mod.rs
··· 4 4 video::{encoders::vgv::VGVTranscodeMode, engine::EngineOutput}, 5 5 }; 6 6 use anyhow::Result; 7 + use itertools::Chunk; 8 + use rayon::iter::ParallelIterator; 7 9 use std::path::PathBuf; 8 10 9 11 pub mod ffmpeg; ··· 11 13 12 14 pub trait Encoder { 13 15 fn name(&self) -> String; 14 - fn encode_frame(&mut self, output: EngineOutput) -> Result<()>; 16 + fn encode_frames( 17 + &mut self, 18 + outputs: Vec<EngineOutput>, 19 + ) -> Result<std::ops::ControlFlow<()>>; 15 20 fn finish(&mut self) -> Result<()>; 16 21 fn finish_message(&self, time_elapsed: std::time::Duration) -> String; 17 22 fn progress_message(&self, current: u64, total: u64) -> String {
+22 -13
src/video/encoders/vgv.rs
··· 7 7 use ::vgv::Transcoder; 8 8 use anyhow::Result; 9 9 use itertools::Itertools; 10 - use std::path::PathBuf; 10 + use rayon::iter::ParallelIterator; 11 + use std::{ops::ControlFlow, path::PathBuf}; 11 12 12 13 #[derive(strum_macros::Display)] 13 14 pub enum VGVTranscodeMode { ··· 57 58 "VGV".into() 58 59 } 59 60 60 - fn encode_frame(&mut self, output: EngineOutput) -> Result<()> { 61 - if let EngineOutput::Frame { ref svg, .. } = output { 62 - self.encoder.encode_svg(match svg { 63 - svg::Node::Text(text) => text.to_string(), 64 - svg::Node::SVG(svg) => svg.to_string(), 65 - svg::Node::Element(element) => element 66 - .children 67 - .iter() 68 - .map(|child| child.to_string()) 69 - .join(""), 70 - }); 61 + fn encode_frames( 62 + &mut self, 63 + outputs: Vec<EngineOutput>, 64 + ) -> Result<ControlFlow<()>> { 65 + for output in outputs { 66 + match output { 67 + EngineOutput::Finished => return Ok(ControlFlow::Break(())), 68 + EngineOutput::Frame { ref svg, .. } => { 69 + self.encoder.encode_svg(match svg { 70 + svg::Node::Text(text) => text.to_string(), 71 + svg::Node::SVG(svg) => svg.to_string(), 72 + svg::Node::Element(element) => element 73 + .children 74 + .iter() 75 + .map(|child| child.to_string()) 76 + .join(""), 77 + }); 78 + } 79 + } 71 80 } 72 81 73 - Ok(()) 82 + Ok(ControlFlow::Continue(())) 74 83 } 75 84 76 85 fn finish(&mut self) -> Result<()> {
+18 -12
src/video/encoding.rs
··· 4 4 use crate::video::encoders::Encoder; 5 5 use crate::video::engine::{EngineControl, EngineController, EngineOutput}; 6 6 use anyhow::{Result, anyhow}; 7 + use itertools::Itertools; 7 8 use measure_time::debug_time; 8 9 use std::path::PathBuf; 9 10 use std::thread; ··· 78 79 79 80 let (tx, rx) = std::sync::mpsc::sync_channel::<EngineOutput>(1_000); 80 81 82 + #[cfg(feature = "channels-console")] 83 + let (tx, rx) = 84 + channels_console::instrument!((tx, rx), capacity = 1_000, log = true); 85 + 86 + let parallelism = std::thread::available_parallelism() 87 + .map(|n| n.get()) 88 + .unwrap_or(1); 89 + 90 + pb.log( 91 + "Starting", 92 + &format!("encoder with parallelism of {parallelism}"), 93 + ); 94 + 81 95 let encoder_thread = 82 96 thread::spawn(move || -> Result<std::time::Duration> { 83 - for output in rx.iter() { 84 - match output { 85 - EngineOutput::Finished => break, 86 - EngineOutput::Frame { .. } => { 87 - pb.inc(1); 88 - pb.set_message(encoder.progress_message( 89 - pb.position(), 90 - pb.length().unwrap(), 91 - )); 92 - } 97 + for outputs in &rx.iter().chunks(parallelism) { 98 + match encoder.encode_frames(outputs.collect())? { 99 + std::ops::ControlFlow::Break(_) => break, 100 + _ => (), 93 101 } 94 - 95 - encoder.encode_frame(output)?; 96 102 } 97 103 98 104 let time_taken = pb.elapsed();
+5 -2
src/video/engine.rs
··· 9 9 pub type EngineController<C> = dyn Fn(&Context<'_, C>) -> EngineControl; 10 10 11 11 /// What data is sent to the output by the rendering engine for each rendered frame 12 + #[derive(Debug)] 12 13 pub enum EngineOutput { 13 14 Finished, 14 15 Frame { 16 + index: usize, 17 + size: (usize, usize), 15 18 svg: svg::Node, 16 - dimensions: (usize, usize), 17 19 }, 18 20 } 19 21 ··· 155 157 if context.frame() != previous_rendered_frame { 156 158 if !skip_rendering { 157 159 output.send(EngineOutput::Frame { 158 - dimensions: (canvas.width(), canvas.height()), 160 + index: context.rendered_frames, 161 + size: (canvas.width(), canvas.height()), 159 162 svg: canvas.render_to_svg( 160 163 canvas.colormap.clone(), 161 164 canvas.cell_size,