@ -8,9 +8,9 @@
/** @file video_driver.cpp Common code between video driver implementations. */
# include "../stdafx.h"
# include "../debug.h"
# include "../core/random_func.hpp"
# include "../network/network.h"
# include "../debug.h"
# include "../gfx_func.h"
# include "../progress.h"
# include "../thread.h"
@ -19,40 +19,84 @@
bool _video_hw_accel ; ///< Whether to consider hardware accelerated video drivers.
void VideoDriver : : Tick ( )
void VideoDriver : : GameLoop ( )
{
auto cur_ticks = std : : chrono : : steady_clock : : now ( ) ;
this- > next_game_tick + = this - > GetGameInterval ( ) ;
if ( cur_ticks > = this - > next_game_tick ) {
this - > next_game_tick + = this - > GetGameInterval ( ) ;
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
if ( this - > next_game_tick < cur_ticks - ALLOWED_DRIFT * this - > GetGameInterval ( ) ) this - > next_game_tick = cur_ticks ;
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
auto now = std : : chrono : : steady_clock : : now ( ) ;
if ( this - > next_game_tick < now - ALLOWED_DRIFT * this - > GetGameInterval ( ) ) this - > next_game_tick = now ;
{
std : : lock_guard < std : : mutex > lock ( this - > game_state_mutex ) ;
/* The game loop is the part that can run asynchronously.
* The rest except sleeping can ' t . */
this - > UnlockVideoBuffer ( ) ;
: : GameLoop ( ) ;
this - > LockVideoBuffer ( ) ;
}
}
void VideoDriver : : GameThread ( )
{
while ( ! _exit_game ) {
this - > GameLoop ( ) ;
auto now = std : : chrono : : steady_clock : : now ( ) ;
if ( this - > next_game_tick > now ) {
std : : this_thread : : sleep_for ( this - > next_game_tick - now ) ;
} else {
/* Ensure we yield this thread if drawings wants to take a lock on
* the game state . This is mainly because most OSes have an
* optimization that if you unlock / lock a mutex in the same thread
* quickly , it will never context switch even if there is another
* thread waiting to take the lock on the same mutex . */
std : : lock_guard < std : : mutex > lock ( this - > game_thread_wait_mutex ) ;
}
}
}
/* static */ void VideoDriver : : GameThreadThunk ( VideoDriver * drv )
{
drv - > GameThread ( ) ;
}
void VideoDriver : : StartGameThread ( )
{
if ( this - > is_game_threaded ) {
this - > is_game_threaded = StartNewThread ( & this - > game_thread , " ottd:game " , & VideoDriver : : GameThreadThunk , this ) ;
}
DEBUG ( driver , 1 , " using %sthread for game-loop " , this - > is_game_threaded ? " " : " no " ) ;
}
void VideoDriver : : StopGameThread ( )
{
if ( ! this - > is_game_threaded ) return ;
this - > game_thread . join ( ) ;
}
void VideoDriver : : Tick ( )
{
if ( ! this - > is_game_threaded & & std : : chrono : : steady_clock : : now ( ) > = this - > next_game_tick ) {
this - > GameLoop ( ) ;
/* For things like dedicated server, don't run a separate draw-tick. */
if ( ! this - > HasGUI ( ) ) {
: : InputLoop ( ) ;
UpdateWindows ( ) ;
: : UpdateWindows ( ) ;
this - > next_draw_tick = this - > next_game_tick ;
}
}
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if ( this - > HasGUI ( ) & & cur_ticks > = this - > next_draw_tick & & ( _switch_mode = = SM_NONE | | _game_mode = = GM_BOOTSTRAP | | HasModalProgress ( ) ) ) {
auto now = std : : chrono : : steady_clock : : now ( ) ;
if ( this - > HasGUI ( ) & & now > = this - > next_draw_tick ) {
this - > next_draw_tick + = this - > GetDrawInterval ( ) ;
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
if ( this - > next_draw_tick < cur_ticks - ALLOWED_DRIFT * this - > GetDrawInterval ( ) ) this - > next_draw_tick = cur_ticks ;
if ( this - > next_draw_tick < now - ALLOWED_DRIFT * this - > GetDrawInterval ( ) ) this - > next_draw_tick = now ;
/* Keep the interactive randomizer a bit more random by requesting
* new values when - ever we can . */
InteractiveRandom ( ) ;
while ( this - > PollEvent ( ) ) { }
this - > InputLoop ( ) ;
/* Check if the fast-forward button is still pressed. */
@ -64,23 +108,41 @@ void VideoDriver::Tick()
this - > fast_forward_via_key = false ;
}
: : InputLoop ( ) ;
UpdateWindows ( ) ;
{
/* Tell the game-thread to stop so we can have a go. */
std : : lock_guard < std : : mutex > lock_wait ( this - > game_thread_wait_mutex ) ;
std : : lock_guard < std : : mutex > lock_state ( this - > game_state_mutex ) ;
this - > LockVideoBuffer ( ) ;
while ( this - > PollEvent ( ) ) { }
: : InputLoop ( ) ;
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if ( _game_mode = = GM_BOOTSTRAP | | _switch_mode = = SM_NONE | | HasModalProgress ( ) ) {
: : UpdateWindows ( ) ;
}
this - > PopulateSystemSprites ( ) ;
}
this - > CheckPaletteAnim ( ) ;
this - > Paint ( ) ;
this - > UnlockVideoBuffer ( ) ;
}
}
void VideoDriver : : SleepTillNextTick ( )
{
/* 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 ( this - > next_draw_tick , this - > next_game_tick ) ;
auto next_tick = this - > next_draw_tick ;
auto now = std : : chrono : : steady_clock : : now ( ) ;
if ( ! this - > is_game_threaded ) {
next_tick = min ( next_tick , this - > next_game_tick ) ;
}
if ( next_tick > now ) {
this - > UnlockVideoBuffer ( ) ;
std : : this_thread : : sleep_for ( next_tick - now ) ;
this - > LockVideoBuffer ( ) ;
}
}