(&mut self)
| 121 | } |
| 122 | |
| 123 | pub fn end_frame(&mut self) { |
| 124 | if self.direct_2d_mode { |
| 125 | // Fast path for pure-2D games: render direct to the swapchain, |
| 126 | // skipping scene prep, shadow maps, HDR/tonemap, SSAO, bloom, |
| 127 | // SDF/WSRC bakes and mesh-card capture. On mobile GPUs the |
| 128 | // full deferred pipeline easily costs tens of ms/frame even |
| 129 | // when the scene graph is empty; this path typically hits |
| 130 | // 60 fps on the same device. |
| 131 | self.profiler.begin("render_total"); |
| 132 | self.renderer.end_frame(); |
| 133 | self.profiler.end("render_total"); |
| 134 | } else { |
| 135 | self.profiler.begin("scene_prepare"); |
| 136 | self.scene.prepare( |
| 137 | &self.renderer.device, |
| 138 | &self.renderer.queue, |
| 139 | &self.renderer.vp_matrix(), |
| 140 | &self.renderer.prev_vp_matrix, |
| 141 | self.renderer.uniform_3d_layout(), |
| 142 | ); |
| 143 | self.scene.prepare_materials(&self.renderer); |
| 144 | self.profiler.end("scene_prepare"); |
| 145 | |
| 146 | // Phase 6 — drain hot-reload events and rebuild any |
| 147 | // material whose .wgsl file changed on disk. Cheap when |
| 148 | // nothing has changed (just a try_recv on an empty queue). |
| 149 | self.renderer.poll_material_hot_reload(); |
| 150 | |
| 151 | // Sync material-system PerFrame + PerView UBOs with the |
| 152 | // current clock + camera before dispatching any queued |
| 153 | // material draws. Without this, group 1 (view/proj/camera/ |
| 154 | // lights/shadow) is zero and shaders that read e.g. |
| 155 | // `view.view_proj` produce offscreen geometry. |
| 156 | let t = self.get_time() as f32; |
| 157 | let dt = self.delta_time as f32; |
| 158 | self.renderer.material_system_begin_frame(t, dt); |
| 159 | |
| 160 | self.profiler.begin("render_total"); |
| 161 | self.renderer.end_frame_with_scene(&mut self.scene, &mut self.profiler); |
| 162 | self.profiler.end("render_total"); |
| 163 | } |
| 164 | |
| 165 | self.profiler.frame_end(&self.renderer.device); |
| 166 | self.input.end_frame(); |
| 167 | |
| 168 | // Vsync (PresentMode::Fifo, the wgpu default) already caps frame rate. |
| 169 | // Only apply CPU sleep-based cap when vsync is not active. |
| 170 | // On WASM, frame pacing is handled by requestAnimationFrame. |
| 171 | #[cfg(not(target_arch = "wasm32"))] |
| 172 | if self.target_fps > 0.0 && !self.renderer.vsync_active() { |
| 173 | let target_frame_time = 1.0 / self.target_fps; |
| 174 | let elapsed = self.last_frame_time.elapsed().as_secs_f64(); |
| 175 | if elapsed < target_frame_time { |
| 176 | let sleep_time = target_frame_time - elapsed; |
| 177 | std::thread::sleep(std::time::Duration::from_secs_f64(sleep_time)); |
| 178 | } |
| 179 | } |
| 180 | } |
nothing calls this directly
no test coverage detected