Separate hardware display drawing engine from software

This commit is contained in:
Ted John 2017-06-08 22:42:22 +01:00
parent df16e6a4f7
commit 0ad2c075e5
3 changed files with 286 additions and 252 deletions

View File

@ -0,0 +1,249 @@
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 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, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include <openrct2/common.h>
#include <SDL.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/IDrawingEngine.h>
#include <openrct2/drawing/X8DrawingEngine.h>
#include <openrct2/ui/UiContext.h>
#include "DrawingEngines.h"
extern "C"
{
#include <openrct2/drawing/lightfx.h>
#include <openrct2/game.h>
}
using namespace OpenRCT2;
using namespace OpenRCT2::Drawing;
using namespace OpenRCT2::Ui;
class HardwareDisplayDrawingEngine final : public X8DrawingEngine
{
private:
IUiContext * const _uiContext;
SDL_Window * _window = nullptr;
SDL_Renderer * _sdlRenderer = nullptr;
SDL_Texture * _screenTexture = nullptr;
SDL_PixelFormat * _screenTextureFormat = nullptr;
uint32 _paletteHWMapped[256] = { 0 };
#ifdef __ENABLE_LIGHTFX__
uint32 _lightPaletteHWMapped[256] = { 0 };
#endif
// Steam overlay checking
uint32 _pixelBeforeOverlay = 0;
uint32 _pixelAfterOverlay = 0;
bool _overlayActive = false;
bool _pausedBeforeOverlay = false;
public:
explicit HardwareDisplayDrawingEngine(IUiContext * uiContext)
: _uiContext(uiContext)
{
UNUSED(_uiContext); // Will be used in due course to retrieve window information
}
~HardwareDisplayDrawingEngine() override
{
SDL_DestroyTexture(_screenTexture);
SDL_FreeFormat(_screenTextureFormat);
SDL_DestroyRenderer(_sdlRenderer);
}
void Initialise(SDL_Window * window) override
{
_window = window;
_sdlRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}
void Resize(uint32 width, uint32 height) override
{
SDL_DestroyTexture(_screenTexture);
SDL_FreeFormat(_screenTextureFormat);
SDL_RendererInfo rendererInfo;
SDL_GetRendererInfo(_sdlRenderer, &rendererInfo);
uint32 pixelFormat = SDL_PIXELFORMAT_UNKNOWN;
for (uint32 i = 0; i < rendererInfo.num_texture_formats; i++)
{
uint32 format = rendererInfo.texture_formats[i];
if (!SDL_ISPIXELFORMAT_FOURCC(format) &&
!SDL_ISPIXELFORMAT_INDEXED(format) &&
(pixelFormat == SDL_PIXELFORMAT_UNKNOWN || SDL_BYTESPERPIXEL(format) < SDL_BYTESPERPIXEL(pixelFormat)))
{
pixelFormat = format;
}
}
_screenTexture = SDL_CreateTexture(_sdlRenderer, pixelFormat, SDL_TEXTUREACCESS_STREAMING, width, height);
uint32 format;
SDL_QueryTexture(_screenTexture, &format, 0, 0, 0);
_screenTextureFormat = SDL_AllocFormat(format);
ConfigureBits(width, height, width);
}
void SetPalette(const rct_palette_entry * palette) override
{
if (_screenTextureFormat != nullptr)
{
for (sint32 i = 0; i < 256; i++)
{
_paletteHWMapped[i] = SDL_MapRGB(_screenTextureFormat, palette[i].red, palette[i].green, palette[i].blue);
}
#ifdef __ENABLE_LIGHTFX__
if (gConfigGeneral.enable_light_fx)
{
const SDL_Color * lightPalette = lightfx_get_palette();
for (sint32 i = 0; i < 256; i++)
{
_lightPaletteHWMapped[i] = SDL_MapRGBA(_screenTextureFormat, lightPalette[i].r, lightPalette[i].g, lightPalette[i].b, lightPalette[i].a);
}
}
#endif
}
}
void Draw() override
{
X8DrawingEngine::Draw();
Display();
}
private:
void Display()
{
#ifdef __ENABLE_LIGHTFX__
if (gConfigGeneral.enable_light_fx)
{
lightfx_render_to_texture(_screenTexture, _bits, _width, _height, _paletteHWMapped, _lightPaletteHWMapped);
}
else
#endif
{
CopyBitsToTexture(_screenTexture, _bits, (sint32)_width, (sint32)_height, _paletteHWMapped);
}
SDL_RenderCopy(_sdlRenderer, _screenTexture, nullptr, nullptr);
bool isSteamOverlayActive = GetContext()->GetUiContext()->IsSteamOverlayActive();
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
{
OverlayPreRenderCheck();
}
SDL_RenderPresent(_sdlRenderer);
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
{
OverlayPostRenderCheck();
}
}
void CopyBitsToTexture(SDL_Texture * texture, uint8 * src, sint32 width, sint32 height, uint32 * palette)
{
void * pixels;
sint32 pitch;
if (SDL_LockTexture(texture, nullptr, &pixels, &pitch) == 0)
{
sint32 padding = pitch - (width * 4);
if (pitch == width * 4)
{
uint32 * dst = (uint32 *)pixels;
for (sint32 i = width * height; i > 0; i--)
{
*dst++ = palette[*src++];
}
}
else
{
if (pitch == (width * 2) + padding)
{
uint16 * dst = (uint16 *)pixels;
for (sint32 y = height; y > 0; y--)
{
for (sint32 x = width; x > 0; x--)
{
const uint8 lower = *(uint8 *)(&palette[*src++]);
const uint8 upper = *(uint8 *)(&palette[*src++]);
*dst++ = (lower << 8) | upper;
}
dst = (uint16*)(((uint8 *)dst) + padding);
}
}
else if (pitch == width + padding)
{
uint8 * dst = (uint8 *)pixels;
for (sint32 y = height; y > 0; y--)
{
for (sint32 x = width; x > 0; x--)
{
*dst++ = *(uint8 *)(&palette[*src++]);
}
dst += padding;
}
}
}
SDL_UnlockTexture(texture);
}
}
void ReadCentrePixel(uint32 * pixel)
{
SDL_Rect centrePixelRegion = { (sint32)(_width / 2), (sint32)(_height / 2), 1, 1 };
SDL_RenderReadPixels(_sdlRenderer, &centrePixelRegion, SDL_PIXELFORMAT_RGBA8888, pixel, sizeof(uint32));
}
// Should be called before SDL_RenderPresent to capture frame buffer before Steam overlay is drawn.
void OverlayPreRenderCheck()
{
ReadCentrePixel(&_pixelBeforeOverlay);
}
// Should be called after SDL_RenderPresent, when Steam overlay has had the chance to be drawn.
void OverlayPostRenderCheck()
{
ReadCentrePixel(&_pixelAfterOverlay);
// Detect an active Steam overlay by checking if the center pixel is changed by the gray fade.
// Will not be triggered by applications rendering to corners, like FRAPS, MSI Afterburner and Friends popups.
bool newOverlayActive = _pixelBeforeOverlay != _pixelAfterOverlay;
// Toggle game pause state consistently with base pause state
if (!_overlayActive && newOverlayActive)
{
_pausedBeforeOverlay = gGamePaused & GAME_PAUSED_NORMAL;
if (!_pausedBeforeOverlay)
{
pause_toggle();
}
}
else if (_overlayActive && !newOverlayActive && !_pausedBeforeOverlay)
{
pause_toggle();
}
_overlayActive = newOverlayActive;
}
};
IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(IUiContext * uiContext)
{
return new HardwareDisplayDrawingEngine(uiContext);
}

View File

@ -14,30 +14,19 @@
*****************************************************************************/
#pragma endregion
#include <openrct2/common.h>
#include <SDL.h>
#include <openrct2/config/Config.h>
#include <openrct2/Context.h>
#include <openrct2/ui/UiContext.h>
#include <openrct2/core/Guard.hpp>
#include <openrct2/core/Math.hpp>
#include <openrct2/core/Memory.hpp>
#include <openrct2/drawing/IDrawingContext.h>
#include <openrct2/drawing/IDrawingEngine.h>
#include <openrct2/drawing/Rain.h>
#include <openrct2/drawing/X8DrawingEngine.h>
#include <openrct2/ui/UiContext.h>
#include "DrawingEngines.h"
extern "C"
{
#include <openrct2/config/Config.h>
#include <openrct2/game.h>
#include <openrct2/interface/screenshot.h>
#include <openrct2/interface/viewport.h>
#include <openrct2/interface/window.h>
#include <openrct2/intro.h>
#include <openrct2/platform/platform.h>
#include <openrct2/rct2.h>
#include <openrct2/drawing/drawing.h>
#include <openrct2/drawing/lightfx.h>
}
using namespace OpenRCT2;
@ -48,32 +37,14 @@ class SoftwareDrawingEngine final : public X8DrawingEngine
{
private:
IUiContext * const _uiContext;
bool const _hardwareDisplay;
SDL_Window * _window = nullptr;
SDL_Surface * _surface = nullptr;
SDL_Surface * _RGBASurface = nullptr;
SDL_Palette * _palette = nullptr;
// For hardware display only
SDL_Renderer * _sdlRenderer = nullptr;
SDL_Texture * _screenTexture = nullptr;
SDL_PixelFormat * _screenTextureFormat = nullptr;
uint32 _paletteHWMapped[256] = { 0 };
#ifdef __ENABLE_LIGHTFX__
uint32 _lightPaletteHWMapped[256] = { 0 };
#endif
// Steam overlay checking
uint32 _pixelBeforeOverlay = 0;
uint32 _pixelAfterOverlay = 0;
bool _overlayActive = false;
bool _pausedBeforeOverlay = false;
SDL_Window * _window = nullptr;
SDL_Surface * _surface = nullptr;
SDL_Surface * _RGBASurface = nullptr;
SDL_Palette * _palette = nullptr;
public:
explicit SoftwareDrawingEngine(IUiContext * uiContext, bool hardwareDisplay)
: _uiContext(uiContext),
_hardwareDisplay(hardwareDisplay)
explicit SoftwareDrawingEngine(IUiContext * uiContext)
: _uiContext(uiContext)
{
UNUSED(_uiContext); // Will be used in due course to retrieve window information
}
@ -83,19 +54,11 @@ public:
SDL_FreeSurface(_surface);
SDL_FreeSurface(_RGBASurface);
SDL_FreePalette(_palette);
SDL_DestroyTexture(_screenTexture);
SDL_FreeFormat(_screenTextureFormat);
SDL_DestroyRenderer(_sdlRenderer);
}
void Initialise(SDL_Window * window) override
{
_window = window;
if (_hardwareDisplay)
{
// Try to create the accelerated renderer.
_sdlRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}
}
void Resize(uint32 width, uint32 height) override
@ -103,109 +66,49 @@ public:
SDL_FreeSurface(_surface);
SDL_FreeSurface(_RGBASurface);
SDL_FreePalette(_palette);
SDL_DestroyTexture(_screenTexture);
SDL_FreeFormat(_screenTextureFormat);
if (_sdlRenderer != nullptr)
_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
_RGBASurface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
SDL_SetSurfaceBlendMode(_RGBASurface, SDL_BLENDMODE_NONE);
_palette = SDL_AllocPalette(256);
if (_surface == nullptr ||
_palette == nullptr ||
_RGBASurface == nullptr)
{
SDL_RendererInfo rendererInfo;
SDL_GetRendererInfo(_sdlRenderer, &rendererInfo);
uint32 pixelFormat = SDL_PIXELFORMAT_UNKNOWN;
for (uint32 i = 0; i < rendererInfo.num_texture_formats; i++)
{
uint32 format = rendererInfo.texture_formats[i];
if (!SDL_ISPIXELFORMAT_FOURCC(format) &&
!SDL_ISPIXELFORMAT_INDEXED(format) &&
(pixelFormat == SDL_PIXELFORMAT_UNKNOWN || SDL_BYTESPERPIXEL(format) < SDL_BYTESPERPIXEL(pixelFormat)))
{
pixelFormat = format;
}
}
_screenTexture = SDL_CreateTexture(_sdlRenderer, pixelFormat, SDL_TEXTUREACCESS_STREAMING, width, height);
uint32 format;
SDL_QueryTexture(_screenTexture, &format, 0, 0, 0);
_screenTextureFormat = SDL_AllocFormat(format);
ConfigureBits(width, height, width);
log_fatal("%p || %p || %p == NULL %s", _surface, _palette, _RGBASurface, SDL_GetError());
exit(-1);
}
else
if (SDL_SetSurfacePalette(_surface, _palette))
{
_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
_RGBASurface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
SDL_SetSurfaceBlendMode(_RGBASurface, SDL_BLENDMODE_NONE);
_palette = SDL_AllocPalette(256);
if (_surface == nullptr ||
_palette == nullptr ||
_RGBASurface == nullptr)
{
log_fatal("%p || %p || %p == NULL %s", _surface, _palette, _RGBASurface, SDL_GetError());
exit(-1);
}
if (SDL_SetSurfacePalette(_surface, _palette))
{
log_fatal("SDL_SetSurfacePalette failed %s", SDL_GetError());
exit(-1);
}
ConfigureBits(width, height, _surface->pitch);
log_fatal("SDL_SetSurfacePalette failed %s", SDL_GetError());
exit(-1);
}
ConfigureBits(width, height, _surface->pitch);
}
void SetPalette(const rct_palette_entry * palette) override
{
if (_sdlRenderer != nullptr)
SDL_Surface * windowSurface = SDL_GetWindowSurface(_window);
if (windowSurface != nullptr && _palette != nullptr)
{
if (_screenTextureFormat != nullptr)
{
for (sint32 i = 0; i < 256; i++)
{
_paletteHWMapped[i] = SDL_MapRGB(_screenTextureFormat, palette[i].red, palette[i].green, palette[i].blue);
}
#ifdef __ENABLE_LIGHTFX__
if (gConfigGeneral.enable_light_fx)
{
const SDL_Color * lightPalette = lightfx_get_palette();
for (sint32 i = 0; i < 256; i++)
{
_lightPaletteHWMapped[i] = SDL_MapRGBA(_screenTextureFormat, lightPalette[i].r, lightPalette[i].g, lightPalette[i].b, lightPalette[i].a);
}
}
#endif
}
}
else
{
SDL_Surface * windowSurface = SDL_GetWindowSurface(_window);
if (windowSurface != nullptr && _palette != nullptr)
{
SDL_Colour colours[256];
for (sint32 i = 0; i < 256; i++) {
colours[i].r = palette[i].red;
colours[i].g = palette[i].green;
colours[i].b = palette[i].blue;
colours[i].a = palette[i].alpha;
}
SDL_SetPaletteColors(_palette, colours, 0, 256);
SDL_Colour colours[256];
for (sint32 i = 0; i < 256; i++) {
colours[i].r = palette[i].red;
colours[i].g = palette[i].green;
colours[i].b = palette[i].blue;
colours[i].a = palette[i].alpha;
}
SDL_SetPaletteColors(_palette, colours, 0, 256);
}
}
void Draw() override
{
X8DrawingEngine::Draw();
if (_sdlRenderer != nullptr)
{
DisplayViaTexture();
}
else
{
Display();
}
Display();
}
private:
@ -263,128 +166,9 @@ private:
exit(1);
}
}
void DisplayViaTexture()
{
#ifdef __ENABLE_LIGHTFX__
if (gConfigGeneral.enable_light_fx)
{
lightfx_render_to_texture(_screenTexture, _bits, _width, _height, _paletteHWMapped, _lightPaletteHWMapped);
}
else
#endif
{
CopyBitsToTexture(_screenTexture, _bits, (sint32)_width, (sint32)_height, _paletteHWMapped);
}
SDL_RenderCopy(_sdlRenderer, _screenTexture, nullptr, nullptr);
bool isSteamOverlayActive = GetContext()->GetUiContext()->IsSteamOverlayActive();
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
{
OverlayPreRenderCheck();
}
SDL_RenderPresent(_sdlRenderer);
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
{
OverlayPostRenderCheck();
}
}
void CopyBitsToTexture(SDL_Texture * texture, uint8 * src, sint32 width, sint32 height, uint32 * palette)
{
void * pixels;
sint32 pitch;
if (SDL_LockTexture(texture, nullptr, &pixels, &pitch) == 0)
{
sint32 padding = pitch - (width * 4);
if (pitch == width * 4)
{
uint32 * dst = (uint32 *)pixels;
for (sint32 i = width * height; i > 0; i--)
{
*dst++ = palette[*src++];
}
}
else
{
if (pitch == (width * 2) + padding)
{
uint16 * dst = (uint16 *)pixels;
for (sint32 y = height; y > 0; y--)
{
for (sint32 x = width; x > 0; x--)
{
const uint8 lower = *(uint8 *)(&palette[*src++]);
const uint8 upper = *(uint8 *)(&palette[*src++]);
*dst++ = (lower << 8) | upper;
}
dst = (uint16*)(((uint8 *)dst) + padding);
}
}
else if (pitch == width + padding)
{
uint8 * dst = (uint8 *)pixels;
for (sint32 y = height; y > 0; y--)
{
for (sint32 x = width; x > 0; x--)
{
*dst++ = *(uint8 *)(&palette[*src++]);
}
dst += padding;
}
}
}
SDL_UnlockTexture(texture);
}
}
void ReadCentrePixel(uint32 * pixel)
{
SDL_Rect centrePixelRegion = { (sint32)(_width / 2), (sint32)(_height / 2), 1, 1 };
SDL_RenderReadPixels(_sdlRenderer, &centrePixelRegion, SDL_PIXELFORMAT_RGBA8888, pixel, sizeof(uint32));
}
// Should be called before SDL_RenderPresent to capture frame buffer before Steam overlay is drawn.
void OverlayPreRenderCheck()
{
ReadCentrePixel(&_pixelBeforeOverlay);
}
// Should be called after SDL_RenderPresent, when Steam overlay has had the chance to be drawn.
void OverlayPostRenderCheck()
{
ReadCentrePixel(&_pixelAfterOverlay);
// Detect an active Steam overlay by checking if the center pixel is changed by the gray fade.
// Will not be triggered by applications rendering to corners, like FRAPS, MSI Afterburner and Friends popups.
bool newOverlayActive = _pixelBeforeOverlay != _pixelAfterOverlay;
// Toggle game pause state consistently with base pause state
if (!_overlayActive && newOverlayActive)
{
_pausedBeforeOverlay = gGamePaused & GAME_PAUSED_NORMAL;
if (!_pausedBeforeOverlay)
{
pause_toggle();
}
}
else if (_overlayActive && !newOverlayActive && !_pausedBeforeOverlay)
{
pause_toggle();
}
_overlayActive = newOverlayActive;
}
};
IDrawingEngine * OpenRCT2::Ui::CreateSoftwareDrawingEngine(IUiContext * uiContext)
{
return new SoftwareDrawingEngine(uiContext, false);
}
IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(IUiContext * uiContext)
{
return new SoftwareDrawingEngine(uiContext, true);
return new SoftwareDrawingEngine(uiContext);
}

View File

@ -29,6 +29,7 @@
<ClCompile Include="audio\MemoryAudioSource.cpp" />
<ClCompile Include="CursorData.cpp" />
<ClCompile Include="CursorRepository.cpp" />
<ClCompile Include="drawing\engines\HardwareDisplayDrawingEngine.cpp" />
<ClCompile Include="drawing\engines\opengl\CopyFramebufferShader.cpp" />
<ClCompile Include="drawing\engines\opengl\DrawImageShader.cpp" />
<ClCompile Include="drawing\engines\opengl\DrawLineShader.cpp" />