diff --git a/src/drawing/NewDrawing.cpp b/src/drawing/NewDrawing.cpp index 2409ef442b..5c2af17e2e 100644 --- a/src/drawing/NewDrawing.cpp +++ b/src/drawing/NewDrawing.cpp @@ -43,7 +43,7 @@ extern "C" _drawingEngine = DrawingEngineFactory::CreateSoftware(); break; case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY: - _drawingEngine = DrawingEngineFactory::CreateSoftware(); + _drawingEngine = DrawingEngineFactory::CreateSoftwareWithHardwareDisplay(); break; case DRAWING_ENGINE_OPENGL: _drawingEngine = DrawingEngineFactory::CreateOpenGL(); diff --git a/src/drawing/engines/SoftwareDrawingEngine.cpp b/src/drawing/engines/SoftwareDrawingEngine.cpp index ef960803ec..7c9b66a0cc 100644 --- a/src/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/drawing/engines/SoftwareDrawingEngine.cpp @@ -23,6 +23,7 @@ extern "C" { #include "../../config.h" + #include "../../game.h" #include "../../interface/screenshot.h" #include "../../interface/window.h" #include "../../intro.h" @@ -185,6 +186,18 @@ private: 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 }; + + // Steam overlay checking + uint32 _pixelBeforeOverlay = 0; + uint32 _pixelAfterOverlay = 0; + bool _overlayActive = false; + bool _pausedBeforeOverlay = false; + uint32 _width = 0; uint32 _height = 0; uint32 _pitch = 0; @@ -213,6 +226,9 @@ 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 @@ -225,42 +241,88 @@ public: SDL_FreeSurface(_surface); SDL_FreeSurface(_RGBASurface); SDL_FreePalette(_palette); + SDL_DestroyTexture(_screenTexture); + SDL_FreeFormat(_screenTextureFormat); + SDL_DestroyRenderer(_sdlRenderer); - _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) + if (_hardwareDisplay) { - log_fatal("%p || %p || %p == NULL %s", _surface, _palette, _RGBASurface, SDL_GetError()); - exit(-1); - } + _sdlRenderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (SDL_SetSurfacePalette(_surface, _palette)) + 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); + } + else { - log_fatal("SDL_SetSurfacePalette failed %s", SDL_GetError()); - exit(-1); - } + _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); - ConfigureBits(width, height, _surface->pitch); + 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); + } } void SetPalette(SDL_Color * palette) override { - SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow); - if (windowSurface == nullptr) + if (_hardwareDisplay) { - log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError()); - exit(1); + if (_screenTextureFormat != nullptr) + { + for (int i = 0; i < 256; i++) + { + _paletteHWMapped[i] = SDL_MapRGB(_screenTextureFormat, palette[i].r, palette[i].g, palette[i].b); + } + } } - - if (_palette != nullptr && SDL_SetPaletteColors(_palette, gPalette, 0, 256)) + else { - log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError()); - exit(1); + SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow); + if (windowSurface == nullptr) + { + log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError()); + exit(1); + } + + if (_palette != nullptr && SDL_SetPaletteColors(_palette, palette, 0, 256)) + { + log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError()); + exit(1); + } } } @@ -317,7 +379,15 @@ public: rct2_draw(&_bitsDPI); } - Display(); + + if (_hardwareDisplay) + { + DisplayViaTexture(); + } + else + { + Display(); + } } void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) override @@ -588,6 +658,106 @@ private: exit(1); } } + + void DisplayViaTexture() + { + void * pixels; + int pitch; + if (SDL_LockTexture(_screenTexture, nullptr, &pixels, &pitch) == 0) + { + uint8 * src = _bits; + int padding = pitch - (_width * 4); + if (pitch == _width * 4) { + uint32 * dst = (uint32 *)pixels; + for (int i = _width * _height; i > 0; i--) + { + *dst++ = *(uint32 *)(&_paletteHWMapped[*src++]); + } + } + else + { + if (pitch == (_width * 2) + padding) + { + uint16 * dst = (uint16 *)pixels; + for (sint32 y = (sint32)_height; y > 0; y--) { + for (sint32 x = (sint32)_width; x > 0; x--) { + const uint8 lower = *(uint8 *)(&_paletteHWMapped[*src++]); + const uint8 upper = *(uint8 *)(&_paletteHWMapped[*src++]); + *dst++ = (lower << 8) | upper; + } + dst = (uint16*)(((uint8 *)dst) + padding); + } + } + else + { + if (pitch == _width + padding) + { + uint8 * dst = (uint8 *)pixels; + for (sint32 y = (sint32)_height; y > 0; y--) { + for (sint32 x = (sint32)_width; x > 0; x--) + { + *dst++ = *(uint8 *)(&_paletteHWMapped[*src++]); + } + dst += padding; + } + } + } + } + SDL_UnlockTexture(_screenTexture); + } + + SDL_RenderCopy(_sdlRenderer, _screenTexture, NULL, NULL); + + if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) + { + OverlayPreRenderCheck(); + } + + SDL_RenderPresent(_sdlRenderer); + + if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) + { + OverlayPostRenderCheck(); + } + } + + void ReadCentrePixel(uint32 * pixel) + { + SDL_Rect centrePixelRegion = { (sint32)(_width / 2), (sint32)(_height / 2), 1, 1 }; + SDL_RenderReadPixels(_sdlRenderer, ¢rePixelRegion, 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 * DrawingEngineFactory::CreateSoftware() diff --git a/src/platform/shared.c b/src/platform/shared.c index 5d197d084b..0cb44eaba6 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -171,39 +171,6 @@ void platform_get_closest_resolution(int inWidth, int inHeight, int *outWidth, i } } -static void read_center_pixel(int width, int height, uint32 *pixel) { - SDL_Rect centerPixelRegion = {width / 2, height / 2, 1, 1}; - SDL_RenderReadPixels(gRenderer, ¢erPixelRegion, SDL_PIXELFORMAT_RGBA8888, pixel, sizeof(uint32)); -} - -// Should be called before SDL_RenderPresent to capture frame buffer before Steam overlay is drawn. -static void overlay_pre_render_check(int width, int height) { - read_center_pixel(width, height, &_pixelBeforeOverlay); -} - -// Should be called after SDL_RenderPresent, when Steam overlay has had the chance to be drawn. -static void overlay_post_render_check(int width, int height) { - static bool overlayActive = false; - static bool pausedBeforeOverlay = false; - - read_center_pixel(width, height, &_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; -} - void platform_draw() { if (!gOpenRCT2Headless) {