diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index e32dd6c30..64a514137 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -10,10 +10,7 @@ class GraphicsContext { public: virtual ~GraphicsContext() {} - // Threaded backends (that need to do init on the final render thread, like GL) - // call this from the render thread. Init() should block until InitFromThread is done. - // Other backends can ignore this. - virtual bool InitFromThread() { return true; } + virtual bool InitFromRenderThread(std::string *errorMessage) { return true; } virtual void Shutdown() = 0; virtual void SwapInterval(int interval) = 0; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 60bddd949..21d0653e0 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -213,7 +213,6 @@ void FramebufferManagerGLES::CompilePostShader() { postShaderProgram_ = nullptr; usePostShader_ = false; } - glsl_unbind(); } void FramebufferManagerGLES::Bind2DShader() { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 439aaf344..1575109cd 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -254,7 +254,7 @@ void EmuScreen::bootComplete() { #if !PPSSPP_PLATFORM(UWP) if (GetGPUBackend() == GPUBackend::OPENGL) { - const char *renderer = (const char*)glGetString(GL_RENDERER); + const char *renderer = gl_extensions.model; if (strstr(renderer, "Chainfire3D") != 0) { osm.Show(sc->T("Chainfire3DWarning", "WARNING: Chainfire3D detected, may cause problems"), 10.0f, 0xFF30a0FF, -1, true); } else if (strstr(renderer, "GLTools") != 0) { diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 36dd44c34..7e5ed4f48 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -33,6 +33,9 @@ static std::thread renderThread; static std::atomic renderThreadReady; static bool useRenderThread; +static bool renderThreadFailed; +static bool renderThreadSucceeded; +static std::string g_error_message; extern std::vector GetWideCmdLine(); @@ -85,15 +88,23 @@ bool EmuThread_Ready() { void RenderThreadFunc() { setCurrentThreadName("Render"); + renderThreadFailed = false; + renderThreadSucceeded = false; while (!g_graphicsContext) { - sleep_ms(50); + sleep_ms(10); continue; } - g_graphicsContext->InitFromThread(); - while (true) { - g_graphicsContext->ThreadFrame(); - break; + + std::string error_message; + if (!g_graphicsContext->InitFromRenderThread(&error_message)) { + g_error_message = error_message; + renderThreadFailed = true; + return; + } else { + renderThreadSucceeded = true; } + + g_graphicsContext->ThreadFrame(); } void EmuThreadFunc() { @@ -119,10 +130,25 @@ void EmuThreadFunc() { host->UpdateUI(); - GraphicsContext *graphicsContext = nullptr; - std::string error_string; - if (!host->InitGraphics(&error_string, &graphicsContext)) { + 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; + } + } + } + + if (!success) { // Before anything: are we restarting right now? if (performingRestart) { // Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart. @@ -171,7 +197,7 @@ void EmuThreadFunc() { exit(1); } - NativeInitGraphics(graphicsContext); + NativeInitGraphics(g_graphicsContext); NativeResized(); INFO_LOG(BOOT, "Done."); @@ -194,7 +220,7 @@ void EmuThreadFunc() { // This way they can load a new game. if (!Core_IsActive()) UpdateUIState(UISTATE_MENU); - Core_Run(graphicsContext); + Core_Run(g_graphicsContext); } shutdown: diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index b4c069224..08bcc9a5b 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -21,6 +21,7 @@ #include "gfx/gl_common.h" #include "gfx/gl_debug_log.h" #include "gfx_es2/gpu_features.h" +#include "thin3d/GLRenderManager.h" #include "GL/gl.h" #include "GL/wglew.h" #include "Core/Config.h" @@ -34,7 +35,8 @@ #include "Windows/GPU/WindowsGLContext.h" void WindowsGLContext::SwapBuffers() { - ::SwapBuffers(hDC); + renderManager_->Swap(); + // Used during fullscreen switching to prevent rendering. if (pauseRequested) { SetEvent(pauseEvent); @@ -151,10 +153,16 @@ void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity, } bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) { + hInst_ = hInst; + hWnd_ = window; + *error_message = "ok"; + return true; +} + +bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { glslang::InitializeProcess(); *error_message = "ok"; - hWnd = window; GLuint PixelFormat; // TODO: Change to use WGL_ARB_pixel_format instead @@ -179,7 +187,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes 0, 0, 0 // Layer Masks Ignored }; - hDC = GetDC(hWnd); + hDC = GetDC(hWnd_); if (!hDC) { *error_message = "Failed to get a device context."; @@ -239,7 +247,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes std::wstring title = ConvertUTF8ToWString(err->T("OpenGLDriverError", "OpenGL driver error")); std::wstring combined = versionDetected + error; - bool yes = IDYES == MessageBox(hWnd, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); + bool yes = IDYES == MessageBox(hWnd_, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); if (yes) { // Change the config to D3D and restart. @@ -362,8 +370,10 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); SetGPUBackend(GPUBackend::OPENGL); bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these. + renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); }); assert(success); CHECK_GL_ERROR_IF_DEBUG(); return true; // Success @@ -393,16 +403,20 @@ void WindowsGLContext::Shutdown() { hRC = NULL; } - if (hDC && !ReleaseDC(hWnd,hDC)) { + if (hDC && !ReleaseDC(hWnd_, hDC)) { DWORD err = GetLastError(); if (err != ERROR_DC_NOT_FOUND) { MessageBox(NULL, L"Release device context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); } hDC = NULL; } - hWnd = NULL; + hWnd_ = NULL; glslang::FinalizeProcess(); } void WindowsGLContext::Resize() { } + +void WindowsGLContext::ThreadFrame() { + renderManager_->ThreadFunc(); +} diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index 4ddd28ba6..b628ee25d 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -7,10 +7,14 @@ namespace Draw { class DrawContext; } +class GLRenderManager; + class WindowsGLContext : public WindowsGraphicsContext { public: bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; + bool InitFromRenderThread(std::string *errorMessage) override; + void Shutdown() override; void SwapInterval(int interval) override; void SwapBuffers() override; @@ -21,13 +25,18 @@ public: void Resume() override; void Resize() override; + void ThreadFrame() override; + Draw::DrawContext *GetDrawContext() override { return draw_; } private: + bool renderThread_; Draw::DrawContext *draw_; + GLRenderManager *renderManager_; + HINSTANCE hInst_; HDC hDC; // Private GDI Device Context HGLRC hRC; // Permanent Rendering Context - HWND hWnd; // Holds Our Window Handle + HWND hWnd_; // Holds Our Window Handle volatile bool pauseRequested; volatile bool resumeRequested; HANDLE pauseEvent; diff --git a/Windows/main.cpp b/Windows/main.cpp index 5bbe6ec01..66c09110b 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -530,7 +530,9 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin } // Emu thread (and render thread, if any) is always running! - EmuThread_Start(false); // g_Config.iGPUBackend == GPU_BACKEND_VULKAN); + // 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); InputDevice::BeginPolling(); HACCEL hAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_ACCELS); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 452497d3e..33d1be696 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -44,7 +44,8 @@ GLRenderManager::GLRenderManager() { } if (!useThread_) { - queueRunner_.CreateDeviceObjects(); + // The main thread is also the render thread. + ThreadStartup(); } } @@ -54,16 +55,22 @@ GLRenderManager::~GLRenderManager() { } if (!useThread_) { - queueRunner_.DestroyDeviceObjects(); + // The main thread is also the render thread. + ThreadEnd(); } } -void GLRenderManager::ThreadFunc() { - setCurrentThreadName("RenderMan"); - int threadFrame = threadInitFrame_; - bool nextFrame = false; - bool firstFrame = true; +void GLRenderManager::ThreadStartup() { queueRunner_.CreateDeviceObjects(); +} + +void GLRenderManager::ThreadEnd() { + queueRunner_.DestroyDeviceObjects(); +} + +void GLRenderManager::ThreadFunc() { + ThreadStartup(); + int threadFrame = threadInitFrame_; while (true) { { if (nextFrame) { @@ -344,7 +351,9 @@ void GLRenderManager::EndSubmitFrame(int frame) { Submit(frame, true); if (!frameData.skipSwap) { - // glSwapBuffers(); + if (swapFunction_) { + swapFunction_(); + } } else { frameData.skipSwap = false; } @@ -540,4 +549,4 @@ size_t GLPushBuffer::GetTotalSize() const { sum += size_ * (buffers_.size() - 1); sum += offset_; return sum; -} \ No newline at end of file +} diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c351ed307..d6716f626 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -622,7 +623,20 @@ public: frameData_[frame].activePushBuffers.erase(iter); } + void SetSwapFunction(std::function swapFunction) { + swapFunction_ = swapFunction; + } + + void Swap() { + if (!useThread_ && swapFunction_) { + swapFunction_(); + } + } + private: + void ThreadStartup(); + void ThreadEnd(); + void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); void Submit(int frame, bool triggerFence); @@ -668,17 +682,22 @@ private: // Execution time state bool run_ = true; - // Thread is managed elsewhere, and should call ThreadFunc. + // Thread is managed elsewhere, and should call ThreadFrame. std::mutex mutex_; int threadInitFrame_ = 0; GLQueueRunner queueRunner_; + bool nextFrame = false; + bool firstFrame = true; + GLDeleter deleter_; - bool useThread_ = false; + bool useThread_ = true; int curFrame_ = 0; + std::function swapFunction_; + int targetWidth_ = 0; int targetHeight_ = 0; };