Last active
March 5, 2025 12:28
-
-
Save qatoqat/9c429db6c39d133e7d3f32b0329c49d7 to your computer and use it in GitHub Desktop.
SDL3 RGB Circles Benchmark - Surface to Texture vs Streaming Texture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Total textures created (Surface -> Texture): 300 | |
| // Time taken: 2.1722655s | |
| // Memory Usage: 1533 MB | |
| // Total textures created (Streaming Texture): 300 | |
| // Time taken: 798.8623ms | |
| // Memory Usage: 1567 MB | |
| use std::ffi::{c_int, CString}; | |
| use sdl3_sys::everything::*; | |
| use std::ptr::{null, null_mut, NonNull}; | |
| use std::time::Instant; | |
| #[cfg(target_os = "linux")] | |
| fn print_memory_usage() { | |
| use std::fs; | |
| if let Ok(status) = fs::read_to_string("/proc/self/status") { | |
| if let Some(line) = status.lines().find(|l| l.starts_with("VmRSS:")) { | |
| println!("{}", line); | |
| } | |
| } | |
| } | |
| #[cfg(target_os = "windows")] | |
| fn print_memory_usage() { | |
| use std::mem::zeroed; | |
| use winapi::um::processthreadsapi::GetCurrentProcess; | |
| use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}; | |
| unsafe { | |
| let mut pmc: PROCESS_MEMORY_COUNTERS = zeroed(); | |
| if GetProcessMemoryInfo( | |
| GetCurrentProcess(), | |
| &mut pmc, | |
| std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32, | |
| ) != 0 | |
| { | |
| println!("Memory Usage: {} KB", pmc.WorkingSetSize / 1024); | |
| } | |
| } | |
| } | |
| #[cfg(not(any(target_os = "linux", target_os = "windows")))] | |
| fn print_memory_usage() { | |
| println!("Memory usage tracking not implemented for this OS."); | |
| } | |
| fn generate_rgb_circle(width: usize, height: usize, rotation_degrees: f32) -> Vec<u8> { | |
| let mut pixels = vec![0; width * height * 4]; | |
| let center_x = (width / 2) as f32; | |
| let center_y = (height / 2) as f32; | |
| let radius = center_x.min(center_y); | |
| let rotation_radians = rotation_degrees.to_radians(); | |
| let cos_theta = rotation_radians.cos(); | |
| let sin_theta = rotation_radians.sin(); | |
| for y in 0..height { | |
| for x in 0..width { | |
| let dx = x as f32 - center_x; | |
| let dy = y as f32 - center_y; | |
| let distance = (dx * dx + dy * dy).sqrt(); | |
| if distance <= radius { | |
| let rotated_x = cos_theta * dx - sin_theta * dy; | |
| let rotated_y = sin_theta * dx + cos_theta * dy; | |
| let fx = (rotated_x / radius + 1.0) * 0.5; | |
| let fy = (rotated_y / radius + 1.0) * 0.5; | |
| let r = ((1.0 - fx) * 255.0) as u8; | |
| let g = (fx * 255.0) as u8; | |
| let b = (fy * 255.0) as u8; | |
| let index = (y * width + x) * 4; | |
| pixels[index] = 255; | |
| pixels[index + 1] = b; | |
| pixels[index + 2] = g; | |
| pixels[index + 3] = r; | |
| } | |
| } | |
| } | |
| pixels | |
| } | |
| fn draw_to_surface_and_texture( | |
| renderer: *mut SDL_Renderer, | |
| rgb_circles: &Vec<Vec<u8>>, | |
| width: usize, | |
| height: usize, | |
| iterations: usize, | |
| ) { | |
| unsafe { | |
| let start = Instant::now(); | |
| let mut textures = vec![]; | |
| for i in 0..iterations { | |
| let surface = | |
| SDL_CreateSurface(width as c_int, height as c_int, SDL_PIXELFORMAT_RGBA8888); | |
| if SDL_LockSurface(surface) { | |
| let surface_ptr = (*surface).pixels as *mut u8; | |
| let len = (*surface).pitch as usize * (*surface).h as usize; | |
| std::ptr::copy_nonoverlapping(rgb_circles[i].as_ptr(), surface_ptr, len); | |
| SDL_UnlockSurface(surface); | |
| } | |
| let texture = SDL_CreateTextureFromSurface(renderer, surface); | |
| SDL_DestroySurface(surface); | |
| if !texture.is_null() { | |
| textures.push(NonNull::new(texture).unwrap()); | |
| } | |
| } | |
| println!( | |
| "Total textures created (Surface -> Texture): {}", | |
| textures.len() | |
| ); | |
| let duration = start.elapsed(); | |
| println!("Time taken: {:?}", duration); | |
| print_memory_usage(); | |
| // Now render all textures | |
| render_textures(renderer, &textures, iterations); | |
| for texture in textures { | |
| SDL_DestroyTexture(texture.as_ptr()); | |
| } | |
| } | |
| } | |
| fn draw_to_texture( | |
| renderer: *mut SDL_Renderer, | |
| rgb_circles: &Vec<Vec<u8>>, | |
| width: usize, | |
| height: usize, | |
| iterations: usize, | |
| ) { | |
| unsafe { | |
| let start = Instant::now(); | |
| let mut textures = vec![]; | |
| for i in 0..iterations { | |
| let texture = SDL_CreateTexture( | |
| renderer, | |
| SDL_PIXELFORMAT_RGBA8888, | |
| SDL_TEXTUREACCESS_STREAMING, | |
| width as c_int, | |
| height as c_int, | |
| ); | |
| SDL_UpdateTexture( | |
| texture, | |
| null(), | |
| rgb_circles[i].as_ptr() as *const _, | |
| (width * 4) as c_int, | |
| ); | |
| if !texture.is_null() { | |
| textures.push(NonNull::new(texture).unwrap()); | |
| } | |
| } | |
| println!( | |
| "Total textures created (Streaming Texture): {}", | |
| textures.len() | |
| ); | |
| let duration = start.elapsed(); | |
| println!("Time taken: {:?}", duration); | |
| print_memory_usage(); | |
| render_textures(renderer, &textures, iterations); | |
| for texture in textures { | |
| SDL_DestroyTexture(texture.as_ptr()); | |
| } | |
| } | |
| } | |
| fn render_textures( | |
| renderer: *mut SDL_Renderer, | |
| textures: &Vec<NonNull<SDL_Texture>>, | |
| iterations: usize, | |
| ) { | |
| unsafe { | |
| for texture in textures { | |
| let mut event = SDL_Event::default(); | |
| if SDL_PollEvent(&mut event) { | |
| match event.r#type { | |
| _ => {} | |
| } | |
| } | |
| SDL_RenderClear(renderer); | |
| SDL_RenderTexture(renderer, texture.as_ptr(), null_mut(), null_mut()); | |
| SDL_RenderPresent(renderer); | |
| SDL_Delay((5000 / iterations) as Uint32); | |
| } | |
| } | |
| } | |
| use crossbeam::channel; | |
| use sdl3_sys::everything::*; | |
| use std::sync::{ | |
| atomic::{AtomicUsize, Ordering}, Arc, | |
| Mutex, | |
| }; | |
| use std::thread; | |
| /// Updates the window title with the given text | |
| unsafe fn update_window_title(window: *mut SDL_Window, text: &str) { | |
| let title = format!("{} - SDL3 Benchmark", text); | |
| let title = CString::new(title).unwrap(); | |
| SDL_SetWindowTitle(window, title.as_ptr() as *const i8); | |
| } | |
| /// Generates multiple RGB circles concurrently, updating progress in window title | |
| fn generate_rgb_circles_concurrently( | |
| window: *mut SDL_Window, | |
| width: usize, | |
| height: usize, | |
| iterations: usize, | |
| ) -> Vec<Vec<u8>> { | |
| let (tx, rx) = channel::unbounded(); // Channel to send generated circles | |
| let rgb_circles = Arc::new(Mutex::new(vec![vec![]; iterations])); // Pre-allocate storage | |
| let completed = Arc::new(AtomicUsize::new(0)); // Tracks completed circles | |
| // Spawn worker threads | |
| let handles: Vec<_> = (0..iterations) | |
| .map(|i| { | |
| let tx = tx.clone(); | |
| let completed = Arc::clone(&completed); | |
| thread::spawn(move || { | |
| let circle = | |
| generate_rgb_circle(width, height, (i as f32 / iterations as f32) * 360.0); | |
| tx.send((i, circle)).unwrap(); | |
| completed.fetch_add(1, Ordering::Relaxed); | |
| }) | |
| }) | |
| .collect(); | |
| // Collect results & update window title | |
| for _ in 0..iterations { | |
| let (i, circle) = rx.recv().unwrap(); | |
| rgb_circles.lock().unwrap()[i] = circle; | |
| // Update window title with progress | |
| let count = completed.load(Ordering::Relaxed); | |
| unsafe { | |
| update_window_title( | |
| window, | |
| &format!("Loading Circles: {}/{}", count, iterations), | |
| ) | |
| }; | |
| } | |
| // Wait for all threads to finish | |
| for handle in handles { | |
| handle.join().unwrap(); | |
| } | |
| Arc::try_unwrap(rgb_circles).unwrap().into_inner().unwrap() | |
| } | |
| fn main() { | |
| let (width, height) = (800, 800); | |
| let iterations = 300; | |
| let fps = iterations / 5; | |
| unsafe { | |
| SDL_Init(SDL_INIT_VIDEO); | |
| let window = SDL_CreateWindow( | |
| b"SDL3 Benchmark\0".as_ptr() as *const i8, | |
| width as c_int, | |
| height as c_int, | |
| SDL_WINDOW_RESIZABLE, | |
| ); | |
| let now = Instant::now(); | |
| let rgb_circles = generate_rgb_circles_concurrently(window, width, height, iterations); | |
| println!("Loaded circles in {:?}", now.elapsed()); | |
| let renderer = SDL_CreateRenderer(window, null()); | |
| update_window_title( | |
| window, | |
| &format!("(Streaming Texture) - {fps} fps in 5 secs"), | |
| ); | |
| draw_to_texture(renderer, &rgb_circles, width, height, iterations); | |
| update_window_title( | |
| window, | |
| &format!("(Surface -> Texture) - {fps} fps in 5 secs"), | |
| ); | |
| draw_to_surface_and_texture(renderer, &rgb_circles, width, height, iterations); | |
| SDL_DestroyRenderer(renderer); | |
| SDL_DestroyWindow(window); | |
| SDL_Quit(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment