this repo has no description
3
fork

Configure Feed

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

⚡Make it ridiculously faster

By parallelizing conversion of pixmap to hwv frame

authored by

Gwenn Le Bihan and committed by
Gwen Le Bihan
aa49a3d1 e84512fc

+22 -68
+1
Cargo.lock
··· 1910 1910 "nih_plug", 1911 1911 "once_cell", 1912 1912 "rand", 1913 + "rayon", 1913 1914 "resvg", 1914 1915 "rgb2yuv420", 1915 1916 "roxmltree 0.19.0",
+1
Cargo.toml
··· 67 67 rgb2yuv420 = "0.2.3" 68 68 video-rs = { version = "0.10.3", features = ["ndarray"] } 69 69 ffmpeg-next = "7.1.0" 70 + rayon = "1.10.0" 70 71 71 72 72 73 [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}} --duration 10 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}} 25 25 26 26 example-image out="out.png" args='': 27 27 ./shapemaker image --colors colorschemes/palenight.css --resolution 1400 {{out}} {{args}}
+18 -61
src/canvas.rs
··· 1 1 use core::panic; 2 - use std::{collections::HashMap, ops::Range}; 2 + use rayon::prelude::*; 3 + use std::{collections::HashMap, fs::OpenOptions, io::Write, ops::Range}; 3 4 4 5 use itertools::Itertools as _; 5 6 use measure_time::info_time; ··· 625 626 info_time!("render_to_hwc_frame"); 626 627 let (width, height) = self.resolution_to_size(resolution); 627 628 let pixmap = self.render_to_pixmap_no_cache(width, height)?; 628 - Ok(video_rs::Frame::from_shape_fn( 629 - (pixmap.height() as usize, pixmap.width() as usize, 3), 630 - |(y, x, c)| { 629 + 630 + let mut data = vec![0u8; height as usize * width as usize * 3]; 631 + data.par_chunks_exact_mut(3) 632 + .enumerate() 633 + .for_each(|(index, chunk)| { 634 + let x = index / (height as usize); 635 + let y = index % (height as usize); 636 + 631 637 let pixel = pixmap 632 638 .pixel(x as u32, y as u32) 633 639 .expect(&format!("No pixel found at x, y = {x}, {y}")); 634 - match c { 635 - 0 => pixel.red(), 636 - 1 => pixel.green(), 637 - 2 => pixel.blue(), 638 - _ => unreachable!(), 639 - } 640 - }, 641 - )) 642 - // let mut rgb_pixels = ndarray::Array3::from_shape_vec( 643 - // [pixmap.width() as usize, pixmap.height() as usize, 3], 644 - // pixmap 645 - // .pixels() 646 - // .iter() 647 - // .map(|pixel| [pixel.red(), pixel.green(), pixel.blue()]) 648 - // .flatten() 649 - // .collect(), 650 - // )?; 651 - 652 - // // Go from WHC to HWC 653 - // rgb_pixels = rgb_pixels.permuted_axes([1, 0, 2]); 654 - 655 - // println!("rgb_pixels: {:?}", rgb_pixels.shape()); 656 - 657 - // // Flatten before giving to YUV conversion 658 - // let rgb_pixels_flat = rgb_pixels.into_flat(); 659 - 660 - // println!("rgb_pixels_flat: {:?}", rgb_pixels_flat.len()); 661 640 662 - // let yuv_pixels = convert_rgb_to_yuv420p( 663 - // &rgb_pixels_flat 664 - // .as_slice() 665 - // .expect("Failed to convert HWC RGB data to a slice for YUV420p conversion"), 666 - // pixmap.height(), 667 - // pixmap.width(), 668 - // 3, 669 - // ); 670 - 671 - // println!("yuv_pixels: {:?}", yuv_pixels.len()); 672 - 673 - // // info_time!("render_to_hwc_frame -> convert_rgb_to_yuv420p"); 674 - // // let yuv_pixels = convert_rgb_to_yuv420p( 675 - // // pixmap.data(), 676 - // // pixmap.width(), 677 - // // pixmap.height(), 678 - // // tiny_skia::BYTES_PER_PIXEL, 679 - // // ); 680 - 681 - // // println!("yuv_pixels: {:?}", yuv_pixels.len()); 641 + chunk[0] = pixel.red(); 642 + chunk[1] = pixel.green(); 643 + chunk[2] = pixel.blue(); 644 + }); 682 645 683 - // info_time!("render_to_hwc_frame -> create_frame"); 684 - 685 - // let frame_data = ndarray::Array3::from_shape_vec( 686 - // [pixmap.height() as usize, pixmap.width() as usize, 3], 687 - // yuv_pixels, 688 - // )?; 689 - 690 - // println!("frame_data: {:?}", frame_data.shape()); 691 - 692 - // Ok(video_rs::Frame::from(frame_data)) 646 + Ok(video_rs::Frame::from_shape_vec( 647 + [height as usize, width as usize, 3], 648 + data, 649 + )?) 693 650 } 694 651 695 652 fn usvg_tree_to_pixmap(
+1 -6
src/video.rs
··· 9 9 10 10 use ffmpeg::{codec, format, media, Packet}; 11 11 use ffmpeg_next::encoder; 12 - use std::path::Path; 13 12 extern crate ffmpeg_next as ffmpeg; 14 13 use anyhow::Result; 15 14 use chrono::{DateTime, NaiveDateTime}; ··· 554 553 } 555 554 556 555 if context.frame != previous_rendered_frame { 557 - // canvas.render_to_png( 558 - // &self.frame_output_path(context.frame), 559 - // self.resolution, 560 - // Some(&self.frame_output_path(previous_rendered_frame)), 561 - // )?; 556 + info_time!("render_frame"); 562 557 self.encoder 563 558 .as_mut() 564 559 .expect("Encoder was not initialized")