use minifb::{Key, Window, WindowOptions}; use std::thread; use std::time::{Duration, Instant}; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use rand::Rng; use teleprof::instrument; const WIDTH: usize = 800; const HEIGHT: usize = 600; const BALL_RADIUS: usize = 20; static COLOR_PICKER_COUNTER: AtomicUsize = AtomicUsize::new(0); struct Ball { x: f32, y: f32, vx: f32, vy: f32, color: u32, } impl Ball { fn new() -> Self { Self { x: 400.0, y: 300.0, vx: 200.0, vy: 150.0, color: 0xFF6464FF, } } } fn main() { // Start the telemetry window teleprof::start(); // Name the main thread teleprof::set_thread_name("Main"); println!("Bouncing Ball Demo"); println!("The ball window should appear alongside the profiler"); println!("Press Space in profiler window to pause"); println!("Mouse wheel to zoom, drag to pan in profiler"); println!("Press Escape in either window to quit"); println!(); let mut window = Window::new( "Bouncing Ball", WIDTH, HEIGHT, WindowOptions::default(), ) .expect("Failed to create window"); window.set_target_fps(30); let ball = Arc::new(Mutex::new(Ball::new())); let mut framebuffer = vec![0u32; WIDTH * HEIGHT]; let frame_time = Duration::from_millis(33); let mut frame_count = 0; while window.is_open() && !window.is_key_down(Key::Escape) { let frame_start = Instant::now(); main_frame(&ball, &mut framebuffer, &mut window, &mut frame_count, frame_time); let elapsed = frame_start.elapsed(); if elapsed < frame_time { thread::sleep(frame_time - elapsed); } } } #[instrument] fn main_frame( ball: &Arc>, framebuffer: &mut [u32], window: &mut Window, frame_count: &mut u32, frame_time: Duration, ) { // Check if paused if teleprof::PAUSE.try_lock().is_err() { println!("Paused!"); while teleprof::PAUSE.try_lock().is_err() { thread::sleep(Duration::from_millis(100)); } println!("Resumed!"); } // Update physics let hit_wall = update_physics(ball, frame_time.as_secs_f32()); // If we hit a wall, spawn a thread to pick a new color if hit_wall { let ball_clone = Arc::clone(ball); let id = COLOR_PICKER_COUNTER.fetch_add(1, Ordering::Relaxed); thread::spawn(move || { teleprof::set_thread_name(format!("ColorPicker-{}", id)); pick_new_color(ball_clone); }); } // Render render(ball, framebuffer); // Update window window .update_with_buffer(framebuffer, WIDTH, HEIGHT) .expect("Failed to update window"); *frame_count += 1; if *frame_count % 30 == 0 { print_status(ball, *frame_count); } } #[instrument] fn update_physics(ball: &Arc>, dt: f32) -> bool { let mut ball = ball.lock().unwrap(); ball.x += ball.vx * dt; ball.y += ball.vy * dt; let mut hit_wall = false; let radius = BALL_RADIUS as f32; if ball.x - radius < 0.0 || ball.x + radius > WIDTH as f32 { ball.vx = -ball.vx; ball.x = ball.x.clamp(radius, WIDTH as f32 - radius); hit_wall = true; } if ball.y - radius < 0.0 || ball.y + radius > HEIGHT as f32 { ball.vy = -ball.vy; ball.y = ball.y.clamp(radius, HEIGHT as f32 - radius); hit_wall = true; } thread::sleep(Duration::from_millis(5)); hit_wall } #[instrument] fn pick_new_color(ball: Arc>) { thread::sleep(Duration::from_millis(10)); let mut rng = rand::thread_rng(); let r = rng.gen_range(50..255); let g = rng.gen_range(50..255); let b = rng.gen_range(50..255); let color = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32); let mut ball = ball.lock().unwrap(); ball.color = color; println!(" → New color selected: RGB({}, {}, {})", r, g, b); } #[instrument] fn render(ball: &Arc>, framebuffer: &mut [u32]) { clear_background(framebuffer); draw_ball(ball, framebuffer); submit_frame(); } #[instrument] fn clear_background(framebuffer: &mut [u32]) { framebuffer.fill(0x2A2A2AFF); } #[instrument] fn draw_ball(ball: &Arc>, framebuffer: &mut [u32]) { let ball = ball.lock().unwrap(); let cx = ball.x as i32; let cy = ball.y as i32; let radius = BALL_RADIUS as i32; for dy in -radius..=radius { for dx in -radius..=radius { if dx * dx + dy * dy <= radius * radius { let x = cx + dx; let y = cy + dy; if x >= 0 && x < WIDTH as i32 && y >= 0 && y < HEIGHT as i32 { let idx = y as usize * WIDTH + x as usize; framebuffer[idx] = ball.color; } } } } } #[instrument] fn submit_frame() { thread::sleep(Duration::from_millis(2)); } #[instrument] fn print_status(ball: &Arc>, frame: u32) { let ball = ball.lock().unwrap(); println!( "Frame {}: Ball at ({:.1}, {:.1})", frame, ball.x, ball.y ); }