Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: configurable refresh-rate and change default to 60fps #8680

Merged
merged 8 commits into from Feb 19, 2021
4 changes: 2 additions & 2 deletions src/framerate_gui.cpp
Expand Up @@ -189,7 +189,7 @@ namespace {
PerformanceData(1), // PFE_ACC_GL_AIRCRAFT
PerformanceData(1), // PFE_GL_LANDSCAPE
PerformanceData(1), // PFE_GL_LINKGRAPH
PerformanceData(GL_RATE), // PFE_DRAWING
PerformanceData(1000.0 / 30), // PFE_DRAWING
PerformanceData(1), // PFE_ACC_DRAWWORLD
PerformanceData(60.0), // PFE_VIDEO
PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
Expand Down Expand Up @@ -468,7 +468,7 @@ struct FramerateWindow : Window {
this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0);
if (this->small) return; // in small mode, this is everything needed

this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate);
this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _settings_client.gui.refresh_rate);

int new_active = 0;
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
Expand Down
8 changes: 4 additions & 4 deletions src/genworld_gui.cpp
Expand Up @@ -1188,7 +1188,7 @@ struct GenWorldStatus {
StringID cls;
uint current;
uint total;
int timer;
std::chrono::steady_clock::time_point timer;
};

static GenWorldStatus _gws;
Expand Down Expand Up @@ -1294,7 +1294,7 @@ void PrepareGenerateWorldProgress()
_gws.current = 0;
_gws.total = 0;
_gws.percent = 0;
_gws.timer = 0; // Forces to paint the progress window immediately
_gws.timer = std::chrono::steady_clock::now() - std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT * 2); // Ensure we draw on first update
}

/**
Expand Down Expand Up @@ -1329,7 +1329,7 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin
}

/* Don't update the screen too often. So update it once in every once in a while... */
if (!_network_dedicated && _gws.timer != 0 && _realtime_tick - _gws.timer < MODAL_PROGRESS_REDRAW_TIMEOUT) return;
if (!_network_dedicated && std::chrono::steady_clock::now() - _gws.timer < std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT)) return;

/* Percentage is about the number of completed tasks, so 'current - 1' */
_gws.percent = percent_table[cls] + (percent_table[cls + 1] - percent_table[cls]) * (_gws.current == 0 ? 0 : _gws.current - 1) / _gws.total;
Expand Down Expand Up @@ -1365,7 +1365,7 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin
_modal_progress_work_mutex.lock();
_modal_progress_paint_mutex.unlock();

_gws.timer = _realtime_tick;
_gws.timer = std::chrono::steady_clock::now();
}

/**
Expand Down
3 changes: 1 addition & 2 deletions src/gfx.cpp
Expand Up @@ -1470,9 +1470,8 @@ void DrawDirtyBlocks()
_modal_progress_paint_mutex.unlock();
_modal_progress_work_mutex.unlock();

/* Wait a while and update _realtime_tick so we are given the rights */
/* Wait a while and hope the modal gives us a bit of time to draw the GUI. */
if (!IsFirstModalProgressLoop()) CSleep(MODAL_PROGRESS_REDRAW_TIMEOUT);
_realtime_tick += MODAL_PROGRESS_REDRAW_TIMEOUT;

/* Modal progress thread may need blitter access while we are waiting for it. */
VideoDriver::GetInstance()->ReleaseBlitterLock();
Expand Down
3 changes: 0 additions & 3 deletions src/openttd.cpp
Expand Up @@ -1465,7 +1465,6 @@ void GameLoop()
if (_game_mode == GM_BOOTSTRAP) {
/* Check for UDP stuff */
if (_network_available) NetworkBackgroundLoop();
InputLoop();
return;
}

Expand Down Expand Up @@ -1505,8 +1504,6 @@ void GameLoop()

if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();

InputLoop();

SoundDriver::GetInstance()->MainLoop();
MusicLoop();
}
1 change: 1 addition & 0 deletions src/settings_type.h
Expand Up @@ -147,6 +147,7 @@ struct GUISettings {
byte starting_colour; ///< default color scheme for the company to start a new game with
bool show_newgrf_name; ///< Show the name of the NewGRF in the build vehicle window
bool auto_remove_signals; ///< automatically remove signals when in the way during rail construction
uint16 refresh_rate; ///< How often we refresh the screen (time between draw-ticks).

uint16 console_backlog_timeout; ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity.
uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed.
Expand Down
10 changes: 10 additions & 0 deletions src/table/settings.ini
Expand Up @@ -3354,6 +3354,16 @@ def = 100
min = 10
max = 65500

[SDTC_VAR]
var = gui.refresh_rate
type = SLE_UINT16
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
def = 60
min = 10
max = 1000
cat = SC_EXPERT
startup = true

[SDTC_BOOL]
var = sound.news_ticker
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
Expand Down
78 changes: 44 additions & 34 deletions src/video/allegro_v.cpp
Expand Up @@ -24,6 +24,7 @@
#include "../core/math_func.hpp"
#include "../framerate_type.h"
#include "../thread.h"
#include "../window_func.h"
#include "allegro_v.h"
#include <allegro.h>

Expand Down Expand Up @@ -445,34 +446,16 @@ void VideoDriver_Allegro::Stop()
if (--_allegro_instance_count == 0) allegro_exit();
}

#if defined(UNIX) || defined(__OS2__)
# include <sys/time.h> /* gettimeofday */

static uint32 GetTime()
{
struct timeval tim;

gettimeofday(&tim, nullptr);
return tim.tv_usec / 1000 + tim.tv_sec * 1000;
}
#else
static uint32 GetTime()
{
return GetTickCount();
}
#endif


void VideoDriver_Allegro::MainLoop()
{
uint32 cur_ticks = GetTime();
uint32 last_cur_ticks = cur_ticks;
uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
auto cur_ticks = std::chrono::steady_clock::now();
auto last_realtime_tick = cur_ticks;
auto next_game_tick = cur_ticks;
auto next_draw_tick = cur_ticks;

CheckPaletteAnim();

for (;;) {
uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
InteractiveRandom(); // randomness

PollEvent();
Expand All @@ -491,11 +474,32 @@ void VideoDriver_Allegro::MainLoop()
_fast_forward = 0;
}

cur_ticks = GetTime();
if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
_realtime_tick += cur_ticks - last_cur_ticks;
last_cur_ticks = cur_ticks;
next_tick = cur_ticks + MILLISECONDS_PER_TICK;
cur_ticks = std::chrono::steady_clock::now();

/* If more than a millisecond has passed, increase the _realtime_tick. */
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
_realtime_tick += delta.count();
last_realtime_tick += delta;
}

if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
if (_fast_forward && !_pause_mode) {
next_game_tick = cur_ticks + this->GetGameInterval();
} else {
next_game_tick += this->GetGameInterval();
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
}

GameLoop();
}

/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
next_draw_tick += this->GetDrawInterval();
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;

bool old_ctrl_pressed = _ctrl_pressed;

Expand All @@ -511,16 +515,22 @@ void VideoDriver_Allegro::MainLoop()

if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();

GameLoop();

InputLoop();
UpdateWindows();
CheckPaletteAnim();

DrawSurfaceToScreen();
} else {
CSleep(1);
NetworkDrawChatMessage();
DrawMouseCursor();
DrawSurfaceToScreen();
}

/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
if (!_fast_forward || _pause_mode) {
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
auto next_tick = std::min(next_draw_tick, next_game_tick);
auto now = std::chrono::steady_clock::now();

if (next_tick > now) {
std::this_thread::sleep_for(next_tick - now);
}
}
}
}
Expand Down
80 changes: 44 additions & 36 deletions src/video/cocoa/cocoa_v.mm
Expand Up @@ -36,6 +36,7 @@
#include "../../gfx_func.h"
#include "../../thread.h"
#include "../../core/random_func.hpp"
#include "../../progress.h"
#include "../../settings_type.h"
#include "../../window_func.h"
#include "../../window_gui.h"
Expand Down Expand Up @@ -634,19 +635,14 @@ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
/** Main game loop. */
void VideoDriver_Cocoa::GameLoop()
{
uint32 cur_ticks = GetTick();
uint32 last_cur_ticks = cur_ticks;
uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;

#ifdef _DEBUG
uint32 et0 = GetTick();
uint32 st = 0;
#endif
auto cur_ticks = std::chrono::steady_clock::now();
auto last_realtime_tick = cur_ticks;
auto next_game_tick = cur_ticks;
auto next_draw_tick = cur_ticks;

for (;;) {
@autoreleasepool {

uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
InteractiveRandom(); // randomness

while (this->PollEvent()) {}
Expand All @@ -669,11 +665,32 @@ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
_fast_forward = 0;
}

cur_ticks = GetTick();
if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
_realtime_tick += cur_ticks - last_cur_ticks;
last_cur_ticks = cur_ticks;
next_tick = cur_ticks + MILLISECONDS_PER_TICK;
cur_ticks = std::chrono::steady_clock::now();

/* If more than a millisecond has passed, increase the _realtime_tick. */
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
_realtime_tick += delta.count();
last_realtime_tick += delta;
}

if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
if (_fast_forward && !_pause_mode) {
next_game_tick = cur_ticks + this->GetGameInterval();
} else {
next_game_tick += this->GetGameInterval();
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
}

::GameLoop();
}

/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
next_draw_tick += this->GetDrawInterval();
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;

bool old_ctrl_pressed = _ctrl_pressed;

Expand All @@ -682,34 +699,25 @@ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)

if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();

::GameLoop();

InputLoop();
UpdateWindows();
this->CheckPaletteAnim();
this->Draw();
} else {
#ifdef _DEBUG
uint32 st0 = GetTick();
#endif
CSleep(1);
#ifdef _DEBUG
st += GetTick() - st0;
#endif
NetworkDrawChatMessage();
DrawMouseCursor();

this->Draw();
}
}
}

#ifdef _DEBUG
uint32 et = GetTick();
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
if (!_fast_forward || _pause_mode) {
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
auto next_tick = std::min(next_draw_tick, next_game_tick);
auto now = std::chrono::steady_clock::now();

DEBUG(driver, 1, "cocoa_v: nextEventMatchingMask took %i ms total", _tEvent);
DEBUG(driver, 1, "cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, et - et0 - st);
DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double)_tEvent / (double)(et - et0) * 100);
DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double)_tEvent / (double)(et - et0 - st) * 100);
#endif
if (next_tick > now) {
std::this_thread::sleep_for(next_tick - now);
}
}
}
}
}


Expand Down