diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 9a33896d0f..4f580d5f26 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -433,7 +433,7 @@ void VulkanRenderManager::StopThreads() { // Not sure this is a sensible check - should be ok even if not. // _dbg_assert_(steps_.empty()); - _dbg_assert_(renderThread_.joinable()); + _dbg_assert_(!useRenderThread_ || renderThread_.joinable()); if (useRenderThread_ && renderThread_.joinable()) { // Tell the render thread to quit when it's done. VKRRenderThreadTask *task = new VKRRenderThreadTask(VKRRunType::EXIT); diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index 65aa8cfc1a..9b8ece60d3 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -59,6 +59,5 @@ public: sleep_ms(5, "exit poll"); } } - } }; diff --git a/Common/System/System.h b/Common/System/System.h index deebbac2d5..dbb10375d4 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -250,6 +250,7 @@ enum SystemProperty { SYSPROP_INSTALLER_NAME, // Useful on Android to check if we were installed from the play store. }; +// NOTE: Unlike requests or UIMessage, these are synchronous! enum class SystemNotification { UI, MEM_VIEW, @@ -273,6 +274,8 @@ enum class SystemNotification { AUDIO_MODE_CHANGED, APP_SWITCH_MODE_CHANGED, PAD_STATE_CHANGED, + CONFIG_LOADED, + BEFORE_CONFIG_SAVE_ON_EXIT, }; // I guess it's not super great architecturally to centralize this, since it's not general - but same with a lot of diff --git a/Core/HLE/sceNetAdhocMatching.cpp b/Core/HLE/sceNetAdhocMatching.cpp index d96b0d8c90..7330bae1af 100644 --- a/Core/HLE/sceNetAdhocMatching.cpp +++ b/Core/HLE/sceNetAdhocMatching.cpp @@ -1784,7 +1784,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle getLocalMac(&localmac); // Clear Memory - context = {}; + *context = {}; // Allocate Receive Buffer context->rxbuf = (uint8_t*)malloc(rxbuflen); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index f36fed109a..25ece3e25b 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2792,7 +2792,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } - // Check early for the degenerate case from Tactics Ogre. + // Check early for the degenerate case from Tactics Ogre (EN version, japanese is worse!). if (pure3D && plan.levelsToLoad == 2 && gstate.getTextureAddress(0) == gstate.getTextureAddress(1)) { // Simply treat it as a regular 2D texture, no fake mipmaps or anything. // levelsToLoad/Create gets set to 1 on the way out from the surrounding if. diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 14f53a5a23..12b2fd1f78 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -515,8 +515,12 @@ bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuf stage.useFinalOutputSize ? dstWidth : srcWidth * stage.dstWidthScale, stage.useFinalOutputSize ? dstHeight : srcHeight * stage.dstHeightScale, }; + VkPipeline pipeline = computeShaderManager_.GetPipeline(multipassCS_[stage.shaderIndex], "Multipass Compute Shader"); + if (!pipeline) { + return false; + } VkDescriptorSet stageSet = computeShaderManager_.GetDescriptorSet(outputView, inputBuffer, inputOffset, inputRange, VK_NULL_HANDLE, 0, 0, inputImage, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); - vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(multipassCS_[stage.shaderIndex])); + vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &stageSet, 0, nullptr); vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), ¶ms); vkCmdDispatch(cmdInit, (params.dstWidth + 7) / 8, (params.dstHeight + 7) / 8, 1); @@ -542,7 +546,11 @@ bool TextureCacheVulkan::ScaleBufferToImage(VulkanContext *vulkan, VkCommandBuff } VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(dstView, texBuf, bufferOffset, srcSize, VK_NULL_HANDLE, 0, 0, VK_NULL_HANDLE, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); struct Params { int x; int y; } params{ srcWidth, srcHeight }; - vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(singlePassCS_)); + VkPipeline pipeline = computeShaderManager_.GetPipeline(singlePassCS_, "Single Pass Compute Shader"); + if (!pipeline) { + return false; + } + vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &descSet, 0, nullptr); vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), ¶ms); vkCmdDispatch(cmdInit, (srcWidth + 7) / 8, (srcHeight + 7) / 8, 1); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index b3a4659d7e..4ccf56cf46 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -262,7 +262,7 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, return desc; } -VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { +VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs, const char *tag) { PipelineKey key{ cs }; VkPipeline pipeline; if (pipelines_.Get(key, &pipeline)) { @@ -278,7 +278,11 @@ VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { pci.flags = 0; VkResult res = vkCreateComputePipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pci, nullptr, &pipeline); - _assert_(res == VK_SUCCESS); + if (res != VK_SUCCESS) { + ERROR_LOG(Log::G3D, "Failed to create compute pipeline from shader module (%s)", tag); + pipelines_.Insert(key, VK_NULL_HANDLE); + } + _dbg_assert_msg_(res == VK_SUCCESS, "Failed to create compute pipeline from shader module (%s)", tag); pipelines_.Insert(key, pipeline); return pipeline; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 6e6d683d29..5f6fa11eee 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -58,7 +58,7 @@ public: VkDescriptorSet GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2, VkImageView image2, VkBuffer cbuffer, VkDeviceSize cbufferSize); // This of course caches though. - VkPipeline GetPipeline(VkShaderModule cs); + VkPipeline GetPipeline(VkShaderModule cs, const char *tag); VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void ClearPipelines(); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 81857bfae1..eafea2dfe7 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -575,6 +575,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Note that if we don't have storage permission here, loading the config will // fail and it will be set to the default. Later, we load again when we get permission. g_Config.Load(); + System_Notify(SystemNotification::CONFIG_LOADED); #endif const char *fileToLog = nullptr; @@ -1708,6 +1709,8 @@ void NativeShutdown() { g_screenManager = nullptr; } + System_Notify(SystemNotification::BEFORE_CONFIG_SAVE_ON_EXIT); + g_Config.Save("NativeShutdown"); g_i18nrepo.LogMissingKeys(); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index fbd86c37a5..edebbd644f 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -11,15 +11,12 @@ #include "Common/Log.h" #include "Common/StringUtils.h" #include "Common/GraphicsContext.h" -#include "Common/TimeUtil.h" #include "Common/Thread/ThreadUtil.h" #include "Windows/EmuThread.h" #include "Windows/W32Util/Misc.h" #include "Windows/MainWindow.h" -#include "Windows/resource.h" #include "Core/Reporting.h" -#include "Core/MemMap.h" #include "Core/Core.h" #include "Core/System.h" #include "Core/Config.h" @@ -43,23 +40,28 @@ static std::thread emuThread; static std::atomic g_emuThreadState(EmuThreadState::DISABLED); static std::thread mainThread; -static bool useEmuThread; static std::string g_error_message; static bool g_inLoop; +static const char** g_argv; +static int g_argc; + extern std::vector GetWideCmdLine(); class GraphicsContext; static GraphicsContext *g_graphicsContext; -void MainThreadFunc(); +void MainThreadFunc(int argc, const char *argv[]); -// On most other platforms, we let the "main" thread become the render thread and -// start a separate emu thread from that, if needed. Should probably switch to that -// to make it the same on all platforms. -void MainThread_Start(bool separateEmuThread) { - useEmuThread = separateEmuThread; - mainThread = std::thread(&MainThreadFunc); +void MainThread_Start(int argc, const char *argv[]) { + // This is just to survive WM_USER_RESTART_EMUTHREAD. + if (g_argc == 0) { + g_argc = argc; + g_argv = argv; + } + mainThread = std::thread([]() { + MainThreadFunc(g_argc, g_argv); + }); } void MainThread_Stop() { @@ -70,6 +72,9 @@ void MainThread_Stop() { } bool MainThread_Ready() { + if (g_inLoop) { + _dbg_assert_(mainThread.joinable()); + } return g_inLoop; } @@ -148,41 +153,15 @@ bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { } } -void MainThreadFunc() { - // We'll start up a separate thread we'll call Emu - SetCurrentThreadName(useEmuThread ? "RenderThread" : "EmuThread"); +void MainThreadFunc(int argc, const char *argv[]) { + const bool useEmuThread = g_Config.iGPUBackend == (int)GPUBackend::OPENGL; - const HWND console = GetConsoleWindow(); - if (console && g_Config.iConsoleWindowX != -1 && g_Config.iConsoleWindowY != -1) { - SetWindowPos(console, NULL, g_Config.iConsoleWindowX, g_Config.iConsoleWindowY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); - } + SetCurrentThreadName(useEmuThread ? "RenderThread" : "EmuThread"); System_SetWindowTitle(""); - // We convert command line arguments to UTF-8 immediately. - std::vector wideArgs = GetWideCmdLine(); - std::vector argsUTF8; - for (auto& string : wideArgs) { - argsUTF8.push_back(ConvertWStringToUTF8(string)); - } - std::vector args; - for (auto& string : argsUTF8) { - args.push_back(string.c_str()); - } - bool performingRestart = NativeIsRestarting(); - NativeInit(static_cast(args.size()), &args[0], "", "", nullptr); - - if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { - if (!useEmuThread) { - // Okay, we must've switched to OpenGL. Let's flip the emu thread on. - useEmuThread = true; - SetCurrentThreadName("Render"); - } - } else if (useEmuThread) { - // We must've failed over from OpenGL, flip the emu thread off. - useEmuThread = false; - SetCurrentThreadName("EmuThread"); - } + const bool performingRestart = NativeIsRestarting(); + NativeInit(argc, argv, "", "", nullptr); if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { Reporting::ReportMessage("Graphics init error: %s", "ALL"); @@ -313,18 +292,11 @@ void MainThreadFunc() { g_graphicsContext->ThreadEnd(); g_graphicsContext->ShutdownFromRenderThread(); - g_graphicsContext->Shutdown(); delete g_graphicsContext; g_graphicsContext = nullptr; - RECT rc; - if (console && GetWindowRect(console, &rc) && !IsIconic(console)) { - g_Config.iConsoleWindowX = rc.left; - g_Config.iConsoleWindowY = rc.top; - } - NativeShutdown(); PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0); diff --git a/Windows/EmuThread.h b/Windows/EmuThread.h index bc04bc3f06..34dc544fe7 100644 --- a/Windows/EmuThread.h +++ b/Windows/EmuThread.h @@ -17,6 +17,8 @@ #pragma once -void MainThread_Start(bool separateRenderThread); +void MainThread_Start(int argc, const char *argv[]); void MainThread_Stop(); bool MainThread_Ready(); + +void MainThreadFunc(int argc, const char *argv[]); diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 961de47173..eb60d4fcc1 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -1102,7 +1102,7 @@ namespace MainWindow { g_InputManager.StopPolling(); MainThread_Stop(); UpdateUIState(UISTATE_MENU); - MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); + MainThread_Start(0, nullptr); g_InputManager.BeginPolling(); break; diff --git a/Windows/main.cpp b/Windows/main.cpp index 5d7ea4bf0d..fe7610d58c 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -594,6 +594,21 @@ void System_Notify(SystemNotification notification) { case SystemNotification::APP_SWITCH_MODE_CHANGED: case SystemNotification::UI_STATE_CHANGED: break; + case SystemNotification::CONFIG_LOADED: + { + // This is too early to move the console into position - hasn't been allocated yet. + break; + } + case SystemNotification::BEFORE_CONFIG_SAVE_ON_EXIT: + { + RECT rc{}; + const HWND console = GetConsoleWindow(); + if (console && GetWindowRect(console, &rc) && !IsIconic(console)) { + g_Config.iConsoleWindowX = rc.left; + g_Config.iConsoleWindowY = rc.top; + } + break; + } } } @@ -893,7 +908,6 @@ static bool DetectVulkanInExternalProcess() { std::vector GetWideCmdLine() { wchar_t **wargv; int wargc = -1; - // This is used for the WM_USER_RESTART_EMUTHREAD path. if (!restartArgs.empty()) { std::wstring wargs = ConvertUTF8ToWString("PPSSPP " + restartArgs); wargv = CommandLineToArgvW(wargs.c_str(), &wargc); @@ -904,7 +918,6 @@ std::vector GetWideCmdLine() { std::vector wideArgs(wargv, wargv + wargc); LocalFree(wargv); - return wideArgs; } @@ -1008,7 +1021,7 @@ static void WinMainCleanup() { } int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { - std::vector wideArgs = GetWideCmdLine(); + const std::vector wideArgs = GetWideCmdLine(); // Check for the Vulkan workaround before any serious init. for (size_t i = 1; i < wideArgs.size(); ++i) { @@ -1029,7 +1042,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin SetCurrentThreadName("Main"); TimeInit(); - WinMainInit(); #ifndef _DEBUG @@ -1088,6 +1100,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // if it's not loaded here first. g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); g_Config.Load(configFilename.c_str(), controlsConfigFilename.c_str()); + System_Notify(SystemNotification::CONFIG_LOADED); bool debugLogLevel = false; @@ -1170,6 +1183,12 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // - It should be possible to log to a file without showing the console. g_logManager.GetConsoleListener()->Init(showLog, 150, 120); + // Move the console into position. + const HWND console = GetConsoleWindow(); + if (console && g_Config.iConsoleWindowX != -1 && g_Config.iConsoleWindowY != -1) { + SetWindowPos(console, NULL, g_Config.iConsoleWindowX, g_Config.iConsoleWindowY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + } + if (debugLogLevel) { g_logManager.SetAllLogLevels(LogLevel::LDEBUG); } @@ -1208,7 +1227,18 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // Emu thread (and render thread, if any) is always running! // Only OpenGL uses an externally managed render thread (due to GL's single-threaded context design). Vulkan // manages its own render thread. - MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); + + // We convert command line arguments to UTF-8. + std::vector argsUTF8; + for (const auto &string : wideArgs) { + argsUTF8.push_back(ConvertWStringToUTF8(string)); + } + std::vector args; + for (const auto &string : argsUTF8) { + args.push_back(string.c_str()); + } + + MainThread_Start((int)args.size(), args.data()); g_InputManager.BeginPolling(); diff --git a/headless/Compare.cpp b/headless/Compare.cpp index 5deac80a73..48bba6bce1 100644 --- a/headless/Compare.cpp +++ b/headless/Compare.cpp @@ -317,7 +317,7 @@ std::vector TranslateDebugBufferToCompare(const GPUDebugBuffer *buffer, u32 outStride = -outStride; } - // Skip the bottom of the image in the buffer was smaller. Remember, we're flipped. + // Skip the bottom of the image if the buffer was smaller. Remember, we're flipped. u32 *dst = &data[0]; if (safeH < h) { dst += (h - safeH) * stride; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index f2e984c67e..a70910d47a 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -262,7 +262,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const } if (draw) { - draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Backbuffer"); + draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "BackBuffer"); // Vulkan may get angry if we don't do a final present. if (gpu) { gpu->SetCurFramebufferDirty(true); diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index edce2489c3..ac00aa0d1f 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -180,11 +180,7 @@ bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext * gfx_->ThreadStart(); threadState_ = RenderThreadState::STARTED; - while (threadState_ != RenderThreadState::STOP_REQUESTED) { - if (!gfx_->ThreadFrame(true)) { - break; - } - } + gfx_->ThreadFrameUntilCondition([this] { return threadState_ == RenderThreadState::STOP_REQUESTED; }); threadState_ = RenderThreadState::STOPPING; gfx_->ThreadEnd(); @@ -201,6 +197,7 @@ bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext * } void SDLHeadlessHost::ShutdownGraphics() { + threadState_ = RenderThreadState::STOP_REQUESTED; while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::START_FAILED) sleep_ms(1, "sdl-stop-poll"); diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 76f1d1ceca..0afcc2c08d 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -29,6 +29,7 @@ #include "Common/Log.h" #include "Common/File/FileUtil.h" #include "Common/TimeUtil.h" +#include "Common/Thread/ThreadUtil.h" #include "Core/CoreParameter.h" #include "Core/System.h" @@ -81,13 +82,11 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte } WindowsGraphicsContext *graphicsContext = nullptr; - bool needRenderThread = false; switch (gpuCore_) { case GPUCORE_GLES: #if PPSSPP_API(ANY_GL) case GPUCORE_SOFTWARE: graphicsContext = new WindowsGLContext(); - needRenderThread = true; break; #endif case GPUCORE_DIRECTX11: @@ -112,8 +111,11 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte return false; } + bool needRenderThread = gpuCore_ == GPUCORE_GLES; + if (needRenderThread) { - std::thread th([&]{ + renderThread_ = std::thread([this]{ + SetCurrentThreadName("RenderThread"); while (threadState_ == RenderThreadState::IDLE) sleep_ms(1, "render-thread-idle-poll"); threadState_ = RenderThreadState::STARTING; @@ -126,18 +128,13 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte gfx_->ThreadStart(); threadState_ = RenderThreadState::STARTED; - while (threadState_ != RenderThreadState::STOP_REQUESTED) { - if (!gfx_->ThreadFrame(true)) { - break; - } - } + gfx_->ThreadFrameUntilCondition([this] { return threadState_ == RenderThreadState::STOP_REQUESTED; }); threadState_ = RenderThreadState::STOPPING; gfx_->ThreadEnd(); gfx_->ShutdownFromRenderThread(); threadState_ = RenderThreadState::STOPPED; }); - th.detach(); } if (needRenderThread) { @@ -152,8 +149,12 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte } void WindowsHeadlessHost::ShutdownGraphics() { - while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::IDLE) - sleep_ms(1, "render-thread-stop-poll"); + if (renderThread_.joinable()) { + threadState_ = RenderThreadState::STOP_REQUESTED; + while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::IDLE) + sleep_ms(1, "render-thread-stop-poll"); + renderThread_.join(); + } gfx_->Shutdown(); delete gfx_; diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 5d5bd0d956..8282cea874 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -25,8 +25,7 @@ #include "Common/CommonWindows.h" -class WindowsHeadlessHost : public HeadlessHost -{ +class WindowsHeadlessHost : public HeadlessHost { public: bool InitGraphics(std::string *error_message, GraphicsContext **ctx, GPUCore core) override; void ShutdownGraphics() override; @@ -51,4 +50,5 @@ protected: HDC hDC; HGLRC hRC; volatile RenderThreadState threadState_ = RenderThreadState::IDLE; + std::thread renderThread_; };