Skip to content

Commit

Permalink
Refactor kdtree-tester-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Oom committed Aug 10, 2024
1 parent 913386d commit 561596e
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 174 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["geometry", "kdtree", "kdtree-cli", "kdtree-tester", "pathtracer-cli", "pathtracer-gui", "raytracer-cli", "scene", "tracing", "wavefront", "wavefront-cli"]
members = ["geometry", "kdtree", "kdtree-cli", "kdtree-tester-cli", "pathtracer-cli", "pathtracer-gui", "raytracer-cli", "scene", "tracing", "wavefront", "wavefront-cli"]
resolver = "2"

[profile.release]
Expand Down
2 changes: 1 addition & 1 deletion kdtree-tester/Cargo.toml → kdtree-tester-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "kdtree-tester"
name = "kdtree-tester-cli"
version = "0.1.0"
edition = "2021"

Expand Down
File renamed without changes.
File renamed without changes.
102 changes: 102 additions & 0 deletions kdtree-tester-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use clap::{Parser, Subcommand};
use kdtree::{sah::SahCost, MAX_DEPTH};
use ray_tester::kdtree_ray_tester;
use reducer::kdtree_reduce;
use size::Size;

mod checked_intersection;
mod ray_bouncer;
mod ray_tester;
mod reducer;
mod size;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
/// Compare kdtree intersection with naive intersection
Test {
/// Wavefront OBJ input path
#[arg(short = 'i', long, required = true)]
input: std::path::PathBuf,

/// Output ray fail binary data path
#[arg(short = 'o', long)]
output: Option<std::path::PathBuf>,
/// Image size in pixels
#[arg(short, long, default_value_t = Size::new(512, 512))]
size: Size,
/// Max number of bounces to test
#[arg(short, long, default_value_t = 10)]
bounces: u32,

/// Maximum kd-tree depth
#[arg(long, default_value_t = MAX_DEPTH as u32)]
max_depth: u32,
/// SAH kd-tree traverse cost
#[arg(long, default_value_t = SahCost::default().traverse_cost)]
traverse_cost: f32,
/// SAH kd-tree intersect cost
#[arg(long, default_value_t = SahCost::default().intersect_cost)]
intersect_cost: f32,
/// SAH kd-tree empty factor
#[arg(long, default_value_t = SahCost::default().empty_factor)]
empty_factor: f32,
},
/// Reduce tree size for a specific intersection error
Reduce {
/// Wavefront OBJ input path
#[arg(short = 'i', long, required = true)]
input: std::path::PathBuf,

/// Output reduced kd-tree JSON data path
#[arg(short = 'o', long, required = true)]
output: std::path::PathBuf,

/// Output ray fail binary data path
#[arg(short = 'f', long)]
fail: Option<std::path::PathBuf>,

/// Seed for random generator used to shuffle input geometry
#[arg(short = 's', long, required = true)]
seed: u64,
},
}

fn main() {
let args = Cli::parse();
match args.command {
Commands::Test {
input,
output,
size,
bounces,
max_depth,
traverse_cost,
intersect_cost,
empty_factor,
} => kdtree_ray_tester(
input,
output,
size,
bounces,
max_depth,
SahCost {
traverse_cost,
intersect_cost,
empty_factor,
},
),
Commands::Reduce {
input,
output,
fail,
seed,
} => kdtree_reduce(input, output, fail, seed),
}
}
File renamed without changes.
67 changes: 67 additions & 0 deletions kdtree-tester-cli/src/ray_tester.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use kdtree::{build::build_kdtree, sah::SahCost};
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use scene::{camera::Pinhole, Scene};
use std::{
fs::File,
io::{BufWriter, Write},
path::PathBuf,
};

use crate::{ray_bouncer::RayBouncer, size::Size};

pub(crate) fn kdtree_ray_tester(
input: PathBuf,
output: Option<PathBuf>,
size: Size,
bounces: u32,
max_depth: u32,
sah: SahCost,
) {
let scene = Scene::read_obj_file_with_print_logging(&input);

println!("Building kdtree...");
let kdtree = build_kdtree(&scene.geometries, max_depth, &sah);

println!("Testing up to {} rays...", size.x * size.y * bounces);
let camera = Pinhole::new(scene.cameras[0].clone(), size.as_uvec2());
let bouncer = RayBouncer {
scene,
kdtree,
camera,
size: size.as_uvec2(),
bounces,
};

let xs = 0..size.x;
let ys = 0..size.y;
let pixels = ys
.flat_map(|y| xs.clone().map(move |x| (x, y)))
.collect::<Vec<_>>();
let pixel_count = pixels.len();
let fails = pixels
.into_par_iter()
.enumerate()
.filter_map(|(i, pixel)| {
let result = bouncer.bounce_pixel(pixel);
if let Some(fail) = &result {
eprintln!(
"Fail on pixel {} x {} ({} / {})",
pixel.0, pixel.1, i, pixel_count
);
eprintln!(" {:?}", fail.ray);
eprintln!(" Expected: {:?}", fail.reference);
eprintln!(" Actual: {:?}", fail.kdtree);
}
result
})
.collect::<Vec<_>>();
println!("Found {} fails", fails.len());

if let Some(path) = output {
println!("Writing failed rays to {:?}...", path);
let mut logger = BufWriter::new(File::create(path).unwrap());
fails.iter().enumerate().for_each(|(i, fail)| {
logger.write_all(&fail.as_bytes(i as u16)).unwrap();
});
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::{
fs::File,
io::{BufReader, BufWriter, Write},
path::PathBuf,
time::Instant,
};

use clap::Parser;
use kdtree_tester::checked_intersection::CheckedIntersection;
use rand::{rngs::SmallRng, seq::SliceRandom, SeedableRng};

use geometry::{geometry::Geometry, intersection::RayIntersection, ray::Ray, triangle::Triangle};
Expand All @@ -15,6 +14,8 @@ use kdtree::{
};
use wavefront::obj;

use crate::checked_intersection::CheckedIntersection;

fn build_test_tree(geometries: &[Geometry]) -> KdNode {
build_kdtree(geometries, MAX_DEPTH as u32, &SahCost::default())
}
Expand Down Expand Up @@ -101,29 +102,7 @@ fn reduce_tree(
(geometries, tree)
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Wavefront OBJ input path
#[arg(short = 'i', long, required = true)]
input: std::path::PathBuf,

/// Output reduced kd-tree JSON data path
#[arg(short = 'o', long, required = true)]
output: std::path::PathBuf,

/// Output ray fail binary data path
#[arg(short = 'f', long)]
fail: Option<std::path::PathBuf>,

/// Seed for random generator used to shuffle input geometry
#[arg(short = 's', long, required = true)]
seed: u64,
}

fn main() {
let args = Args::parse();

pub(crate) fn kdtree_reduce(input: PathBuf, output: PathBuf, fail: Option<PathBuf>, seed: u64) {
let intersection = CheckedIntersection {
ray: Ray::new(
[3.897963, 0.24242611, -4.203691].into(),
Expand All @@ -146,14 +125,14 @@ fn main() {
},
)),
};
eprintln!("Seed: {}", args.seed);
eprintln!("Seed: {}", seed);
eprintln!("Testing with failed intersection:");
eprintln!(" {:?}", &intersection.ray);
eprintln!(" Expected: {:?}", &intersection.reference);
eprintln!(" Actual: {:?}", &intersection.kdtree);

eprintln!("Loading {}...", args.input.display());
let obj = obj::obj(&mut BufReader::new(File::open(&args.input).unwrap()));
eprintln!("Loading {}...", input.display());
let obj = obj::obj(&mut BufReader::new(File::open(&input).unwrap()));
eprintln!(" Chunks: {}", obj.chunks.len());
eprintln!(" Vertices: {}", obj.vertices.len());
eprintln!(" Normals: {}", obj.normals.len());
Expand Down Expand Up @@ -182,19 +161,19 @@ fn main() {
.collect::<Vec<_>>();
eprintln!(" Geometries: {}", geometries.len());

if let Some(path) = args.fail {
if let Some(path) = fail {
eprintln!("Writing test ray to {:?}...", path);
let file = File::create(path).unwrap();
let mut buf = BufWriter::new(file);
buf.write_all(&intersection.as_bytes(1)).unwrap();
}

eprintln!("Reducing tree...");
let (geometries, tree) = reduce_tree(args.seed, intersection, geometries);
let (geometries, tree) = reduce_tree(seed, intersection, geometries);

eprintln!("Writing reduced tree to {:?}...", args.output);
eprintln!("Writing reduced tree to {:?}...", output);
write_tree_json(
&mut BufWriter::new(File::create(args.output).unwrap()),
&mut BufWriter::new(File::create(output).unwrap()),
&geometries,
&tree,
)
Expand Down
37 changes: 37 additions & 0 deletions kdtree-tester-cli/src/size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::{fmt::Display, str::FromStr};

use glam::UVec2;

#[derive(Clone, Copy, Debug)]
pub(crate) struct Size {
pub(crate) x: u32,
pub(crate) y: u32,
}

impl Size {
pub(crate) fn new(x: u32, y: u32) -> Self {
Size { x, y }
}

pub(crate) fn as_uvec2(self) -> UVec2 {
UVec2::new(self.x, self.y)
}
}

impl FromStr for Size {
type Err = <u32 as FromStr>::Err;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let pos = s.find('x').expect("Could not parse");
Ok(Size {
x: s[0..pos].parse()?,
y: s[pos + 1..].parse()?,
})
}
}

impl Display for Size {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}x{}", self.x, self.y)
}
}
Loading

0 comments on commit 561596e

Please sign in to comment.