diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 06d482d66..892cb2a74 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -965,7 +965,7 @@ void GuiMenu::openOtherOptions() }); #if defined(_WIN64) - // Hide taskbar during the ES program session. + // Hide taskbar during the program session. auto hide_taskbar = std::make_shared(mWindow); hide_taskbar->setState(Settings::getInstance()->getBool("HideTaskbar")); s->addWithLabel("HIDE TASKBAR (REQUIRES RESTART)", hide_taskbar); @@ -977,6 +977,19 @@ void GuiMenu::openOtherOptions() }); #endif +#if defined(__APPLE__) + // macOS Monterey VSync bug workaround (hack for broken OpenGL drivers). + auto vsyncWorkaround = std::make_shared(mWindow); + vsyncWorkaround->setState(Settings::getInstance()->getBool("VSyncWorkaround")); + s->addWithLabel("MACOS MONTEREY VSYNC BUG WORKAROUND", vsyncWorkaround); + s->addSaveFunc([vsyncWorkaround, s] { + if (vsyncWorkaround->getState() != Settings::getInstance()->getBool("VSyncWorkaround")) { + Settings::getInstance()->setBool("VSyncWorkaround", vsyncWorkaround->getState()); + s->setNeedsSaving(); + } + }); +#endif + // Run ES in the background when a game has been launched. auto run_in_background = std::make_shared(mWindow); run_in_background->setState(Settings::getInstance()->getBool("RunInBackground")); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 211ac1465..75ac30cdb 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -244,6 +244,9 @@ void Settings::setDefaults() mStringMap["SaveGamelistsMode"] = {"always", "always"}; #if defined(_WIN64) mBoolMap["HideTaskbar"] = {false, false}; +#endif +#if defined(__APPLE__) + mBoolMap["VSyncWorkaround"] = {false, false}; #endif mBoolMap["RunInBackground"] = {false, false}; #if defined(_WIN64) diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp index c476be97d..0c8cd9acc 100644 --- a/es-core/src/renderers/Renderer_GL21.cpp +++ b/es-core/src/renderers/Renderer_GL21.cpp @@ -12,6 +12,10 @@ #include "Shader_GL21.h" #include "renderers/Renderer.h" +#if defined(__APPLE__) +#include +#endif + namespace Renderer { static SDL_GLContext sdlContext = nullptr; @@ -410,7 +414,30 @@ namespace Renderer void swapBuffers() { +#if defined(__APPLE__) + // This is an ugly hack to work around an OpenGL VSync bug introduced in macOS 12 Monterey. + // What happens is that the VSync setting in SDL gets effectively ignored so the operating + // system will try to render as many frames as it can manage which eats all the available + // resources. If the corresponding setting is enabled via the main menu, a 10 millisecond + // delay will be introduced if the swap took less than 3 milliseconds to complete. This is + // very crude and not accurate at all but it will hopefully avoid the worst slowdowns until + // Apple releases an OS update that properly fixes the problem. + if (Settings::getInstance()->getBool("VSyncWorkaround")) { + const auto beforeSwap = std::chrono::system_clock::now(); + SDL_GL_SwapWindow(getSDLWindow()); + const auto afterSwap = std::chrono::system_clock::now(); + + if (std::chrono::duration_cast(afterSwap - beforeSwap) + .count() < 3.0) + SDL_Delay(10); + } + else { + SDL_GL_SwapWindow(getSDLWindow()); + } + +#else SDL_GL_SwapWindow(getSDLWindow()); +#endif GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); }