profiling_demo/
profiling_demo.rs1use std::sync::Arc;
17
18use astrelis_core::logging;
19use astrelis_core::profiling::{
20 ProfilingBackend, init_profiling, new_frame, profile_function, profile_scope,
21};
22use astrelis_render::{
23 Color, GraphicsContext, GraphicsContextDescriptor, RenderWindow, RenderWindowBuilder, wgpu,
24};
25use astrelis_winit::{
26 FrameTime, WindowId,
27 app::{App, AppCtx, run_app},
28 event::EventBatch,
29 window::{WindowDescriptor, WinitPhysicalSize},
30};
31
32struct ProfilingDemo {
33 #[allow(dead_code)]
34 context: Arc<GraphicsContext>,
35 window: RenderWindow,
36 window_id: WindowId,
37 frame_count: u64,
38}
39
40fn main() {
41 logging::init();
42 init_profiling(ProfilingBackend::PuffinHttp);
43
44 run_app(|ctx| {
45 profile_function!();
46
47 let graphics_ctx = pollster::block_on(GraphicsContext::new_owned_with_descriptor(
49 GraphicsContextDescriptor::new()
50 .request_capability::<astrelis_render::gpu_profiling::GpuFrameProfiler>(),
51 ))
52 .expect("Failed to create graphics context");
53
54 let window = ctx
55 .create_window(WindowDescriptor {
56 title: "Profiling Demo — CPU + GPU".to_string(),
57 size: Some(WinitPhysicalSize::new(800.0, 600.0)),
58 ..Default::default()
59 })
60 .expect("Failed to create window");
61
62 #[allow(unused_mut)]
63 let mut window = RenderWindowBuilder::new()
64 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
65 .with_depth_default()
66 .build(window, graphics_ctx.clone())
67 .expect("Failed to create render window");
68
69 let window_id = window.id();
70
71 let has_gpu_profiling;
73 #[cfg(feature = "gpu-profiling")]
74 {
75 match astrelis_render::gpu_profiling::GpuFrameProfiler::new(&graphics_ctx) {
76 Ok(profiler) => {
77 let has_timestamps = profiler.has_timestamp_queries();
78 window.set_gpu_profiler(Arc::new(profiler));
79 has_gpu_profiling = true;
80 if has_timestamps {
81 println!(" GPU profiling: enabled with TIMESTAMP_QUERY (full timing)");
82 } else {
83 println!(" GPU profiling: enabled (debug groups only, no timing data)");
84 println!(" TIMESTAMP_QUERY not supported by this GPU");
85 }
86 }
87 Err(e) => {
88 has_gpu_profiling = false;
89 tracing::warn!("Failed to create GPU profiler: {e}. GPU profiling disabled.");
90 println!(" GPU profiling: failed to create profiler");
91 }
92 }
93 }
94 #[cfg(not(feature = "gpu-profiling"))]
95 {
96 has_gpu_profiling = false;
97 }
98
99 println!();
100 println!("═══════════════════════════════════════════════════");
101 println!(" PROFILING DEMO — CPU + GPU");
102 println!("═══════════════════════════════════════════════════");
103 println!();
104 println!(" CPU profiling: enabled (puffin)");
105 if !has_gpu_profiling {
106 #[cfg(not(feature = "gpu-profiling"))]
107 println!(" GPU profiling: disabled (compile with --features gpu-profiling)");
108 }
109 println!();
110 println!(" Open puffin_viewer at 127.0.0.1:8585 to see the flame graph.");
111 println!(" Install with: cargo install puffin_viewer");
112 println!("═══════════════════════════════════════════════════");
113 println!();
114
115 Box::new(ProfilingDemo {
116 context: graphics_ctx,
117 window,
118 window_id,
119 frame_count: 0,
120 })
121 });
122}
123
124impl App for ProfilingDemo {
125 fn update(&mut self, _ctx: &mut AppCtx, _time: &FrameTime) {
126 profile_function!();
127
128 {
130 profile_scope!("simulate_game_logic");
131 let mut _sum = 0.0f64;
132 for i in 0..1000 {
133 _sum += (i as f64).sin();
134 }
135 }
136
137 self.frame_count += 1;
138 if self.frame_count.is_multiple_of(300) {
139 tracing::info!("Frame {}", self.frame_count);
140 }
141 }
142
143 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
144 new_frame();
145 profile_function!();
146
147 if window_id != self.window_id {
148 return;
149 }
150
151 events.dispatch(|event| {
152 if let astrelis_winit::event::Event::WindowResized(size) = event {
153 self.window.resized(*size);
154 astrelis_winit::event::HandleStatus::consumed()
155 } else {
156 astrelis_winit::event::HandleStatus::ignored()
157 }
158 });
159
160 let t = (self.frame_count as f32 * 0.01).sin() * 0.5 + 0.5;
162 let clear_color = Color::rgb(0.05 + t * 0.1, 0.05, 0.15 + (1.0 - t) * 0.1);
163
164 let Some(frame) = self.window.begin_frame() else {
168 return; };
170
171 {
172 profile_scope!("render_frame");
173 let _pass = frame
174 .render_pass()
175 .clear_color(clear_color)
176 .label("profiling_pass")
177 .build();
178 profile_scope!("draw_commands");
179 }
181 {
185 profile_scope!("post_render");
186 let _ = &self.context;
187 }
188 }
189}