/* * This software is licensed under the terms of the MIT License. * See COPYING for further information. * --- * Copyright (c) 2011-2024, Lukas Weber . * Copyright (c) 2012-2024, Andrei Alexeyev . */ #include "eventloop_private.h" #include "global.h" #include "thread.h" #include "util/env.h" void eventloop_run(void) { assert(thread_current_is_main()); if(evloop.stack_ptr == NULL) { return; } LoopFrame *frame = evloop.stack_ptr; evloop.frame_times.target = frame->frametime; evloop.frame_times.start = time_get(); evloop.frame_times.next = evloop.frame_times.start + evloop.frame_times.target; int32_t sleep = env_get("TAISEI_FRAMELIMITER_SLEEP", 3); bool compensate = env_get("TAISEI_FRAMELIMITER_COMPENSATE", 1); bool uncapped_rendering_env, uncapped_rendering; if(global.is_replay_verification) { uncapped_rendering_env = false; } else { uncapped_rendering_env = env_get("TAISEI_FRAMELIMITER_LOGIC_ONLY", 0); } uncapped_rendering = uncapped_rendering_env; uint32_t frame_num = 0; begin_main_loop: while(frame != NULL) { #ifdef DEBUG if(uncapped_rendering_env) { uncapped_rendering = !gamekeypressed(KEY_FPSLIMIT_OFF); } #endif evloop.frame_times.start = time_get(); begin_frame: global.fps.busy.last_update_time = time_get(); evloop.frame_times.target = frame->frametime; ++frame_num; LogicFrameAction lframe_action = LFRAME_WAIT; if(uncapped_rendering) { attr_unused uint32_t logic_frames = 0; while(lframe_action != LFRAME_STOP && evloop.frame_times.next < evloop.frame_times.start) { lframe_action = handle_logic(&frame, &evloop.frame_times); if(!frame || lframe_action == LFRAME_STOP) { goto begin_main_loop; } ++logic_frames; hrtime_t total = time_get() - evloop.frame_times.start; if(total > evloop.frame_times.target) { evloop.frame_times.next = evloop.frame_times.start; log_debug("Executing logic took too long (%"PRIuTIME"), giving up", total); } else { evloop.frame_times.next += evloop.frame_times.target; } } if(logic_frames > 1) { log_debug( "Dropped %u logic frame%s in superframe #%u", logic_frames - 1, logic_frames > 2 ? "s" : "", frame_num ); } } else { lframe_action = handle_logic(&frame, &evloop.frame_times); if(!frame || lframe_action == LFRAME_STOP) { goto begin_main_loop; } } if((uncapped_rendering || !(frame_num % get_effective_frameskip())) && !global.is_replay_verification) { run_render_frame(frame); } fpscounter_update(&global.fps.busy); if(uncapped_rendering || global.frameskip > 0 || global.is_replay_verification) { continue; } #ifdef DEBUG if(gamekeypressed(KEY_FPSLIMIT_OFF)) { continue; } #endif evloop.frame_times.next = evloop.frame_times.start + evloop.frame_times.target; if(compensate) { hrtime_t rt = time_get(); if(rt > evloop.frame_times.next) { // frame took too long... // try to compensate in the next frame to avoid slowdown evloop.frame_times.start = rt - min(rt - evloop.frame_times.next, evloop.frame_times.target); goto begin_frame; } } if(sleep > 0) { // CAUTION: All of these casts are important! while((shrtime_t)evloop.frame_times.next - (shrtime_t)time_get() > (shrtime_t)evloop.frame_times.target / sleep) { uint32_t nap_multiplier = 1; uint32_t nap_divisor = 3; hrtime_t nap_raw = max(0, (shrtime_t)evloop.frame_times.next - (shrtime_t)time_get()); uint32_t nap_sdl = (nap_multiplier * nap_raw * 1000) / (HRTIME_RESOLUTION * nap_divisor); nap_sdl = max(nap_sdl, 1); SDL_Delay(nap_sdl); } } while(time_get() < evloop.frame_times.next); } }