From b5e0b4523db09e0c5493c25988694e8d9dcc0793 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Fri, 23 Oct 2020 10:27:06 -0230 Subject: [PATCH] Fairly huge refactoring of FrameBuffer class into FBBackend and friends. Only tested in Linux and libretro for now; Windows and Mac will follow soon. --- ...{FrameBufferSDL2.cxx => FBBackendSDL2.cxx} | 185 +++++++--------- ...{FrameBufferSDL2.hxx => FBBackendSDL2.hxx} | 191 ++++++++-------- src/common/FBSurfaceSDL2.cxx | 22 +- src/common/FBSurfaceSDL2.hxx | 9 +- src/common/MediaFactory.hxx | 10 +- src/common/module.mk | 2 +- src/common/sdl_blitter/BilinearBlitter.cxx | 4 +- src/common/sdl_blitter/BilinearBlitter.hxx | 6 +- src/common/sdl_blitter/BlitterFactory.cxx | 2 +- src/common/sdl_blitter/BlitterFactory.hxx | 4 +- src/common/sdl_blitter/QisBlitter.cxx | 6 +- src/common/sdl_blitter/QisBlitter.hxx | 8 +- src/emucore/Console.cxx | 38 +++- src/emucore/Console.hxx | 16 +- src/emucore/FBBackend.hxx | 207 ++++++++++++++++++ src/emucore/FBSurface.hxx | 3 +- src/emucore/FrameBuffer.cxx | 58 +++-- src/emucore/FrameBuffer.hxx | 199 ++++------------- src/emucore/OSystem.cxx | 13 +- src/emucore/TIASurface.cxx | 8 +- src/emucore/TIASurface.hxx | 8 +- ...fferLIBRETRO.cxx => FBBackendLIBRETRO.cxx} | 36 +-- src/libretro/FBBackendLIBRETRO.hxx | 113 ++++++++++ src/libretro/FBSurfaceLIBRETRO.cxx | 16 +- src/libretro/FBSurfaceLIBRETRO.hxx | 26 +-- src/libretro/FrameBufferLIBRETRO.hxx | 202 ----------------- src/libretro/Makefile.common | 2 +- src/libretro/StellaLIBRETRO.cxx | 35 +-- src/libretro/StellaLIBRETRO.hxx | 40 ++-- src/windows/Stella.vcxproj | 6 +- src/windows/Stella.vcxproj.filters | 6 +- 31 files changed, 734 insertions(+), 747 deletions(-) rename src/common/{FrameBufferSDL2.cxx => FBBackendSDL2.cxx} (77%) rename src/common/{FrameBufferSDL2.hxx => FBBackendSDL2.hxx} (75%) create mode 100644 src/emucore/FBBackend.hxx rename src/libretro/{FrameBufferLIBRETRO.cxx => FBBackendLIBRETRO.cxx} (52%) create mode 100644 src/libretro/FBBackendLIBRETRO.hxx delete mode 100644 src/libretro/FrameBufferLIBRETRO.hxx diff --git a/src/common/FrameBufferSDL2.cxx b/src/common/FBBackendSDL2.cxx similarity index 77% rename from src/common/FrameBufferSDL2.cxx rename to src/common/FBBackendSDL2.cxx index 039548f9a..643c099e9 100644 --- a/src/common/FrameBufferSDL2.cxx +++ b/src/common/FBBackendSDL2.cxx @@ -27,11 +27,11 @@ #include "ThreadDebugging.hxx" #include "FBSurfaceSDL2.hxx" -#include "FrameBufferSDL2.hxx" +#include "FBBackendSDL2.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem) - : FrameBuffer(osystem) +FBBackendSDL2::FBBackendSDL2(OSystem& osystem) + : myOSystem(osystem) { ASSERT_MAIN_THREAD; @@ -43,7 +43,7 @@ FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem) Logger::error(buf.str()); throw runtime_error("FATAL ERROR"); } - Logger::debug("FrameBufferSDL2::FrameBufferSDL2 SDL_Init()"); + Logger::debug("FBBackendSDL2::FBBackendSDL2 SDL_Init()"); // We need a pixel format for palette value calculations // It's done this way (vs directly accessing a FBSurfaceSDL2 object) @@ -53,7 +53,7 @@ FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameBufferSDL2::~FrameBufferSDL2() +FBBackendSDL2::~FBBackendSDL2() { ASSERT_MAIN_THREAD; @@ -61,12 +61,6 @@ FrameBufferSDL2::~FrameBufferSDL2() if(myRenderer) { - // Make sure to free surfaces/textures before destroying the renderer itself - // Most platforms are fine with doing this in either order, but it seems - // that OpenBSD in particular crashes when attempting to destroy textures - // *after* the renderer is already destroyed - freeSurfaces(); - SDL_DestroyRenderer(myRenderer); myRenderer = nullptr; } @@ -81,9 +75,9 @@ FrameBufferSDL2::~FrameBufferSDL2() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::queryHardware(vector& fullscreenRes, - vector& windowedRes, - VariantList& renderers) +void FBBackendSDL2::queryHardware(vector& fullscreenRes, + vector& windowedRes, + VariantList& renderers) { ASSERT_MAIN_THREAD; @@ -105,7 +99,7 @@ void FrameBufferSDL2::queryHardware(vector& fullscreenRes, string lastRes = ""; - for (int m = 0; m < numModes; m++) + for(int m = 0; m < numModes; ++m) { SDL_DisplayMode mode; ostringstream res; @@ -196,7 +190,7 @@ void FrameBufferSDL2::queryHardware(vector& fullscreenRes, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::isCurrentWindowPositioned() const +bool FBBackendSDL2::isCurrentWindowPositioned() const { ASSERT_MAIN_THREAD; @@ -205,7 +199,7 @@ bool FrameBufferSDL2::isCurrentWindowPositioned() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Common::Point FrameBufferSDL2::getCurrentWindowPos() const +Common::Point FBBackendSDL2::getCurrentWindowPos() const { ASSERT_MAIN_THREAD; @@ -217,7 +211,7 @@ Common::Point FrameBufferSDL2::getCurrentWindowPos() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Int32 FrameBufferSDL2::getCurrentDisplayIndex() const +Int32 FBBackendSDL2::getCurrentDisplayIndex() const { ASSERT_MAIN_THREAD; @@ -225,8 +219,8 @@ Int32 FrameBufferSDL2::getCurrentDisplayIndex() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::activateVideoMode(const string& title, - const VideoModeHandler::Mode& mode) +bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode, + int winIdx, const Common::Point& winPos) { ASSERT_MAIN_THREAD; @@ -236,26 +230,22 @@ bool FrameBufferSDL2::activateVideoMode(const string& title, const bool fullScreen = mode.fsIndex != -1; bool forceCreateRenderer = false; - - // Get windowed window's last display - Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey())); - // Get windowed window's last position - myWindowedPos = myOSystem.settings().getPoint(getPositionKey()); + Int32 displayIndex = std::min(myNumDisplays, winIdx); int posX, posY; myCenter = myOSystem.settings().getBool("center"); - if (myCenter) + if(myCenter) posX = posY = SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex); else { - posX = myWindowedPos.x; - posY = myWindowedPos.y; + posX = winPos.x; + posY = winPos.y; // Make sure the window is at least partially visibile int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - for (int display = SDL_GetNumVideoDisplays() - 1; display >= 0; display--) + for(int display = SDL_GetNumVideoDisplays() - 1; display >= 0; --display) { SDL_Rect rect; @@ -273,16 +263,22 @@ bool FrameBufferSDL2::activateVideoMode(const string& title, #ifdef ADAPTABLE_REFRESH_SUPPORT SDL_DisplayMode adaptedSdlMode; - const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh") - && gameRefreshRate() + const int gameRefreshRate = + myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0; + const bool shouldAdapt = fullScreen + && myOSystem.settings().getBool("tia.fs_refresh") + && gameRefreshRate // take care of 59.94 Hz - && refreshRate() % gameRefreshRate() != 0 && refreshRate() % (gameRefreshRate() - 1) != 0; - const bool adaptRefresh = shouldAdapt && adaptRefreshRate(displayIndex, adaptedSdlMode); + && refreshRate() % gameRefreshRate != 0 + && refreshRate() % (gameRefreshRate - 1) != 0; + const bool adaptRefresh = shouldAdapt && + adaptRefreshRate(displayIndex, adaptedSdlMode); #else const bool adaptRefresh = false; #endif const uInt32 flags = SDL_WINDOW_ALLOW_HIGHDPI - | (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + | (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN : + SDL_WINDOW_FULLSCREEN_DESKTOP : 0); // Don't re-create the window if its display and size hasn't changed, // as it's not necessary, and causes flashing in fullscreen mode @@ -303,13 +299,13 @@ bool FrameBufferSDL2::activateVideoMode(const string& title, if(myWindow) { // Even though window size stayed the same, the title may have changed - SDL_SetWindowTitle(myWindow, title.c_str()); + SDL_SetWindowTitle(myWindow, myScreenTitle.c_str()); SDL_SetWindowPosition(myWindow, posX, posY); } else { forceCreateRenderer = true; - myWindow = SDL_CreateWindow(title.c_str(), posX, posY, + myWindow = SDL_CreateWindow(myScreenTitle.c_str(), posX, posY, mode.screenS.w, mode.screenS.h, flags); if(myWindow == nullptr) { @@ -333,7 +329,8 @@ bool FrameBufferSDL2::activateVideoMode(const string& title, { ostringstream msg; - msg << "Display refresh rate changed to " << adaptedSdlMode.refresh_rate << " Hz"; + msg << "Display refresh rate changed to " + << adaptedSdlMode.refresh_rate << " Hz"; Logger::info(msg.str()); } } @@ -343,8 +340,11 @@ bool FrameBufferSDL2::activateVideoMode(const string& title, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode) +bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex, + SDL_DisplayMode& adaptedSdlMode) { + ASSERT_MAIN_THREAD; + SDL_DisplayMode sdlMode; if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0) @@ -354,7 +354,8 @@ bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adap } const int currentRefreshRate = sdlMode.refresh_rate; - const int wantedRefreshRate = gameRefreshRate(); + const int wantedRefreshRate = + myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0; // Take care of rounded refresh rates (e.g. 59.94 Hz) float factor = std::min(float(currentRefreshRate) / wantedRefreshRate, float(currentRefreshRate) / (wantedRefreshRate - 1)); @@ -398,8 +399,10 @@ bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adap } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::createRenderer(bool force) +bool FBBackendSDL2::createRenderer(bool force) { + ASSERT_MAIN_THREAD; + // A new renderer is only created when necessary: // - new myWindow (force = true) // - no renderer existing @@ -451,7 +454,7 @@ bool FrameBufferSDL2::createRenderer(bool force) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::setTitle(const string& title) +void FBBackendSDL2::setTitle(const string& title) { ASSERT_MAIN_THREAD; @@ -462,7 +465,7 @@ void FrameBufferSDL2::setTitle(const string& title) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string FrameBufferSDL2::about() const +string FBBackendSDL2::about() const { ASSERT_MAIN_THREAD; @@ -484,7 +487,7 @@ string FrameBufferSDL2::about() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::showCursor(bool show) +void FBBackendSDL2::showCursor(bool show) { ASSERT_MAIN_THREAD; @@ -492,7 +495,7 @@ void FrameBufferSDL2::showCursor(bool show) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::grabMouse(bool grab) +void FBBackendSDL2::grabMouse(bool grab) { ASSERT_MAIN_THREAD; @@ -500,7 +503,7 @@ void FrameBufferSDL2::grabMouse(bool grab) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::fullScreen() const +bool FBBackendSDL2::fullScreen() const { ASSERT_MAIN_THREAD; @@ -512,7 +515,7 @@ bool FrameBufferSDL2::fullScreen() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int FrameBufferSDL2::refreshRate() const +int FBBackendSDL2::refreshRate() const { ASSERT_MAIN_THREAD; @@ -529,20 +532,7 @@ int FrameBufferSDL2::refreshRate() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int FrameBufferSDL2::gameRefreshRate() const -{ - if(myOSystem.hasConsole()) - { - const string format = myOSystem.console().getFormatString(); - const bool isNtsc = format == "NTSC" || format == "PAL60" || format == "SECAM60"; - - return isNtsc ? 60 : 50; // The code will take care of 59/49 Hz - } - return 0; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::renderToScreen() +void FBBackendSDL2::renderToScreen() { ASSERT_MAIN_THREAD; @@ -551,7 +541,7 @@ void FrameBufferSDL2::renderToScreen() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::setWindowIcon() +void FBBackendSDL2::setWindowIcon() { #if !defined(BSPF_MACOS) && !defined(RETRON77) #include "stella_icon.hxx" @@ -565,19 +555,20 @@ void FrameBufferSDL2::setWindowIcon() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -unique_ptr FrameBufferSDL2::createSurface( +unique_ptr FBBackendSDL2::createSurface( uInt32 w, uInt32 h, - ScalingInterpolation interpolation, + ScalingInterpolation inter, const uInt32* data ) const { - return make_unique(const_cast(*this), w, h, interpolation, data); + return make_unique + (const_cast(*this), w, h, inter, data); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::readPixels(uInt8* pixels, uInt32 pitch, - const Common::Rect& rect) const +void FBBackendSDL2::readPixels(uInt8* pixels, uInt32 pitch, + const Common::Rect& rect) const { ASSERT_MAIN_THREAD; @@ -589,7 +580,7 @@ void FrameBufferSDL2::readPixels(uInt8* pixels, uInt32 pitch, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::clear() +void FBBackendSDL2::clear() { ASSERT_MAIN_THREAD; @@ -597,49 +588,34 @@ void FrameBufferSDL2::clear() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SDL_Renderer* FrameBufferSDL2::renderer() -{ - return myRenderer; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::isInitialized() const -{ - return myRenderer != nullptr; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const SDL_PixelFormat& FrameBufferSDL2::pixelFormat() const -{ - return *myPixelFormat; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::detectFeatures() +void FBBackendSDL2::detectFeatures() { myRenderTargetSupport = detectRenderTargetSupport(); - if (myRenderer) { - if (!myRenderTargetSupport) { - Logger::info("Render targets are not supported --- QIS not available"); - } - } + if(myRenderer && !myRenderTargetSupport) + Logger::info("Render targets are not supported --- QIS not available"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::detectRenderTargetSupport() +bool FBBackendSDL2::detectRenderTargetSupport() { - if (myRenderer == nullptr) return false; + ASSERT_MAIN_THREAD; + + if(myRenderer == nullptr) + return false; SDL_RendererInfo info; - SDL_GetRendererInfo(myRenderer, &info); - if (!(info.flags & SDL_RENDERER_TARGETTEXTURE)) return false; + if(!(info.flags & SDL_RENDERER_TARGETTEXTURE)) + return false; - SDL_Texture* tex = SDL_CreateTexture(myRenderer, myPixelFormat->format, SDL_TEXTUREACCESS_TARGET, 16, 16); + SDL_Texture* tex = + SDL_CreateTexture(myRenderer, myPixelFormat->format, + SDL_TEXTUREACCESS_TARGET, 16, 16); - if (!tex) return false; + if(!tex) + return false; int sdlError = SDL_SetRenderTarget(myRenderer, tex); SDL_SetRenderTarget(myRenderer, nullptr); @@ -650,20 +626,17 @@ bool FrameBufferSDL2::detectRenderTargetSupport() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL2::hasRenderTargetSupport() const +void FBBackendSDL2::determineDimensions() { - return myRenderTargetSupport; -} + ASSERT_MAIN_THREAD; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::determineDimensions() -{ SDL_GetWindowSize(myWindow, &myWindowW, &myWindowH); - if (myRenderer == nullptr) { + if(myRenderer == nullptr) + { myRenderW = myWindowW; myRenderH = myWindowH; - } else { - SDL_GetRendererOutputSize(myRenderer, &myRenderW, &myRenderH); } + else + SDL_GetRendererOutputSize(myRenderer, &myRenderW, &myRenderH); } diff --git a/src/common/FrameBufferSDL2.hxx b/src/common/FBBackendSDL2.hxx similarity index 75% rename from src/common/FrameBufferSDL2.hxx rename to src/common/FBBackendSDL2.hxx index 0e1a1c36d..67a5a2a57 100644 --- a/src/common/FrameBufferSDL2.hxx +++ b/src/common/FBBackendSDL2.hxx @@ -15,8 +15,8 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#ifndef FRAMEBUFFER_SDL2_HXX -#define FRAMEBUFFER_SDL2_HXX +#ifndef FB_BACKEND_SDL2_HXX +#define FB_BACKEND_SDL2_HXX #include "SDL_lib.hxx" @@ -24,27 +24,55 @@ class OSystem; class FBSurfaceSDL2; #include "bspf.hxx" -#include "FrameBuffer.hxx" +#include "FBBackend.hxx" /** - This class implements a standard SDL2 2D, hardware accelerated framebuffer. - Behind the scenes, it may be using Direct3D, OpenGL(ES), etc. + This class implements a standard SDL2 2D, hardware accelerated framebuffer + backend. Behind the scenes, it may be using Direct3D, OpenGL(ES), etc. @author Stephen Anthony */ -class FrameBufferSDL2 : public FrameBuffer +class FBBackendSDL2 : public FBBackend { public: /** Creates a new SDL2 framebuffer */ - explicit FrameBufferSDL2(OSystem& osystem); - ~FrameBufferSDL2() override; + explicit FBBackendSDL2(OSystem& osystem); + ~FBBackendSDL2() override; - ////////////////////////////////////////////////////////////////////// - // The following are derived from public methods in FrameBuffer.hxx - ////////////////////////////////////////////////////////////////////// + public: + /** + Get a pointer to the SDL renderer. + */ + SDL_Renderer* renderer() { return myRenderer; } + /** + Is the renderer initialized? + */ + bool isInitialized() const { return myRenderer != nullptr; } + + /** + Get the SDL pixel format. + */ + const SDL_PixelFormat& pixelFormat() const { return *myPixelFormat; } + + /** + Does the renderer support render targets? + */ + bool hasRenderTargetSupport() const { return myRenderTargetSupport; } + + /** + Transform from window to renderer coordinates, x direction + */ + int scaleX(int x) const override { return (x * myRenderW) / myWindowW; } + + /** + Transform from window to renderer coordinates, y direction + */ + int scaleY(int y) const override { return (y * myRenderH) / myWindowH; } + + protected: /** Updates window title. @@ -93,7 +121,8 @@ class FrameBufferSDL2 : public FrameBuffer @param pitch The pitch (in bytes) for the pixel data @param rect The bounding rectangle for the buffer */ - void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const override; + void readPixels(uInt8* buffer, uInt32 pitch, + const Common::Rect& rect) const override; /** This method is called to query if the current window is not centered @@ -110,6 +139,7 @@ class FrameBufferSDL2 : public FrameBuffer @return The position of the currently displayed window */ Common::Point getCurrentWindowPos() const override; + /** This method is called to query the video hardware for the index of the display the current window is displayed on @@ -124,40 +154,6 @@ class FrameBufferSDL2 : public FrameBuffer */ void clear() override; - /** - Get a pointer to the SDL renderer. - */ - SDL_Renderer* renderer(); - - /** - Get the SDL pixel format. - */ - const SDL_PixelFormat& pixelFormat() const; - - /** - Is the renderer initialized? - */ - bool isInitialized() const; - - /** - Does the renderer support render targets? - */ - bool hasRenderTargetSupport() const; - - /** - Transform from window to renderer coordinates, x direction - */ - int scaleX(int x) const override { return (x * myRenderW) / myWindowW; } - - /** - Transform from window to renderer coordinates, y direction - */ - int scaleY(int y) const override { return (y * myRenderH) / myWindowH; } - - protected: - ////////////////////////////////////////////////////////////////////// - // The following are derived from protected methods in FrameBuffer.hxx - ////////////////////////////////////////////////////////////////////// /** This method is called to query and initialize the video hardware for desktop and fullscreen resolution information. Since several @@ -174,46 +170,28 @@ class FrameBufferSDL2 : public FrameBuffer /** This method is called to change to the given video mode. - @param title The title for the created window - @param mode The video mode to use + @param mode The video mode to use + @param winIdx The display/monitor that the window last opened on + @param winPos The position that the window last opened at @return False on any errors, else true */ - bool activateVideoMode(const string& title, - const VideoModeHandler::Mode& mode) override; - - /** - Checks if the display refresh rate should be adapted to game refresh rate in (real) fullscreen mode - - @param displayIndex The display which should be checked - @param adaptedSdlMode The best matching mode if the refresh rate should be changed - - @return True if the refresh rate should be changed - */ - bool adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode); - - /** - Create a new renderer if required - - @param force If true, force new renderer creation - - @return False on any errors, else true - */ - bool createRenderer(bool force); + bool setVideoMode(const VideoModeHandler::Mode& mode, + int winIdx, const Common::Point& winPos) override; /** This method is called to create a surface with the given attributes. - @param w The requested width of the new surface. - @param h The requested height of the new surface. - @param interpolation Interpolation mode - @param data If non-null, use the given data values as a static surface + @param w The requested width of the new surface. + @param h The requested height of the new surface. + @param inter Interpolation mode + @param data If non-null, use the given data values as a static surface */ unique_ptr createSurface( uInt32 w, uInt32 h, - ScalingInterpolation interpolation, + ScalingInterpolation inter, const uInt32* data ) const override; @@ -223,28 +201,49 @@ class FrameBufferSDL2 : public FrameBuffer void grabMouse(bool grab) override; /** - Set the icon for the main SDL window. - */ - void setWindowIcon() override; - - /** - This method is called to provide information about the FrameBuffer. + This method is called to provide information about the backend. */ string about() const override; + /** + Create a new renderer if required. + + @param force If true, force new renderer creation + + @return False on any errors, else true + */ + bool createRenderer(bool force); + /** This method must be called after all drawing is done, and indicates that the buffers should be pushed to the physical screen. */ void renderToScreen() override; + /** + Retrieve the current display's refresh rate, or 0 if no window. + */ + int refreshRate() const override; + + /** + Checks if the display refresh rate should be adapted to game refresh + rate in (real) fullscreen mode. + + @param displayIndex The display which should be checked + @param adaptedSdlMode The best matching mode if the refresh rate + should be changed + + @return True if the refresh rate should be changed + */ + bool adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode); + /** After the renderer has been created, detect the features it supports. */ void detectFeatures(); /** - Detect render target support; + Detect render target support. */ bool detectRenderTargetSupport(); @@ -254,16 +253,13 @@ class FrameBufferSDL2 : public FrameBuffer void determineDimensions(); /** - Retrieve the current display's refresh rate, or 0 if no window + Set the icon for the main SDL window. */ - int refreshRate() const override; - - /** - Retrieve the current game's refresh rate, or 60 if no game - */ - int gameRefreshRate() const; + void setWindowIcon(); private: + OSystem& myOSystem; + // The SDL video buffer SDL_Window* myWindow{nullptr}; SDL_Renderer* myRenderer{nullptr}; @@ -274,22 +270,25 @@ class FrameBufferSDL2 : public FrameBuffer // Center setting of current window bool myCenter{false}; - // last position of windowed window - Common::Point myWindowedPos; - // Does the renderer support render targets? bool myRenderTargetSupport{false}; + // Title of the main window/screen + string myScreenTitle; + + // Number of displays + int myNumDisplays{1}; + // Window and renderer dimensions int myWindowW{0}, myWindowH{0}, myRenderW{0}, myRenderH{0}; private: // Following constructors and assignment operators not supported - FrameBufferSDL2() = delete; - FrameBufferSDL2(const FrameBufferSDL2&) = delete; - FrameBufferSDL2(FrameBufferSDL2&&) = delete; - FrameBufferSDL2& operator=(const FrameBufferSDL2&) = delete; - FrameBufferSDL2& operator=(FrameBufferSDL2&&) = delete; + FBBackendSDL2() = delete; + FBBackendSDL2(const FBBackendSDL2&) = delete; + FBBackendSDL2(FBBackendSDL2&&) = delete; + FBBackendSDL2& operator=(const FBBackendSDL2&) = delete; + FBBackendSDL2& operator=(FBBackendSDL2&&) = delete; }; #endif diff --git a/src/common/FBSurfaceSDL2.cxx b/src/common/FBSurfaceSDL2.cxx index efc99f015..543195165 100644 --- a/src/common/FBSurfaceSDL2.cxx +++ b/src/common/FBSurfaceSDL2.cxx @@ -22,9 +22,9 @@ #include "sdl_blitter/BlitterFactory.hxx" namespace { - BlitterFactory::ScalingAlgorithm scalingAlgorithm(ScalingInterpolation interpolation) + BlitterFactory::ScalingAlgorithm scalingAlgorithm(ScalingInterpolation inter) { - switch (interpolation) { + switch (inter) { case ScalingInterpolation::none: return BlitterFactory::ScalingAlgorithm::nearestNeighbour; @@ -41,12 +41,12 @@ namespace { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, +FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend, uInt32 width, uInt32 height, - ScalingInterpolation interpolation, + ScalingInterpolation inter, const uInt32* staticData) - : myFB(buffer), - myInterpolationMode(interpolation) + : myBackend(backend), + myInterpolationMode(inter) { createSurface(width, height, staticData); } @@ -214,7 +214,7 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height, ASSERT_MAIN_THREAD; // Create a surface in the same format as the parent GL class - const SDL_PixelFormat& pf = myFB.pixelFormat(); + const SDL_PixelFormat& pf = myBackend.pixelFormat(); mySurface = SDL_CreateRGBSurface(0, width, height, pf.BitsPerPixel, pf.Rmask, pf.Gmask, pf.Bmask, pf.Amask); @@ -242,11 +242,13 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::reinitializeBlitter() { - if (!myBlitter && myFB.isInitialized()) - myBlitter = BlitterFactory::createBlitter(myFB, scalingAlgorithm(myInterpolationMode)); + if (!myBlitter && myBackend.isInitialized()) + myBlitter = BlitterFactory::createBlitter( + myBackend, scalingAlgorithm(myInterpolationMode)); if (myBlitter) - myBlitter->reinitialize(mySrcR, myDstR, myAttributes, myIsStatic ? mySurface : nullptr); + myBlitter->reinitialize(mySrcR, myDstR, myAttributes, + myIsStatic ? mySurface : nullptr); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/FBSurfaceSDL2.hxx b/src/common/FBSurfaceSDL2.hxx index 01acd2d1c..f446bb17b 100644 --- a/src/common/FBSurfaceSDL2.hxx +++ b/src/common/FBSurfaceSDL2.hxx @@ -20,7 +20,7 @@ #include "bspf.hxx" #include "FBSurface.hxx" -#include "FrameBufferSDL2.hxx" +#include "FBBackendSDL2.hxx" #include "sdl_blitter/Blitter.hxx" /** @@ -32,9 +32,8 @@ class FBSurfaceSDL2 : public FBSurface { public: - FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height, - ScalingInterpolation interpolation, - const uInt32* staticData); + FBSurfaceSDL2(FBBackendSDL2& backend, uInt32 width, uInt32 height, + ScalingInterpolation inter, const uInt32* staticData); ~FBSurfaceSDL2() override; // Most of the surface drawing primitives are implemented in FBSurface; @@ -95,7 +94,7 @@ class FBSurfaceSDL2 : public FBSurface FBSurfaceSDL2& operator=(FBSurfaceSDL2&&) = delete; private: - FrameBufferSDL2& myFB; + FBBackendSDL2& myBackend; unique_ptr myBlitter; ScalingInterpolation myInterpolationMode diff --git a/src/common/MediaFactory.hxx b/src/common/MediaFactory.hxx index ad18685cb..8360d9b38 100644 --- a/src/common/MediaFactory.hxx +++ b/src/common/MediaFactory.hxx @@ -51,10 +51,10 @@ #if defined(__LIB_RETRO__) #include "EventHandlerLIBRETRO.hxx" - #include "FrameBufferLIBRETRO.hxx" + #include "FBBackendLIBRETRO.hxx" #elif defined(SDL_SUPPORT) #include "EventHandlerSDL2.hxx" - #include "FrameBufferSDL2.hxx" + #include "FBBackendSDL2.hxx" #else #error Unsupported backend! #endif @@ -128,12 +128,12 @@ class MediaFactory #endif } - static unique_ptr createVideo(OSystem& osystem) + static unique_ptr createVideoBackend(OSystem& osystem) { #if defined(__LIB_RETRO__) - return make_unique(osystem); + return make_unique(osystem); #elif defined(SDL_SUPPORT) - return make_unique(osystem); + return make_unique(osystem); #else #error Unsupported platform for FrameBuffer! #endif diff --git a/src/common/module.mk b/src/common/module.mk index 46248b23e..7fa2cf6f9 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -5,9 +5,9 @@ MODULE_OBJS := \ src/common/AudioSettings.o \ src/common/Base.o \ src/common/EventHandlerSDL2.o \ + src/common/FBBackendSDL2.o \ src/common/FBSurfaceSDL2.o \ src/common/FpsMeter.o \ - src/common/FrameBufferSDL2.o \ src/common/FSNodeZIP.o \ src/common/JoyMap.o \ src/common/KeyMap.o \ diff --git a/src/common/sdl_blitter/BilinearBlitter.cxx b/src/common/sdl_blitter/BilinearBlitter.cxx index 295d8e8e2..d8f25d133 100644 --- a/src/common/sdl_blitter/BilinearBlitter.cxx +++ b/src/common/sdl_blitter/BilinearBlitter.cxx @@ -15,12 +15,12 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "FrameBufferSDL2.hxx" +#include "FBBackendSDL2.hxx" #include "ThreadDebugging.hxx" #include "BilinearBlitter.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate) +BilinearBlitter::BilinearBlitter(FBBackendSDL2& fb, bool interpolate) : myFB(fb), myInterpolate(interpolate) { diff --git a/src/common/sdl_blitter/BilinearBlitter.hxx b/src/common/sdl_blitter/BilinearBlitter.hxx index 8588e390e..684b966c8 100644 --- a/src/common/sdl_blitter/BilinearBlitter.hxx +++ b/src/common/sdl_blitter/BilinearBlitter.hxx @@ -18,7 +18,7 @@ #ifndef BILINEAR_BLITTER_HXX #define BILINEAR_BLITTER_HXX -class FrameBufferSDL2; +class FBBackendSDL2; #include "Blitter.hxx" #include "SDL_lib.hxx" @@ -27,7 +27,7 @@ class BilinearBlitter : public Blitter { public: - BilinearBlitter(FrameBufferSDL2& fb, bool interpolate); + BilinearBlitter(FBBackendSDL2& fb, bool interpolate); ~BilinearBlitter() override; @@ -41,7 +41,7 @@ class BilinearBlitter : public Blitter { virtual void blit(SDL_Surface& surface) override; private: - FrameBufferSDL2& myFB; + FBBackendSDL2& myFB; SDL_Texture* myTexture{nullptr}; SDL_Texture* mySecondaryTexture{nullptr}; diff --git a/src/common/sdl_blitter/BlitterFactory.cxx b/src/common/sdl_blitter/BlitterFactory.cxx index df567a874..3c969b71b 100644 --- a/src/common/sdl_blitter/BlitterFactory.cxx +++ b/src/common/sdl_blitter/BlitterFactory.cxx @@ -21,7 +21,7 @@ #include "BilinearBlitter.hxx" #include "QisBlitter.hxx" -unique_ptr BlitterFactory::createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling) +unique_ptr BlitterFactory::createBlitter(FBBackendSDL2& fb, ScalingAlgorithm scaling) { if (!fb.isInitialized()) { throw runtime_error("BlitterFactory requires an initialized framebuffer!"); diff --git a/src/common/sdl_blitter/BlitterFactory.hxx b/src/common/sdl_blitter/BlitterFactory.hxx index 8c1a0cfbf..96f3c9706 100644 --- a/src/common/sdl_blitter/BlitterFactory.hxx +++ b/src/common/sdl_blitter/BlitterFactory.hxx @@ -21,8 +21,8 @@ #include #include "Blitter.hxx" +#include "FBBackendSDL2.hxx" #include "bspf.hxx" -#include "FrameBufferSDL2.hxx" class BlitterFactory { public: @@ -35,7 +35,7 @@ class BlitterFactory { public: - static unique_ptr createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling); + static unique_ptr createBlitter(FBBackendSDL2& fb, ScalingAlgorithm scaling); }; #endif // BLITTER_FACTORY_HXX diff --git a/src/common/sdl_blitter/QisBlitter.cxx b/src/common/sdl_blitter/QisBlitter.cxx index c68c4bff9..c0b7f7dbd 100644 --- a/src/common/sdl_blitter/QisBlitter.cxx +++ b/src/common/sdl_blitter/QisBlitter.cxx @@ -15,12 +15,12 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "FrameBufferSDL2.hxx" +#include "FBBackendSDL2.hxx" #include "ThreadDebugging.hxx" #include "QisBlitter.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -QisBlitter::QisBlitter(FrameBufferSDL2& fb) +QisBlitter::QisBlitter(FBBackendSDL2& fb) : myFB(fb) { } @@ -32,7 +32,7 @@ QisBlitter::~QisBlitter() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool QisBlitter::isSupported(FrameBufferSDL2 &fb) +bool QisBlitter::isSupported(FBBackendSDL2& fb) { if (!fb.isInitialized()) throw runtime_error("framebuffer not initialized"); diff --git a/src/common/sdl_blitter/QisBlitter.hxx b/src/common/sdl_blitter/QisBlitter.hxx index 9aa68e4c0..7fc7c3393 100644 --- a/src/common/sdl_blitter/QisBlitter.hxx +++ b/src/common/sdl_blitter/QisBlitter.hxx @@ -18,7 +18,7 @@ #ifndef QIS_BLITTER_HXX #define QIS_BLITTER_HXX -class FrameBufferSDL2; +class FBBackendSDL2; #include "Blitter.hxx" #include "SDL_lib.hxx" @@ -27,9 +27,9 @@ class QisBlitter : public Blitter { public: - explicit QisBlitter(FrameBufferSDL2& fb); + explicit QisBlitter(FBBackendSDL2& fb); - static bool isSupported(FrameBufferSDL2 &fb); + static bool isSupported(FBBackendSDL2& fb); ~QisBlitter() override; @@ -44,7 +44,7 @@ class QisBlitter : public Blitter { private: - FrameBufferSDL2& myFB; + FBBackendSDL2& myFB; SDL_Texture* mySrcTexture{nullptr}; SDL_Texture* mySecondarySrcTexture{nullptr}; diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 0e2532b6e..099773989 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -250,7 +250,6 @@ Console::~Console() myOSystem.sound().close(); } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Console::setConsoleTiming() { @@ -728,7 +727,8 @@ void Console::changeVSizeAdjust(int direction) ostringstream val; - val << (newAdjustVSize ? newAdjustVSize > 0 ? "+" : "" : " ") << newAdjustVSize << "%"; + val << (newAdjustVSize ? newAdjustVSize > 0 ? "+" : "" : " ") + << newAdjustVSize << "%"; myOSystem.frameBuffer().showMessage("V-Size", val.str(), newAdjustVSize, -5, 5); } @@ -743,7 +743,8 @@ void Console::toggleCorrectAspectRatio(bool toggle) myOSystem.settings().setValue("tia.correct_aspect", enabled); initializeVideo(); } - const string message = string("Correct aspect ratio ") + (enabled ? "enabled" : "disabled"); + const string& message = string("Correct aspect ratio ") + + (enabled ? "enabled" : "disabled"); myOSystem.frameBuffer().showMessage(message); } @@ -755,8 +756,7 @@ void Console::setTIAProperties() static_cast(BSPF::stringToInt(myProperties.get(PropType::Display_VCenter))), TIAConstants::minVcenter, TIAConstants::maxVcenter ); - if(myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" || - myDisplayFormat == "SECAM60") + if(gameRefreshRate() == 60) { // Assume we've got ~262 scanlines (NTSC-like format) myTIA->setLayout(FrameLayout::ntsc); @@ -805,17 +805,22 @@ void Console::setControllers(const string& romMd5) myLeftControl = std::move(myCMHandler->leftController()); myRightControl = std::move(myCMHandler->rightController()); - myOSystem.eventHandler().defineKeyControllerMappings(Controller::Type::CompuMate, Controller::Jack::Left); - myOSystem.eventHandler().defineJoyControllerMappings(Controller::Type::CompuMate, Controller::Jack::Left); + myOSystem.eventHandler().defineKeyControllerMappings( + Controller::Type::CompuMate, Controller::Jack::Left); + myOSystem.eventHandler().defineJoyControllerMappings( + Controller::Type::CompuMate, Controller::Jack::Left); } else { // Setup the controllers based on properties - Controller::Type leftType = Controller::getType(myProperties.get(PropType::Controller_Left)); - Controller::Type rightType = Controller::getType(myProperties.get(PropType::Controller_Right)); + Controller::Type leftType = + Controller::getType(myProperties.get(PropType::Controller_Left)); + Controller::Type rightType = + Controller::getType(myProperties.get(PropType::Controller_Right)); size_t size = 0; const ByteBuffer& image = myCart->getImage(size); - const bool swappedPorts = myProperties.get(PropType::Console_SwapPorts) == "YES"; + const bool swappedPorts = + myProperties.get(PropType::Console_SwapPorts) == "YES"; // Try to detect controllers if(image != nullptr && size != 0) @@ -827,7 +832,8 @@ void Console::setControllers(const string& romMd5) !swappedPorts ? Controller::Jack::Right : Controller::Jack::Left, myOSystem.settings()); } - unique_ptr leftC = getControllerPort(leftType, Controller::Jack::Left, romMd5), + unique_ptr + leftC = getControllerPort(leftType, Controller::Jack::Left, romMd5), rightC = getControllerPort(rightType, Controller::Jack::Right, romMd5); // Swap the ports if necessary @@ -985,13 +991,21 @@ void Console::changeAutoFireRate(int direction) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -float Console::getFramerate() const +float Console::currentFrameRate() const { return (myConsoleTiming == ConsoleTiming::ntsc ? 262.F * 60.F : 312.F * 50.F) / myTIA->frameBufferScanlinesLastFrame(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int Console::gameRefreshRate() const +{ + return + myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" || + myDisplayFormat == "SECAM60" ? 60 : 50; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Console::toggleTIABit(TIABit bit, const string& bitname, bool show, bool toggle) const { diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index b16d9c2c8..aa9a75f38 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -185,6 +185,11 @@ class Console : public Serializable, public ConsoleIO */ EmulationTiming& emulationTiming() { return myEmulationTiming; } + /** + Retrieve the current game's refresh rate, or 0 if no game. + */ + int refreshRate() const; + public: /** Toggle between NTSC/PAL/SECAM (and variants) display format. @@ -282,9 +287,16 @@ class Console : public Serializable, public ConsoleIO void toggleCorrectAspectRatio(bool toggle = true); /** - Returns the current framerate. + Returns the current framerate. Note that this is the actual, + dynamic frame rate while a game is running. */ - float getFramerate() const; + float currentFrameRate() const; + + /** + Retrieve the current game's refresh rate. Note that this is a + static, basic frame rate based on the current TV format. + */ + int gameRefreshRate() const; /** Toggles the TIA bit specified in the method name. diff --git a/src/emucore/FBBackend.hxx b/src/emucore/FBBackend.hxx new file mode 100644 index 000000000..ffbb5043a --- /dev/null +++ b/src/emucore/FBBackend.hxx @@ -0,0 +1,207 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef FB_BACKEND_HXX +#define FB_BACKEND_HXX + +class FBSurface; + +#include "Rect.hxx" +#include "Variant.hxx" +#include "FrameBufferConstants.hxx" +#include "VideoModeHandler.hxx" +#include "bspf.hxx" + +/** + This class provides an interface/abstraction for platform-specific, + framebuffer-related rendering operations. Different graphical + platforms will inherit from this. For most ports that means SDL2, + but some (such as libretro) use their own graphical subsystem. + + @author Stephen Anthony +*/ +class FBBackend +{ + friend class FrameBuffer; + + public: + FBBackend() = default; + virtual ~FBBackend() = default; + + protected: + /** + This method is called to query and initialize the video hardware + for desktop and fullscreen resolution information. Since several + monitors may be attached, we need the resolution for all of them. + + @param fullscreenRes Maximum resolution supported in fullscreen mode + @param windowedRes Maximum resolution supported in windowed mode + @param renderers List of renderer names (internal name -> end-user name) + */ + virtual void queryHardware(vector& fullscreenRes, + vector& windowedRes, + VariantList& renderers) = 0; + + /** + This method is called to change to the given video mode. + + @param mode The video mode to use + @param winIdx The display/monitor that the window last opened on + @param winPos The position that the window last opened at + + @return False on any errors, else true + */ + virtual bool setVideoMode(const VideoModeHandler::Mode& mode, + int winIdx, const Common::Point& winPos) = 0; + + /** + Clear the framebuffer. + */ + virtual void clear() = 0; + + /** + Transform from window to renderer coordinates, x direction + */ + virtual int scaleX(int x) const = 0; + + /** + Transform from window to renderer coordinates, y direction + */ + virtual int scaleY(int y) const = 0; + + /** + Updates window title. + + @param title The title of the application / window + */ + virtual void setTitle(const string& title) = 0; + + /** + Shows or hides the cursor based on the given boolean value. + */ + virtual void showCursor(bool show) = 0; + + /** + Grabs or ungrabs the mouse based on the given boolean value. + */ + virtual void grabMouse(bool grab) = 0; + + /** + This method must be called after all drawing is done, and indicates + that the buffers should be pushed to the physical screen. + */ + virtual void renderToScreen() = 0; + + /** + Answers if the display is currently in fullscreen mode. + + @return Whether the display is actually in fullscreen mode + */ + virtual bool fullScreen() const = 0; + + /** + Retrieve the current display's refresh rate. + */ + virtual int refreshRate() const = 0; + + /** + This method is called to retrieve the R/G/B data from the given pixel. + + @param pixel The pixel containing R/G/B data + @param r The red component of the color + @param g The green component of the color + @param b The blue component of the color + */ + virtual void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const = 0; + + /** + This method is called to map a given R/G/B triple to the screen palette. + + @param r The red component of the color. + @param g The green component of the color. + @param b The blue component of the color. + */ + virtual uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const = 0; + + /** + This method is called to get the specified ARGB data from the viewable + FrameBuffer area. Note that this isn't the same as any internal + surfaces that may be in use; it should return the actual data as it + is currently seen onscreen. + + @param buffer The actual pixel data in ARGB8888 format + @param pitch The pitch (in bytes) for the pixel data + @param rect The bounding rectangle for the buffer + */ + virtual void readPixels(uInt8* buffer, uInt32 pitch, + const Common::Rect& rect) const = 0; + + /** + This method is called to query if the current window is not + centered or fullscreen. + + @return True, if the current window is positioned + */ + virtual bool isCurrentWindowPositioned() const = 0; + + /** + This method is called to query the video hardware for position of + the current window. + + @return The position of the currently displayed window + */ + virtual Common::Point getCurrentWindowPos() const = 0; + + /** + This method is called to query the video hardware for the index + of the display the current window is displayed on. + + @return The current display index or a negative value if no + window is displayed + */ + virtual Int32 getCurrentDisplayIndex() const = 0; + + /** + This method is called to create a surface with the given attributes. + + @param w The requested width of the new surface. + @param h The requested height of the new surface. + @param inter Interpolation mode + @param data If non-null, use the given data values as a static surface + */ + virtual unique_ptr + createSurface( + uInt32 w, + uInt32 h, + ScalingInterpolation inter = ScalingInterpolation::none, + const uInt32* data = nullptr + ) const = 0; + + /** + This method is called to provide information about the backend. + */ + virtual string about() const = 0; + + private: + // Following constructors and assignment operators not supported + FBBackend(const FBBackend&) = delete; + FBBackend(FBBackend&&) = delete; + FBBackend& operator=(const FBBackend&) = delete; + FBBackend& operator=(FBBackend&&) = delete; +}; + +#endif diff --git a/src/emucore/FBSurface.hxx b/src/emucore/FBSurface.hxx index 108831ce1..a018ac2c9 100644 --- a/src/emucore/FBSurface.hxx +++ b/src/emucore/FBSurface.hxx @@ -177,7 +177,8 @@ class FBSurface @param y The destination y-location to start drawing pixels @param numpixels The number of pixels to draw */ - virtual void drawPixels(const uInt32* data, uInt32 x, uInt32 y, uInt32 numpixels); + virtual void drawPixels(const uInt32* data, uInt32 x, uInt32 y, + uInt32 numpixels); /** This method should be called to draw a rectangular box with sides diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index e9a5afb4a..e5c46c588 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -25,6 +25,7 @@ #include "Settings.hxx" #include "TIA.hxx" #include "Sound.hxx" +#include "MediaFactory.hxx" #include "FBSurface.hxx" #include "TIASurface.hxx" @@ -63,14 +64,24 @@ FrameBuffer::FrameBuffer(OSystem& osystem) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FrameBuffer::~FrameBuffer() { + // Make sure to free surfaces/textures before destroying the backend itself + // Most platforms are fine with doing this in either order, but it seems + // that OpenBSD in particular crashes when attempting to destroy textures + // *after* the renderer is already destroyed + freeSurfaces(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBuffer::initialize() +void FrameBuffer::initialize() { + // First create the platform-specific backend; it is needed before anything + // else can be used + try { myBackend = MediaFactory::createVideoBackend(myOSystem); } + catch(const runtime_error& e) { throw e; } + // Get desktop resolution and supported renderers vector windowedDisplays; - queryHardware(myFullscreenDisplays, windowedDisplays, myRenderers); + myBackend->queryHardware(myFullscreenDisplays, windowedDisplays, myRenderers); uInt32 query_w = windowedDisplays[0].w, query_h = windowedDisplays[0].h; // Check the 'maxres' setting, which is an undocumented developer feature @@ -116,8 +127,6 @@ bool FrameBuffer::initialize() // Create a TIA surface; we need it for rendering TIA images myTIASurface = make_unique(myOSystem); - - return true; } #ifdef GUI_SUPPORT @@ -210,7 +219,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, Common::Size size, bool honourHiDPI) { ++myInitializedCount; - myScreenTitle = title; + myBackend->setTitle(title); // In HiDPI mode, all created displays must be scaled appropriately if(honourHiDPI && hidpiEnabled()) @@ -254,7 +263,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, myBufferType = type; // Initialize video subsystem - string pre_about = about(); + string pre_about = myBackend->about(); FBInitStatus status = applyVideoMode(); if(status != FBInitStatus::Success) return status; @@ -289,11 +298,11 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, // Print initial usage message, but only print it later if the status has changed if(myInitializedCount == 1) { - Logger::info(about()); + Logger::info(myBackend->about()); } else { - string post_about = about(); + string post_about = myBackend->about(); if(post_about != pre_about) Logger::info(post_about); } @@ -467,7 +476,7 @@ void FrameBuffer::update(bool force) // Push buffers to screen only when necessary if(force) - renderToScreen(); + myBackend->renderToScreen(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -493,7 +502,7 @@ void FrameBuffer::updateInEmulationMode(float framesPerSecond) drawMessage(); // Push buffers to screen - renderToScreen(); + myBackend->renderToScreen(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -600,7 +609,8 @@ void FrameBuffer::drawFrameStats(float framesPerSecond) ss << myOSystem.console().tia().frameBufferScanlinesLastFrame() << " / " - << std::fixed << std::setprecision(1) << myOSystem.console().getFramerate() + << std::fixed << std::setprecision(1) + << myOSystem.console().currentFrameRate() << "Hz => " << info.DisplayFormat; @@ -803,11 +813,11 @@ void FrameBuffer::setPauseDelay() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - shared_ptr FrameBuffer::allocateSurface( - int w, int h, ScalingInterpolation interpolation, const uInt32* data + int w, int h, ScalingInterpolation inter, const uInt32* data ) { // Add new surface to the list - mySurfaceList.push_back(createSurface(w, h, interpolation, data)); + mySurfaceList.push_back(myBackend->createSurface(w, h, inter, data)); // And return a pointer to it (pointer should be treated read-only) return mySurfaceList.at(mySurfaceList.size() - 1); @@ -945,9 +955,11 @@ string FrameBuffer::getPositionKey() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBuffer::saveCurrentWindowPosition() { - myOSystem.settings().setValue(getDisplayKey(), getCurrentDisplayIndex()); - if(isCurrentWindowPositioned()) - myOSystem.settings().setValue(getPositionKey(), getCurrentWindowPos()); + myOSystem.settings().setValue( + getDisplayKey(), myBackend->getCurrentDisplayIndex()); + if(myBackend->isCurrentWindowPositioned()) + myOSystem.settings().setValue( + getPositionKey(), myBackend->getCurrentWindowPos()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -997,7 +1009,7 @@ void FrameBuffer::toggleFullscreen(bool toggle) ostringstream msg; msg << "Fullscreen "; if(isFullscreen) - msg << "enabled (" << refreshRate() << " Hz, "; + msg << "enabled (" << myBackend->refreshRate() << " Hz, "; else msg << "disabled ("; msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)"; @@ -1033,7 +1045,7 @@ void FrameBuffer::toggleAdaptRefresh(bool toggle) msg << "Adapt refresh rate "; msg << (isAdaptRefresh ? "enabled" : "disabled"); - msg << " (" << refreshRate() << " Hz)"; + msg << " (" << myBackend->refreshRate() << " Hz)"; showMessage(msg.str()); } @@ -1112,7 +1124,7 @@ FBInitStatus FrameBuffer::applyVideoMode() const Settings& s = myOSystem.settings(); if(s.getBool("fullscreen")) { - Int32 fsIndex = std::max(getCurrentDisplayIndex(), 0); + Int32 fsIndex = std::max(myBackend->getCurrentDisplayIndex(), 0); myVidModeHandler.setDisplaySize(myFullscreenDisplays[fsIndex], fsIndex); } else @@ -1130,7 +1142,11 @@ FBInitStatus FrameBuffer::applyVideoMode() // So we mute the sound until the operation completes bool oldMuteState = myOSystem.sound().mute(true); FBInitStatus status = FBInitStatus::FailNotSupported; - if(activateVideoMode(myScreenTitle, mode)) + + if(myBackend->setVideoMode(mode, + myOSystem.settings().getInt(getDisplayKey()), + myOSystem.settings().getPoint(getPositionKey())) + ) { myActiveVidMode = mode; status = FBInitStatus::Success; @@ -1218,7 +1234,7 @@ void FrameBuffer::setCursorState() break; } - grabMouse(emulation && (analog || alwaysUseMouse) && myGrabMouse); + myBackend->grabMouse(emulation && (analog || alwaysUseMouse) && myGrabMouse); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index a4c7e20c8..689a0935d 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -33,6 +33,7 @@ class TIASurface; #include "Rect.hxx" #include "Variant.hxx" #include "TIAConstants.hxx" +#include "FBBackend.hxx" #include "FrameBufferConstants.hxx" #include "EventHandlerConstants.hxx" #include "VideoModeHandler.hxx" @@ -55,16 +56,14 @@ class FrameBuffer static constexpr float ZOOM_STEPS = 0.25; public: - /** - Creates a new Frame Buffer - */ FrameBuffer(OSystem& osystem); - virtual ~FrameBuffer(); + ~FrameBuffer(); /** - Initialize the framebuffer object (set up the underlying hardware) + Initialize the framebuffer object (set up the underlying hardware). + Throws an exception upon encountering any errors. */ - bool initialize(); + void initialize(); /** (Re)creates the framebuffer display. This must be called before any @@ -141,17 +140,17 @@ class FrameBuffer Allocate a new surface. The FrameBuffer class takes all responsibility for freeing this surface (ie, other classes must not delete it directly). - @param w The requested width of the new surface. - @param h The requested height of the new surface. - @param interpolation Interpolation mode - @param data If non-null, use the given data values as a static surface + @param w The requested width of the new surface + @param h The requested height of the new surface + @param inter Interpolation mode + @param data If non-null, use the given data values as a static surface - @return A pointer to a valid surface object, or nullptr. + @return A pointer to a valid surface object, or nullptr */ shared_ptr allocateSurface( int w, int h, - ScalingInterpolation interpolation = ScalingInterpolation::none, + ScalingInterpolation inter = ScalingInterpolation::none, const uInt32* data = nullptr ); @@ -304,27 +303,15 @@ class FrameBuffer FontDesc getFontDesc(const string& name) const; #endif - ////////////////////////////////////////////////////////////////////// - // The following methods are system-specific and can/must be - // implemented in derived classes. - ////////////////////////////////////////////////////////////////////// - public: - /** - Updates window title - - @param title The title of the application / window - */ - virtual void setTitle(const string& title) = 0; - /** Shows or hides the cursor based on the given boolean value. */ - virtual void showCursor(bool show) = 0; + void showCursor(bool show) { myBackend->showCursor(show); } /** Answers if the display is currently in fullscreen mode. */ - virtual bool fullScreen() const = 0; + bool fullScreen() const { return myBackend->fullScreen(); } /** This method is called to retrieve the R/G/B data from the given pixel. @@ -334,7 +321,9 @@ class FrameBuffer @param g The green component of the color @param b The blue component of the color */ - virtual void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const = 0; + void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const { + myBackend->getRGB(pixel, r, g, b); + } /** This method is called to map a given R/G/B triple to the screen palette. @@ -343,7 +332,9 @@ class FrameBuffer @param g The green component of the color. @param b The blue component of the color. */ - virtual uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const = 0; + uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const { + return myBackend->mapRGB(r, g, b); + } /** This method is called to get the specified ARGB data from the viewable @@ -355,88 +346,22 @@ class FrameBuffer @param pitch The pitch (in bytes) for the pixel data @param rect The bounding rectangle for the buffer */ - virtual void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const = 0; - - /** - This method is called to query if the current window is not centered or fullscreen. - - @return True, if the current window is positioned - */ - virtual bool isCurrentWindowPositioned() const = 0; - - /** - This method is called to query the video hardware for position of - the current window - - @return The position of the currently displayed window - */ - virtual Common::Point getCurrentWindowPos() const = 0; - - /** - This method is called to query the video hardware for the index - of the display the current window is displayed on - - @return the current display index or a negative value if no - window is displayed - */ - virtual Int32 getCurrentDisplayIndex() const = 0; + void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const { + myBackend->readPixels(buffer, pitch, rect); + } /** Clear the framebuffer. */ - virtual void clear() = 0; + void clear() { myBackend->clear(); } /** - Transform from window to renderer coordinates, x direction + Transform from window to renderer coordinates, x/y direction */ - virtual int scaleX(int x) const { return x; } - - /** - Transform from window to renderer coordinates, y direction - */ - virtual int scaleY(int y) const { return y; } - - protected: - /** - This method is called to query and initialize the video hardware - for desktop and fullscreen resolution information. Since several - monitors may be attached, we need the resolution for all of them. - - @param fullscreenRes Maximum resolution supported in fullscreen mode - @param windowedRes Maximum resolution supported in windowed mode - @param renderers List of renderer names (internal name -> end-user name) - */ - virtual void queryHardware(vector& fullscreenRes, - vector& windowedRes, - VariantList& renderers) = 0; - - /** - This method is called to change to the given video mode. - - @param title The title for the created window - @param mode The video mode to use - - @return False on any errors, else true - */ - virtual bool activateVideoMode(const string& title, - const VideoModeHandler::Mode& mode) = 0; - - /** - This method is called to create a surface with the given attributes. - - @param w The requested width of the new surface. - @param h The requested height of the new surface. - @param interpolation Interpolation mode - @param data If non-null, use the given data values as a static surface - */ - virtual unique_ptr - createSurface( - uInt32 w, - uInt32 h, - ScalingInterpolation interpolation = ScalingInterpolation::none, - const uInt32* data = nullptr - ) const = 0; + int scaleX(int x) const { return myBackend->scaleX(x); } + int scaleY(int y) const { return myBackend->scaleY(y); } + private: /** Calls 'free()' on all surfaces that the framebuffer knows about. */ @@ -447,42 +372,6 @@ class FrameBuffer */ void reloadSurfaces(); - /** - Grabs or ungrabs the mouse based on the given boolean value. - */ - virtual void grabMouse(bool grab) = 0; - - /** - Set the icon for the main window. - */ - virtual void setWindowIcon() = 0; - - /** - This method must be called after all drawing is done, and indicates - that the buffers should be pushed to the physical screen. - */ - virtual void renderToScreen() = 0; - - /** - This method is called to provide information about the FrameBuffer. - */ - virtual string about() const = 0; - - /** - Retrieve the current display's refresh rate - */ - virtual int refreshRate() const { return 0; } - - protected: - // The parent system for the framebuffer - OSystem& myOSystem; - - private: - // Maximum message width [chars] - static constexpr int MESSAGE_WIDTH = 56; - // Maximum gauge bar width [chars] - static constexpr int GAUGEBAR_WIDTH = 30; - /** Draw pending messages. @@ -523,22 +412,13 @@ class FrameBuffer void setupFonts(); #endif - protected: - // Title of the main window/screen - string myScreenTitle; - - // Type of the frame buffer - BufferType myBufferType{BufferType::None}; - - // Number of displays - int myNumDisplays{1}; - - // The resolution of the attached displays in fullscreen mode - // The primary display is typically the first in the array - // Windowed modes use myDesktopSize directly - vector myFullscreenDisplays; - private: + // The parent system for the framebuffer + OSystem& myOSystem; + + // Backend used for all platform-specific graphics operations + unique_ptr myBackend; + // Indicates the number of times the framebuffer was initialized uInt32 myInitializedCount{0}; @@ -553,6 +433,11 @@ class FrameBuffer // Maximum absolute dimensions of the desktop area Common::Size myAbsDesktopSize; + // The resolution of the attached displays in fullscreen mode + // The primary display is typically the first in the array + // Windowed modes use myDesktopSize directly + vector myFullscreenDisplays; + // Supported renderers VariantList myRenderers; @@ -561,6 +446,9 @@ class FrameBuffer VideoModeHandler myVidModeHandler; VideoModeHandler::Mode myActiveVidMode; + // Type of the frame buffer + BufferType myBufferType{BufferType::None}; + #ifdef GUI_SUPPORT // The font object to use for the normal in-game GUI unique_ptr myFont; @@ -609,6 +497,11 @@ class FrameBuffer // Holds a reference to all the surfaces that have been created vector> mySurfaceList; + // Maximum message width [chars] + static constexpr int MESSAGE_WIDTH = 56; + // Maximum gauge bar width [chars] + static constexpr int GAUGEBAR_WIDTH = 30; + FullPaletteArray myFullPalette; // Holds UI palette data (for each variation) static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette, diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 39b83384d..611439e45 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -143,10 +143,15 @@ bool OSystem::create() // Get relevant information about the video hardware // This must be done before any graphics context is created, since // it may be needed to initialize the size of graphical objects - try { myFrameBuffer = MediaFactory::createVideo(*this); } - catch(...) { return false; } - if(!myFrameBuffer->initialize()) + try + { + myFrameBuffer = make_unique(*this); + myFrameBuffer->initialize(); + } + catch(...) + { return false; + } // Create the event handler for the system myEventHandler = MediaFactory::createEventHandler(*this); @@ -722,7 +727,7 @@ string OSystem::getROMInfo(const Console& console) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - float OSystem::frameRate() const { - return myConsole ? myConsole->getFramerate() : 0; + return myConsole ? myConsole->currentFrameRate() : 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index 3402ed8cc..3858817a7 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -167,7 +167,6 @@ uInt32 TIASurface::mapIndexedPixel(uInt8 indexedColor, uInt8 shift) const return myPalette[indexedColor | shift]; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIASurface::setNTSC(NTSCFilter::Preset preset, bool show) { @@ -512,13 +511,16 @@ void TIASurface::renderForSnapshot() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIASurface::updateSurfaceSettings() { - myTiaSurface->setScalingInterpolation(interpolationModeFromSettings(myOSystem.settings())); + myTiaSurface->setScalingInterpolation( + interpolationModeFromSettings(myOSystem.settings()) + ); mySLineSurface->setScalingInterpolation( interpolationModeFromSettings(myOSystem.settings()) ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool TIASurface::correctAspect() const { +bool TIASurface::correctAspect() const +{ return myOSystem.settings().getBool("tia.correct_aspect"); } diff --git a/src/emucore/TIASurface.hxx b/src/emucore/TIASurface.hxx index 4c7a710d8..013fd6deb 100644 --- a/src/emucore/TIASurface.hxx +++ b/src/emucore/TIASurface.hxx @@ -67,7 +67,8 @@ class TIASurface @param rgb_palette The RGB components of the palette, needed for calculating a phosphor palette */ - void setPalette(const PaletteArray& tia_palette, const PaletteArray& rgb_palette); + void setPalette(const PaletteArray& tia_palette, + const PaletteArray& rgb_palette); /** Get a TIA surface that has no post-processing whatsoever. This is @@ -77,6 +78,11 @@ class TIASurface */ const FBSurface& baseSurface(Common::Rect& rect) const; + /** + Get a underlying FBSurface that the TIA is being rendered into. + */ + const FBSurface& tiaSurface() const { return *myTiaSurface; } + /** Use the palette to map a single indexed pixel color. This is used by the TIA output widget. diff --git a/src/libretro/FrameBufferLIBRETRO.cxx b/src/libretro/FBBackendLIBRETRO.cxx similarity index 52% rename from src/libretro/FrameBufferLIBRETRO.cxx rename to src/libretro/FBBackendLIBRETRO.cxx index 31136bae9..f7e4299de 100644 --- a/src/libretro/FrameBufferLIBRETRO.cxx +++ b/src/libretro/FBBackendLIBRETRO.cxx @@ -16,25 +16,13 @@ //============================================================================ #include "bspf.hxx" - -#include "OSystem.hxx" -#include "AtariNTSC.hxx" -#include "TIAConstants.hxx" - #include "FBSurfaceLIBRETRO.hxx" -#include "FrameBufferLIBRETRO.hxx" +#include "FBBackendLIBRETRO.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameBufferLIBRETRO::FrameBufferLIBRETRO(OSystem& osystem) - : FrameBuffer(osystem), - myRenderSurface(nullptr) -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferLIBRETRO::queryHardware(vector& fullscreenRes, - vector& windowedRes, - VariantList& renderers) +void FBBackendLIBRETRO::queryHardware(vector& fullscreenRes, + vector& windowedRes, + VariantList& renderers) { fullscreenRes.emplace_back(1920, 1080); windowedRes.emplace_back(1920, 1080); @@ -43,18 +31,8 @@ void FrameBufferLIBRETRO::queryHardware(vector& fullscreenRes, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -unique_ptr - FrameBufferLIBRETRO::createSurface(uInt32 w, uInt32 h, ScalingInterpolation, const uInt32* data) const +unique_ptr FBBackendLIBRETRO::createSurface( + uInt32 w, uInt32 h, ScalingInterpolation, const uInt32*) const { - unique_ptr ptr = make_unique - (const_cast(*this), w, h, data); - - if(w == AtariNTSC::outWidth(TIAConstants::frameBufferWidth) && - h == TIAConstants::frameBufferHeight) - { - uInt32 pitch; - ptr.get()->basePtr(myRenderSurface, pitch); - } - - return ptr; + return make_unique(w, h); } diff --git a/src/libretro/FBBackendLIBRETRO.hxx b/src/libretro/FBBackendLIBRETRO.hxx new file mode 100644 index 000000000..a8948156c --- /dev/null +++ b/src/libretro/FBBackendLIBRETRO.hxx @@ -0,0 +1,113 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef FB_BACKEND_LIBRETRO_HXX +#define FB_BACKEND_LIBRETRO_HXX + +class OSystem; + +#include "bspf.hxx" +#include "FBBackend.hxx" + +/** + This class implements a standard LIBRETRO backend. Most of the + functionality is not used, since libretro has its own rendering system. + + @author Stephen Anthony +*/ +class FBBackendLIBRETRO : public FBBackend +{ + public: + /** + Creates a new LIBRETRO framebuffer. + */ + explicit FBBackendLIBRETRO(OSystem&) { } + ~FBBackendLIBRETRO() override { } + + protected: + /** + This method is called to map a given R/G/B triple to the screen palette. + + @param r The red component of the color. + @param g The green component of the color. + @param b The blue component of the color. + */ + uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override { + return (r << 16) | (g << 8) | b; + } + + /** + This method is called to query and initialize the video hardware + for desktop and fullscreen resolution information. Since several + monitors may be attached, we need the resolution for all of them. + + @param fullscreenRes Maximum resolution supported in fullscreen mode + @param windowedRes Maximum resolution supported in windowed mode + @param renderers List of renderer names (internal name -> end-user name) + */ + void queryHardware(vector& fullscreenRes, + vector& windowedRes, + VariantList& renderers) override; + + /** + This method is called to create a surface with the given attributes. + + @param w The requested width of the new surface. + @param h The requested height of the new surface. + */ + unique_ptr + createSurface(uInt32 w, uInt32 h, ScalingInterpolation, + const uInt32*) const override; + + /** + This method is called to provide information about the backend. + */ + string about() const override { return "Video system: libretro"; } + + + ////////////////////////////////////////////////////////////////////// + // Most methods here aren't used at all. See FBBacked class for + // description, if needed. + ////////////////////////////////////////////////////////////////////// + + int scaleX(int x) const override { return x; } + int scaleY(int y) const override { return y; } + void setTitle(const string&) override { } + void showCursor(bool) override { } + bool fullScreen() const override { return true; } + void getRGB(uInt32, uInt8*, uInt8*, uInt8*) const override { } + void readPixels(uInt8*, uInt32, const Common::Rect&) const override { } + bool isCurrentWindowPositioned() const override { return true; } + Common::Point getCurrentWindowPos() const override { return Common::Point{}; } + Int32 getCurrentDisplayIndex() const override { return 0; } + void clear() override { } + bool setVideoMode(const VideoModeHandler::Mode&, + int, const Common::Point&) override { return true; } + void grabMouse(bool) override { } + void renderToScreen() override { } + int refreshRate() const override { return 0; } + + private: + // Following constructors and assignment operators not supported + FBBackendLIBRETRO() = delete; + FBBackendLIBRETRO(const FBBackendLIBRETRO&) = delete; + FBBackendLIBRETRO(FBBackendLIBRETRO&&) = delete; + FBBackendLIBRETRO& operator=(const FBBackendLIBRETRO&) = delete; + FBBackendLIBRETRO& operator=(FBBackendLIBRETRO&&) = delete; +}; + +#endif diff --git a/src/libretro/FBSurfaceLIBRETRO.cxx b/src/libretro/FBSurfaceLIBRETRO.cxx index 0857ee128..1f3d7e549 100644 --- a/src/libretro/FBSurfaceLIBRETRO.cxx +++ b/src/libretro/FBSurfaceLIBRETRO.cxx @@ -18,15 +18,7 @@ #include "FBSurfaceLIBRETRO.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FBSurfaceLIBRETRO::FBSurfaceLIBRETRO(FrameBufferLIBRETRO&, - uInt32 width, uInt32 height, const uInt32* data) -{ - createSurface(width, height, data); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FBSurfaceLIBRETRO::createSurface(uInt32 width, uInt32 height, - const uInt32* data) +FBSurfaceLIBRETRO::FBSurfaceLIBRETRO(uInt32 width, uInt32 height) { myWidth = width; myHeight = height; @@ -39,9 +31,3 @@ void FBSurfaceLIBRETRO::createSurface(uInt32 width, uInt32 height, myPitch = width; //////////////////////////////////////////////////// } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FBSurfaceLIBRETRO::render() -{ - return true; -} diff --git a/src/libretro/FBSurfaceLIBRETRO.hxx b/src/libretro/FBSurfaceLIBRETRO.hxx index c436ceaa3..43cd276bd 100644 --- a/src/libretro/FBSurfaceLIBRETRO.hxx +++ b/src/libretro/FBSurfaceLIBRETRO.hxx @@ -20,23 +20,22 @@ #include "bspf.hxx" #include "FBSurface.hxx" -#include "FrameBufferLIBRETRO.hxx" /** - An FBSurface suitable for the LIBRETRO Render2D API, making use of hardware - acceleration behind the scenes. + An FBSurface suitable for the LIBRETRO Render2D API. As with FBBackend, + most of the functionality here is handled by libretro directly. @author Stephen Anthony */ class FBSurfaceLIBRETRO : public FBSurface { public: - FBSurfaceLIBRETRO(FrameBufferLIBRETRO& buffer, uInt32 width, uInt32 height, - const uInt32* data); + FBSurfaceLIBRETRO(uInt32 width, uInt32 height); ~FBSurfaceLIBRETRO() override { } // Most of the surface drawing primitives are implemented in FBSurface; - void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color) override { } + void fillRect(uInt32 x, uInt32 y, uInt32 w, + uInt32 h, ColorId color) override { } uInt32 width() const override { return myWidth; } uInt32 height() const override { return myHeight; } @@ -50,7 +49,7 @@ class FBSurfaceLIBRETRO : public FBSurface void setVisible(bool visible) override { } void translateCoords(Int32& x, Int32& y) const override { } - bool render() override; + bool render() override { return true; } void invalidate() override { } void free() override { } void reload() override { } @@ -61,22 +60,17 @@ class FBSurfaceLIBRETRO : public FBSurface void applyAttributes() override { } private: - void createSurface(uInt32 width, uInt32 height, const uInt32* data); + unique_ptr myPixelData; + uInt32 myWidth, myHeight; + Common::Rect mySrcGUIR, myDstGUIR; + private: // Following constructors and assignment operators not supported FBSurfaceLIBRETRO() = delete; FBSurfaceLIBRETRO(const FBSurfaceLIBRETRO&) = delete; FBSurfaceLIBRETRO(FBSurfaceLIBRETRO&&) = delete; FBSurfaceLIBRETRO& operator=(const FBSurfaceLIBRETRO&) = delete; FBSurfaceLIBRETRO& operator=(FBSurfaceLIBRETRO&&) = delete; - - private: - Common::Rect mySrcGUIR, myDstGUIR; - - private: - unique_ptr myPixelData; - - uInt32 myWidth, myHeight; }; #endif diff --git a/src/libretro/FrameBufferLIBRETRO.hxx b/src/libretro/FrameBufferLIBRETRO.hxx deleted file mode 100644 index 3816e7c50..000000000 --- a/src/libretro/FrameBufferLIBRETRO.hxx +++ /dev/null @@ -1,202 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony -// and the Stella Team -// -// See the file "License.txt" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -//============================================================================ - -#ifndef FRAMEBUFFER_LIBRETRO_HXX -#define FRAMEBUFFER_LIBRETRO_HXX - -class OSystem; -class FBSurfaceLIBRETRO; - -#include "bspf.hxx" -#include "FrameBuffer.hxx" - -/** - This class implements a standard LIBRETRO 2D, hardware accelerated framebuffer. - Behind the scenes, it may be using Direct3D, OpenGL(ES), etc. - - @author Stephen Anthony -*/ -class FrameBufferLIBRETRO : public FrameBuffer -{ - friend class FBSurfaceLIBRETRO; - - public: - /** - Creates a new LIBRETRO framebuffer - */ - explicit FrameBufferLIBRETRO(OSystem& osystem); - ~FrameBufferLIBRETRO() override { } - - ////////////////////////////////////////////////////////////////////// - // The following are derived from public methods in FrameBuffer.hxx - ////////////////////////////////////////////////////////////////////// - - /** - Updates window title. - - @param title The title of the application / window - */ - void setTitle(const string& title) override { } - - /** - Shows or hides the cursor based on the given boolean value. - */ - void showCursor(bool show) override { } - - /** - Answers if the display is currently in fullscreen mode. - */ - bool fullScreen() const override { return true; } - - /** - This method is called to retrieve the R/G/B data from the given pixel. - - @param pixel The pixel containing R/G/B data - @param r The red component of the color - @param g The green component of the color - @param b The blue component of the color - */ - inline void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override { } - - /** - This method is called to map a given R/G/B triple to the screen palette. - - @param r The red component of the color. - @param g The green component of the color. - @param b The blue component of the color. - */ - inline uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override { return (r << 16) | (g << 8) | b; } - - /** - This method is called to get a copy of the specified ARGB data from the - viewable FrameBuffer area. Note that this isn't the same as any - internal surfaces that may be in use; it should return the actual data - as it is currently seen onscreen. - - @param buffer A copy of the pixel data in ARGB8888 format - @param pitch The pitch (in bytes) for the pixel data - @param rect The bounding rectangle for the buffer - */ - void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const override { } - - /** - Clear the frame buffer - */ - void clear() override { } - - protected: - ////////////////////////////////////////////////////////////////////// - // The following are derived from protected methods in FrameBuffer.hxx - ////////////////////////////////////////////////////////////////////// - /** - This method is called to query and initialize the video hardware - for desktop and fullscreen resolution information. Since several - monitors may be attached, we need the resolution for all of them. - - @param fullscreenRes Maximum resolution supported in fullscreen mode - @param windowedRes Maximum resolution supported in windowed mode - @param renderers List of renderer names (internal name -> end-user name) - */ - void queryHardware(vector& fullscreenRes, - vector& windowedRes, - VariantList& renderers) override; - - /** - This method is called to query if the current window is not centered or fullscreen. - - @return True, if the current window is positioned - */ - bool isCurrentWindowPositioned() const override { return true; } - - /** - This method is called to query the video hardware for position of - the current window - - @return The position of the currently displayed window - */ - Common::Point getCurrentWindowPos() const override { return Common::Point{}; } - - /** - This method is called to query the video hardware for the index - of the display the current window is displayed on - - @return the current display index or a negative value if no - window is displayed - */ - Int32 getCurrentDisplayIndex() const override { return 0; } - - /** - This method is called to change to the given video mode. - - @param title The title for the created window - @param mode The video mode to use - - @return False on any errors, else true - */ - bool activateVideoMode(const string& title, - const VideoModeHandler::Mode& mode) override { return true; } - - /** - This method is called to create a surface with the given attributes. - - @param w The requested width of the new surface. - @param h The requested height of the new surface. - @param data If non-null, use the given data values as a static surface - */ - unique_ptr - createSurface(uInt32 w, uInt32 h, ScalingInterpolation, const uInt32* data) const override; - - /** - Grabs or ungrabs the mouse based on the given boolean value. - */ - void grabMouse(bool grab) override { } - - /** - Set the icon for the main SDL window. - */ - void setWindowIcon() override { } - - /** - This method is called to provide information about the FrameBuffer. - */ - string about() const override { return "Video system: libretro"; } - - /** - This method must be called after all drawing is done, and indicates - that the buffers should be pushed to the physical screen. - */ - void renderToScreen() override { } - - public: - /** - Returns a pointer to the output rendering buffer - */ - uInt32* getRenderSurface() const { return myRenderSurface; } - - private: - mutable uInt32* myRenderSurface; - - private: - // Following constructors and assignment operators not supported - FrameBufferLIBRETRO() = delete; - FrameBufferLIBRETRO(const FrameBufferLIBRETRO&) = delete; - FrameBufferLIBRETRO(FrameBufferLIBRETRO&&) = delete; - FrameBufferLIBRETRO& operator=(const FrameBufferLIBRETRO&) = delete; - FrameBufferLIBRETRO& operator=(FrameBufferLIBRETRO&&) = delete; -}; - -#endif diff --git a/src/libretro/Makefile.common b/src/libretro/Makefile.common index ba98f0825..e26399c7e 100644 --- a/src/libretro/Makefile.common +++ b/src/libretro/Makefile.common @@ -9,8 +9,8 @@ SOURCES_CXX := \ $(CORE_DIR)/libretro/libretro.cxx \ $(CORE_DIR)/libretro/EventHandlerLIBRETRO.cxx \ $(CORE_DIR)/libretro/FSNodeLIBRETRO.cxx \ + $(CORE_DIR)/libretro/FBBackendLIBRETRO.cxx \ $(CORE_DIR)/libretro/FBSurfaceLIBRETRO.cxx \ - $(CORE_DIR)/libretro/FrameBufferLIBRETRO.cxx \ $(CORE_DIR)/libretro/OSystemLIBRETRO.cxx \ $(CORE_DIR)/libretro/SoundLIBRETRO.cxx \ $(CORE_DIR)/libretro/StellaLIBRETRO.cxx \ diff --git a/src/libretro/StellaLIBRETRO.cxx b/src/libretro/StellaLIBRETRO.cxx index 38e52e734..b5aaecffc 100644 --- a/src/libretro/StellaLIBRETRO.cxx +++ b/src/libretro/StellaLIBRETRO.cxx @@ -18,10 +18,10 @@ #include "bspf.hxx" #include "StellaLIBRETRO.hxx" #include "SoundLIBRETRO.hxx" -#include "FrameBufferLIBRETRO.hxx" +#include "FBBackendLIBRETRO.hxx" +#include "FBSurfaceLIBRETRO.hxx" #include "AtariNTSC.hxx" -#include "PaletteHandler.hxx" #include "AudioSettings.hxx" #include "Serializer.hxx" #include "StateManager.hxx" @@ -33,27 +33,7 @@ StellaLIBRETRO::StellaLIBRETRO() { audio_buffer = make_unique(audio_buffer_max); - - console_timing = ConsoleTiming::ntsc; - console_format = "AUTO"; - - video_aspect_ntsc = 0; - video_aspect_pal = 0; - - video_palette = PaletteHandler::SETTING_STANDARD; - video_filter = NTSCFilter::Preset::OFF; - video_ready = false; - - audio_samples = 0; - audio_mode = "byrom"; - - video_phosphor = "byrom"; - video_phosphor_blend = 60; - phosphor_default = false; - rom_image = make_unique(getROMMax()); - - system_ready = false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -302,9 +282,16 @@ float StellaLIBRETRO::getVideoAspect() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void* StellaLIBRETRO::getVideoBuffer() const { - FrameBufferLIBRETRO& frame = static_cast(myOSystem->frameBuffer()); + if (!render_surface) + { + const FBSurface& surface = + myOSystem->frameBuffer().tiaSurface().tiaSurface(); - return static_cast(frame.getRenderSurface()); + uInt32 pitch = 0; + surface.basePtr(render_surface, pitch); + } + + return render_surface; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libretro/StellaLIBRETRO.hxx b/src/libretro/StellaLIBRETRO.hxx index 64bc91651..d4ae39c87 100644 --- a/src/libretro/StellaLIBRETRO.hxx +++ b/src/libretro/StellaLIBRETRO.hxx @@ -29,6 +29,7 @@ #include "EventHandler.hxx" #include "M6532.hxx" #include "Paddles.hxx" +#include "PaletteHandler.hxx" #include "System.hxx" #include "TIA.hxx" #include "TIASurface.hxx" @@ -122,7 +123,7 @@ class StellaLIBRETRO void setAudioStereo(int mode); void setInputEvent(Event::Type type, Int32 state) { - myOSystem->eventHandler().handleEvent(type, state); + myOSystem->eventHandler().handleEvent(type, state); } Controller::Type getLeftControllerType() const { @@ -153,39 +154,40 @@ class StellaLIBRETRO StellaLIBRETRO& operator=(StellaLIBRETRO&&) = delete; unique_ptr myOSystem; - uInt32 system_ready; + uInt32 system_ready{false}; ByteBuffer rom_image; - uInt32 rom_size; + uInt32 rom_size{0}; string rom_path; - ConsoleTiming console_timing; - string console_format; + ConsoleTiming console_timing{ConsoleTiming::ntsc}; + string console_format{"AUTO"}; - uInt32 render_width, render_height; + mutable uInt32* render_surface{nullptr}; + uInt32 render_width{0}, render_height{0}; - bool video_ready; + bool video_ready{false}; unique_ptr audio_buffer; - uInt32 audio_samples; - - // (31440 rate / 50 Hz) * 16-bit stereo * 1.25x padding - const uInt32 audio_buffer_max = (31440 / 50 * 4 * 5) / 4; + uInt32 audio_samples{0}; uInt8 system_ram[128]; + // (31440 rate / 50 Hz) * 16-bit stereo * 1.25x padding + static constexpr uInt32 audio_buffer_max = (31440 / 50 * 4 * 5) / 4; + private: - string video_palette; - string video_phosphor; - uInt32 video_phosphor_blend; + string video_palette{PaletteHandler::SETTING_STANDARD}; + string video_phosphor{"byrom"}; + uInt32 video_phosphor_blend{60}; - uInt32 video_aspect_ntsc; - uInt32 video_aspect_pal; - NTSCFilter::Preset video_filter; + uInt32 video_aspect_ntsc{0}; + uInt32 video_aspect_pal{0}; + NTSCFilter::Preset video_filter{NTSCFilter::Preset::OFF}; - string audio_mode; + string audio_mode{"byrom"}; - bool phosphor_default; + bool phosphor_default{false}; }; #endif diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 5d7bef977..829fb3669 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -495,9 +495,9 @@ + - @@ -1508,9 +1508,9 @@ + - @@ -2060,4 +2060,4 @@ - \ No newline at end of file + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 81d029742..5423da071 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -72,7 +72,7 @@ - + Source Files @@ -1034,7 +1034,7 @@ Header Files - + Header Files @@ -2126,4 +2126,4 @@ Resource Files - \ No newline at end of file +