use minifb::{Key, Window, WindowOptions}; use std::thread; use std::time::{Duration, Instant}; use std::sync::{Arc, Mutex}; use rand::Rng; const WIDTH: usize = 800; const HEIGHT: usize = 600; const BALL_RADIUS: usize = 20; // Simple ball state 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, // pixels per second vy: 150.0, color: 0xFF6464FF, // Red-ish } } } fn main() { // Start the telemetry window teleprof::start(); println!("Bouncing Ball Demo"); println!("The ball window should appear alongside the profiler"); println!("Press Space in profiler window to pause"); 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]; // Target 30 FPS 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(); teleprof::span!("main_frame"); // 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); thread::spawn(move || { pick_new_color(ball_clone); }); } // Render render(&ball, &mut 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); } // Sleep to maintain 30fps let elapsed = frame_start.elapsed(); if elapsed < frame_time { thread::sleep(frame_time - elapsed); } } } fn update_physics(ball: &Arc>, dt: f32) -> bool { teleprof::span!("update_physics"); let mut ball = ball.lock().unwrap(); // Update position ball.x += ball.vx * dt; ball.y += ball.vy * dt; let mut hit_wall = false; // Bounce off walls 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; } // Simulate some physics computation thread::sleep(Duration::from_millis(5)); hit_wall } fn pick_new_color(ball: Arc>) { teleprof::span!("pick_new_color"); // Simulate some "expensive" color selection 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); } fn render(ball: &Arc>, framebuffer: &mut [u32]) { teleprof::span!("render"); { teleprof::span!("clear_background"); // Clear to dark gray framebuffer.fill(0x2A2A2AFF); } { teleprof::span!("draw_ball"); let ball = ball.lock().unwrap(); // Draw ball as a filled circle 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 { // Check if point is inside circle 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; } } } } } { teleprof::span!("submit_frame"); // Simulate GPU submission thread::sleep(Duration::from_millis(2)); } } fn print_status(ball: &Arc>, frame: u32) { teleprof::span!("print_status"); let ball = ball.lock().unwrap(); println!( "Frame {}: Ball at ({:.1}, {:.1})", frame, ball.x, ball.y ); }