11use std:: sync:: atomic:: { AtomicU32 , AtomicU64 , Ordering } ;
2- use std:: sync:: mpsc:: channel;
32use std:: sync:: { Arc , Mutex } ;
43use std:: time:: { Duration , Instant } ;
54
65use minifb:: { Key , MouseButton , MouseMode , Window , WindowOptions } ;
76use nalgebra:: { Rotation3 , Unit , Vector3 } ;
8- use rand:: Rng ;
9- use threadpool:: ThreadPool ;
107
118use crate :: camera:: Camera ;
129use crate :: data:: color64:: Color64 ;
1310use crate :: data:: point64:: Point64 ;
11+ use crate :: util:: render:: render_frame;
1412use crate :: util:: worlds:: World ;
1513
1614const PITCH_LIMIT : f64 = 1.553 ;
@@ -110,68 +108,6 @@ const RENDER_SCALE: u32 = 2;
110108/// Max ray bounce depth for interactive rendering. Lower = faster per ray.
111109const INTERACTIVE_MAX_DEPTH : i32 = 8 ;
112110
113- fn render_pass (
114- pool : & ThreadPool ,
115- world : & Arc < World > ,
116- camera : & Camera ,
117- accum : & mut [ Color64 ] ,
118- render_w : u32 ,
119- render_h : u32 ,
120- view_gen : u64 ,
121- generation : & Arc < AtomicU64 > ,
122- ) -> bool {
123- let ( tx, rx) = channel :: < ( u32 , Vec < Color64 > ) > ( ) ;
124- let rw = render_w as usize ;
125- let du = render_w. saturating_sub ( 1 ) . max ( 1 ) as f64 ;
126- let dv = render_h. saturating_sub ( 1 ) . max ( 1 ) as f64 ;
127-
128- let mut y = 0u32 ;
129- while y < render_h {
130- let y_end = ( y + ROWS_PER_TASK ) . min ( render_h) ;
131- let tx = tx. clone ( ) ;
132- let world = world. clone ( ) ;
133- let camera = camera. clone ( ) ;
134- let generation = Arc :: clone ( generation) ;
135- pool. execute ( move || {
136- if generation. load ( Ordering :: Acquire ) != view_gen {
137- return ;
138- }
139- let mut rng = rand:: rng ( ) ;
140- for row_y in y..y_end {
141- let flipped_y = render_h - row_y - 1 ;
142- let mut row = Vec :: with_capacity ( rw) ;
143- for x in 0 ..render_w {
144- let u = ( x as f64 + rng. random :: < f64 > ( ) ) / du;
145- let v = ( row_y as f64 + rng. random :: < f64 > ( ) ) / dv;
146- let ray = camera. get_ray ( u, v) ;
147- let c = ray. color_in_world (
148- & world. hittable ,
149- & world. background_color ,
150- INTERACTIVE_MAX_DEPTH ,
151- & mut rng,
152- ) ;
153- row. push ( c) ;
154- }
155- let _ = tx. send ( ( flipped_y, row) ) ;
156- }
157- } ) ;
158- y = y_end;
159- }
160-
161- drop ( tx) ;
162-
163- for ( flipped_y, row) in rx. iter ( ) {
164- if generation. load ( Ordering :: Acquire ) != view_gen {
165- return false ;
166- }
167- let base = flipped_y as usize * rw;
168- for ( x, c) in row. into_iter ( ) . enumerate ( ) {
169- accum[ base + x] += c;
170- }
171- }
172-
173- generation. load ( Ordering :: Acquire ) == view_gen
174- }
175111
176112/// Tonemap the render-resolution accum buffer into the full-resolution display
177113/// buffer, upscaling each render pixel to a RENDER_SCALE×RENDER_SCALE block.
@@ -200,11 +136,10 @@ fn tonemap_to_display(
200136}
201137
202138fn render_thread ( world : Arc < World > , shared : Arc < SharedRender > ) {
203- let pool = ThreadPool :: new ( num_cpus:: get ( ) ) ;
204139 let display_w = world. image_width as usize ;
205- let render_w = ( world. image_width / RENDER_SCALE ) as usize ;
206- let render_h = ( world. image_height / RENDER_SCALE ) as usize ;
207- let render_len = render_w * render_h;
140+ let render_w = world. image_width / RENDER_SCALE ;
141+ let render_h = world. image_height / RENDER_SCALE ;
142+ let render_len = ( render_w * render_h) as usize ;
208143 // Local accumulation buffer at render resolution — no mutex needed.
209144 let mut accum = vec ! [ Color64 :: new( 0. , 0. , 0. ) ; render_len] ;
210145
@@ -228,30 +163,29 @@ fn render_thread(world: Arc<World>, shared: Arc<SharedRender>) {
228163
229164 let orbit = shared. orbit . lock ( ) . unwrap ( ) . clone ( ) ;
230165 let camera = orbit. to_camera ( world. as_ref ( ) ) ;
231-
232- if !render_pass (
233- & pool,
234- & world,
235- & camera,
236- & mut accum,
237- render_w as u32 ,
238- render_h as u32 ,
239- view_gen,
240- & shared. generation ,
241- ) {
242- break ;
166+ let cancel = Some ( ( shared. generation . clone ( ) , view_gen) ) ;
167+
168+ match render_frame ( camera, world. clone ( ) , render_w, render_h, INTERACTIVE_MAX_DEPTH , ROWS_PER_TASK , 1 , cancel) {
169+ None => break ,
170+ Some ( rows) => {
171+ for ( flipped_y, row) in rows {
172+ let base = flipped_y as usize * render_w as usize ;
173+ for ( x, c) in row. into_iter ( ) . enumerate ( ) {
174+ accum[ base + x] += c;
175+ }
176+ }
177+ local_samples += 1 ;
178+ shared. samples . store ( local_samples, Ordering :: Release ) ;
179+ tonemap_to_display (
180+ & accum,
181+ local_samples,
182+ & mut shared. display . lock ( ) . unwrap ( ) ,
183+ render_w as usize ,
184+ render_h as usize ,
185+ display_w,
186+ ) ;
187+ }
243188 }
244- local_samples += 1 ;
245- shared. samples . store ( local_samples, Ordering :: Release ) ;
246-
247- tonemap_to_display (
248- & accum,
249- local_samples,
250- & mut shared. display . lock ( ) . unwrap ( ) ,
251- render_w,
252- render_h,
253- display_w,
254- ) ;
255189 }
256190 }
257191}
0 commit comments