From 3a55441762459e6f7f1b13dacf9946a534c5221c Mon Sep 17 00:00:00 2001 From: prushton2 Date: Thu, 9 Apr 2026 18:46:46 -0400 Subject: [PATCH 1/2] Increased threads and changed callstack to per thread memory --- src/shaders/main.wgsl | 2 +- src/shaders/material.wgsl | 60 +++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/shaders/main.wgsl b/src/shaders/main.wgsl index faec53e..c65543d 100644 --- a/src/shaders/main.wgsl +++ b/src/shaders/main.wgsl @@ -1,4 +1,4 @@ -@compute @workgroup_size(8, 8, 1) +@compute @workgroup_size(16, 16, 1) fn main( @builtin(global_invocation_id) gid: vec3, @builtin(local_invocation_index) local_invocation_index: u32, diff --git a/src/shaders/material.wgsl b/src/shaders/material.wgsl index 3ab8cfc..de6df09 100644 --- a/src/shaders/material.wgsl +++ b/src/shaders/material.wgsl @@ -7,25 +7,25 @@ struct Call { depth: u32, } -var callstack: array, 64>; +var callstack: array; fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { var callstack_len = 1; let light_dir = vec3(0.57735,0.57735,0); - callstack[tid][0].caller = -1; - callstack[tid][0].ray_pos = ray_pos; - callstack[tid][0].ray_dir = ray_dir; - callstack[tid][0].output_id = 0u; - callstack[tid][0].outputs[0] = vec3(-1.0, -1.0, -1.0); - callstack[tid][0].outputs[1] = vec3(-1.0, -1.0, -1.0); - callstack[tid][0].outputs[2] = vec3(-1.0, -1.0, -1.0); - callstack[tid][0].depth = 4u; + callstack[0].caller = -1; + callstack[0].ray_pos = ray_pos; + callstack[0].ray_dir = ray_dir; + callstack[0].output_id = 0u; + callstack[0].outputs[0] = vec3(-1.0, -1.0, -1.0); + callstack[0].outputs[1] = vec3(-1.0, -1.0, -1.0); + callstack[0].outputs[2] = vec3(-1.0, -1.0, -1.0); + callstack[0].depth = 4u; for (var i = 0; i < 32; i++) { if callstack_len == 0 { break; } let index = callstack_len-1; - var call = callstack[tid][index]; + var call = callstack[index]; let record = closest_hit(call.ray_pos, call.ray_dir); @@ -34,7 +34,7 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { if call.caller == -1 { return 0x00BADBEDu; } - callstack[tid][call.caller].outputs[call.output_id] = vec3(186.0, 219.0, 237.0); + callstack[call.caller].outputs[call.output_id] = vec3(186.0, 219.0, 237.0); callstack_len -= 1; continue; } @@ -45,7 +45,7 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { // kill recursion if call.depth == 0 { - callstack[tid][call.caller].outputs[call.output_id] = material.color; + callstack[call.caller].outputs[call.output_id] = material.color; callstack_len -= 1; continue; } @@ -63,14 +63,14 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { if material.translucent != 0u && call.outputs[0].x <= -1.0 { let new_origin = ray_at(call.ray_pos, call.ray_dir, record.t + 0.00001); - callstack[tid][callstack_len].caller = index; - callstack[tid][callstack_len].ray_pos = new_origin; - callstack[tid][callstack_len].ray_dir = call.ray_dir; - callstack[tid][callstack_len].output_id = 0; - callstack[tid][callstack_len].outputs[0] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].outputs[1] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].outputs[2] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].depth = call.depth - 1; + callstack[callstack_len].caller = index; + callstack[callstack_len].ray_pos = new_origin; + callstack[callstack_len].ray_dir = call.ray_dir; + callstack[callstack_len].output_id = 0; + callstack[callstack_len].outputs[0] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].outputs[1] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].outputs[2] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].depth = call.depth - 1; callstack_len += 1; pushed_to_stack = true; @@ -80,14 +80,14 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { let new_dir = call.ray_dir - (2.0 * dot(call.ray_dir, record.normal) * record.normal); let new_origin = ray_at(call.ray_pos, call.ray_dir, record.t - 0.000001); - callstack[tid][callstack_len].caller = index; - callstack[tid][callstack_len].ray_pos = new_origin; - callstack[tid][callstack_len].ray_dir = new_dir; - callstack[tid][callstack_len].output_id = 1; - callstack[tid][callstack_len].outputs[0] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].outputs[1] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].outputs[2] = vec3(-1.0, -1.0, -1.0); - callstack[tid][callstack_len].depth = call.depth - 1; + callstack[callstack_len].caller = index; + callstack[callstack_len].ray_pos = new_origin; + callstack[callstack_len].ray_dir = new_dir; + callstack[callstack_len].output_id = 1; + callstack[callstack_len].outputs[0] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].outputs[1] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].outputs[2] = vec3(-1.0, -1.0, -1.0); + callstack[callstack_len].depth = call.depth - 1; callstack_len += 1; pushed_to_stack = true; @@ -110,7 +110,7 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { } if !pushed_to_stack { - call = callstack[tid][index]; + call = callstack[index]; var color = f32(material.translucent) * call.outputs[0] + f32(material.reflect) * call.outputs[1] + f32(100 - material.translucent - material.reflect) * lit_color; @@ -118,7 +118,7 @@ fn ray_color(ray_pos: vec3, ray_dir: vec3, tid: u32) -> u32 { color = color / 100.0; if call.caller != -1 { - callstack[tid][call.caller].outputs[call.output_id] = color; + callstack[call.caller].outputs[call.output_id] = color; callstack_len -= 1; } else { return (u32(color.x) << 16) | (u32(color.y) << 8) | (u32(color.z)); From a19fe7ae59240dab9e31fcac346dd461cf461eeb Mon Sep 17 00:00:00 2001 From: prushton2 Date: Thu, 9 Apr 2026 18:47:38 -0400 Subject: [PATCH 2/2] Added flags to replace arbitrary globals --- Cargo.lock | 75 ++++++++++++++++++++++++++++++++- Cargo.toml | 1 + README.md | 9 ++++ src/main.rs | 117 ++++++++++++++++++++++++++++++++-------------------- 4 files changed, 155 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8094400..1a60f9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "1.93.0" dependencies = [ "auto_ops", "bytemuck", + "clap", "downcast-rs 2.0.2", "env_logger", "image", @@ -123,7 +124,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", - "anstyle-parse", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -146,6 +162,15 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.1.5" @@ -457,6 +482,46 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream 1.0.0", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -723,7 +788,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "env_filter", "jiff", @@ -2566,6 +2631,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" diff --git a/Cargo.toml b/Cargo.toml index 220486b..8af1bf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ resolver = "2" [dependencies] auto_ops = "0.3.0" bytemuck = "1.25.0" +clap = { version = "4.6.0", features = ["derive"] } downcast-rs = "2.0.2" env_logger = "0.11.9" image = "0.25.10" diff --git a/README.md b/README.md index 8380c43..e735cfa 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ This is a custom render engine written in rust and WGSL. +## Arguments + +| Flag | Long | Short | Default +|--|--|--|--| +| Resolution | --resolution | -r | 1280x720 +| Mouse sensitivity | --sensitivity | -s | 0.001 +| Max framerate | --framelimit | -f | 144 +| Camera speed | --movespeed | -c | 1.5 + ## Features ### Shapes diff --git a/src/main.rs b/src/main.rs index 8e5540f..0c47a90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::sync::{Arc, RwLock}; +use clap::Parser; use winit::application::ApplicationHandler; use winit::event::{ElementState, KeyEvent, WindowEvent, DeviceEvent, DeviceId}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; @@ -16,10 +17,41 @@ mod material; mod object; mod ds; -const WIDTH: usize = 1280; -const HEIGHT: usize = 720; -const SENSITIVITY: f64 = 0.001; -const SPEED: f64 = 1.5; +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Arguments { + #[arg(short, long, default_value_t = String::from("1280x720"))] + pub resolution: String, + + #[arg(short, long, default_value_t = 0.001)] + pub sensitivity: f64, + + #[arg(short, long, default_value_t = 144)] + pub framelimit: u64, + + #[arg(short, long, default_value_t = 1.5)] + pub movespeed: f64, + + #[arg(default_value_t = 1920)] + pub width: usize, + #[arg(default_value_t = 1080)] + pub height: usize, +} + +impl Arguments { + fn update(&mut self) { + let mut split = self.resolution.split('x'); + let res_err = "Invalid resolution"; + (self.width, self.height) = ( + split.next().expect(res_err).parse().expect(res_err), + split.next().expect(res_err).parse().expect(res_err) + ); + + if self.framelimit > 1000 { + self.framelimit = 1000; + } + } +} struct App { // window @@ -28,11 +60,12 @@ struct App { // scene player: RwLock, - objects: Vec>, + objects: Vec>, // input - keyboard: HashMap, + keyboard: HashMap, mouse_delta: (f64, f64), + config: Arguments, // statistics last_frame: std::time::Instant, @@ -44,12 +77,25 @@ struct App { } impl App { - pub fn consume_player(&mut self, player: object::Player) { - self.player = RwLock::new(player); - } + pub fn new(config: Arguments, player: object::Player, objects: Vec>) -> Self { + Self { + window: None, + gpu: wgpu_handler::GpuHandler::default(), + + player: RwLock::new(player), + objects: objects, - pub fn consume_objects(&mut self, objects: Vec>) { - self.objects = objects; + keyboard: HashMap::new(), + mouse_delta: (0.0, 0.0), + config: config, + + last_frame: std::time::Instant::now(), + deltatime: 0.0, + + fps_stat: 0, + deltatime_stat: 0, + statistics_timer: std::time::Instant::now(), + } } pub fn handle_movement(&mut self) { @@ -72,7 +118,7 @@ impl App { for (key, dir) in key_movements { if self.keyboard.get(key) == Some(&true) { - player_ref.move_player(&(dir * SPEED * self.deltatime)); + player_ref.move_player(&(dir * self.config.movespeed * self.deltatime)); } } @@ -109,35 +155,13 @@ impl App { } } -impl Default for App { - fn default() -> Self { - Self { - window: None, - gpu: wgpu_handler::GpuHandler::default(), - - player: RwLock::new(object::Player::new(object::Camera::zero())), - objects: vec![], - - keyboard: HashMap::new(), - mouse_delta: (0.0, 0.0), - - last_frame: std::time::Instant::now(), - deltatime: 0.0, - - fps_stat: 0, - deltatime_stat: 0, - statistics_timer: std::time::Instant::now(), - } - } -} - impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = Arc::new( event_loop.create_window( Window::default_attributes() .with_title("Render Engine") - .with_inner_size(winit::dpi::LogicalSize::new(WIDTH as f64, HEIGHT as f64)) + .with_inner_size(winit::dpi::LogicalSize::new(self.config.width as f64, self.config.height as f64)) ).unwrap() ); @@ -150,8 +174,8 @@ impl ApplicationHandler for App { pollster::block_on( self.gpu.init( window.clone(), - WIDTH as u32, - HEIGHT as u32, + self.config.width as u32, + self.config.height as u32, vec!["textures/dirt.png", "textures/grass_side.png", "textures/grass_top.png"]) ); @@ -168,7 +192,7 @@ impl ApplicationHandler for App { ) { match event { DeviceEvent::MouseMotion { delta: (dx, dy) } => { - self.mouse_delta = (self.mouse_delta.0 + (dx as f64)*SENSITIVITY, self.mouse_delta.1 + (dy as f64)*SENSITIVITY); + self.mouse_delta = (self.mouse_delta.0 + (dx as f64)*self.config.sensitivity, self.mouse_delta.1 + (dy as f64)*self.config.sensitivity); }, _ => {} } @@ -217,8 +241,10 @@ impl ApplicationHandler for App { // this makes the deltatime not crash out when the fps gets too high, // but caps the fps at 1000 - if self.deltatime < 1.0 { - std::thread::sleep(std::time::Duration::from_millis(1)); + if self.deltatime < 1000.0/self.config.framelimit as f64 { + let mut duration = std::time::Duration::from_millis(1000/self.config.framelimit); + duration -= std::time::Duration::from_millis(self.deltatime as u64); + std::thread::sleep(duration); } if let Some(window) = &self.window { @@ -263,10 +289,13 @@ impl ApplicationHandler for App { fn main() { debug_assert_eq!(std::mem::size_of::() % 256, 0); + let mut args = Arguments::parse(); + args.update(); + let camera = object::Camera::new( ds::Vector3::new(0.0, 0.0, 0.0), 3.0, - (WIDTH as f64, HEIGHT as f64), + (args.width as f64, args.height as f64), 60.0 ); @@ -274,7 +303,7 @@ fn main() { camera ); - let objects: Vec> = vec![ + let objects: Vec> = vec![ // three spheres @@ -327,9 +356,7 @@ fn main() { let event_loop = EventLoop::new().expect("Failed to create event loop"); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = App::default(); - app.consume_player(player); - app.consume_objects(objects); + let mut app = App::new(args, player, objects); event_loop.run_app(&mut app).expect("Event loop failed"); } \ No newline at end of file