OpenTTD Source  1.11.0-beta2
sdl2_default_v.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 <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../gfx_func.h"
13 #include "../rev.h"
14 #include "../blitter/factory.hpp"
15 #include "../network/network.h"
16 #include "../thread.h"
17 #include "../progress.h"
18 #include "../core/random_func.hpp"
19 #include "../core/math_func.hpp"
20 #include "../core/mem_func.hpp"
21 #include "../core/geometry_func.hpp"
22 #include "../fileio_func.h"
23 #include "../framerate_type.h"
24 #include "../window_func.h"
25 #include "sdl2_default_v.h"
26 #include <SDL.h>
27 #include <mutex>
28 #include <condition_variable>
29 #ifdef __EMSCRIPTEN__
30 # include <emscripten.h>
31 # include <emscripten/html5.h>
32 #endif
33 
34 #include "../safeguards.h"
35 
36 static FVideoDriver_SDL_Default iFVideoDriver_SDL_Default;
37 
38 static SDL_Surface *_sdl_surface;
39 static SDL_Surface *_sdl_rgb_surface;
40 static SDL_Surface *_sdl_real_surface;
41 static SDL_Palette *_sdl_palette;
42 
43 void VideoDriver_SDL_Default::UpdatePalette()
44 {
45  SDL_Color pal[256];
46 
47  for (int i = 0; i != this->local_palette.count_dirty; i++) {
48  pal[i].r = this->local_palette.palette[this->local_palette.first_dirty + i].r;
49  pal[i].g = this->local_palette.palette[this->local_palette.first_dirty + i].g;
50  pal[i].b = this->local_palette.palette[this->local_palette.first_dirty + i].b;
51  pal[i].a = 0;
52  }
53 
54  SDL_SetPaletteColors(_sdl_palette, pal, this->local_palette.first_dirty, this->local_palette.count_dirty);
55  SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
56 }
57 
58 void VideoDriver_SDL_Default::MakePalette()
59 {
60  if (_sdl_palette == nullptr) {
61  _sdl_palette = SDL_AllocPalette(256);
62  if (_sdl_palette == nullptr) usererror("SDL2: Couldn't allocate palette: %s", SDL_GetError());
63  }
64 
68  this->UpdatePalette();
69 
70  if (_sdl_surface != _sdl_real_surface) {
71  /* When using a shadow surface, also set our palette on the real screen. This lets SDL
72  * allocate as many colors (or approximations) as
73  * possible, instead of using only the default SDL
74  * palette. This allows us to get more colors exactly
75  * right and might allow using better approximations for
76  * other colors.
77  *
78  * Note that colors allocations are tried in-order, so
79  * this favors colors further up into the palette. Also
80  * note that if two colors from the same animation
81  * sequence are approximated using the same color, that
82  * animation will stop working.
83  *
84  * Since changing the system palette causes the colours
85  * to change right away, and allocations might
86  * drastically change, we can't use this for animation,
87  * since that could cause weird coloring between the
88  * palette change and the blitting below, so we only set
89  * the real palette during initialisation.
90  */
91  SDL_SetSurfacePalette(_sdl_real_surface, _sdl_palette);
92  }
93 }
94 
96 {
97  PerformanceMeasurer framerate(PFE_VIDEO);
98 
99  if (IsEmptyRect(this->dirty_rect) && _cur_palette.count_dirty == 0) return;
100 
101  if (_cur_palette.count_dirty != 0) {
103 
104  switch (blitter->UsePaletteAnimation()) {
106  this->UpdatePalette();
107  break;
108 
110  bool need_buf = _screen.dst_ptr == nullptr;
111  if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
112  blitter->PaletteAnimate(this->local_palette);
113  if (need_buf) {
114  this->ReleaseVideoPointer();
115  _screen.dst_ptr = nullptr;
116  }
117  break;
118  }
119 
121  break;
122 
123  default:
124  NOT_REACHED();
125  }
127  }
128 
129  SDL_Rect r = { this->dirty_rect.left, this->dirty_rect.top, this->dirty_rect.right - this->dirty_rect.left, this->dirty_rect.bottom - this->dirty_rect.top };
130 
131  if (_sdl_surface != _sdl_real_surface) {
132  SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
133  }
134  SDL_UpdateWindowSurfaceRects(this->sdl_window, &r, 1);
135 
136  this->dirty_rect = {};
137 }
138 
140 {
141  /* First tell the main thread we're started */
142  std::unique_lock<std::recursive_mutex> lock(*this->draw_mutex);
143  this->draw_signal->notify_one();
144 
145  /* Now wait for the first thing to draw! */
146  this->draw_signal->wait(*this->draw_mutex);
147 
148  while (this->draw_continue) {
149  /* Then just draw and wait till we stop */
150  this->Paint();
151  this->draw_signal->wait(lock);
152  }
153 }
154 
155 bool VideoDriver_SDL_Default::AllocateBackingStore(int w, int h, bool force)
156 {
158 
159  _sdl_real_surface = SDL_GetWindowSurface(this->sdl_window);
160  if (_sdl_real_surface == nullptr) usererror("SDL2: Couldn't get window surface: %s", SDL_GetError());
161 
162  if (!force && w == _sdl_real_surface->w && h == _sdl_real_surface->h) return false;
163 
164  /* Free any previously allocated rgb surface. */
165  if (_sdl_rgb_surface != nullptr) {
166  SDL_FreeSurface(_sdl_rgb_surface);
167  _sdl_rgb_surface = nullptr;
168  }
169 
170  if (bpp == 8) {
171  _sdl_rgb_surface = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
172  if (_sdl_rgb_surface == nullptr) usererror("SDL2: Couldn't allocate shadow surface: %s", SDL_GetError());
173 
174  _sdl_surface = _sdl_rgb_surface;
175  } else {
176  _sdl_surface = _sdl_real_surface;
177  }
178 
179  /* X11 doesn't appreciate it if we invalidate areas outside the window
180  * if shared memory is enabled (read: it crashes). So, as we might have
181  * gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower
182  * will mark the whole screen dirty again anyway, but this time with the
183  * new dimensions. */
184  this->dirty_rect = {};
185 
186  _screen.width = _sdl_surface->w;
187  _screen.height = _sdl_surface->h;
188  _screen.pitch = _sdl_surface->pitch / (bpp / 8);
189  _screen.dst_ptr = this->GetVideoPointer();
190 
191  this->MakePalette();
192 
193  return true;
194 }
195 
197 {
198  return _sdl_surface->pixels;
199 }
Palette::first_dirty
int first_dirty
The first dirty element.
Definition: gfx_type.h:315
FVideoDriver_SDL_Default
Factory for the SDL video driver.
Definition: sdl2_default_v.h:34
usererror
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:100
PFE_VIDEO
@ PFE_VIDEO
Speed of painting drawn video buffer.
Definition: framerate_type.h:59
Blitter::UsePaletteAnimation
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
lock
std::mutex lock
synchronization for playback status fields
Definition: win32_m.cpp:34
Blitter
How all blitters should look like.
Definition: base.hpp:28
Blitter::GetScreenDepth
virtual uint8 GetScreenDepth()=0
Get the screen depth this blitter works for.
PerformanceMeasurer
RAII class for measuring simple elements of performance.
Definition: framerate_type.h:92
VideoDriver_SDL_Default::ReleaseVideoPointer
void ReleaseVideoPointer() override
Hand video buffer back to the painting backend.
Definition: sdl2_default_v.h:26
VideoDriver_SDL_Default::PaintThread
void PaintThread() override
Thread function for threaded drawing.
Definition: sdl2_default_v.cpp:139
VideoDriver_SDL_Default::AllocateBackingStore
bool AllocateBackingStore(int w, int h, bool force=false) override
(Re-)create the backing store.
Definition: sdl2_default_v.cpp:155
Palette::palette
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition: gfx_type.h:314
sdl2_default_v.h
VideoDriver_SDL_Base::draw_signal
std::condition_variable_any * draw_signal
Signal to draw the next frame.
Definition: sdl2_v.h:53
VideoDriver_SDL_Base::local_palette
Palette local_palette
Copy of _cur_palette.
Definition: sdl2_v.h:50
BlitterFactory::GetCurrentBlitter
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:140
Palette::count_dirty
int count_dirty
The number of dirty elements.
Definition: gfx_type.h:316
VideoDriver_SDL_Base::draw_mutex
std::recursive_mutex * draw_mutex
Mutex to keep the access to the shared memory controlled.
Definition: sdl2_v.h:52
IsEmptyRect
static bool IsEmptyRect(const Rect &r)
Check if a rectangle is empty.
Definition: geometry_func.hpp:22
Blitter::PALETTE_ANIMATION_VIDEO_BACKEND
@ PALETTE_ANIMATION_VIDEO_BACKEND
Palette animation should be done by video backend (8bpp only!)
Definition: base.hpp:51
VideoDriver_SDL_Base::dirty_rect
Rect dirty_rect
Rectangle encompassing the dirty area of the video buffer.
Definition: sdl2_v.h:56
Blitter::PALETTE_ANIMATION_NONE
@ PALETTE_ANIMATION_NONE
No palette animation.
Definition: base.hpp:50
VideoDriver_SDL_Base::draw_continue
volatile bool draw_continue
Should we keep continue drawing?
Definition: sdl2_v.h:54
_cur_palette
Palette _cur_palette
Current palette.
Definition: gfx.cpp:48
VideoDriver_SDL_Default::Paint
void Paint() override
Paint the window.
Definition: sdl2_default_v.cpp:95
OpenGLBackend::UpdatePalette
void UpdatePalette(const Colour *pal, uint first, uint length)
Update the stored palette.
Definition: opengl.cpp:987
Blitter::PaletteAnimate
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
Blitter::PALETTE_ANIMATION_BLITTER
@ PALETTE_ANIMATION_BLITTER
The blitter takes care of the palette animation.
Definition: base.hpp:52
VideoDriver_SDL_Default::GetVideoPointer
void * GetVideoPointer() override
Get a pointer to the video buffer.
Definition: sdl2_default_v.cpp:196
VideoDriver_SDL_Base::sdl_window
struct SDL_Window * sdl_window
Main SDL window.
Definition: sdl2_v.h:49