@@ -26,7 +26,7 @@ struct InstanceData {
2626 pulse : f32 ,
2727}
2828
29- #[ derive( Default ) ]
29+ #[ derive( Default , Clone ) ]
3030struct VisState {
3131 positions : [ Vec3 ; 3 ] ,
3232 colors : [ Vec4 ; 3 ] ,
@@ -48,6 +48,8 @@ struct GpuState<'w> {
4848 height : u32 ,
4949 last_frame : Instant ,
5050 shared : Arc < Mutex < VisState > > ,
51+ // Local snapshot to render when shared state is locked by audio thread
52+ last_vis_snapshot : VisState ,
5153}
5254
5355impl < ' w > GpuState < ' w > {
@@ -212,6 +214,9 @@ impl<'w> GpuState<'w> {
212214 multiview : None ,
213215 } ) ;
214216
217+ // Take an initial snapshot of visual state (non-blocking best-effort)
218+ let initial_snapshot = shared. lock ( ) . map ( |v| v. clone ( ) ) . unwrap_or_default ( ) ;
219+
215220 Ok ( Self {
216221 window,
217222 surface,
@@ -227,6 +232,7 @@ impl<'w> GpuState<'w> {
227232 height : size. height ,
228233 last_frame : Instant :: now ( ) ,
229234 shared,
235+ last_vis_snapshot : initial_snapshot,
230236 } )
231237 }
232238
@@ -267,35 +273,46 @@ impl<'w> GpuState<'w> {
267273 } ) ,
268274 ) ;
269275
270- // Build instance data from shared state
271- let mut vis = self . shared . lock ( ) . unwrap ( ) ;
272- // decay pulses
276+ // Build instance data from shared state without blocking the render thread.
277+ // If the mutex is held by the audio scheduler, render using the last snapshot.
273278 let dt_sec = dt. as_secs_f32 ( ) ;
274- for p in vis. pulses . iter_mut ( ) {
275- * p = ( * p - dt_sec * 1.5 ) . max ( 0.0 ) ;
276- }
279+ let vis_local: VisState = if let Ok ( mut vis) = self . shared . try_lock ( ) {
280+ // Decay pulses in shared state and copy snapshot
281+ for p in vis. pulses . iter_mut ( ) {
282+ * p = ( * p - dt_sec * 1.5 ) . max ( 0.0 ) ;
283+ }
284+ let snapshot = vis. clone ( ) ;
285+ self . last_vis_snapshot = snapshot. clone ( ) ;
286+ snapshot
287+ } else {
288+ // Decay locally; avoid writing back to shared state
289+ for p in self . last_vis_snapshot . pulses . iter_mut ( ) {
290+ * p = ( * p - dt_sec * 1.5 ) . max ( 0.0 ) ;
291+ }
292+ self . last_vis_snapshot . clone ( )
293+ } ;
294+
277295 let z_offset = app_core:: z_offset_vec3 ( ) ;
278296 let spread = SPREAD ;
279297 let positions = [
280- vis . positions [ 0 ] * spread + z_offset,
281- vis . positions [ 1 ] * spread + z_offset,
282- vis . positions [ 2 ] * spread + z_offset,
298+ vis_local . positions [ 0 ] * spread + z_offset,
299+ vis_local . positions [ 1 ] * spread + z_offset,
300+ vis_local . positions [ 2 ] * spread + z_offset,
283301 ] ;
284302 let scales = [
285- BASE_SCALE + vis . pulses [ 0 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
286- BASE_SCALE + vis . pulses [ 1 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
287- BASE_SCALE + vis . pulses [ 2 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
303+ BASE_SCALE + vis_local . pulses [ 0 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
304+ BASE_SCALE + vis_local . pulses [ 1 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
305+ BASE_SCALE + vis_local . pulses [ 2 ] * app_core:: SCALE_PULSE_MULTIPLIER ,
288306 ] ;
289307 let mut instances: Vec < InstanceData > = Vec :: with_capacity ( 3 ) ;
290308 for i in 0 ..3 {
291309 instances. push ( InstanceData {
292310 pos : positions[ i] . to_array ( ) ,
293311 scale : scales[ i] ,
294- color : vis . colors [ i] . to_array ( ) ,
295- pulse : vis . pulses [ i] ,
312+ color : vis_local . colors [ i] . to_array ( ) ,
313+ pulse : vis_local . pulses [ i] ,
296314 } ) ;
297315 }
298- drop ( vis) ;
299316 self . queue
300317 . write_buffer ( & self . instance_vb , 0 , bytemuck:: cast_slice ( & instances) ) ;
301318
@@ -628,13 +645,16 @@ fn start_audio_engine(
628645 }
629646 drop ( guard) ;
630647 // Kick visual pulses
631- let mut vis = vis_clone. lock ( ) . unwrap ( ) ;
632- for ev in & events {
633- let i = ev. voice_index . min ( 2 ) ;
634- vis. pulses [ i] = ( vis. pulses [ i] + ev. velocity ) . min ( 1.5 ) ;
648+ // Try to update visual pulses without blocking; if busy, skip this tick
649+ if let Ok ( mut vis) = vis_clone. try_lock ( ) {
650+ for ev in & events {
651+ let i = ev. voice_index . min ( 2 ) ;
652+ vis. pulses [ i] = ( vis. pulses [ i] + ev. velocity ) . min ( 1.5 ) ;
653+ }
635654 }
636655 }
637- std:: thread:: sleep ( Duration :: from_millis ( 15 ) ) ;
656+ // Small sleep to limit CPU without inducing long stalls
657+ std:: thread:: sleep ( Duration :: from_millis ( 8 ) ) ;
638658 }
639659 } )
640660 . ok ( ) ?;
0 commit comments