mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-23 16:19:44 +00:00
OpenGL: Now run GL on a secondary thread. Sync issues remain.
This commit is contained in:
parent
e4752a887f
commit
af6431986d
@ -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;
|
||||
|
@ -213,7 +213,6 @@ void FramebufferManagerGLES::CompilePostShader() {
|
||||
postShaderProgram_ = nullptr;
|
||||
usePostShader_ = false;
|
||||
}
|
||||
glsl_unbind();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::Bind2DShader() {
|
||||
|
@ -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) {
|
||||
|
@ -33,6 +33,9 @@ static std::thread renderThread;
|
||||
static std::atomic<int> renderThreadReady;
|
||||
|
||||
static bool useRenderThread;
|
||||
static bool renderThreadFailed;
|
||||
static bool renderThreadSucceeded;
|
||||
static std::string g_error_message;
|
||||
|
||||
extern std::vector<std::wstring> 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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
@ -622,7 +623,20 @@ public:
|
||||
frameData_[frame].activePushBuffers.erase(iter);
|
||||
}
|
||||
|
||||
void SetSwapFunction(std::function<void()> 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<void()> swapFunction_;
|
||||
|
||||
int targetWidth_ = 0;
|
||||
int targetHeight_ = 0;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user