OpenTTD Source: src/video/video_driver.cpp Source File

OpenTTD Source 15.3

video_driver.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "../core/random_func.hpp"
12#include "../network/network.h"
13#include "../blitter/factory.hpp"
14#include "../debug.h"
15#include "../driver.h"
16#include "../fontcache.h"
17#include "../gfx_func.h"
18#include "../gfxinit.h"
19#include "../progress.h"
20#include "../rev.h"
21#include "../thread.h"
22#include "../window_func.h"
23#include "video_driver.hpp"
24
25#include "../safeguards.h"
26
27 bool _video_hw_accel;
28 bool _video_vsync;
29
30void VideoDriver::GameLoop()
31{
32 this->next_game_tick += this->GetGameInterval();
33
34 /* Avoid next_game_tick getting behind more and more if it cannot keep up. */
35 auto now = std::chrono::steady_clock::now();
36 if (this->next_game_tick < now - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = now;
37
38 {
39 std::lock_guard<std::mutex> lock(this->game_state_mutex);
40
41 ::GameLoop();
42 }
43}
44
45void VideoDriver::GameThread()
46{
47 while (!_exit_game) {
48 this->GameLoop();
49
50 auto now = std::chrono::steady_clock::now();
51 if (this->next_game_tick > now) {
52 std::this_thread::sleep_for(this->next_game_tick - now);
53 } else {
54 /* Ensure we yield this thread if drawings wants to take a lock on
55 * the game state. This is mainly because most OSes have an
56 * optimization that if you unlock/lock a mutex in the same thread
57 * quickly, it will never context switch even if there is another
58 * thread waiting to take the lock on the same mutex. */
59 std::lock_guard<std::mutex> lock(this->game_thread_wait_mutex);
60 }
61 }
62}
63
68 void VideoDriver::GameLoopPause()
69{
70 /* If we are not called from the game-thread, ignore this request. */
71 if (std::this_thread::get_id() != this->game_thread.get_id()) return;
72
73 this->game_state_mutex.unlock();
74
75 {
76 /* See GameThread() for more details on this lock. */
77 std::lock_guard<std::mutex> lock(this->game_thread_wait_mutex);
78 }
79
80 this->game_state_mutex.lock();
81}
82
83/* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv)
84{
85 drv->GameThread();
86}
87
88 void VideoDriver::StartGameThread()
89{
90 if (this->is_game_threaded) {
91 this->is_game_threaded = StartNewThread(&this->game_thread, "ottd:game", &VideoDriver::GameThreadThunk, this);
92 }
93
94 Debug(driver, 1, "using {}thread for game-loop", this->is_game_threaded ? "" : "no ");
95}
96
97 void VideoDriver::StopGameThread()
98{
99 if (!this->is_game_threaded) return;
100
101 this->game_thread.join();
102}
103
104 void VideoDriver::Tick()
105{
106 if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) {
107 this->GameLoop();
108
109 /* For things like dedicated server, don't run a separate draw-tick. */
110 if (!this->HasGUI()) {
111 ::InputLoop();
112 ::UpdateWindows();
113 this->next_draw_tick = this->next_game_tick;
114 }
115 }
116
117 auto now = std::chrono::steady_clock::now();
118 if (this->HasGUI() && now >= this->next_draw_tick) {
119 this->next_draw_tick += this->GetDrawInterval();
120 /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
121 if (this->next_draw_tick < now - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = now;
122
123 /* Locking video buffer can block (especially with vsync enabled), do it before taking game state lock. */
124 this->LockVideoBuffer();
125
126 {
127 /* Tell the game-thread to stop so we can have a go. */
128 std::lock_guard<std::mutex> lock_wait(this->game_thread_wait_mutex);
129 std::lock_guard<std::mutex> lock_state(this->game_state_mutex);
130
131 /* Keep the interactive randomizer a bit more random by requesting
132 * new values when-ever we can. */
133 InteractiveRandom();
134
135 this->DrainCommandQueue();
136
137 while (this->PollEvent()) {}
138 this->InputLoop();
139
140 /* Check if the fast-forward button is still pressed. */
141 if (fast_forward_key_pressed && !_networking && _game_mode != GM_MENU) {
142 ChangeGameSpeed(true);
143 this->fast_forward_via_key = true;
144 } else if (this->fast_forward_via_key) {
145 ChangeGameSpeed(false);
146 this->fast_forward_via_key = false;
147 }
148
149 ::InputLoop();
150
151 /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
152 if (_game_mode == GM_BOOTSTRAP || _switch_mode == SM_NONE || HasModalProgress()) {
153 ::UpdateWindows();
154 }
155
156 this->PopulateSystemSprites();
157 }
158
159 this->CheckPaletteAnim();
160 this->Paint();
161
162 this->UnlockVideoBuffer();
163
164 /* Wait till the first successful drawing tick before marking the driver as operational. */
165 static bool first_draw_tick = true;
166 if (first_draw_tick) {
167 first_draw_tick = false;
168 DriverFactoryBase::MarkVideoDriverOperational();
169 }
170 }
171}
172
173 void VideoDriver::SleepTillNextTick()
174{
175 auto next_tick = this->next_draw_tick;
176 auto now = std::chrono::steady_clock::now();
177
178 if (!this->is_game_threaded) {
179 next_tick = min(next_tick, this->next_game_tick);
180 }
181
182 if (next_tick > now) {
183 std::this_thread::sleep_for(next_tick - now);
184 }
185}
186
191/* static */ std::string VideoDriver::GetCaption()
192{
193 return fmt::format("OpenTTD {}", _openttd_revision);
194}
DriverFactoryBase::MarkVideoDriverOperational
static void MarkVideoDriverOperational()
Mark the current video driver as operational.
Definition driver.cpp:208
VideoDriver
The base of all video drivers.
Definition video_driver.hpp:35
VideoDriver::InputLoop
virtual void InputLoop()
Handle input logic, is CTRL pressed, should we fast-forward, etc.
Definition video_driver.hpp:244
VideoDriver::PopulateSystemSprites
virtual void PopulateSystemSprites()
Populate all sprites in cache.
Definition video_driver.hpp:103
VideoDriver::fast_forward_key_pressed
bool fast_forward_key_pressed
The fast-forward key is being pressed.
Definition video_driver.hpp:346
VideoDriver::Tick
void Tick()
Give the video-driver a tick.
Definition video_driver.cpp:104
VideoDriver::SleepTillNextTick
void SleepTillNextTick()
Sleep till the next tick is about to happen.
Definition video_driver.cpp:173
VideoDriver::StartGameThread
void StartGameThread()
Start the loop for game-tick.
Definition video_driver.cpp:88
VideoDriver::HasGUI
virtual bool HasGUI() const
Whether the driver has a graphical user interface with the end user.
Definition video_driver.hpp:118
VideoDriver::GetCaption
static std::string GetCaption()
Get the caption to use for the game's title bar.
Definition video_driver.cpp:191
VideoDriver::StopGameThread
void StopGameThread()
Stop the loop for the game-tick.
Definition video_driver.cpp:97
VideoDriver::PollEvent
virtual bool PollEvent()
Process a single system event.
Definition video_driver.hpp:274
VideoDriver::Paint
virtual void Paint()
Paint the window.
Definition video_driver.hpp:263
VideoDriver::UnlockVideoBuffer
virtual void UnlockVideoBuffer()
Unlock a previously locked video buffer.
Definition video_driver.hpp:258
VideoDriver::fast_forward_via_key
bool fast_forward_via_key
The fast-forward was enabled by key press.
Definition video_driver.hpp:347
VideoDriver::CheckPaletteAnim
virtual void CheckPaletteAnim()
Process any pending palette animation.
Definition video_driver.hpp:268
VideoDriver::LockVideoBuffer
virtual bool LockVideoBuffer()
Make sure the video buffer is ready for drawing.
Definition video_driver.hpp:250
VideoDriver::GameLoopPause
void GameLoopPause()
Pause the game-loop for a bit, releasing the game-state lock.
Definition video_driver.cpp:68
VideoDriver::DrainCommandQueue
void DrainCommandQueue()
Execute all queued commands.
Definition video_driver.hpp:326
Debug
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
_switch_mode
SwitchMode _switch_mode
The next mainloop command.
Definition gfx.cpp:49
UpdateWindows
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3127
_networking
bool _networking
are we in networking mode?
Definition network.cpp:67
HasModalProgress
bool HasModalProgress()
Check if we are currently in a modal progress state.
Definition progress.h:17
StartNewThread
bool StartNewThread(std::thread *thr, std::string_view name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
_video_vsync
bool _video_vsync
Whether we should use vsync (only if active video driver supports HW acceleration).
Definition video_driver.cpp:28
_video_hw_accel
bool _video_hw_accel
Whether to consider hardware accelerated video drivers on startup.
Definition video_driver.cpp:27
video_driver.hpp
Base of all video drivers.
lock
std::mutex lock
synchronization for playback status fields
Definition win32_m.cpp:35