summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@gnome.org>2023-01-30 16:15:47 -0600
committerFederico Mena Quintero <federico@gnome.org>2023-01-30 16:15:47 -0600
commit6947b3b9409d14c3fcea64cf86a0141b04fe00da (patch)
treee8ee5000d9e54c6b9708aff3100bbe97c584d24b
parent3b93fce5929a83bc23cc652570d9f0478b0fd9e8 (diff)
downloadlibrsvg-6947b3b9409d14c3fcea64cf86a0141b04fe00da.tar.gz
Copy the sources of rsvg-bench to here.
For Shinigami's Outreachy internship, we'll need a way to have a benchmark that uses an exact librsvg commit. So, instead of messing around with cargo options to use an exact commit for the crate, let's build the benchmark into the librsvg source tree. Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/789>
-rw-r--r--src/bin/rsvg-bench.rs215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/bin/rsvg-bench.rs b/src/bin/rsvg-bench.rs
new file mode 100644
index 00000000..82aae657
--- /dev/null
+++ b/src/bin/rsvg-bench.rs
@@ -0,0 +1,215 @@
+#![allow(unknown_lints)]
+#![deny(clippy)]
+#![warn(unused)]
+
+use cairo;
+use librsvg;
+
+use failure::{self, Error, Fail};
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process;
+use std::thread;
+use std::time::Duration;
+use structopt::{self, StructOpt};
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+#[derive(StructOpt, Debug)]
+#[structopt(name = "rsvg-bench", about = "Benchmarking utility for librsvg.")]
+struct Opt {
+ #[structopt(short = "s",
+ long = "sleep",
+ help = "Number of seconds to sleep before starting to process SVGs",
+ default_value = "0")]
+ sleep_secs: usize,
+
+ #[structopt(short = "p",
+ long = "num-parse",
+ help = "Number of times to parse each file",
+ default_value = "100")]
+ num_parse: usize,
+
+ #[structopt(short = "r",
+ long = "num-render",
+ help = "Number of times to render each file",
+ default_value = "100")]
+ num_render: usize,
+
+ #[structopt(help = "Input files or directories", parse(from_os_str))]
+ inputs: Vec<PathBuf>,
+
+ #[structopt(long = "hard-failures",
+ help = "Whether to stop all processing when a file cannot be rendered")]
+ hard_failures: bool,
+}
+
+#[derive(Debug)]
+enum LoadingError {
+ Skipped,
+ Rsvg(librsvg::LoadingError),
+}
+
+#[derive(Debug, Fail)]
+enum ProcessingError {
+ #[fail(display = "Cairo error: {:?}", status)]
+ CairoError { status: cairo::Status },
+
+ #[fail(display = "Rendering error")]
+ RenderingError,
+}
+
+impl From<cairo::Status> for ProcessingError {
+ fn from(status: cairo::Status) -> ProcessingError {
+ ProcessingError::CairoError { status: status }
+ }
+}
+
+impl From<librsvg::RenderingError> for ProcessingError {
+ fn from(_: librsvg::RenderingError) -> ProcessingError {
+ ProcessingError::RenderingError
+ }
+}
+
+fn process_path<P: AsRef<Path>>(opt: &Opt, path: P) -> Result<(), Error> {
+ let meta = fs::metadata(&path)?;
+
+ if meta.is_dir() {
+ process_directory(opt, path)?;
+ } else if let Some(ext) = path.as_ref().extension() {
+ if ext == "svg" || ext == "SVG" {
+ process_file(opt, &path)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn process_directory<P: AsRef<Path>>(opt: &Opt, path: P) -> Result<(), Error> {
+ println!("Processing {:?}", path.as_ref());
+
+ for entry in fs::read_dir(path)? {
+ let entry = entry?;
+ process_path(opt, &entry.path())?;
+ }
+
+ Ok(())
+}
+
+fn read_svg(opt: &Opt, path: &Path) -> Result<librsvg::SvgHandle, LoadingError> {
+ match (opt.hard_failures, librsvg::Loader::new().read_path(path)) {
+ (_, Ok(h)) => Ok(h),
+ (false, Err(e)) => {
+ println!("skipping {} due to error when loading: {}", path.to_string_lossy(), e);
+ Err(LoadingError::Skipped)
+ },
+ (true, Err(e)) => Err(LoadingError::Rsvg(e)),
+ }
+}
+
+fn process_file<P: AsRef<Path>>(opt: &Opt, path: P) -> Result<(), Error> {
+ println!("Processing {:?}", path.as_ref());
+
+ assert!(opt.num_parse > 0);
+
+ let path = path.as_ref();
+
+ for _ in 0..opt.num_parse - 1 {
+ match read_svg(opt, path.as_ref()) {
+ Ok(_) => (),
+ Err(LoadingError::Skipped) => return Ok(()),
+ Err(LoadingError::Rsvg(e)) => return Err(e.into()),
+ }
+ }
+
+ let handle = match read_svg(opt, path.as_ref()) {
+ Ok(h) => h,
+ Err(LoadingError::Skipped) => return Ok(()),
+ Err(LoadingError::Rsvg(e)) => return Err(e.into()),
+ };
+
+ for _ in 0..opt.num_render {
+ render_to_cairo(opt, &handle)?;
+ }
+
+ Ok(())
+}
+
+fn render_to_cairo(opt: &Opt, handle: &librsvg::SvgHandle) -> Result<(), ProcessingError> {
+ let renderer = librsvg::CairoRenderer::new(handle);
+
+ let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100)?;
+ let cr = cairo::Context::new(&surface);
+
+ let viewport = cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 100.0,
+ height: 100.0,
+ };
+
+ match (opt.hard_failures, renderer.render_document(&cr, &viewport)) {
+ (_, Ok(_)) => Ok(()),
+ (false, Err(e)) => {
+ println!("could not render: {}", e);
+ Ok(())
+ },
+ (true, Err(e)) => Err(e.into()),
+ }
+}
+
+fn sleep(secs: usize) {
+ thread::sleep(Duration::from_secs(secs as u64))
+}
+
+fn print_options(opt: &Opt) {
+ println!("Will parse each file {} times", opt.num_parse);
+ println!("Will render each file {} times", opt.num_render);
+ if opt.num_render > 0 {
+ println!("Rendering to Cairo image surface");
+ }
+ println!("Sleeping for {} seconds before processing SVGs...",
+ opt.sleep_secs);
+}
+
+fn run(opt: &Opt) -> Result<(), Error> {
+ print_options(opt);
+
+ sleep(opt.sleep_secs);
+ println!("Processing files!");
+
+ for path in &opt.inputs {
+ process_path(opt, &path)?;
+ }
+
+ Ok(())
+}
+
+fn main() {
+ let opt = Opt::from_args();
+
+ if opt.inputs.is_empty() {
+ eprintln!("No input files or directories specified\n");
+
+ let app = Opt::clap();
+ let mut out = io::stderr();
+ app.write_help(&mut out).expect("failed to write to stderr");
+ eprintln!("");
+ process::exit(1);
+ }
+
+ if opt.num_parse < 1 {
+ eprintln!("Must parse files at least 1 time; please specify a higher number\n");
+ process::exit(1);
+ }
+
+ println!("hard_failures: {:?}", opt.hard_failures);
+
+ match run(&opt) {
+ Ok(_) => (),
+ Err(e) => {
+ eprintln!("{}", e);
+ process::exit(1);
+ }
+ }
+}