This repository has no description
0

Configure Feed

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

🚧 Work on frames rendering parallelization

not faster yet

+98 -32
+1
Cargo.lock
··· 1908 1908 "nanoid", 1909 1909 "ndarray", 1910 1910 "nih_plug", 1911 + "num_cpus", 1911 1912 "once_cell", 1912 1913 "rand", 1913 1914 "rayon",
+1
Cargo.toml
··· 68 68 video-rs = { version = "0.10.3", features = ["ndarray"] } 69 69 ffmpeg-next = "7.1.0" 70 70 rayon = "1.10.0" 71 + num_cpus = "1.16.0" 71 72 72 73 73 74 [dev-dependencies]
+1 -1
Justfile
··· 21 21 cp shapemaker ~/.local/bin/ 22 22 23 23 example-video out="out.mp4" args='': 24 - RUST_BACKTRACE=1 ./shapemaker video --colors colorschemes/palenight.css {{out}} --sync-with fixtures/schedule-hell.midi --audio fixtures/schedule-hell.flac --grid-size 16x10 --resolution 480 {{args}} 24 + RUST_BACKTRACE=full ./shapemaker video --colors colorschemes/palenight.css {{out}} --sync-with fixtures/schedule-hell.midi --audio fixtures/schedule-hell.flac --grid-size 16x10 --resolution 480 {{args}} 25 25 26 26 example-image out="out.png" args='': 27 27 ./shapemaker image --colors colorschemes/palenight.css --resolution 1400 {{out}} {{args}}
+31 -19
src/canvas.rs
··· 4 4 use std::{collections::HashMap, ops::Range, sync::Arc}; 5 5 6 6 use itertools::Itertools as _; 7 - use measure_time::info_time; 7 + use measure_time::{debug_time, info_time}; 8 8 use rand::Rng; 9 9 10 10 use crate::{ ··· 208 208 self.fontdb.is_some() 209 209 } 210 210 211 - fn load_fonts(&mut self) -> anyhow::Result<()> { 211 + pub fn load_fonts(&mut self) -> anyhow::Result<()> { 212 212 if self.fonts_loaded() { 213 213 return Ok(()); 214 214 } ··· 556 556 } 557 557 558 558 pub fn render_to_svg(&mut self) -> anyhow::Result<String> { 559 - info_time!("render_to_svg"); 559 + debug_time!("render_to_svg"); 560 560 let background_color = self.background.unwrap_or_default(); 561 561 let mut svg = svg::Document::new(); 562 562 svg = svg.add( ··· 601 601 Ok(rendered) 602 602 } 603 603 604 - pub fn render_to_pixmap_no_cache( 605 - &mut self, 604 + pub fn svg_to_pixmap( 605 + &self, 606 606 width: u32, 607 607 height: u32, 608 + contents: &str, 608 609 ) -> anyhow::Result<tiny_skia::Pixmap> { 609 - info_time!("render_to_pixmap_no_cache"); 610 - 611 - self.load_fonts()?; 610 + info_time!("svg_to_pixmap"); 612 611 613 612 let mut pixmap = self.create_pixmap(width, height); 614 613 615 - let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?, &self.fontdb)?; 614 + let parsed_svg = &svg_to_usvg_tree(contents, &self.fontdb)?; 616 615 617 616 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg); 618 617 619 618 Ok(pixmap) 619 + } 620 + 621 + pub fn render_to_pixmap_no_cache( 622 + &mut self, 623 + width: u32, 624 + height: u32, 625 + ) -> anyhow::Result<tiny_skia::Pixmap> { 626 + let svg_contents = self.render_to_svg()?; 627 + self.svg_to_pixmap(width, height, &svg_contents) 620 628 } 621 629 622 630 // Returns None if we had a render cache hit -- pixmap is in self.png_render_cache in that case ··· 637 645 } 638 646 } 639 647 640 - let mut pixmap = self.create_pixmap(width, height); 641 - 642 - let parsed_svg = &svg_to_usvg_tree(&new_svg_contents, &self.fontdb)?; 643 - 644 - self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg); 648 + let pixmap = self.svg_to_pixmap(width, height, &new_svg_contents)?; 645 649 646 650 self.png_render_cache = Some(new_svg_contents); 647 651 648 652 Ok(Some(pixmap)) 649 653 } 650 654 651 - pub fn render_to_hwc_frame(&mut self, resolution: u32) -> anyhow::Result<video_rs::Frame> { 652 - info_time!("render_to_hwc_frame"); 655 + pub fn pixmap_to_hwc_frame( 656 + &self, 657 + resolution: u32, 658 + pixmap: &tiny_skia::Pixmap, 659 + ) -> anyhow::Result<video_rs::Frame> { 660 + info_time!("pixmap_to_hwc_frame"); 653 661 let (width, height) = self.resolution_to_size(resolution); 654 - let pixmap = self.render_to_pixmap_no_cache(width, height)?; 655 - 656 662 let (width, height) = (width as usize, height as usize); 657 663 let mut data = vec![0u8; height * width * 3]; 658 664 ··· 674 680 Ok(video_rs::Frame::from_shape_vec([height, width, 3], data)?) 675 681 } 676 682 683 + pub fn render_to_hwc_frame(&mut self, resolution: u32) -> anyhow::Result<video_rs::Frame> { 684 + let (width, height) = self.resolution_to_size(resolution); 685 + let pixmap = self.render_to_pixmap_no_cache(width, height)?; 686 + self.pixmap_to_hwc_frame(resolution, &pixmap) 687 + } 688 + 677 689 fn usvg_tree_to_pixmap( 678 - &mut self, 690 + &self, 679 691 width: u32, 680 692 height: u32, 681 693 mut pixmap_mut: tiny_skia::PixmapMut<'_>,
+64 -12
src/video.rs
··· 1 1 use std::str::FromStr; 2 + use std::sync::{Arc, Mutex}; 2 3 use std::{ 3 4 fmt::Formatter, 4 5 fs::create_dir_all, ··· 10 11 use anyhow::Result; 11 12 use chrono::{DateTime, NaiveDateTime}; 12 13 use indicatif::{ProgressBar, ProgressIterator}; 13 - use measure_time::info_time; 14 + use itertools::Itertools; 15 + use measure_time::{debug_time, info_time}; 16 + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; 14 17 use video_rs::Time; 15 18 16 19 use crate::{ ··· 49 52 pub duration_override: Option<usize>, 50 53 pub start_rendering_at: usize, 51 54 pub progress_bar: indicatif::ProgressBar, 52 - encoder: Option<video_rs::Encoder>, 55 + encoder: Option<Arc<Mutex<video_rs::Encoder>>>, 53 56 } 54 57 55 58 pub struct Hook<C> { ··· 115 118 fn setup_encoder(&mut self, output_path: &str) -> anyhow::Result<()> { 116 119 let (width, height) = self.initial_canvas.resolution_to_size(self.resolution); 117 120 118 - self.encoder = Some( 121 + self.encoder = Some(Arc::new(Mutex::new( 119 122 video_rs::Encoder::new( 120 123 PathBuf::from_str(output_path)?, 121 124 video_rs::encode::Settings::preset_h264_yuv420p( ··· 125 128 ), 126 129 ) 127 130 .expect("Failed to build encoder"), 128 - ); 131 + ))); 129 132 130 133 Ok(()) 131 134 } ··· 481 484 482 485 self.progress_bar.set_length(render_ms_range.len() as u64); 483 486 487 + let mut frames_to_encode: Vec<(Time, String)> = vec![]; 488 + 484 489 for _ in render_ms_range 485 490 .into_iter() 486 491 .progress_with(self.progress_bar.clone()) ··· 550 555 } 551 556 552 557 if context.frame != previous_rendered_frame { 553 - info_time!("render_frame"); 554 - self.encoder 555 - .as_mut() 556 - .expect("Encoder was not initialized") 557 - .encode( 558 - &canvas.render_to_hwc_frame(self.resolution)?, 559 - Time::from_secs_f64(context.ms as f64 * 1e-3), 560 - )?; 558 + debug_time!("compute_frame"); 559 + frames_to_encode.push(( 560 + Time::from_secs_f64(context.ms as f64 * 1e-3), 561 + canvas.render_to_svg()?, 562 + )); 561 563 562 564 written_frames_count += 1; 563 565 ··· 566 568 } 567 569 } 568 570 571 + self.initial_canvas.load_fonts()?; 572 + 573 + self.progress_bar.set_position(0); 574 + self.progress_bar.set_length(frames_to_encode.len() as u64); 575 + self.progress_bar.set_message("Rasterizing"); 576 + 577 + let (hwc_frames_send, hwc_frames_receive) = 578 + std::sync::mpsc::channel::<(Time, video_rs::Frame)>(); 579 + 580 + let resolution = self.resolution.clone(); 581 + let pb = self.progress_bar.clone(); 582 + let canvas = self.initial_canvas.clone(); 583 + frames_to_encode.par_iter().for_each(|(time, svg)| { 584 + let (width, height) = canvas.resolution_to_size(resolution); 585 + let pixmap = canvas 586 + .svg_to_pixmap(width, height, &svg) 587 + .expect("Failed to render frame"); 588 + 589 + let frame = canvas 590 + .pixmap_to_hwc_frame(resolution, &pixmap) 591 + .expect("Failed to convert pixmap to frame"); 592 + 593 + hwc_frames_send 594 + .send((*time, frame)) 595 + .expect("Failed to send frame"); 596 + 597 + pb.inc(1); 598 + }); 599 + 600 + drop(hwc_frames_send); 601 + 602 + self.progress_bar.set_position(0); 603 + self.progress_bar.set_length(frames_to_encode.len() as u64); 604 + self.progress_bar.set_message("Encoding"); 605 + 606 + for (time, frame) in hwc_frames_receive 607 + .iter() 608 + .sorted_by(|(a, _), (b, _)| a.as_secs_f64().total_cmp(&b.as_secs_f64())) 609 + { 610 + self.encoder 611 + .as_mut() 612 + .expect("Encoder was not initialized") 613 + .lock() 614 + .unwrap() 615 + .encode(&frame, time) 616 + .expect("Failed to encode frame"); 617 + } 618 + 569 619 Ok(written_frames_count) 570 620 } 571 621 ··· 592 642 self.encoder 593 643 .as_mut() 594 644 .expect("Encoder is missing somehow") 645 + .lock() 646 + .unwrap() 595 647 .finish()?; 596 648 597 649 self.progress_bar.log(