diff --git a/src/console_cmd.c b/src/console_cmd.c index 3d8314c44d..21887cd9de 100644 --- a/src/console_cmd.c +++ b/src/console_cmd.c @@ -480,7 +480,7 @@ void param_completion_for_magic_instance(PlayerNumber plyr_idx, char *args_str, TbBool cmd_stats(PlayerNumber plyr_idx, char * args) { - targeted_message_add(MsgType_Player, plyr_idx, plyr_idx, GUI_MESSAGES_DELAY, "turn fps is %d, draw fps is %d", turns_per_second, turns_per_second_draw_current); + targeted_message_add(MsgType_Player, plyr_idx, plyr_idx, GUI_MESSAGES_DELAY, "turn fps is %d, draw fps is %d", turns_per_second, fps_limit_current); return true; } @@ -498,7 +498,7 @@ TbBool cmd_fps_turn(PlayerNumber plyr_idx, char * args) TbBool cmd_fps_draw(PlayerNumber plyr_idx, char * args) { - parse_draw_fps_config_val(args, &turns_per_second_draw_main, &turns_per_second_draw_secondary); + parse_draw_fps_config_val(args, &fps_limit_main, &fps_limit_secondary); redetect_screen_refresh_rate_for_draw(); return true; } diff --git a/src/frontend.cpp b/src/frontend.cpp index 5f82020d94..93aac85b02 100644 --- a/src/frontend.cpp +++ b/src/frontend.cpp @@ -96,7 +96,6 @@ extern "C" { #endif -extern long double last_draw_completed_time; /******************************************************************************/ TbClockMSec gui_message_timeout = 0; char gui_message_text[TEXT_BUFFER_LENGTH]; @@ -3375,7 +3374,6 @@ short frontend_draw(void) draw_debug_messages(); perform_any_screen_capturing(); LbScreenUnlock(); - last_draw_completed_time = get_time_tick_ns(); return result; } diff --git a/src/game_legacy.h b/src/game_legacy.h index 42de8087d6..2c0a43af48 100644 --- a/src/game_legacy.h +++ b/src/game_legacy.h @@ -411,9 +411,9 @@ struct Game { extern struct Game game; extern int32_t turns_per_second; -extern int32_t turns_per_second_draw_current; -extern int32_t turns_per_second_draw_main; -extern int32_t turns_per_second_draw_secondary; +extern int32_t fps_limit_current; +extern int32_t fps_limit_main; +extern int32_t fps_limit_secondary; TbBool network_is_active(void); diff --git a/src/main.cpp b/src/main.cpp index 9b0da041f6..fe5f277495 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -132,7 +132,7 @@ #include "net_input_lag.h" #include "moonphase.h" #include "frontmenu_ingame_map.h" -#include +#include #ifdef FUNCTESTING #include "ftests/ftest.h" @@ -150,12 +150,6 @@ struct StartupParameters start_params; char autostart_multiplayer_campaign[80] = ""; int autostart_multiplayer_level = 0; int32_t turns_per_second; - -int32_t turns_per_second_draw_current = 0; -int32_t turns_per_second_draw_main = 0; // -1 if auto -int32_t turns_per_second_draw_secondary = 0; - - unsigned char *blue_palette; unsigned char *red_palette; unsigned char *dog_palette; @@ -218,15 +212,18 @@ extern void startup_network_game(CoroutineLoop *context, TbBool local); /******************************************************************************/ TbClockMSec timerstarttime = 0; -long double last_draw_completed_time = 0; struct TimerTime Timer; TbBool TimerGame = false; TbBool TimerNoReset = false; TbBool TimerFreeze = false; /******************************************************************************/ +int32_t fps_limit_current = 0; +int32_t fps_limit_main = 0; // -1 if auto +int32_t fps_limit_secondary = 0; +static long double process_frame_time = 0; static long double time_since_last_draw = 0; -static long double average_delta_time = 1; +static long double average_frame_draw_time = 1; static long double multiplayer_clock_adjust = 1; float interpolate_time = 0; /******************************************************************************/ @@ -3261,7 +3258,7 @@ short display_should_be_updated_this_turn(void) if ( (game.turns_fastforward == 0) && (!game.packet_loading_in_progress) ) { find_frame_rate(); - if ( is_feature_on(Ft_DeltaTime) || (game.frame_skip == 0) || ((get_gameturn() % game.frame_skip) == 0)) + if ( (game.frame_skip == 0) || ((get_gameturn() % game.frame_skip) == 0) ) return true; } else if ( ((get_gameturn() & 0x3F)==0) || @@ -3346,75 +3343,29 @@ TbBool keeper_wait_for_next_turn(void) return false; } - -TbBool keeper_wait_for_next_draw(void) -{ - // fps.draw is currently unable to work properly with frame_skip - if (turns_per_second_draw_current > 0 && is_feature_on(Ft_DeltaTime) == true && game.frame_skip == 0) - { - const long double tick_ns_one_sec = 1000000000.0; - const long double tick_ns_one_frame = tick_ns_one_sec/turns_per_second_draw_current; - - static long double tick_ns_last_draw = 0; - long double tick_ns_cur = get_time_tick_ns(); - long double tick_ns_used = tick_ns_cur - tick_ns_last_draw; - long double tick_ns_delay = tick_ns_one_frame - tick_ns_used; - - long double tick_ns_end = tick_ns_cur; - // tick_ns_used: every level, initialized_time_point will be reset, so tick_ns_used may be less than 0 when enter level for the non-first time, Skip it directly to solve the problem. - if (tick_ns_delay > 0 && tick_ns_used >= 0) { - tick_ns_end = tick_ns_cur + tick_ns_delay; - LbSleepUntilExt(tick_ns_end); - } - tick_ns_last_draw = tick_ns_end; - - return true; - } - return false; -} - void redetect_screen_refresh_rate_for_draw() { - turns_per_second_draw_current = 0; + fps_limit_current = 0; - if (turns_per_second_draw_main == -1) { - if (turns_per_second_draw_secondary > 0) - turns_per_second_draw_current = turns_per_second_draw_secondary; + if (fps_limit_main == -1) { + if (fps_limit_secondary > 0) + fps_limit_current = fps_limit_secondary; if (lbWindow != NULL) { int display_index = SDL_GetWindowDisplayIndex(lbWindow); if (display_index >= 0) { SDL_DisplayMode mode; if (SDL_GetCurrentDisplayMode(display_index, &mode) == 0 && mode.refresh_rate > 0) { - turns_per_second_draw_current = mode.refresh_rate; + fps_limit_current = mode.refresh_rate; } } } - } else if (turns_per_second_draw_main > 0) { - turns_per_second_draw_current = turns_per_second_draw_main; + } else if (fps_limit_main > 0) { + fps_limit_current = fps_limit_main; } } -TbBool keeper_wait_for_screen_focus(void) -{ - do { - if ( !poll_inputs() ) - { - force_application_close(); - break; - } - if (LbIsActive()) - return true; - if (network_is_active()) - return true; - if (!freeze_game_on_focus_lost()) - return true; - LbSleepFor(50); - } while ((!exit_keeper) && (!quit_game)); - return false; -} - static bool use_delta_time() { // Always enable interpolation in multiplayer games. @@ -3439,39 +3390,77 @@ static void update_gameplay_delta_time() const int64_t ns = now - prev; prev = now; - const long double dt = min(max(ns / 1e9L * turns_per_second, 0.L), 1.L); + const long double seconds = max(ns / 1e9L, 0.L); + const long double turns = seconds * turns_per_second; + const long double frames = seconds * fps_limit_current; - game.process_turn_time += dt * multiplayer_clock_adjust * max(game.frame_skip, 1); + game.process_turn_time += turns * multiplayer_clock_adjust * max(game.frame_skip, 1); // This sets game.delta_time, which is used to pace locally-displayed // things (eg. tooltip scroll speed). It should not be affected by // multiplayer clock adjustment or frameskip. - time_since_last_draw += dt; + time_since_last_draw += turns; + + // Like process_turn_time, but for the video frame rate. + process_frame_time += frames; } else { // Set to 1 so that these variables don't affect anything. (if something is multiplied by 1 it doesn't change) time_since_last_draw = 1; game.delta_time = 1; game.process_turn_time = 1; + process_frame_time = 1; } } +static bool keeper_wait_for_screen_focus() +{ + do { + if ( !poll_inputs() ) + { + force_application_close(); + break; + } + if (LbIsActive()) + return true; + if (network_is_active()) + return true; + if (!freeze_game_on_focus_lost()) + return true; + LbSleepFor(50); + update_gameplay_delta_time(); + game.process_turn_time = 1.0; + time_since_last_draw = 1.0; + } while ((!exit_keeper) && (!quit_game)); + return false; +} + static void gameplay_loop_draw() { + if (use_delta_time()) + do_draw = true; + update_gameplay_delta_time(); if (game.process_turn_time > 1.0 && time_since_last_draw < 1.0) do_draw = false; - if (is_feature_on(Ft_DeltaTime) && ! network_is_active()) + // Frame rate limiter + if (fps_limit_current > 0) { frametime_start_measurement(Frametime_Sleep); - if (turns_per_second_draw_current > 0) - keeper_wait_for_next_draw(); + if (process_frame_time < 1.0) + { + if (game.process_turn_time < 1.0) + SDL_Delay(1); + do_draw = false; + } + else + { + process_frame_time = min(1.L, process_frame_time - 1.L); + } frametime_end_measurement(Frametime_Sleep); } - update_gameplay_delta_time(); - // Floats are used a lot in the drawing related functions. But keep in mind integers are typically preferred for logic related functions. frametime_start_measurement(Frametime_Draw); @@ -3484,9 +3473,8 @@ static void gameplay_loop_draw() if ( do_draw ) { if (frametime_enabled()) framerate_measurement_capture(Framerate_Draw); - game.delta_time = time_since_last_draw; + game.delta_time = min(time_since_last_draw, 1.L); time_since_last_draw = 0; - average_delta_time += (game.delta_time - average_delta_time) * max(average_delta_time, .05L) / 20; interpolate_time = min(max(game.process_turn_time, 0.L), 1.L); keeper_screen_redraw(); } @@ -3504,7 +3492,12 @@ static void gameplay_loop_draw() keeper_screen_swap(); } frametime_end_measurement(Frametime_Draw); - last_draw_completed_time = get_time_tick_ns(); + + if ( do_draw ) { + update_gameplay_delta_time(); + const long double delta = time_since_last_draw - average_frame_draw_time; + average_frame_draw_time += delta * max(average_frame_draw_time, .05L) / 20; + } } static void gameplay_loop_logic() @@ -3538,7 +3531,8 @@ static void gameplay_loop_logic() // another frame could miss this deadline, skip it. // In a 3-4 player game, clients must be 2 frames early. const int frames = 1 + (netstate.my_id != SERVER_ID && game.active_players_count > 2); - if (game.process_turn_time + frames * average_delta_time < 1.0) + const long double offset = frames * average_frame_draw_time * multiplayer_clock_adjust * max(game.frame_skip, 1); + if (game.process_turn_time + offset < 1.0) return; } else @@ -3642,7 +3636,6 @@ extern "C" void network_yield_draw_gameplay() extern "C" void network_yield_waiting_gameplay_packets() { - do_draw = true; poll_inputs(); gameplay_loop_draw(); update_gameplay_delta_time(); diff --git a/src/main_game.c b/src/main_game.c index 5788978b3f..d3f39e091a 100644 --- a/src/main_game.c +++ b/src/main_game.c @@ -455,9 +455,9 @@ void clear_complete_game(void) else set_selected_level_number(first_singleplayer_level()); turns_per_second = start_params.num_fps; - turns_per_second_draw_current = 0; - turns_per_second_draw_main = start_params.num_fps_draw_main; - turns_per_second_draw_secondary = start_params.num_fps_draw_secondary; + fps_limit_current = 0; + fps_limit_main = start_params.num_fps_draw_main; + fps_limit_secondary = start_params.num_fps_draw_secondary; game.mode_flags = start_params.mode_flags; game.easter_eggs_enabled = start_params.easter_egg; set_flag_value(game.system_flags, GSF_AllowOnePlayer, start_params.one_player);