#![allow(non_camel_case_types)]
#[allow(dead_code)]
mod bench_common;
use bench_common::{
host_string, repeat_index, run_workload, selected_workload, should_run_workload,
tile_top_left_at_pixel, BenchRun, WorkloadPlan, WorkloadResult,
};
use std::ffi::{CStr, CString};
use std::path::PathBuf;
use std::time::Instant;
#[repr(C)]
pub struct openslide_t {
_private: [u8; 0],
}
#[link(name = "openslide")]
extern "C" {
fn openslide_open(filename: *const std::os::raw::c_char) -> *mut openslide_t;
fn openslide_close(osr: *mut openslide_t);
fn openslide_get_error(osr: *mut openslide_t) -> *const std::os::raw::c_char;
fn openslide_get_level_count(osr: *mut openslide_t) -> i32;
fn openslide_get_level_dimensions(osr: *mut openslide_t, level: i32, w: *mut i64, h: *mut i64);
fn openslide_read_region(
osr: *mut openslide_t,
dest: *mut u32,
x: i64,
y: i64,
level: i32,
w: i64,
h: i64,
);
fn openslide_get_associated_image_names(
osr: *mut openslide_t,
) -> *const *const std::os::raw::c_char;
fn openslide_get_associated_image_dimensions(
osr: *mut openslide_t,
name: *const std::os::raw::c_char,
w: *mut i64,
h: *mut i64,
);
fn openslide_read_associated_image(
osr: *mut openslide_t,
name: *const std::os::raw::c_char,
dest: *mut u32,
);
}
struct Slide {
raw: *mut openslide_t,
}
impl Slide {
fn open(path: &std::path::Path) -> Result<Self, String> {
let cpath = CString::new(path.to_str().ok_or("path is not utf-8")?.as_bytes())
.map_err(|e| e.to_string())?;
let raw = unsafe { openslide_open(cpath.as_ptr()) };
if raw.is_null() {
return Err("openslide_open returned NULL".into());
}
let err = unsafe { openslide_get_error(raw) };
if !err.is_null() {
let msg = unsafe { CStr::from_ptr(err) }
.to_string_lossy()
.into_owned();
unsafe { openslide_close(raw) };
return Err(format!("openslide error: {msg}"));
}
Ok(Self { raw })
}
fn level_count(&self) -> i32 {
unsafe { openslide_get_level_count(self.raw) }
}
fn level_dims(&self, level: i32) -> (u64, u64) {
let mut w: i64 = 0;
let mut h: i64 = 0;
unsafe { openslide_get_level_dimensions(self.raw, level, &mut w, &mut h) };
(w as u64, h as u64)
}
fn read_region(&self, x: i64, y: i64, level: i32, w: i64, h: i64) -> Result<Vec<u32>, String> {
let mut buf = vec![0u32; (w * h) as usize];
unsafe { openslide_read_region(self.raw, buf.as_mut_ptr(), x, y, level, w, h) };
let err = unsafe { openslide_get_error(self.raw) };
if !err.is_null() {
let msg = unsafe { CStr::from_ptr(err) }
.to_string_lossy()
.into_owned();
return Err(msg);
}
Ok(buf)
}
fn associated_names(&self) -> Vec<String> {
let names_ptr = unsafe { openslide_get_associated_image_names(self.raw) };
if names_ptr.is_null() {
return vec![];
}
let mut out = Vec::new();
let mut i = 0;
loop {
let p = unsafe { *names_ptr.add(i) };
if p.is_null() {
break;
}
out.push(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned());
i += 1;
}
out
}
fn read_associated(&self, name: &str) -> Result<Vec<u32>, String> {
let cname = CString::new(name).map_err(|e| e.to_string())?;
let mut w: i64 = 0;
let mut h: i64 = 0;
unsafe {
openslide_get_associated_image_dimensions(self.raw, cname.as_ptr(), &mut w, &mut h)
};
if w == 0 || h == 0 {
return Err("associated image has zero dimensions".into());
}
let mut buf = vec![0u32; (w * h) as usize];
unsafe { openslide_read_associated_image(self.raw, cname.as_ptr(), buf.as_mut_ptr()) };
Ok(buf)
}
}
impl Drop for Slide {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { openslide_close(self.raw) };
}
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 2 {
eprintln!("usage: openslide_bench <slide-path>");
std::process::exit(2);
}
let slide_path = PathBuf::from(&args[1]);
let selected_workload = match selected_workload() {
Ok(value) => value,
Err(err) => {
eprintln!("{err}");
std::process::exit(2);
}
};
let repeat_index = match repeat_index() {
Ok(value) => value,
Err(err) => {
eprintln!("{err}");
std::process::exit(2);
}
};
let slide_path_json = slide_path.display().to_string();
let host = host_string();
let mut workloads = Vec::new();
if should_run_workload(selected_workload.as_deref(), "cold_open") {
let mut cold_open = WorkloadResult::new("cold_open");
for _ in 0..10 {
let start = Instant::now();
match Slide::open(&slide_path) {
Ok(_s) => cold_open.samples.push(start.elapsed()),
Err(e) => {
cold_open.error = Some(format!("open failed: {e}"));
break;
}
}
}
let cold_open_failed = cold_open.error.is_some();
workloads.push(cold_open);
if selected_workload.as_deref() == Some("cold_open") || cold_open_failed {
let run = BenchRun::new(
"openslide",
slide_path_json,
host,
selected_workload,
repeat_index,
workloads,
);
println!("{}", run.to_json());
std::process::exit(if cold_open_failed { 1 } else { 0 });
}
}
let slide = match Slide::open(&slide_path) {
Ok(slide) => slide,
Err(err) => {
let workload_name = selected_workload
.clone()
.unwrap_or_else(|| "single_tile_l0".to_string());
let run = BenchRun::new(
"openslide",
slide_path_json,
host,
selected_workload,
repeat_index,
vec![WorkloadResult::with_error(
&workload_name,
format!("open failed: {err}"),
)],
);
println!("{}", run.to_json());
std::process::exit(1);
}
};
let level_count = slide.level_count();
let level_dims: Vec<(u64, u64)> = (0..level_count).map(|l| slide.level_dims(l)).collect();
let plan = WorkloadPlan::compute(&level_dims);
if should_run_workload(selected_workload.as_deref(), "single_tile_l0") {
let (x, y) = tile_top_left_at_pixel(plan.center_l0, plan.tile_px);
workloads.push(run_workload::<_, String>("single_tile_l0", 200, || {
slide.read_region(x, y, 0, plan.tile_px as i64, plan.tile_px as i64)
}));
}
if should_run_workload(selected_workload.as_deref(), "pan_trace_l0") {
let coords = plan.pan_trace_l0();
let mut idx = 0;
workloads.push(run_workload::<_, String>(
"pan_trace_l0",
coords.len(),
|| {
let (x, y) = coords[idx];
idx += 1;
slide.read_region(x, y, 0, plan.tile_px as i64, plan.tile_px as i64)
},
));
}
if should_run_workload(selected_workload.as_deref(), "pan_trace_l2") {
let coords = plan.pan_trace_l2_world_l0();
let mut idx = 0;
workloads.push(run_workload::<_, String>(
"pan_trace_l2",
coords.len(),
|| {
let (x, y) = coords[idx];
idx += 1;
slide.read_region(
x,
y,
plan.level2_idx as i32,
plan.tile_px as i64,
plan.tile_px as i64,
)
},
));
}
if should_run_workload(selected_workload.as_deref(), "pan_trace_l2_dense") {
let dense_coords = plan.pan_trace_l2_dense_world_l0();
let mut idx = 0;
workloads.push(run_workload::<_, String>(
"pan_trace_l2_dense",
dense_coords.len(),
|| {
let (x, y) = dense_coords[idx];
idx += 1;
slide.read_region(
x,
y,
plan.level2_idx as i32,
plan.tile_px as i64,
plan.tile_px as i64,
)
},
));
}
if should_run_workload(selected_workload.as_deref(), "region_2k") {
workloads.push(run_workload::<_, String>("region_2k", 30, || {
let (cx, cy) = plan.center_l0;
slide.read_region(cx - 1024, cy - 1024, 0, 2048, 2048)
}));
}
if should_run_workload(selected_workload.as_deref(), "viewport_region_l2") {
let viewport = plan.centered_viewport_l2(1024);
workloads.push(run_workload::<_, String>("viewport_region_l2", 30, || {
slide.read_region(
viewport.level0_top_left.0,
viewport.level0_top_left.1,
plan.level2_idx as i32,
viewport.side_px as i64,
viewport.side_px as i64,
)
}));
}
if should_run_workload(selected_workload.as_deref(), "thumbnail") {
let assoc_names = slide.associated_names();
let thumbnail = if assoc_names.iter().any(|n| n == "thumbnail") {
run_workload::<_, String>("thumbnail", 30, || slide.read_associated("thumbnail"))
} else {
let deepest = level_count - 1;
let dims = slide.level_dims(deepest);
run_workload::<_, String>("thumbnail", 30, || {
slide.read_region(0, 0, deepest, dims.0 as i64, dims.1 as i64)
})
};
workloads.push(thumbnail);
}
let run = BenchRun::new(
"openslide",
slide_path_json,
host,
selected_workload,
repeat_index,
workloads,
);
println!("{}", run.to_json());
}