137 lines
3.6 KiB
C
137 lines
3.6 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#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);
|
|
}
|
|
}
|