From 0b47a90b3e77bfe6a1eef021d62227e204dfb2c1 Mon Sep 17 00:00:00 2001
From: Leon Styhre <leon@leonstyhre.com>
Date: Wed, 30 Jun 2021 18:08:13 +0200
Subject: [PATCH] (Windows) Added game launch workaround for an AMD and Intel
 GPU issue.

---
 es-app/src/FileData.cpp     |  5 +++++
 es-app/src/guis/GuiMenu.cpp | 40 +++++++++++++++++++++++++++++++++++++
 es-core/src/Platform.cpp    | 17 ++++++++--------
 es-core/src/Settings.cpp    |  3 +++
 4 files changed, 57 insertions(+), 8 deletions(-)

diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp
index c81df9236..a04b3a29e 100644
--- a/es-app/src/FileData.cpp
+++ b/es-app/src/FileData.cpp
@@ -989,7 +989,12 @@ void FileData::launchGame(Window* window)
     // swapBuffers() is called here to turn the screen black to eliminate some potential
     // flickering and to avoid showing the game launch message briefly when returning
     // from the game.
+    #if defined(_WIN64)
+    if (!(Settings::getInstance()->getBool("LaunchWorkaround") ||
+            ViewController::get()->runInBackground(mSystem)))
+    #else
     if (!ViewController::get()->runInBackground(mSystem))
+    #endif
         Renderer::swapBuffers();
 
     Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name"));
diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp
index 555b6357e..808d2add4 100644
--- a/es-app/src/guis/GuiMenu.cpp
+++ b/es-app/src/guis/GuiMenu.cpp
@@ -1046,6 +1046,27 @@ void GuiMenu::openOtherOptions()
         }
     });
 
+    #if defined(_WIN64)
+    // Workaround for launching games on AMD and Intel graphics drivers.
+    auto launch_workaround = std::make_shared<SwitchComponent>(mWindow);
+    launch_workaround->setState(Settings::getInstance()->getBool("LaunchWorkaround"));
+    s->addWithLabel("AMD AND INTEL GPU GAME LAUNCH WORKAROUND", launch_workaround);
+    s->addSaveFunc([launch_workaround, s] {
+        if (launch_workaround->getState() != Settings::getInstance()->getBool("LaunchWorkaround")) {
+            Settings::getInstance()->setBool("LaunchWorkaround", launch_workaround->getState());
+            s->setNeedsSaving();
+        }
+    });
+
+    // If the RunInBackground setting is enabled, then disable this option.
+    if (Settings::getInstance()->getBool("RunInBackground")) {
+        launch_workaround->setEnabled(false);
+        launch_workaround->setOpacity(DISABLED_OPACITY);
+        launch_workaround->getParent()->getChild(launch_workaround->
+                getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
+    }
+    #endif
+
     // Whether to upscale the video frame rate to 60 FPS.
     auto video_upscale_frame_rate = std::make_shared<SwitchComponent>(mWindow);
     video_upscale_frame_rate->setState(Settings::getInstance()->getBool("VideoUpscaleFrameRate"));
@@ -1180,6 +1201,25 @@ void GuiMenu::openOtherOptions()
     });
     #endif
 
+    #if defined(_WIN64)
+    // Switch callback.
+    auto launchWorkAroundToggleFunc = [launch_workaround]() {
+        if (launch_workaround->getEnabled()) {
+            launch_workaround->setEnabled(false);
+            launch_workaround->setOpacity(DISABLED_OPACITY);
+            launch_workaround->getParent()->getChild(launch_workaround->
+                    getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
+        }
+        else {
+            launch_workaround->setEnabled(true);
+            launch_workaround->setOpacity(255);
+            launch_workaround->getParent()->getChild(launch_workaround->
+                    getChildIndex() - 1)->setOpacity(255);
+        }
+    };
+    run_in_background->setCallback(launchWorkAroundToggleFunc);
+    #endif
+
     mWindow->pushGui(s);
 }
 
diff --git a/es-core/src/Platform.cpp b/es-core/src/Platform.cpp
index 658f1bf98..4e509cf1e 100644
--- a/es-core/src/Platform.cpp
+++ b/es-core/src/Platform.cpp
@@ -151,15 +151,16 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
             &si,                                // Pointer to the STARTUPINFOW structure.
             &pi);                               // Pointer to the PROCESS_INFORMATION structure.
 
-    // Unfortunately suspending ES-DE and resuming when the game/emulator process has exited
-    // doesn't work reliably on Windows, so we may need to keep ES-DE running in the background
-    // while the game is launched. I'm not sure if there is a workaround for this, but on most
-    // Windows installations it seems to work fine so we'll let the user choose via a menu option.
-    // Possibly the issue is specific to Windows 8.
-    // Running in the background is also required for Steam games as ES-DE would otherwise
-    // wait forever for Steam to exit unless it was already running when the game was launched.
     if (!runInBackground) {
-        // Wait for the child process to exit.
+        if (Settings::getInstance()->getBool("LaunchWorkaround")) {
+            // Ugly hack to make the emulator window render correctly with some graphics drivers
+            // (probably only those from AMD and Intel as Nvidia seems to work fine without this).
+            // Unfortunately this turns the screen white as the emulator is starting.
+            // This definitely needs a proper solution some time in the future.
+            SDL_HideWindow(Renderer::getSDLWindow());
+            SDL_ShowWindow(Renderer::getSDLWindow());
+        }
+
         WaitForSingleObject(pi.hThread, INFINITE);
         WaitForSingleObject(pi.hProcess, INFINITE);
     }
diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp
index 2bc82f9c4..4625f1c05 100644
--- a/es-core/src/Settings.cpp
+++ b/es-core/src/Settings.cpp
@@ -246,6 +246,9 @@ void Settings::setDefaults()
     mBoolMap["HideTaskbar"] = { false, false };
     #endif
     mBoolMap["RunInBackground"] = { false, false };
+    #if defined(_WIN64)
+    mBoolMap["LaunchWorkaround"] = { true, true };
+    #endif
     mStringMap["MediaDirectory"] = { "", "" };
     mBoolMap["VideoUpscaleFrameRate"] = { false, false };
     mBoolMap["LaunchCommandOverride"] = { true, true };