1use crate::engine::ApplicationLoopController;
24use crate::scene::Scene;
25use crate::{
26 asset::manager::ResourceManager,
27 core::{
28 instant::Instant,
29 log::{Log, MessageKind},
30 task::TaskPool,
31 },
32 engine::{
33 Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext,
34 },
35 event::{Event, WindowEvent},
36 event_loop::{ControlFlow, EventLoop},
37 plugin::Plugin,
38 utils::translate_event,
39 window::WindowAttributes,
40};
41use clap::Parser;
42use fyrox_core::pool::Handle;
43use fyrox_resource::io::FsResourceIo;
44use fyrox_ui::constructor::new_widget_constructor_container;
45use std::cell::Cell;
46use std::collections::VecDeque;
47use std::time::Duration;
48use std::{
49 ops::{Deref, DerefMut},
50 sync::Arc,
51};
52use winit::event_loop::ActiveEventLoop;
53
54#[derive(Parser, Debug, Default)]
55#[clap(author, version, about, long_about = None)]
56struct Args {
57 #[clap(short, long, default_value = None)]
58 override_scene: Option<String>,
59}
60
61pub struct Executor {
63 event_loop: Option<EventLoop<()>>,
64 engine: Engine,
65 desired_update_rate: f32,
66 throttle_threshold: f32,
67 throttle_frame_interval: usize,
68 resource_hot_reloading: bool,
69}
70
71impl Deref for Executor {
72 type Target = Engine;
73
74 fn deref(&self) -> &Self::Target {
75 &self.engine
76 }
77}
78
79impl DerefMut for Executor {
80 fn deref_mut(&mut self) -> &mut Self::Target {
81 &mut self.engine
82 }
83}
84
85impl Executor {
86 pub const DEFAULT_UPDATE_RATE: f32 = 60.0;
88 pub const DEFAULT_TIME_STEP: f32 = 1.0 / Self::DEFAULT_UPDATE_RATE;
90
91 pub fn from_params(
95 event_loop: Option<EventLoop<()>>,
96 graphics_context_params: GraphicsContextParams,
97 ) -> Self {
98 let serialization_context = Arc::new(SerializationContext::new());
99 let task_pool = Arc::new(TaskPool::new());
100 let io = Arc::new(FsResourceIo);
101 let engine = Engine::new(EngineInitParams {
102 graphics_context_params,
103 resource_manager: ResourceManager::new(io, task_pool.clone()),
104 serialization_context,
105 task_pool,
106 widget_constructors: Arc::new(new_widget_constructor_container()),
107 dyn_type_constructors: Default::default(),
108 })
109 .unwrap();
110
111 Self {
112 event_loop,
113 engine,
114 desired_update_rate: Self::DEFAULT_UPDATE_RATE,
115 throttle_threshold: 2.0 * Self::DEFAULT_TIME_STEP,
116 throttle_frame_interval: 5,
117 resource_hot_reloading: true,
118 }
119 }
120
121 pub fn new(event_loop: Option<EventLoop<()>>) -> Self {
125 let mut window_attributes = WindowAttributes::default();
126 window_attributes.resizable = true;
127 window_attributes.title = "Fyrox Game".to_string();
128
129 Self::from_params(
130 event_loop,
131 GraphicsContextParams {
132 window_attributes,
133 vsync: true,
134 msaa_sample_count: None,
135 graphics_server_constructor: Default::default(),
136 named_objects: false,
137 },
138 )
139 }
140
141 pub fn set_resource_hot_reloading_enabled(&mut self, enabled: bool) {
149 self.resource_hot_reloading = enabled;
150 }
151
152 pub fn is_resource_hot_reloading_enabled(&self) -> bool {
154 self.resource_hot_reloading
155 }
156
157 pub fn set_throttle_threshold(&mut self, threshold: f32) {
169 self.throttle_threshold = threshold.max(0.001);
170 }
171
172 pub fn throttle_threshold(&self) -> f32 {
174 self.throttle_threshold
175 }
176
177 pub fn set_throttle_frame_interval(&mut self, interval: usize) {
183 self.throttle_frame_interval = interval;
184 }
185
186 pub fn throttle_frame_interval(&self) -> usize {
189 self.throttle_frame_interval
190 }
191
192 pub fn set_desired_update_rate(&mut self, update_rate: f32) {
194 self.desired_update_rate = update_rate.abs();
195 }
196
197 pub fn desired_update_rate(&self) -> f32 {
199 self.desired_update_rate
200 }
201
202 pub fn add_plugin<P>(&mut self, plugin: P)
204 where
205 P: Plugin + 'static,
206 {
207 self.engine.add_plugin(plugin)
208 }
209
210 pub fn run(self) {
212 Log::info("Initializing resource registry.");
213 self.engine.resource_manager.update_or_load_registry();
214
215 let engine = self.engine;
216 let event_loop = self.event_loop;
217 let throttle_threshold = self.throttle_threshold;
218 let throttle_frame_interval = self.throttle_frame_interval;
219
220 if self.resource_hot_reloading {
221 #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
222 {
223 use crate::core::watcher::FileSystemWatcher;
224 use std::time::Duration;
225 match FileSystemWatcher::new(".", Duration::from_secs(1)) {
226 Ok(watcher) => {
227 engine.resource_manager.state().set_watcher(Some(watcher));
228 }
229 Err(e) => {
230 Log::err(format!("Unable to create resource watcher. Reason {e:?}"));
231 }
232 }
233 }
234 }
235
236 let args = Args::try_parse().unwrap_or_default();
237
238 match event_loop {
239 Some(event_loop) => run_normal(
240 engine,
241 args.override_scene.as_deref(),
242 event_loop,
243 throttle_threshold,
244 throttle_frame_interval,
245 self.desired_update_rate,
246 ),
247 None => run_headless(
248 engine,
249 args.override_scene.as_deref(),
250 throttle_threshold,
251 throttle_frame_interval,
252 self.desired_update_rate,
253 ),
254 }
255 }
256}
257
258fn run_headless(
259 mut engine: Engine,
260 override_scene: Option<&str>,
261 throttle_threshold: f32,
262 throttle_frame_interval: usize,
263 desired_update_rate: f32,
264) {
265 let mut previous = Instant::now();
266 let fixed_time_step = 1.0 / desired_update_rate;
267 let mut lag = fixed_time_step;
268 let mut frame_counter = 0usize;
269 let mut last_throttle_frame_number = 0usize;
270 let is_running = Cell::new(true);
271
272 while is_running.get() {
273 if !engine.plugins_enabled && engine.resource_manager.registry_is_loaded() {
274 engine.enable_plugins(
275 override_scene,
276 true,
277 ApplicationLoopController::Headless {
278 running: &is_running,
279 },
280 );
281 }
282
283 register_scripted_scenes(&mut engine);
284
285 game_loop_iteration(
286 &mut engine,
287 ApplicationLoopController::Headless {
288 running: &is_running,
289 },
290 &mut previous,
291 &mut lag,
292 fixed_time_step,
293 throttle_threshold,
294 throttle_frame_interval,
295 frame_counter,
296 &mut last_throttle_frame_number,
297 );
298
299 frame_counter += 1;
300
301 let sleep_time = (fixed_time_step - previous.elapsed().as_secs_f32()).max(0.0) * 0.66666;
303
304 if sleep_time > 0.0 {
305 std::thread::sleep(Duration::from_secs_f32(sleep_time));
306 }
307 }
308}
309
310fn run_normal(
311 mut engine: Engine,
312 override_scene: Option<&str>,
313 event_loop: EventLoop<()>,
314 throttle_threshold: f32,
315 throttle_frame_interval: usize,
316 desired_update_rate: f32,
317) {
318 let mut previous = Instant::now();
319 let fixed_time_step = 1.0 / desired_update_rate;
320 let mut lag = 0.0;
321 let mut frame_counter = 0usize;
322 let mut last_throttle_frame_number = 0usize;
323
324 let override_scene = override_scene.map(|s| s.to_string());
325
326 enum GraphicsEvent {
327 GraphicsContextInitialized,
328 GraphicsContextDestroyed,
329 }
330
331 let mut graphics_event_queue = VecDeque::new();
332
333 run_executor(event_loop, move |event, active_event_loop| {
334 active_event_loop.set_control_flow(ControlFlow::Wait);
335
336 engine.handle_os_events(
337 &event,
338 fixed_time_step,
339 ApplicationLoopController::ActiveEventLoop(active_event_loop),
340 &mut lag,
341 );
342
343 if !engine.plugins_enabled && engine.resource_manager.registry_is_loaded() {
344 engine.enable_plugins(
345 override_scene.as_deref(),
346 true,
347 ApplicationLoopController::ActiveEventLoop(active_event_loop),
348 );
349
350 while let Some(graphics_event) = graphics_event_queue.pop_front() {
351 match graphics_event {
352 GraphicsEvent::GraphicsContextInitialized => {
353 engine.handle_graphics_context_created_by_plugins(
354 fixed_time_step,
355 ApplicationLoopController::ActiveEventLoop(active_event_loop),
356 &mut lag,
357 );
358 }
359 GraphicsEvent::GraphicsContextDestroyed => {
360 engine.handle_graphics_context_destroyed_by_plugins(
361 fixed_time_step,
362 ApplicationLoopController::ActiveEventLoop(active_event_loop),
363 &mut lag,
364 );
365 }
366 }
367 }
368 }
369
370 let scripted_scenes = register_scripted_scenes(&mut engine);
371 for scripted_scene in scripted_scenes {
372 engine.handle_os_event_by_scripts(&event, scripted_scene, fixed_time_step);
373 }
374
375 match event {
376 Event::Resumed => {
377 engine
378 .initialize_graphics_context(active_event_loop)
379 .expect("Unable to initialize graphics context!");
380
381 if engine.plugins_enabled {
382 engine.handle_graphics_context_created_by_plugins(
383 fixed_time_step,
384 ApplicationLoopController::ActiveEventLoop(active_event_loop),
385 &mut lag,
386 );
387 } else {
388 graphics_event_queue.push_back(GraphicsEvent::GraphicsContextInitialized);
389 }
390 }
391 Event::Suspended => {
392 engine
393 .destroy_graphics_context()
394 .expect("Unable to destroy graphics context!");
395
396 if engine.plugins_enabled {
397 engine.handle_graphics_context_destroyed_by_plugins(
398 fixed_time_step,
399 ApplicationLoopController::ActiveEventLoop(active_event_loop),
400 &mut lag,
401 );
402 } else {
403 graphics_event_queue.push_back(GraphicsEvent::GraphicsContextDestroyed);
404 }
405 }
406 Event::AboutToWait => {
407 game_loop_iteration(
408 &mut engine,
409 ApplicationLoopController::ActiveEventLoop(active_event_loop),
410 &mut previous,
411 &mut lag,
412 fixed_time_step,
413 throttle_threshold,
414 throttle_frame_interval,
415 frame_counter,
416 &mut last_throttle_frame_number,
417 );
418 }
419 Event::WindowEvent { event, .. } => {
420 match event {
421 WindowEvent::CloseRequested => active_event_loop.exit(),
422 WindowEvent::Resized(size) => {
423 if let Err(e) = engine.set_frame_size(size.into()) {
424 Log::writeln(
425 MessageKind::Error,
426 format!("Unable to set frame size: {e:?}"),
427 );
428 }
429 }
430 WindowEvent::RedrawRequested => {
431 engine.handle_before_rendering_by_plugins(
432 fixed_time_step,
433 ApplicationLoopController::ActiveEventLoop(active_event_loop),
434 &mut lag,
435 );
436
437 engine.render().unwrap();
438
439 frame_counter += 1;
440 }
441 _ => (),
442 }
443
444 if let Some(os_event) = translate_event(&event) {
445 for ui in engine.user_interfaces.iter_mut() {
446 ui.process_os_event(&os_event);
447 }
448 }
449 }
450 _ => (),
451 }
452 })
453}
454
455fn register_scripted_scenes(engine: &mut Engine) -> Vec<Handle<Scene>> {
456 let scenes = engine
457 .scenes
458 .pair_iter()
459 .map(|(s, _)| s)
460 .collect::<Vec<_>>();
461
462 for &scene_handle in scenes.iter() {
463 if !engine.has_scripted_scene(scene_handle) {
464 engine.register_scripted_scene(scene_handle);
465 }
466 }
467
468 scenes
469}
470
471fn game_loop_iteration(
472 engine: &mut Engine,
473 controller: ApplicationLoopController,
474 previous: &mut Instant,
475 lag: &mut f32,
476 fixed_time_step: f32,
477 throttle_threshold: f32,
478 throttle_frame_interval: usize,
479 frame_counter: usize,
480 last_throttle_frame_number: &mut usize,
481) {
482 let elapsed = previous.elapsed();
483 *previous = Instant::now();
484 *lag += elapsed.as_secs_f32();
485
486 while *lag >= fixed_time_step {
488 let time_step;
489 if *lag >= throttle_threshold
490 && (frame_counter - *last_throttle_frame_number >= throttle_frame_interval)
491 {
492 time_step = *lag;
495 *lag = 0.0;
498
499 *last_throttle_frame_number = frame_counter;
500 } else {
501 time_step = fixed_time_step;
502 }
503
504 engine.update(time_step, controller, lag, Default::default());
505
506 if *lag >= fixed_time_step {
509 *lag -= fixed_time_step;
510 } else if *lag < 0.0 {
511 *lag = 0.0;
513 }
514 }
515
516 if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
517 ctx.window.request_redraw();
518 }
519}
520
521#[allow(deprecated)] fn run_executor<F>(event_loop: EventLoop<()>, callback: F)
523where
524 F: FnMut(Event<()>, &ActiveEventLoop) + 'static,
525{
526 #[cfg(target_arch = "wasm32")]
527 {
528 use winit::platform::web::EventLoopExtWebSys;
529 event_loop.spawn(callback);
530 }
531
532 #[cfg(not(target_arch = "wasm32"))]
533 {
534 event_loop.run(callback).unwrap();
535 }
536}