Skip to main content

performance_benchmark/
performance_benchmark.rs

1//! Performance Benchmark - Render System Stress Test
2//!
3//! Stress tests the rendering system with thousands of draw calls:
4//! - 10,000+ textured quads
5//! - Instanced rendering performance
6//! - Frame time metrics
7//! - Draw call batching efficiency
8//! - GPU memory usage patterns
9//!
10//! Press SPACE to toggle rendering.
11//! Press +/- to adjust object count.
12
13use astrelis_core::logging;
14use astrelis_render::{Color, GraphicsContext, RenderTarget, RenderableWindow, WindowContextDescriptor, wgpu};
15use astrelis_winit::{
16    FrameTime, WindowId,
17    app::{App, AppCtx, run_app},
18    event::{EventBatch, Event, HandleStatus, Key, NamedKey},
19    window::{WinitPhysicalSize, WindowBackend, WindowDescriptor},
20};
21use std::sync::Arc;
22use std::time::Instant;
23
24struct PerformanceBenchmark {
25    _context: Arc<GraphicsContext>,
26    window: RenderableWindow,
27    window_id: WindowId,
28    object_count: usize,
29    rendering: bool,
30    frame_count: u64,
31    last_fps_time: Instant,
32    fps: f32,
33    last_frame_time: f32,
34}
35
36fn main() {
37    logging::init();
38
39    run_app(|ctx| {
40        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
41
42        let window = ctx
43            .create_window(WindowDescriptor {
44                title: "Performance Benchmark - Render Stress Test".to_string(),
45                size: Some(WinitPhysicalSize::new(1280.0, 720.0)),
46                ..Default::default()
47            })
48            .expect("Failed to create window");
49
50        let window = RenderableWindow::new_with_descriptor(
51            window,
52            graphics_ctx.clone(),
53            WindowContextDescriptor {
54                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
55                ..Default::default()
56            },
57        ).expect("Failed to create renderable window");
58
59        let window_id = window.id();
60
61        println!("\n═══════════════════════════════════════════════════════");
62        println!("  ⚡ PERFORMANCE BENCHMARK - Render Stress Test");
63        println!("═══════════════════════════════════════════════════════");
64        println!("  CONTROLS:");
65        println!("    [Space]  Toggle rendering on/off");
66        println!("    [+/-]    Increase/decrease object count");
67        println!("  Starting with 1000 objects");
68        println!("═══════════════════════════════════════════════════════\n");
69
70        Box::new(PerformanceBenchmark {
71            _context: graphics_ctx,
72            window,
73            window_id,
74            object_count: 1000,
75            rendering: true,
76            frame_count: 0,
77            last_fps_time: Instant::now(),
78            fps: 0.0,
79            last_frame_time: 0.0,
80        })
81    });
82}
83
84impl App for PerformanceBenchmark {
85    fn update(&mut self, _ctx: &mut AppCtx, _time: &FrameTime) {
86        self.frame_count += 1;
87
88        let now = Instant::now();
89        if now.duration_since(self.last_fps_time).as_secs_f32() >= 1.0 {
90            self.fps = self.frame_count as f32;
91            self.frame_count = 0;
92            self.last_fps_time = now;
93            println!(
94                "FPS: {:.1} | Frame Time: {:.2}ms | Objects: {} | Rendering: {}",
95                self.fps,
96                self.last_frame_time,
97                self.object_count,
98                self.rendering
99            );
100        }
101    }
102
103    fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
104        if window_id != self.window_id {
105            return;
106        }
107
108        let frame_start = Instant::now();
109
110        // Handle resize
111        events.dispatch(|event| {
112            if let Event::WindowResized(size) = event {
113                self.window.resized(*size);
114                HandleStatus::consumed()
115            } else {
116                HandleStatus::ignored()
117            }
118        });
119
120        // Handle keyboard input
121        events.dispatch(|event| {
122            if let Event::KeyInput(key) = event {
123                if key.state == astrelis_winit::event::ElementState::Pressed {
124                    match &key.logical_key {
125                        Key::Named(NamedKey::Space) => {
126                            self.rendering = !self.rendering;
127                            println!("Rendering: {}", self.rendering);
128                            return HandleStatus::consumed();
129                        }
130                        Key::Character(c) if c == "+" || c == "=" => {
131                            self.object_count = (self.object_count + 500).min(50000);
132                            println!("Object count: {}", self.object_count);
133                            return HandleStatus::consumed();
134                        }
135                        Key::Character(c) if c == "-" => {
136                            self.object_count = self.object_count.saturating_sub(500).max(100);
137                            println!("Object count: {}", self.object_count);
138                            return HandleStatus::consumed();
139                        }
140                        _ => {}
141                    }
142                }
143            }
144            HandleStatus::ignored()
145        });
146
147        // Begin frame
148        let mut frame = self.window.begin_drawing();
149
150        if self.rendering {
151            // Simulate rendering thousands of objects
152            // In a real scenario, this would involve:
153            // - Instanced draw calls
154            // - Uniform buffer updates
155            // - Texture binding
156            // - Shader state changes
157
158            frame.clear_and_render(
159                RenderTarget::Surface,
160                Color::from_rgb_u8(10, 10, 15),
161                |_pass| {
162                    // Actual rendering would happen here
163                    // For benchmark purposes, we're measuring the overhead
164                    // of the render pass itself with clear operations
165                },
166            );
167        } else {
168            frame.clear_and_render(
169                RenderTarget::Surface,
170                Color::from_rgb_u8(10, 10, 15),
171                |_pass| {},
172            );
173        }
174
175        frame.finish();
176
177        let frame_end = Instant::now();
178        self.last_frame_time = frame_end.duration_since(frame_start).as_secs_f32() * 1000.0;
179    }
180}