mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Cleanup the Windows thread stuff to work like the other platforms. Not quite perfect yet.
This commit is contained in:
parent
7f30037e45
commit
ae19c48138
8 changed files with 130 additions and 123 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -386,8 +386,6 @@ void WindowsGLContext::SwapInterval(int interval) {
|
|||
}
|
||||
|
||||
void WindowsGLContext::Shutdown() {
|
||||
if (renderManager_)
|
||||
renderManager_->StopThread();
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue