Cleanup the Windows thread stuff to work like the other platforms. Not quite perfect yet.

This commit is contained in:
Henrik Rydgård 2018-02-07 15:52:19 +01:00
parent 7f30037e45
commit ae19c48138
8 changed files with 130 additions and 123 deletions

View file

@ -189,10 +189,7 @@ void UpdateRunLoop() {
return;
}
NativeUpdate();
if (GetUIState() != UISTATE_EXIT) {
NativeRender(graphicsContext);
}
NativeRender(graphicsContext);
}
void KeepScreenAwake() {
@ -268,6 +265,7 @@ reswitch:
if (GetUIState() != UISTATE_INGAME) {
CoreStateProcessed();
if (GetUIState() == UISTATE_EXIT) {
UpdateRunLoop();
return;
}
Core_RunLoop(ctx);

View file

@ -641,13 +641,13 @@ int main(int argc, char *argv[]) {
useEmuThread = false;
}
SDL_SetWindowTitle(window, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
// Since we render from the main thread, there's nothing done here, but we call it to avoid confusion.
if (!graphicsContext->InitFromRenderThread(&error_message)) {
printf("Init from thread error: '%s'\n", error_message.c_str());
}
SDL_SetWindowTitle(window, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
#ifdef MOBILE_DEVICE
SDL_ShowCursor(SDL_DISABLE);
#endif

View file

@ -24,102 +24,94 @@
#include "Core/Config.h"
#include "thread/threadutil.h"
static std::mutex emuThreadLock;
enum class EmuThreadState {
DISABLED,
START_REQUESTED,
RUNNING,
QUIT_REQUESTED,
STOPPED,
};
static std::thread emuThread;
static std::atomic<int> emuThreadState;
static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);
static std::mutex renderThreadLock;
static std::thread renderThread;
static std::atomic<int> renderThreadReady;
static bool useRenderThread;
static bool renderThreadFailed;
static bool renderThreadSucceeded;
static std::thread mainThread;
static bool useEmuThread;
static std::string g_error_message;
static bool g_inLoop;
extern std::vector<std::wstring> GetWideCmdLine();
class GraphicsContext;
static GraphicsContext *g_graphicsContext;
enum EmuThreadStatus : int {
THREAD_NONE = 0,
THREAD_INIT,
THREAD_CORE_LOOP,
THREAD_SHUTDOWN,
THREAD_END,
};
void EmuThreadFunc(GraphicsContext *graphicsContext);
void MainThreadFunc();
void EmuThreadFunc();
void RenderThreadFunc();
// On most other platforms, we let the main thread become the render thread and
// 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 EmuThread_Start(bool separateRenderThread) {
std::lock_guard<std::mutex> guard(emuThreadLock);
emuThread = std::thread(&EmuThreadFunc);
useRenderThread = separateRenderThread;
if (useRenderThread) {
renderThread = std::thread(&RenderThreadFunc);
}
void MainThread_Start(bool separateEmuThread) {
useEmuThread = separateEmuThread;
mainThread = std::thread(&MainThreadFunc);
}
void EmuThread_Stop() {
void MainThread_Stop() {
// Already stopped?
{
std::lock_guard<std::mutex> guard(emuThreadLock);
if (emuThreadState == THREAD_END)
return;
}
UpdateUIState(UISTATE_EXIT);
Core_Stop();
Core_WaitInactive(800);
emuThread.join();
if (useRenderThread) {
renderThread.join();
}
PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0);
mainThread.join();
}
bool EmuThread_Ready() {
return emuThreadState == THREAD_CORE_LOOP;
bool MainThread_Ready() {
return g_inLoop;
}
void RenderThreadFunc() {
setCurrentThreadName("Render");
renderThreadFailed = false;
renderThreadSucceeded = false;
while (!g_graphicsContext) {
sleep_ms(10);
continue;
}
std::string error_message;
if (!g_graphicsContext->InitFromRenderThread(&error_message)) {
g_error_message = error_message;
renderThreadFailed = true;
return;
} else {
renderThreadSucceeded = true;
}
g_graphicsContext->ThreadStart();
while (g_graphicsContext->ThreadFrame()) {
continue;
}
g_graphicsContext->ThreadEnd();
g_graphicsContext->ShutdownFromRenderThread();
}
void EmuThreadFunc() {
emuThreadState = THREAD_INIT;
static void EmuThreadFunc(GraphicsContext *graphicsContext) {
setCurrentThreadName("Emu");
// There's no real requirement that NativeInit happen on this thread.
// We just call the update/render loop here.
emuThreadState = (int)EmuThreadState::RUNNING;
NativeInitGraphics(graphicsContext);
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
// This way they can load a new game.
if (!Core_IsActive())
UpdateUIState(UISTATE_MENU);
Core_Run(g_graphicsContext);
}
emuThreadState = (int)EmuThreadState::STOPPED;
NativeShutdownGraphics();
}
static void EmuThreadStart(GraphicsContext *graphicsContext) {
emuThreadState = (int)EmuThreadState::START_REQUESTED;
emuThread = std::thread(&EmuThreadFunc, graphicsContext);
}
static void EmuThreadStop() {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
}
static void EmuThreadJoin() {
emuThread.join();
emuThread = std::thread();
ILOG("EmuThreadJoin - joined");
}
void MainThreadFunc() {
if (useEmuThread) {
// We'll start up a separate thread we'll call Emu
setCurrentThreadName("Render");
} else {
// This is both Emu and Render.
setCurrentThreadName("Emu");
}
host = new WindowsHost(MainWindow::GetHInstance(), MainWindow::GetHWND(), MainWindow::GetDisplayHWND());
host->SetWindowTitle(nullptr);
@ -142,18 +134,8 @@ void EmuThreadFunc() {
bool success = host->InitGraphics(&error_string, &g_graphicsContext);
if (success) {
if (!useRenderThread) {
// This is also the render thread.
success = g_graphicsContext->InitFromRenderThread(&error_string);
} else {
while (!renderThreadFailed && !renderThreadSucceeded) {
sleep_ms(10);
}
success = renderThreadSucceeded;
if (!success) {
error_string = g_error_message;
}
}
// Main thread is the render thread.
success = g_graphicsContext->InitFromRenderThread(&error_string);
}
if (!success) {
@ -205,8 +187,12 @@ void EmuThreadFunc() {
exit(1);
}
NativeInitGraphics(g_graphicsContext);
NativeResized();
GraphicsContext *graphicsContext = g_graphicsContext;
if (!useEmuThread) {
NativeInitGraphics(graphicsContext);
NativeResized();
}
INFO_LOG(BOOT, "Done.");
_dbg_update_();
@ -216,30 +202,59 @@ void EmuThreadFunc() {
goto shutdown;
}
emuThreadState = THREAD_CORE_LOOP;
g_inLoop = true;
if (useEmuThread) {
EmuThreadStart(graphicsContext);
}
graphicsContext->ThreadStart();
if (g_Config.bBrowse)
PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0);
Core_EnableStepping(false);
while (GetUIState() != UISTATE_EXIT) {
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
// This way they can load a new game.
if (!Core_IsActive())
UpdateUIState(UISTATE_MENU);
Core_Run(g_graphicsContext);
if (useEmuThread) {
while (emuThreadState != (int)EmuThreadState::DISABLED) {
graphicsContext->ThreadFrame();
if (GetUIState() == UISTATE_EXIT) {
break;
}
}
} else {
while (GetUIState() != UISTATE_EXIT) {
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
// This way they can load a new game.
if (!Core_IsActive())
UpdateUIState(UISTATE_MENU);
Core_Run(g_graphicsContext);
}
}
Core_Stop();
Core_WaitInactive(800);
g_inLoop = false;
if (useEmuThread) {
EmuThreadStop();
while (emuThreadState != (int)EmuThreadState::STOPPED) {
// Need to keep eating frames to allow the EmuThread to exit correctly.
graphicsContext->ThreadFrame();
}
EmuThreadJoin();
}
shutdown:
emuThreadState = THREAD_SHUTDOWN;
NativeShutdownGraphics();
if (!useRenderThread)
g_graphicsContext->ShutdownFromRenderThread();
if (!useEmuThread) {
NativeShutdownGraphics();
}
g_graphicsContext->ThreadEnd();
g_graphicsContext->ShutdownFromRenderThread();
// NativeShutdown deletes the graphics context through host->ShutdownGraphics().
NativeShutdown();
emuThreadState = THREAD_END;
}
PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0);
}

View file

@ -17,6 +17,6 @@
#pragma once
void EmuThread_Start(bool separateRenderThread);
void EmuThread_Stop();
bool EmuThread_Ready();
void MainThread_Start(bool separateRenderThread);
void MainThread_Stop();
bool MainThread_Ready();

View file

@ -386,8 +386,6 @@ void WindowsGLContext::SwapInterval(int interval) {
}
void WindowsGLContext::Shutdown() {
if (renderManager_)
renderManager_->StopThread();
glslang::FinalizeProcess();
}

View file

@ -795,7 +795,7 @@ namespace MainWindow
case WM_COMMAND:
{
if (!EmuThread_Ready())
if (!MainThread_Ready())
return DefWindowProc(hWnd, message, wParam, lParam);
MainWindowMenu_Process(hWnd, wParam);
@ -844,7 +844,7 @@ namespace MainWindow
case WM_DROPFILES:
{
if (!EmuThread_Ready())
if (!MainThread_Ready())
return DefWindowProc(hWnd, message, wParam, lParam);
HDROP hdrop = (HDROP)wParam;
@ -866,9 +866,8 @@ namespace MainWindow
case WM_CLOSE:
InputDevice::StopPolling();
EmuThread_Stop();
MainThread_Stop();
WindowsRawInput::Shutdown();
return DefWindowProc(hWnd,message,wParam,lParam);
case WM_DESTROY:
@ -914,10 +913,10 @@ namespace MainWindow
case WM_USER_RESTART_EMUTHREAD:
NativeSetRestarting();
InputDevice::StopPolling();
EmuThread_Stop();
MainThread_Stop();
coreState = CORE_POWERUP;
ResetUIState();
EmuThread_Start(false);
MainThread_Start(false);
InputDevice::BeginPolling();
break;

View file

@ -532,7 +532,7 @@ 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.
EmuThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL);
MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL);
InputDevice::BeginPolling();
HACCEL hAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_ACCELS);
@ -586,9 +586,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
InputDevice::StopPolling();
// The emuthread calls NativeShutdown when shutting down.
EmuThread_Stop();
MainWindow::DestroyDebugWindows();
DialogManager::DestroyAll();
timeEndPeriod(1);

View file

@ -217,7 +217,7 @@ static void EmuThreadStop() {
static void EmuThreadJoin() {
emuThread.join();
emuThread = std::thread();
ILOG("EmuThreadStop - joined.");
ILOG("EmuThreadJoin - joined");
}
static void ProcessFrameCommands(JNIEnv *env);