diff --git a/CMakeLists.txt b/CMakeLists.txt index dd029a30b4..a9a0207f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1678,7 +1678,6 @@ if(IOS AND NOT LIBRETRO) XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES" XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT[variant=Release] "YES" - #XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${BUNDLE_ENTITLEMENTS} XCODE_ATTRIBUTE_SKIP_INSTALL NO XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)" ) diff --git a/Common/GPU/MiscTypes.h b/Common/GPU/MiscTypes.h index 7e1663c53e..f61467cd09 100644 --- a/Common/GPU/MiscTypes.h +++ b/Common/GPU/MiscTypes.h @@ -5,6 +5,21 @@ #include #include + +// Not all will be usable on all platforms, of course... +enum WindowSystem { + WINDOWSYSTEM_UNINITIALIZED, + WINDOWSYSTEM_WIN32, + WINDOWSYSTEM_ANDROID, + WINDOWSYSTEM_METAL_EXT, + WINDOWSYSTEM_XLIB, + WINDOWSYSTEM_XCB, + WINDOWSYSTEM_WAYLAND, + WINDOWSYSTEM_DISPLAY, + WINDOWSYSTEM_SDL, + WINDOWSYSTEM_NONE, +}; + // Flags and structs shared between backends that haven't found a good home. enum class InvalidationFlags { diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 46778e47f2..2d790d008b 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -9,6 +9,7 @@ #include "Common/System/Display.h" #include "Common/Log.h" #include "Common/GPU/Shader.h" +#include "Common/GPU/MiscTypes.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanDebug.h" #include "Common/StringUtils.h" @@ -915,7 +916,7 @@ VkResult VulkanContext::InitDebugUtilsCallback() { return res; } -bool VulkanContext::CreateInstanceAndDevice(const CreateInfo &info) { +bool VulkanContext::CreateInstanceAndDevice(const CreateInfo &info, std::string *deviceName) { VkResult res = CreateInstance(info); if (res != VK_SUCCESS) { ERROR_LOG(Log::G3D, "Failed to create vulkan context: %s", InitError().c_str()); @@ -923,6 +924,8 @@ bool VulkanContext::CreateInstanceAndDevice(const CreateInfo &info) { return false; } + // TODO: Take the device name into account. + int physicalDevice = GetBestPhysicalDevice(); if (physicalDevice < 0) { ERROR_LOG(Log::G3D, "No usable Vulkan device found."); @@ -977,8 +980,8 @@ VkResult VulkanContext::ReinitSurface() { { VkWin32SurfaceCreateInfoKHR win32{ VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR }; win32.flags = 0; - win32.hwnd = (HWND)winsysData2_; win32.hinstance = (HINSTANCE)winsysData1_; + win32.hwnd = (HWND)winsysData2_; retval = vkCreateWin32SurfaceKHR(instance_, &win32, nullptr, &surface_); break; } diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index 82dd15540f..34bdb3adc0 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -10,6 +10,7 @@ #include "Common/Common.h" #include "Common/Log.h" +#include "Common/GPU/MiscTypes.h" #include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/GPU/Vulkan/VulkanDebug.h" #include "Common/GPU/Vulkan/VulkanAlloc.h" @@ -50,32 +51,6 @@ template inline void ChainStruct(R &root, T *newStruct) { root.pNext = newStruct; } -// Not all will be usable on all platforms, of course... -enum WindowSystem { - WINDOWSYSTEM_UNINITIALIZED, -#ifdef _WIN32 - WINDOWSYSTEM_WIN32, -#endif -#ifdef __ANDROID__ - WINDOWSYSTEM_ANDROID, -#endif -#ifdef VK_USE_PLATFORM_METAL_EXT - WINDOWSYSTEM_METAL_EXT, -#endif -#ifdef VK_USE_PLATFORM_XLIB_KHR - WINDOWSYSTEM_XLIB, -#endif -#ifdef VK_USE_PLATFORM_XCB_KHR - WINDOWSYSTEM_XCB, -#endif -#ifdef VK_USE_PLATFORM_WAYLAND_KHR - WINDOWSYSTEM_WAYLAND, -#endif -#ifdef VK_USE_PLATFORM_DISPLAY_KHR - WINDOWSYSTEM_DISPLAY, -#endif -}; - struct VulkanPhysicalDeviceInfo { VkFormat preferredDepthStencilFormat; bool canBlitToPreferredDepthStencilFormat; @@ -194,7 +169,7 @@ public: // Convenience method to avoid code duplication. // If it returns false, delete the context. - bool CreateInstanceAndDevice(const CreateInfo &info); + bool CreateInstanceAndDevice(const CreateInfo &info, std::string *deviceName); // The coreVersion is to avoid enabling extensions that are merged into core Vulkan from a certain version. bool EnableInstanceExtension(const char *extension, uint32_t coreVersion); diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index 4a9c7c33cd..e67f85cd64 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -12,14 +12,16 @@ class GraphicsContext { public: virtual ~GraphicsContext() = default; - // These should be no-ops on backends that manage their own render thread. - virtual bool InitFromRenderThread(std::string *errorMessage) { return true; } - virtual void ShutdownFromRenderThread() {} + // Some implementations will separate init of the API and of the rendering surface. + // wnd here is only to be used for messageboxes and similar, not for the surface. + virtual bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { return true; } + virtual void ShutdownAPI() {} - virtual void BeginShutdown() {} // This is currently only used on Android. - virtual void Shutdown() = 0; + // These should be called on the render thread if NeedsRenderThread. + virtual bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { return true; } + virtual void ShutdownSurface() {} - // Used during window resize. Must be called from the window thread, + // Used during window resize on desktop ONLY. Must be called from the window thread, // not the rendering thread or CPU thread. virtual void Pause() {} virtual void Resume() {} @@ -30,8 +32,8 @@ public: // Needs casting to the appropriate type, unfortunately. Should find a better solution.. virtual void *GetAPIContext() { return nullptr; } - // OpenGL returns true here. - virtual bool NeedsRenderThread() const { return false; } + // OpenGL returns true here. Later we will need to do the same with Vulkan. + virtual bool NeedsSeparateEmuThread() const { return false; } // Called from the render thread from threaded backends. virtual void ThreadStart() {} @@ -42,11 +44,12 @@ public: // Should strive to get rid of these. virtual void Poll() {} + // TODO: Store in a protected variable? virtual Draw::DrawContext *GetDrawContext() = 0; void ThreadFrameUntilCondition(std::function conditionStopped) { - _dbg_assert_(NeedsRenderThread()); - + _dbg_assert_(NeedsSeparateEmuThread()); + BeginShutdownSurface(); bool exitOnEmpty = false; INFO_LOG(Log::System, "Executing graphicsContext->ThreadFrame to clear buffers"); @@ -58,7 +61,7 @@ public: exitOnEmpty = true; } - bool retval = ThreadFrame(false); + const bool retval = ThreadFrame(false); if (!retval && exitOnEmpty) { INFO_LOG(Log::System, "ThreadFrame returned false and emu thread is done, breaking."); break; @@ -67,4 +70,7 @@ public: } } } + +protected: + virtual void BeginShutdownSurface() {} // This is currently only used on Android. }; diff --git a/Common/System/Application.h b/Common/System/Application.h index 75afe29a5f..e5097af80a 100644 --- a/Common/System/Application.h +++ b/Common/System/Application.h @@ -1,9 +1,16 @@ #pragma once +#include "Common/GPU/MiscTypes.h" + class GraphicsContext; class Application { public: + WindowSystem windowSystem; + void *windowData1 = nullptr; + void *windowData2 = nullptr; // This should be the window, if there is one. + + void *Window() const { return windowData2; } virtual ~Application() = default; virtual bool InitGraphics(GraphicsContext *graphicsContext) = 0; virtual void ShutdownGraphics(GraphicsContext *graphicsContext) = 0; diff --git a/Common/System/NativeApp.h b/Common/System/NativeApp.h index 6124978766..a87e6d8913 100644 --- a/Common/System/NativeApp.h +++ b/Common/System/NativeApp.h @@ -5,6 +5,7 @@ #include "Common/Common.h" #include "Common/System/Application.h" +#include "Common/GPU/MiscTypes.h" // The Native App API. // @@ -59,12 +60,6 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext); // and only write dp_xres and dp_yres. void NativeResized(); -// Set a flag to indicate a restart. Reset after NativeInit(). -void NativeSetRestarting(); - -// Retrieve current restarting flag. -bool NativeIsRestarting(); - // Delivers touch/key/axis events "instantly", without waiting for the next frame so that NativeFrame can deliver. // Some systems like UI will buffer these events internally but at least in gameplay we can get the minimum possible // input latency - assuming your main loop is architected properly (NativeFrame called from a different thread than input event handling). @@ -116,14 +111,13 @@ AudioBackend *System_CreateAudioBackend(); class NativeApplication : public Application { public: - NativeApplication() {} bool InitGraphics(GraphicsContext *graphicsContext) override { return NativeInitGraphics(graphicsContext); } void ShutdownGraphics(GraphicsContext *graphicsContext) override { - return NativeShutdownGraphics(graphicsContext); + NativeShutdownGraphics(graphicsContext); } void Frame(GraphicsContext *graphicsContext) override { - return NativeFrame(graphicsContext); + NativeFrame(graphicsContext); } }; diff --git a/Core/EmuThread.cpp b/Core/EmuThread.cpp index 6511340810..6aaadbd74f 100644 --- a/Core/EmuThread.cpp +++ b/Core/EmuThread.cpp @@ -34,14 +34,12 @@ static std::atomic g_inLoop; class GraphicsContext; -void MainThreadFunc(GraphicsContext *graphicsContext); - bool MainThread_Ready() { return g_inLoop; } static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *application, std::function postFrame) { - INFO_LOG(Log::G3D, "Entering emu thread"); + INFO_LOG(Log::G3D, "Entering separate emu thread"); SetCurrentThreadName("EmuThread"); AndroidJNIThreadContext context; @@ -65,7 +63,6 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *applica if (postFrame) { postFrame(); } - if (GetUIState() == UISTATE_EXIT) { g_emuThreadState = EmuThreadState::QUIT_REQUESTED; } @@ -76,7 +73,7 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *applica g_emuThreadState = EmuThreadState::STOPPED; application->ShutdownGraphics(graphicsContext); - INFO_LOG(Log::System, "Leaving emu thread"); + INFO_LOG(Log::System, "Leaving separate emu thread"); } std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *application, std::function postFrame) { @@ -92,7 +89,7 @@ void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread) { g_emuThreadState = EmuThreadState::QUIT_REQUESTED; } _dbg_assert_(emuThread.joinable()); - if (graphicsContext->NeedsRenderThread()) { + if (graphicsContext->NeedsSeparateEmuThread()) { graphicsContext->ThreadFrameUntilCondition([] { // Need to keep eating frames to allow the EmuThread to exit correctly. return g_emuThreadState == EmuThreadState::STOPPED; @@ -102,18 +99,22 @@ void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread) { emuThread = std::thread(); } -void MainThreadFunc(GraphicsContext *graphicsContext, Application *application, std::function postFrame) { - if (graphicsContext->NeedsRenderThread()) { - SetCurrentThreadName("RenderThread"); - // This is now the render thread, and will spawn the emu thread below. +// Call InitAPI and ShutdownAPI outside this! +bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function postFrame) { + // This is now the render thread, and will spawn the emu thread below. + std::string error_string; + bool success = graphicsContext->InitSurface(windowSystem, windowData1, windowData2, &error_string); + if (!success) { + return false; + } - std::string error_string; - bool success = graphicsContext->InitFromRenderThread(&error_string); + std::string errorMessage; + if (graphicsContext->NeedsSeparateEmuThread()) { + SetCurrentThreadName("RenderThread"); DEBUG_LOG(Log::Boot, "Done."); g_inLoop = true; - std::thread emuThread = EmuThread_Start(graphicsContext, application, postFrame); graphicsContext->ThreadStart(); @@ -133,16 +134,16 @@ void MainThreadFunc(GraphicsContext *graphicsContext, Application *application, EmuThread_Join(graphicsContext, emuThread); graphicsContext->ThreadEnd(); - graphicsContext->ShutdownFromRenderThread(); - INFO_LOG(Log::System, "EmuThreadJoin - joined"); + INFO_LOG(Log::System, "RenderThread - joined"); } else { - SetCurrentThreadName("EmuThread"); - // This is the emu thread. the graphics contexts will spawn and handle its own threads if needed. + SetCurrentThreadName("MainThread"); + + // This is the main thread. the graphics contexts will spawn and handle its own threads if needed. + // InitFromRenderThread/ShutdownFromRenderThread are not used. std::string error_string; - bool success = graphicsContext->InitFromRenderThread(&error_string); application->InitGraphics(graphicsContext); // NativeResized(); @@ -169,8 +170,8 @@ void MainThreadFunc(GraphicsContext *graphicsContext, Application *application, application->ShutdownGraphics(graphicsContext); graphicsContext->ThreadEnd(); - graphicsContext->ShutdownFromRenderThread(); } - graphicsContext->Shutdown(); + graphicsContext->ShutdownSurface(); + return true; } diff --git a/Core/EmuThread.h b/Core/EmuThread.h index c34a11de23..b887ffd2e0 100644 --- a/Core/EmuThread.h +++ b/Core/EmuThread.h @@ -30,7 +30,7 @@ class GraphicsContext; // Doesn't take ownership of the graphicsContext, you have to delete it. // This should be used by platforms that launch a separate thread and doesn't // need to run a polling loop in it. -void MainThreadFunc(GraphicsContext * graphicsContext, Application *application, std::function postFrame); +bool MainThreadFunc(GraphicsContext * graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function postFrame); // If you're not using MainThreadFunc, you can at least use these to manage a spinning EmuThread (that calls NativeFrame), // whether your graphics context requires multithreading or not. diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 0fa59d8e75..6443cfc6aa 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -722,7 +722,8 @@ MainUI::~MainUI() { #if defined(MOBILE_DEVICE) delete acc; #endif - graphicsContext->Shutdown(); + graphicsContext->ShutdownSurface(); + graphicsContext->ShutdownAPI(); delete graphicsContext; graphicsContext = nullptr; } @@ -906,6 +907,9 @@ void MainUI::initializeGL() { // OpenGL uses a background thread to do the main processing and only renders on the gl thread. INFO_LOG(Log::System, "Initializing GL graphics context"); graphicsContext = new QtGLGraphicsContext(); + std::string errorMessage; + graphicsContext->InitAPI(nullptr, nullptr, &errorMessage); + graphicsContext->InitSurface(WINDOWSYSTEM_NONE, nullptr, nullptr, &errorMessage); INFO_LOG(Log::System, "Using thread, starting emu thread"); EmuThreadStart(); } else { @@ -919,11 +923,11 @@ void MainUI::paintGL() { SDL_PumpEvents(); #endif updateAccelerometer(); - if (emuThreadState == (int)EmuThreadState::DISABLED) { - NativeFrame(graphicsContext); - } else { + if (graphicsContext->NeedsSeparateEmuThread()) { graphicsContext->ThreadFrame(true); // Do the rest in EmuThreadFunc + } else { + NativeFrame(graphicsContext); } } @@ -982,7 +986,6 @@ void MainAudio::timerEvent(QTimerEvent *) { #endif - void QTCamera::startCamera(int width, int height) { __qt_startCapture(width, height); } diff --git a/Qt/QtMain.h b/Qt/QtMain.h index 3f80b5c82d..be04db87bf 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -47,27 +47,30 @@ void SimulateGamepad(); class QtGLGraphicsContext : public GraphicsContext { public: - QtGLGraphicsContext() { + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override { CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(false); SetGPUBackend(GPUBackend::OPENGL); + // Not used in this context. + return true; + } + + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) override { + // Not used in this context. + draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); bool success = draw_->CreatePresets(); _assert_msg_(success, "Failed to compile preset shaders"); - - // TODO: Need to figure out how to implement SetSwapInterval for Qt. + return true; } - ~QtGLGraphicsContext() { + bool NeedsSeparateEmuThread() const override { return true; } + + void ShutdownSurface() override { delete draw_; draw_ = nullptr; renderManager_ = nullptr; } - - bool NeedsRenderThread() const override { return true; } - - void Shutdown() override {} void Resize() override {} Draw::DrawContext *GetDrawContext() override { diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index ad638400cf..da4ca4e2c1 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -286,20 +286,15 @@ void EGL_Close() { #endif // USING_EGL -bool SDLGLGraphicsContext::InitFromRenderThread(std::string *errorMessage) { - bool retval = GraphicsContext::InitFromRenderThread(errorMessage); - // HACK: Ensure that the swap interval is set after context creation (needed for kmsdrm) - SDL_GL_SetSwapInterval(1); - return retval; -} - -// Returns 0 on success. -int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message, int force_gl_version) { +// TODO: All this stuff doesn't really belong here. +bool SDLGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) { + SDL_Window *window = *(SDL_Window **)data1; + int mode = (int)(uintptr_t)data2; struct GLVersionPair { int major; int minor; }; - GLVersionPair attemptVersions[] = { + static const GLVersionPair attemptVersions[] = { #ifdef USING_GLES2 {3, 2}, {3, 1}, {3, 0}, {2, 0}, #else @@ -312,14 +307,20 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, // On Mac, full screen animates so each attempt is slow. mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; - SDL_GLContext glContext = nullptr; + int x; + int y; + int w; + int h; + SDL_GetWindowPosition(window, &x, &y); + SDL_GetWindowSize(window, &w, &h); + glContext_ = nullptr; for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) { const auto &ver = attemptVersions[i]; // If we force a specific OpenGL version, skip the ones // that do not match, which may be all of them - e.g. // requesting nonsensical "--graphics=opengl0" reliably // skips straight to fallback code below. - if (force_gl_version >= 0 && 10 * ver.major + ver.minor != force_gl_version) + if (force_gl_version_ >= 0 && 10 * ver.major + ver.minor != force_gl_version_) continue; // Make sure to request a somewhat modern GL context at least - the // latest supported by MacOS X (really, really sad...) @@ -332,8 +333,9 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SetGLCoreContext(true); #endif - - window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode); + if (!window) { + window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode); + } if (!window) { // Definitely don't shutdown here: we'll keep trying more GL versions. fprintf(stderr, "SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError()); @@ -345,8 +347,8 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, SDL_SetWindowPosition(window, x, y); } - glContext = SDL_GL_CreateContext(window); - if (glContext != nullptr) { + glContext_ = SDL_GL_CreateContext(window); + if (glContext_ != nullptr) { // Victory, got one. break; } @@ -356,7 +358,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, SDL_DestroyWindow(window); } - if (glContext == nullptr) { + if (glContext_ == nullptr) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); @@ -367,20 +369,20 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, NativeShutdown(); fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); SDL_Quit(); - return 2; + return false; } if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) { SDL_SetWindowPosition(window, x, y); } - glContext = SDL_GL_CreateContext(window); - if (glContext == nullptr) { + glContext_ = SDL_GL_CreateContext(window); + if (glContext_ == nullptr) { // OK, now we really have tried everything. NativeShutdown(); fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); SDL_Quit(); - return 2; + return false; } } @@ -407,7 +409,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, // glx is not required, igore. if (glew_err != GLEW_OK && glew_err != GLEW_ERROR_NO_GLX_DISPLAY) { fprintf(stderr, "Failed to initialize glew!\n"); - return 1; + return false; } // Unfortunately, glew will generate an invalid enum error, ignore. if (gl_extensions.IsCoreContext) @@ -417,7 +419,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, fprintf(stderr, "OpenGL 2.0 or higher.\n"); } else { fprintf(stderr, "Sorry, this program requires OpenGL 2.0.\n"); - return 1; + return false; } #endif @@ -446,18 +448,28 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, }); window_ = window; - return 0; + + // HACK: Ensure that the swap interval is set after context creation (needed for kmsdrm) + SDL_GL_SetSwapInterval(1); + + return true; } -void SDLGLGraphicsContext::ShutdownFromRenderThread() { +void SDLGLGraphicsContext::ShutdownSurface() { delete draw_; draw_ = nullptr; renderManager_ = nullptr; +} +bool SDLGLGraphicsContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { + return true; +} + +void SDLGLGraphicsContext::ShutdownAPI() { #ifdef USING_EGL EGL_Close(); #endif - SDL_GL_DestroyContext(glContext); - glContext = nullptr; + SDL_GL_DestroyContext(glContext_); + glContext_ = nullptr; window_ = nullptr; } diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index b789976c3d..e08289c2b6 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -9,15 +9,17 @@ class SDLGLGraphicsContext : public GraphicsContext { public: + SDLGLGraphicsContext(int force_gl_version) : force_gl_version_(force_gl_version) {} + + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + void ShutdownAPI() override; + // Returns 0 on success. - int Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message, int force_gl_version); + // data1 should be a *pointer* to the SDL_Window pointer, data2 is the SDL_WindowFlags. + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; + void ShutdownSurface() override; - bool NeedsRenderThread() const override { return true; } - - bool InitFromRenderThread(std::string *errorMessage) override; - - void Shutdown() override {} - void ShutdownFromRenderThread() override; + bool NeedsSeparateEmuThread() const override { return true; } void Resize() override {} @@ -40,6 +42,7 @@ public: private: Draw::DrawContext *draw_ = nullptr; SDL_Window *window_ = nullptr; - SDL_GLContext glContext = nullptr; + SDL_GLContext glContext_ = nullptr; + int force_gl_version_ = 0; GLRenderManager *renderManager_ = nullptr; }; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 6362cca242..b82be099b6 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -912,7 +912,7 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string return true; } case SystemRequestType::SET_KEEP_SCREEN_BRIGHT: - INFO_LOG(Log::UI, "SET_KEEP_SCREEN_BRIGHT not implemented."); + VERBOSE_LOG(Log::UI, "SET_KEEP_SCREEN_BRIGHT not implemented."); return true; default: INFO_LOG(Log::UI, "Unhandled system request %s", RequestTypeAsString(type)); @@ -1989,10 +1989,21 @@ int main(int argc, char *argv[]) { g_Config.iGPUBackend = (int)GPUBackend::OPENGL; } + window = SDL_CreateWindow("Initializing graphics...", w, h, (SDL_WindowFlags)mode); + std::string error_message; if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { - SDLGLGraphicsContext *glctx = new SDLGLGraphicsContext(); - if (glctx->Init(window, x, y, w, h, mode, &error_message, cmdLineOptions.force_gl_version) != 0) { + SDLGLGraphicsContext *glctx = new SDLGLGraphicsContext(cmdLineOptions.force_gl_version); + if (!window) { + fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); + exit(1); + } + if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) { + SDL_SetWindowPosition(window, x, y); + } + + glctx->InitAPI(nullptr, nullptr, &error_message); + if (!glctx->InitSurface(WINDOWSYSTEM_NONE, &window, (void *)(uintptr_t)mode, &error_message)) { // Let's try the fallback once per process run. fprintf(stderr, "GL init error '%s' - falling back to Vulkan\n", error_message.c_str()); g_Config.iGPUBackend = (int)GPUBackend::VULKAN; @@ -2001,7 +2012,8 @@ int main(int argc, char *argv[]) { // NOTE : This should match the lines below in the Vulkan case. SDLVulkanGraphicsContext *vkctx = new SDLVulkanGraphicsContext(); - if (!vkctx->Init(window, x, y, w, h, mode | SDL_WINDOW_VULKAN, &error_message)) { + vkctx->InitAPI(nullptr, &g_Config.sVulkanDevice, &error_message); + if (!vkctx->InitSurface(WINDOWSYSTEM_NONE, &window, nullptr, &error_message)) { fprintf(stderr, "Vulkan fallback failed: %s\n", error_message.c_str()); return 1; } @@ -2012,7 +2024,8 @@ int main(int argc, char *argv[]) { #if !PPSSPP_PLATFORM(SWITCH) } else if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { SDLVulkanGraphicsContext *vkctx = new SDLVulkanGraphicsContext(); - if (!vkctx->Init(window, x, y, w, h, mode | SDL_WINDOW_VULKAN, &error_message)) { + vkctx->InitAPI(nullptr, &g_Config.sVulkanDevice, &error_message); + if (!vkctx->InitSurface(WINDOWSYSTEM_NONE, &window, nullptr, &error_message)) { // Let's try the fallback once per process run. fprintf(stderr, "Vulkan init error '%s' - falling back to GL\n", error_message.c_str()); @@ -2021,8 +2034,9 @@ int main(int argc, char *argv[]) { delete vkctx; // NOTE : This should match the three lines above in the OpenGL case. - SDLGLGraphicsContext *glctx = new SDLGLGraphicsContext(); - if (glctx->Init(window, x, y, w, h, mode, &error_message, cmdLineOptions.force_gl_version) != 0) { + SDLGLGraphicsContext *glctx = new SDLGLGraphicsContext(cmdLineOptions.force_gl_version); + glctx->InitAPI(nullptr, nullptr, &error_message); + if (!glctx->InitSurface(WINDOWSYSTEM_NONE, &window, (void *)(uintptr_t)mode, &error_message)) { fprintf(stderr, "GL fallback failed: %s\n", error_message.c_str()); return 1; } @@ -2068,12 +2082,6 @@ int main(int argc, char *argv[]) { imageData = NULL; } - // Since we render from the main thread, there's nothing done here, but we call it to avoid confusion. - if (!graphicsContext->InitFromRenderThread(&error_message)) { - fprintf(stderr, "Init from thread error: '%s'\n", error_message.c_str()); - return 1; - } - // OK, we have a valid graphics backend selected. Let's clear the failures. g_Config.sFailedGPUBackends.clear(); @@ -2097,6 +2105,8 @@ int main(int argc, char *argv[]) { EnableFZ(); NativeApplication application; + + // We use the emuthread both for OpenGL and Vulkan, but in OpenGL mode we also render from the main thread. std::thread emuThread = EmuThread_Start(graphicsContext, &application, nullptr); graphicsContext->ThreadStart(); @@ -2116,7 +2126,7 @@ int main(int argc, char *argv[]) { } } - const bool mainThreadIsRender = graphicsContext->NeedsRenderThread(); + const bool mainThreadIsRender = graphicsContext->NeedsSeparateEmuThread(); if (!mainThreadIsRender) { // Vulkan mode uses this. // We should only be a message pump. This allows for lower latency @@ -2164,7 +2174,7 @@ int main(int argc, char *argv[]) { inputTracker.MouseCaptureControl(window); bool renderThreadPaused = Native_IsWindowHidden() && g_Config.bPauseWhenMinimized; - if (graphicsContext->NeedsRenderThread() && !renderThreadPaused) { + if (graphicsContext->NeedsSeparateEmuThread() && !renderThreadPaused) { if (!graphicsContext->ThreadFrame(true)) break; } @@ -2181,22 +2191,17 @@ int main(int argc, char *argv[]) { g_rebootEmuThread = false; EmuThread_Join(graphicsContext, emuThread); graphicsContext->ThreadEnd(); - graphicsContext->ShutdownFromRenderThread(); + graphicsContext->ShutdownSurface(); fprintf(stderr, "OK, shutdown complete. starting up graphics again.\n"); if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { SDLGLGraphicsContext *ctx = (SDLGLGraphicsContext *)graphicsContext; - if (!ctx->Init(window, x, y, w, h, mode, &error_message, cmdLineOptions.force_gl_version)) { + if (!ctx->InitSurface(WINDOWSYSTEM_SDL, &window, nullptr, &error_message)) { fprintf(stderr, "Failed to reinit graphics.\n"); } } - if (!graphicsContext->InitFromRenderThread(&error_message)) { - System_Toast("Graphics initialization failed. Quitting."); - return 1; - } - emuThread = EmuThread_Start(graphicsContext, &application, nullptr); graphicsContext->ThreadStart(); } @@ -2206,14 +2211,16 @@ int main(int argc, char *argv[]) { delete joystick; + // TODO: This seems redundant. graphicsContext->ThreadEnd(); + // Destroys Draw, which is used in NativeShutdown to shutdown. + graphicsContext->ShutdownSurface(); + graphicsContext->ShutdownAPI(); + delete graphicsContext; + NativeShutdown(); - // Destroys Draw, which is used in NativeShutdown to shutdown. - graphicsContext->ShutdownFromRenderThread(); - graphicsContext->Shutdown(); - delete graphicsContext; for (int i = 0; i < SDL_SYSTEM_CURSOR_COUNT; ++i) { if (g_builtinCursors[i]) { diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index 447f79caca..18a5c5f91f 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -24,28 +24,13 @@ static const bool g_Validate = true; static const bool g_Validate = false; #endif -bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message) { - window = SDL_CreateWindow("Initializing Vulkan...", w, h, (SDL_WindowFlags)mode); - if (!window) { - fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); - exit(1); - } - if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) { - SDL_SetWindowPosition(window, x, y); - } - +bool SDLVulkanGraphicsContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { init_glslang(); - - g_LogOptions.breakOnError = true; - g_LogOptions.breakOnWarning = true; - g_LogOptions.msgBoxOnError = false; - - Version gitVer(PPSSPP_GIT_VERSION); + errorMessage->clear(); std::string errorStr; if (!VulkanLoad(&errorStr)) { - *error_message = "Failed to load Vulkan driver library: "; - (*error_message) += errorStr; + *errorMessage = "Failed to load Vulkan driver library: " + errorStr; return false; } @@ -54,26 +39,37 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in VulkanContext::CreateInfo info{}; InitVulkanCreateInfoFromConfig(&info); if (VK_SUCCESS != vulkan_->CreateInstance(info)) { - *error_message = vulkan_->InitError(); + *errorMessage = vulkan_->InitError(); delete vulkan_; vulkan_ = nullptr; return false; } - int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice); + int deviceNum = vulkan_->GetPhysicalDeviceByName(*deviceName); if (deviceNum < 0) { deviceNum = vulkan_->GetBestPhysicalDevice(); - if (!g_Config.sVulkanDevice.empty()) - g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName; + if (!deviceName->empty()) { + *deviceName = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName; + } } if (vulkan_->CreateDevice(deviceNum) != VK_SUCCESS) { - *error_message = vulkan_->InitError(); + *errorMessage = vulkan_->InitError(); delete vulkan_; vulkan_ = nullptr; return false; } + return true; +} + +bool SDLVulkanGraphicsContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { + SDL_Window *window = *(SDL_Window **)data1; + errorMessage->clear(); + + _dbg_assert_(window); + _dbg_assert_(vulkan_); + vulkan_->SetCbGetDrawSize([window]() { int w=1,h=1; SDL_GetWindowSizeInPixels(window, &w, &h); @@ -106,19 +102,19 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in #if defined(VK_USE_PLATFORM_METAL_EXT) #if PPSSPP_PLATFORM(MAC) if (!surfaceInitialized) { - void *cocoaWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); - if (cocoaWindow != nullptr) { - vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(cocoaWindow), nullptr); - surfaceInitialized = true; - } + void *cocoaWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); + if (cocoaWindow != nullptr) { + vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(cocoaWindow), nullptr); + surfaceInitialized = true; + } } #else if (!surfaceInitialized) { - void *uikitWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr); - if (uikitWindow != nullptr) { - vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(uikitWindow), nullptr); - surfaceInitialized = true; - } + void *uikitWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr); + if (uikitWindow != nullptr) { + vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(uikitWindow), nullptr); + surfaceInitialized = true; + } } #endif #endif @@ -127,19 +123,18 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in exit(1); } + VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); + if (!vulkan_->InitSwapchain(presentMode)) { + *errorMessage = vulkan_->InitError(); + return false; + } + bool useMultiThreading = g_Config.bRenderMultiThreading; if (g_Config.iInflightFrames == 1) { useMultiThreading = false; } draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading); - VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); - if (!vulkan_->InitSwapchain(presentMode)) { - *error_message = vulkan_->InitError(); - Shutdown(); - return false; - } - SetGPUBackend(GPUBackend::VULKAN); bool success = draw_->CreatePresets(); _assert_(success); @@ -150,7 +145,7 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in return true; } -void SDLVulkanGraphicsContext::Shutdown() { +void SDLVulkanGraphicsContext::ShutdownSurface() { if (draw_) draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); delete draw_; @@ -158,6 +153,10 @@ void SDLVulkanGraphicsContext::Shutdown() { vulkan_->WaitUntilQueueIdle(); vulkan_->DestroySwapchain(); vulkan_->DestroySurface(); +} + +void SDLVulkanGraphicsContext::ShutdownAPI() { + _dbg_assert_(!draw_); vulkan_->DestroyDevice(); vulkan_->DestroyInstance(); delete vulkan_; diff --git a/SDL/SDLVulkanGraphicsContext.h b/SDL/SDLVulkanGraphicsContext.h index a8af5e017d..4cb179a22b 100644 --- a/SDL/SDLVulkanGraphicsContext.h +++ b/SDL/SDLVulkanGraphicsContext.h @@ -12,13 +12,12 @@ class VulkanRenderManager; class SDLVulkanGraphicsContext : public GraphicsContext { public: SDLVulkanGraphicsContext() {} - ~SDLVulkanGraphicsContext() { - delete draw_; - } - bool Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message); + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; - void Shutdown() override; + void ShutdownSurface() override; + void ShutdownAPI() override; void Resize() override; diff --git a/UI/Background.cpp b/UI/Background.cpp index 73be05b0b3..df985bc629 100644 --- a/UI/Background.cpp +++ b/UI/Background.cpp @@ -151,6 +151,7 @@ private: void Regenerate(int xres, int yres) { int count = xres * yres / (120.0f * 120.0f); + count = std::clamp(count, 10, 1000); base.resize(count); GMRng rng; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c2fa524e8e..4c38058278 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -177,7 +177,6 @@ std::string config_filename; // Really need to clean this mess of globals up... but instead I add more :P bool g_TakeScreenshot; static bool resized = false; -static bool restarting = false; static int renderCounter = 0; @@ -775,7 +774,6 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO Achievements::Initialize(); // Must be done restarting by now. - restarting = false; g_nativeMainThreadReady = true; } @@ -1634,14 +1632,6 @@ void NativeResized() { resized = true; } -void NativeSetRestarting() { - restarting = true; -} - -bool NativeIsRestarting() { - return restarting; -} - void NativeShutdown() { INFO_LOG(Log::System, "NativeShutdown begin"); ClearAchievementsHostOverride(); @@ -1682,10 +1672,7 @@ void NativeShutdown() { ShaderTranslationShutdown(); // Avoid shutting this down when restarting core. - if (!restarting) { - g_logManager.Shutdown(); - } - + g_logManager.Shutdown(); g_threadManager.Teardown(); #if !PPSSPP_PLATFORM(IOS) diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 9efbcb0080..5e8a6fc3e2 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -314,7 +314,7 @@ UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr reso _assert_(success); } -void UWPGraphicsContext::Shutdown() { +void UWPGraphicsContext::ShutdownAPI() { delete draw_; } diff --git a/UWP/PPSSPP_UWPMain.h b/UWP/PPSSPP_UWPMain.h index a7c176474e..2902da1043 100644 --- a/UWP/PPSSPP_UWPMain.h +++ b/UWP/PPSSPP_UWPMain.h @@ -19,7 +19,7 @@ class UWPGraphicsContext : public GraphicsContext { public: UWPGraphicsContext(std::shared_ptr resources); - void Shutdown() override; + void ShutdownAPI() override; void Resize() override {} Draw::DrawContext * GetDrawContext() override { return draw_; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index d5927d26bc..465fba5d82 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -76,13 +76,14 @@ HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) { return hr; } -bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { - hWnd_ = wnd; +bool D3D11Context::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { LoadD3D11Error result = LoadD3D11(); HRESULT hr = E_FAIL; - std::vector adapterNames; - std::string chosenAdapterName; + adapterNames_.clear(); + chosenAdapterName_.clear(); + adapterNames_.clear(); + if (result == LoadD3D11Error::SUCCESS) { std::vector> adapters; int chosenAdapter = 0; @@ -96,13 +97,13 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { DXGI_ADAPTER_DESC desc; pAdapter->GetDesc(&desc); std::string str = ConvertWStringToUTF8(desc.Description); - adapterNames.push_back(str); - if (str == g_Config.sD3D11Device) { + adapterNames_.push_back(str); + if (deviceName && str == *deviceName) { chosenAdapter = i; } } if (!adapters.empty()) { - chosenAdapterName = adapterNames[chosenAdapter]; + chosenAdapterName_ = adapterNames_[chosenAdapter]; hr = CreateTheDevice(adapters[chosenAdapter].Get()); adapters.clear(); } else { @@ -112,7 +113,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { } } - if (FAILED(hr)) { + if (hr == E_FAIL) { const char *defaultError = "Your GPU does not appear to support Direct3D 11.\n\nWould you like to try again using OpenGL instead?"; auto err = GetI18NCategory(I18NCat::ERRORS); @@ -126,22 +127,26 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { error = ConvertUTF8ToWString(err->T("D3D11NotSupported", defaultError)); std::wstring title = ConvertUTF8ToWString(err->T("D3D11InitializationError", "Direct3D 11 initialization error")); - bool yes = IDYES == MessageBox(hWnd_, error.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); + bool yes = IDYES == MessageBox((HWND)wnd, error.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); if (yes) { // Change the config to OpenGL and restart. g_Config.iGPUBackend = (int)GPUBackend::OPENGL; g_Config.sFailedGPUBackends.clear(); g_Config.Save("save_d3d9_fallback"); - W32Util::ExitAndRestart(); } return false; } + // Success, update the device name. + if (deviceName) { + *deviceName = chosenAdapterName_; + } + + // Try to upgrade the D3D11 version. if (FAILED(device_.As(&device1_))) { device1_ = nullptr; } - if (FAILED(context_.As(&context1_))) { context1_ = nullptr; } @@ -155,7 +160,15 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { } } #endif + return true; +} +bool D3D11Context::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) { + _dbg_assert_(winsys == WINDOWSYSTEM_WIN32); + HINSTANCE hInst = (HINSTANCE)data1; + HWND wnd = (HWND)data2; + + hWnd_ = wnd; int width; int height; @@ -164,7 +177,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { // Obtain DXGI factory from device (since we used nullptr for pAdapter above) ComPtr dxgiFactory; ComPtr dxgiDevice; - hr = device_.As(&dxgiDevice); + HRESULT hr = device_.As(&dxgiDevice); if (SUCCEEDED(hr)) { ComPtr adapter; hr = dxgiDevice->GetAdapter(&adapter); @@ -209,8 +222,8 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { hr = dxgiFactory->CreateSwapChain(device_.Get(), &swapChainDesc_, &swapChain_); dxgiFactory->MakeWindowAssociation(hWnd_, DXGI_MWA_NO_ALT_ENTER); - draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, swapChain_, featureLevel_, hWnd_, adapterNames, g_Config.iInflightFrames); - SetGPUBackend(GPUBackend::DIRECT3D11, chosenAdapterName); + draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, swapChain_, featureLevel_, hWnd_, adapterNames_, g_Config.iInflightFrames); + SetGPUBackend(GPUBackend::DIRECT3D11, chosenAdapterName_); bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think. _assert_msg_(success, "Failed to compile preset shaders"); @@ -250,7 +263,7 @@ void D3D11Context::Resize() { GotBackbuffer(); } -void D3D11Context::Shutdown() { +void D3D11Context::ShutdownSurface() { LostBackbuffer(); delete draw_; @@ -258,6 +271,9 @@ void D3D11Context::Shutdown() { context_->ClearState(); context_->Flush(); +} + +void D3D11Context::ShutdownAPI() { #ifdef _DEBUG if (d3dInfoQueue_) { d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, false); diff --git a/Windows/GPU/D3D11Context.h b/Windows/GPU/D3D11Context.h index d41c14764d..1ebe7367d1 100644 --- a/Windows/GPU/D3D11Context.h +++ b/Windows/GPU/D3D11Context.h @@ -20,17 +20,20 @@ #include "ppsspp_config.h" #include "Common/CommonWindows.h" -#include "Windows/GPU/WindowsGraphicsContext.h" +#include "Common/GraphicsContext.h" +#include "Common/GPU/MiscTypes.h" #include #include #include class DrawContext; -class D3D11Context : public WindowsGraphicsContext { +class D3D11Context : public GraphicsContext { public: - bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; - void Shutdown() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) override; + void ShutdownSurface() override; + void ShutdownAPI() override; void Resize() override; @@ -65,4 +68,6 @@ private: int width = 0; int height = 0; int swapInterval_ = 0; + std::vector adapterNames_; + std::string chosenAdapterName_; }; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index c2ec0a6077..04bf61028d 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -37,21 +37,21 @@ #include "Windows/GPU/WindowsGLContext.h" // Currently, just compile time for debugging. May be NVIDIA only. -static const int simulateGLES = false; +constexpr bool g_simulateGLES = false; void WindowsGLContext::Poll() { // We no longer call RenderManager::Swap here, it's handled by the render thread, which // we're not on here. // Used during fullscreen switching to prevent rendering. - if (pauseRequested) { + if (pauseRequested_) { SetEvent(pauseEvent); - resumeRequested = true; + resumeRequested_ = true; DWORD result = WaitForSingleObject(resumeEvent, INFINITE); if (result == WAIT_TIMEOUT) { ERROR_LOG(Log::G3D, "Wait for resume timed out. Resuming rendering"); } - pauseRequested = false; + pauseRequested_ = false; } } @@ -63,7 +63,7 @@ void WindowsGLContext::Pause() { return; } - pauseRequested = true; + pauseRequested_ = true; DWORD result = WaitForSingleObject(pauseEvent, INFINITE); if (result == WAIT_TIMEOUT) { ERROR_LOG(Log::G3D, "Wait for pause timed out"); @@ -75,16 +75,16 @@ void WindowsGLContext::Resume() { if (!hRC) { return; } - if (Core_IsStepping() && !resumeRequested) { + if (Core_IsStepping() && !resumeRequested_) { return; } - if (!resumeRequested) { + if (!resumeRequested_) { ERROR_LOG(Log::G3D, "Not waiting to get resumed"); } else { SetEvent(resumeEvent); } - resumeRequested = false; + resumeRequested_ = false; } void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenum type, @@ -172,17 +172,22 @@ void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity, } } -bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) { +bool WindowsGLContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { glslang::InitializeProcess(); - - hInst_ = hInst; - hWnd_ = window; - *error_message = "ok"; return true; } -bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { - *error_message = "ok"; +void WindowsGLContext::ShutdownAPI() { + glslang::FinalizeProcess(); +} + +bool WindowsGLContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) { + HINSTANCE hInst = (HINSTANCE)data1; + HWND window = (HWND)data2; + + hInst_ = hInst; + hWnd_ = window; + GLuint PixelFormat; // TODO: Change to use WGL_ARB_pixel_format instead @@ -300,7 +305,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { // Alright, now for the modernity. First try a 4.4, then 4.3, context, if that fails try 3.3. // I can't seem to find a way that lets you simply request the newest version available. if (wglewIsSupported("WGL_ARB_create_context") == 1) { - if (simulateGLES) { + if (g_simulateGLES) { const static int simulateVersions[][2] = { {3, 2}, {3, 1}, {3, 0}, {2, 0} }; for (auto ver : simulateVersions) { const int attribsES[] = { @@ -404,8 +409,8 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { glEnable(GL_DEBUG_OUTPUT); } - pauseRequested = false; - resumeRequested = false; + pauseRequested_ = false; + resumeRequested_ = false; CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(wglSwapIntervalEXT != nullptr); @@ -439,10 +444,6 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { return true; // Success } -void WindowsGLContext::Shutdown() { - glslang::FinalizeProcess(); -} - void WindowsGLContext::ReleaseGLContext() { if (hRC) { // Are we able to release the DC and RC contexts? @@ -467,11 +468,13 @@ void WindowsGLContext::ReleaseGLContext() { hWnd_ = NULL; } -void WindowsGLContext::ShutdownFromRenderThread() { +void WindowsGLContext::ShutdownSurface() { delete draw_; draw_ = nullptr; CloseHandle(pauseEvent); CloseHandle(resumeEvent); + pauseEvent = nullptr; + resumeEvent = nullptr; ReleaseGLContext(); } diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index 9c473a74bb..c2ff94d9b1 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -1,7 +1,8 @@ #pragma once #include "Common/CommonWindows.h" -#include "Windows/GPU/WindowsGraphicsContext.h" +#include "Common/GraphicsContext.h" +#include "Common/GPU/MiscTypes.h" namespace Draw { class DrawContext; @@ -9,16 +10,17 @@ namespace Draw { class GLRenderManager; -class WindowsGLContext : public WindowsGraphicsContext { +class WindowsGLContext : public GraphicsContext { public: - bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; + // This really means that rendering takes over the main/window thread, and will call ThreadStart/ThreadEnd/ThreadFrame + // and the caller needs to also spawn an emulation thread which will call the other APIs. + bool NeedsSeparateEmuThread() const override { return true; } - bool NeedsRenderThread() const override { return true; } + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + void ShutdownAPI() override; - bool InitFromRenderThread(std::string *errorMessage) override; - void ShutdownFromRenderThread() override; - - void Shutdown() override; + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) override; + void ShutdownSurface() override; void Poll() override; @@ -28,6 +30,7 @@ public: void Resume() override; void Resize() override; + // If these are used, they are called from the render thread. If NeedsRenderThread is false, they are not called. void ThreadStart() override; void ThreadEnd() override; bool ThreadFrame(bool waitIfEmpty) override; @@ -44,8 +47,8 @@ private: HDC hDC; // Private GDI Device Context HGLRC hRC; // Permanent Rendering Context HWND hWnd_; // Holds Our Window Handle - volatile bool pauseRequested; - volatile bool resumeRequested; + volatile bool pauseRequested_; + volatile bool resumeRequested_; HANDLE pauseEvent; HANDLE resumeEvent; }; diff --git a/Windows/GPU/WindowsGraphicsContext.h b/Windows/GPU/WindowsGraphicsContext.h deleted file mode 100644 index 41efb96a2f..0000000000 --- a/Windows/GPU/WindowsGraphicsContext.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "Common/GraphicsContext.h" -#include "Common/CommonWindows.h" - -class WindowsGraphicsContext : public GraphicsContext { -public: - virtual bool Init(HINSTANCE hInst, HWND window, std::string *error_message) = 0; -}; diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 74485e80db..48f778db89 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -69,11 +69,12 @@ static const bool g_validate_ = true; static const bool g_validate_ = false; #endif -bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) { - *error_message = "N/A"; +bool WindowsVulkanContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { + *errorMessage = "N/A"; + _dbg_assert_(deviceName); if (vulkan_) { - *error_message = "Already initialized"; + *errorMessage = "Already initialized"; return false; } @@ -87,8 +88,8 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m std::string errorStr; if (!VulkanLoad(&errorStr)) { - *error_message = "Failed to load Vulkan driver library: "; - (*error_message) += errorStr; + *errorMessage = "Failed to load Vulkan driver library: "; + (*errorMessage) += errorStr; return false; } @@ -97,26 +98,34 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m VulkanContext::CreateInfo info{}; InitVulkanCreateInfoFromConfig(&info); if (VK_SUCCESS != vulkan_->CreateInstance(info)) { - *error_message = vulkan_->InitError(); + *errorMessage = vulkan_->InitError(); delete vulkan_; vulkan_ = nullptr; return false; } - int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice); + int deviceNum = vulkan_->GetPhysicalDeviceByName(*deviceName); if (deviceNum < 0) { deviceNum = vulkan_->GetBestPhysicalDevice(); - if (!g_Config.sVulkanDevice.empty()) - g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName; + if (!deviceName->empty()) { + *deviceName = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName; + } } if (vulkan_->CreateDevice(deviceNum) != VK_SUCCESS) { - *error_message = vulkan_->InitError(); + *errorMessage = vulkan_->InitError(); delete vulkan_; vulkan_ = nullptr; return false; } + return true; +} - vulkan_->InitSurface(WINDOWSYSTEM_WIN32, (void *)hInst, (void *)hWnd); +bool WindowsVulkanContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { + _dbg_assert_(winsys == WINDOWSYSTEM_WIN32); + HINSTANCE hInst = (HINSTANCE)data1; + HWND hWnd = (HWND)data2; + + vulkan_->InitSurface(winsys, (void *)hInst, (void *)hWnd); bool useMultiThreading = g_Config.bRenderMultiThreading; if (g_Config.iInflightFrames == 1) { @@ -134,12 +143,11 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m #endif if (!vulkan_->InitSwapchain(presentMode)) { - *error_message = vulkan_->InitError(); - Shutdown(); + *errorMessage = vulkan_->InitError(); return false; } - SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName); + SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties().properties.deviceName); bool success = draw_->CreatePresets(); _assert_msg_(success, "Failed to compile preset shaders"); draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); @@ -147,15 +155,17 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); if (!renderManager_->HasBackbuffers()) { - Shutdown(); + // WTF? + _dbg_assert_(false); return false; } return true; } -void WindowsVulkanContext::Shutdown() { - if (draw_) +void WindowsVulkanContext::ShutdownSurface() { + if (draw_) { draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + } delete draw_; draw_ = nullptr; @@ -163,6 +173,9 @@ void WindowsVulkanContext::Shutdown() { vulkan_->WaitUntilQueueIdle(); vulkan_->DestroySwapchain(); vulkan_->DestroySurface(); +} + +void WindowsVulkanContext::ShutdownAPI() { vulkan_->DestroyDevice(); vulkan_->DestroyInstance(); diff --git a/Windows/GPU/WindowsVulkanContext.h b/Windows/GPU/WindowsVulkanContext.h index b384730da1..ae6872bc3e 100644 --- a/Windows/GPU/WindowsVulkanContext.h +++ b/Windows/GPU/WindowsVulkanContext.h @@ -18,17 +18,18 @@ #pragma once #include "Common/GraphicsContext.h" -#include "Windows/GPU/WindowsGraphicsContext.h" #include "Common/GPU/thin3d.h" class VulkanContext; class VulkanRenderManager; -class WindowsVulkanContext : public WindowsGraphicsContext { +class WindowsVulkanContext : public GraphicsContext { public: WindowsVulkanContext() : draw_(nullptr) {} - bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; - void Shutdown() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage); + void ShutdownSurface() override; + void ShutdownAPI() override; void Resize() override; void Poll() override; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 51494a4b0a..64b1f1e2b7 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1119,7 +1119,6 @@ true true - diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 7a0e7bbf1b..f6a36a31d7 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -387,9 +387,6 @@ Windows\System - - Windows\System - Other Platforms\Qt diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index a1eb1435c1..a83396b677 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -192,7 +192,6 @@ namespace W32Util void ExitAndRestart(bool overrideArgs, const std::string &args) { SpawnNewInstance(overrideArgs, args); - ExitProcess(0); } diff --git a/Windows/main.cpp b/Windows/main.cpp index 21994b52a8..fa6eb653e9 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -656,9 +656,7 @@ static std::wstring MakeWindowsFilter(BrowseFileType type) { bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { switch (type) { case SystemRequestType::EXIT_APP: - if (!NativeIsRestarting()) { - PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_DESTROY, 0, 0); - } + PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_DESTROY, 0, 0); return true; case SystemRequestType::RESTART_APP: { @@ -1028,70 +1026,32 @@ static void WinMainCleanup() { } } -bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { - WindowsGraphicsContext *graphicsContext = nullptr; - switch (g_Config.iGPUBackend) { +static GraphicsContext *CreateGraphicsContext(GPUBackend backend, std::string **deviceName) { + GraphicsContext *graphicsContext = nullptr; + switch (backend) { #if PPSSPP_API(ANY_GL) - case (int)GPUBackend::OPENGL: + case GPUBackend::OPENGL: graphicsContext = new WindowsGLContext(); + *deviceName = nullptr; break; #endif - case (int)GPUBackend::DIRECT3D11: + case GPUBackend::DIRECT3D11: graphicsContext = new D3D11Context(); + *deviceName = &g_Config.sD3D11Device; break; - case (int)GPUBackend::VULKAN: - graphicsContext = new WindowsVulkanContext(); - break; + case GPUBackend::VULKAN: default: - return false; - } - - if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetHWND(), error_message)) { - *ctx = graphicsContext; - return true; - } else { - delete graphicsContext; - *ctx = nullptr; - return false; + graphicsContext = new WindowsVulkanContext(); + *deviceName = &g_Config.sVulkanDevice; + break; } + return graphicsContext; } -// returns null if failed. -static GraphicsContext *CreateGraphicsContextOrExit() { - const bool performingRestart = NativeIsRestarting(); - - if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { - Reporting::ReportMessage("Graphics init error: %s", "ALL"); - - auto err = GetI18NCategory(I18NCat::ERRORS); - const char *defaultErrorAll = "PPSSPP failed to startup with any graphics backend. Try upgrading your graphics and other drivers."; - std::string_view genericError = err->T("GenericAllStartupError", defaultErrorAll); - std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); - MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK); - - // Let's continue (and probably crash) just so they have a way to keep trying. - } - - System_Notify(SystemNotification::UI); - - GraphicsContext *graphicsContext = nullptr; - std::string error_string; - const bool success = CreateGraphicsBackend(&error_string, &graphicsContext); - if (success) { - return graphicsContext; - } - - // Before anything: are we restarting right now? - if (performingRestart) { - // Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart. - // This happens on NVIDIA when switching OpenGL -> Vulkan. - g_Config.Save("switch_graphics_failed"); - W32Util::ExitAndRestart(); - return nullptr; - } - +// This one will always exit or restart the app. No returns. +void HandleGraphicsFailure(const std::string &errorMessage) { auto err = GetI18NCategory(I18NCat::ERRORS); - Reporting::ReportMessage("Graphics init error: %s", error_string.c_str()); + Reporting::ReportMessage("Graphics init error: %s", errorMessage.c_str()); const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:"; const char *defaultErrorOpenGL = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to DirectX 9?\n\nError message:"; @@ -1109,9 +1069,9 @@ static GraphicsContext *CreateGraphicsContextOrExit() { genericError = err->T("GenericOpenGLError", defaultErrorOpenGL); break; } - std::string full_error = StringFromFormat("%.*s\n\n%s", (int)genericError.size(), genericError.data(), error_string.c_str()); + std::string full_error = StringFromFormat("%.*s\n\n%s", (int)genericError.size(), genericError.data(), errorMessage.c_str()); std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); - bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); + const bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); ERROR_LOG(Log::Boot, "%s", full_error.c_str()); if (yes) { @@ -1122,12 +1082,12 @@ static GraphicsContext *CreateGraphicsContextOrExit() { g_Config.Save("save_graphics_fallback"); W32Util::ExitAndRestart(); - return nullptr; + return; } // No safe way out without graphics. ExitProcess(1); - return nullptr; + return; } int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { @@ -1210,6 +1170,16 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin NativeInit((int)args.size(), args.data(), cmdLineOptions, "", "", nullptr); + // Report previous graphics init error to the user. + if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { + auto err = GetI18NCategory(I18NCat::ERRORS); + const char *defaultErrorAll = "PPSSPP failed to startup with any graphics backend. Try upgrading your graphics and other drivers."; + std::string_view genericError = err->T("GenericAllStartupError", defaultErrorAll); + std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); + MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK); + // Let's continue (and probably crash) just so users have a way to keep trying. + } + // Consider at least the following cases before changing this code: // - By default in Release, the console should be hidden by default even if logging is enabled. // - By default in Debug, the console should be shown by default. @@ -1259,16 +1229,22 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // The Emu thread calls NativeInit() and NativeMain() etc. System_SetWindowTitle(""); + System_Notify(SystemNotification::UI); std::thread mainThread = std::thread([]() { NativeApplication application; - std::unique_ptr graphicsContext(CreateGraphicsContextOrExit()); - if (!graphicsContext) { - // We're screwed. Should never get here. - _dbg_assert_(false); + std::string errorMessage; + std::string *deviceNameSetting; + std::unique_ptr graphicsContext(CreateGraphicsContext((GPUBackend)g_Config.iGPUBackend, &deviceNameSetting)); + if (!graphicsContext->InitAPI(MainWindow::GetHWND(), deviceNameSetting, &errorMessage)) { + HandleGraphicsFailure(errorMessage); return; } - MainThreadFunc(graphicsContext.get(), &application, []() {}); + if (!MainThreadFunc(graphicsContext.get(), &application, WINDOWSYSTEM_WIN32, MainWindow::GetHInstance(), MainWindow::GetHWND(), []() {})) { + HandleGraphicsFailure("Failed to initialize main thread function."); + return; + } + graphicsContext->ShutdownAPI(); PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0); }); diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index a23ed5c666..2f5209be29 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -10,7 +10,16 @@ AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() { SetGPUBackend(GPUBackend::OPENGL); } -bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(std::string *errorMessage) { +bool AndroidJavaEGLGraphicsContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { + // OpenGL handles rotated rendering in the driver. + g_display.rotation = DisplayRotation::ROTATE_0; + g_display.rot_matrix.setIdentity(); + return true; +} + +void AndroidJavaEGLGraphicsContext::ShutdownAPI() {} + +bool AndroidJavaEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { INFO_LOG(Log::G3D, "AndroidJavaEGLGraphicsContext::InitFromRenderThread"); if (!CheckGLExtensions()) { *errorMessage = "CheckExtensions failed"; @@ -18,10 +27,6 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(std::string *errorMessa return false; } - // OpenGL handles rotated rendering in the driver. - g_display.rotation = DisplayRotation::ROTATE_0; - g_display.rot_matrix.setIdentity(); - draw_ = Draw::T3DCreateGLContext(false); // Can't fail renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); @@ -34,7 +39,7 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(std::string *errorMessa return true; } -void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() { +void AndroidJavaEGLGraphicsContext::ShutdownSurface() { INFO_LOG(Log::G3D, "AndroidJavaEGLGraphicsContext::Shutdown"); renderManager_ = nullptr; // owned by draw_. delete draw_; diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h index bcce18aeba..394d2e8362 100644 --- a/android/jni/AndroidJavaGLContext.h +++ b/android/jni/AndroidJavaGLContext.h @@ -9,13 +9,14 @@ public: AndroidJavaEGLGraphicsContext(); ~AndroidJavaEGLGraphicsContext() override { delete draw_; } - bool NeedsRenderThread() const override { return true; } + bool NeedsSeparateEmuThread() const override { return true; } - // This performs the actual initialization, - bool InitFromRenderThread(std::string *errorMessage) override; - void ShutdownFromRenderThread() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + void ShutdownAPI() override; + + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; + void ShutdownSurface() override; - void Shutdown() override {} void Resize() override {} Draw::DrawContext *GetDrawContext() override { @@ -30,14 +31,13 @@ public: return renderManager_->ThreadFrame(waitIfEmpty); } - void BeginShutdown() override { - renderManager_->SetSkipGLCalls(); - } - void ThreadEnd() override { renderManager_->ThreadEnd(); } - +protected: + void BeginShutdownSurface() override { + renderManager_->SetSkipGLCalls(); + } private: Draw::DrawContext *draw_ = nullptr; GLRenderManager *renderManager_ = nullptr; diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index 2b4763db5a..6119c32c47 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -17,12 +17,12 @@ AndroidVulkanContext::AndroidVulkanContext() {} AndroidVulkanContext::~AndroidVulkanContext() { - delete g_Vulkan; - g_Vulkan = nullptr; + delete vulkan_; + vulkan_ = nullptr; } -bool AndroidVulkanContext::InitAPI() { - _dbg_assert_(!g_Vulkan); +bool AndroidVulkanContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { + _dbg_assert_(!vulkan_); INFO_LOG(Log::G3D, "AndroidVulkanContext::Init"); init_glslang(); @@ -36,19 +36,19 @@ bool AndroidVulkanContext::InitAPI() { return false; } - if (!g_Vulkan) { - // TODO: Assert if g_Vulkan already exists here ? + if (!vulkan_) { + // TODO: Assert if vulkan_ already exists here ? INFO_LOG(Log::G3D, "Creating Vulkan context."); - g_Vulkan = new VulkanContext(); + vulkan_ = new VulkanContext(); } else { INFO_LOG(Log::G3D, "Reusing existing Vulkan context."); } VulkanContext::CreateInfo info{}; InitVulkanCreateInfoFromConfig(&info); - if (!g_Vulkan->CreateInstanceAndDevice(info)) { - delete g_Vulkan; - g_Vulkan = nullptr; + if (!vulkan_->CreateInstanceAndDevice(info, deviceName)) { + delete vulkan_; + vulkan_ = nullptr; return false; } @@ -56,16 +56,18 @@ bool AndroidVulkanContext::InitAPI() { return true; } -bool AndroidVulkanContext::Init(ANativeWindow *wnd) { - INFO_LOG(Log::G3D, "AndroidVulkanContext::InitFromRenderThread"); - if (!g_Vulkan) { - ERROR_LOG(Log::G3D, "AndroidVulkanContext::InitFromRenderThread: No Vulkan context"); +bool AndroidVulkanContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) { + ANativeWindow *wnd_ = (ANativeWindow *)data1; + + INFO_LOG(Log::G3D, "AndroidVulkanContext::Init"); + if (!vulkan_) { + ERROR_LOG(Log::G3D, "AndroidVulkanContext::Init: No Vulkan context"); return false; } - VkResult res = g_Vulkan->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd, nullptr); + VkResult res = vulkan_->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd_, nullptr); if (res != VK_SUCCESS) { - ERROR_LOG(Log::G3D, "g_Vulkan->InitSurface failed: '%s'", VulkanResultToString(res)); + ERROR_LOG(Log::G3D, "vulkan_->InitSurface failed: '%s'", VulkanResultToString(res)); return false; } @@ -73,25 +75,25 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd) { if (g_Config.iInflightFrames == 1) { useMultiThreading = false; } - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, useMultiThreading); + draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading); VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); - if (!g_Vulkan->InitSwapchain(presentMode)) { - g_Vulkan->DestroySurface(); + if (!vulkan_->InitSwapchain(presentMode)) { + vulkan_->DestroySurface(); return false; } SetGPUBackend(GPUBackend::VULKAN); bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. _assert_msg_(success, "Failed to compile preset shaders"); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager->SetInflightFrames(g_Config.iInflightFrames); if (!renderManager->HasBackbuffers()) { ERROR_LOG(Log::G3D, "VulkanRenderManager has no backbuffers after InitFromRenderThread"); - g_Vulkan->DestroySwapchain(); - g_Vulkan->DestroySurface(); + vulkan_->DestroySwapchain(); + vulkan_->DestroySurface(); delete draw_; draw_ = nullptr; return false; @@ -101,47 +103,33 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd) { return true; } -void AndroidVulkanContext::ShutdownFromRenderThread() { - INFO_LOG(Log::G3D, "AndroidVulkanContext::ShutdownFromRenderThread"); - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); +void AndroidVulkanContext::ShutdownSurface() { + INFO_LOG(Log::G3D, "AndroidVulkanContext::ShutdownSurface"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); delete draw_; draw_ = nullptr; - g_Vulkan->WaitUntilQueueIdle(); - g_Vulkan->PerformPendingDeletes(); - g_Vulkan->DestroySwapchain(); - g_Vulkan->DestroySurface(); + vulkan_->WaitUntilQueueIdle(); + vulkan_->PerformPendingDeletes(); + vulkan_->DestroySwapchain(); + vulkan_->DestroySurface(); INFO_LOG(Log::G3D, "Done with ShutdownFromRenderThread"); } -void AndroidVulkanContext::Shutdown() { +void AndroidVulkanContext::ShutdownAPI() { INFO_LOG(Log::G3D, "AndroidVulkanContext::Shutdown"); - g_Vulkan->DestroyDevice(); - g_Vulkan->DestroyInstance(); - // We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app. + vulkan_->DestroyDevice(); + vulkan_->DestroyInstance(); + // We keep the vulkan_ context around to avoid invalidating a ton of pointers around the app. // TODO: Actually the above is inaccurate.. finalize_glslang(); INFO_LOG(Log::G3D, "AndroidVulkanContext::Shutdown completed"); } void AndroidVulkanContext::Resize() { - INFO_LOG(Log::G3D, "AndroidVulkanContext::Resize begin (oldsize: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - - // This stuff does not seem to be needed, as the window doesn't change here. -#if 0 - g_Vulkan->DestroySwapchain(); - - // TODO: We should only destroy the surface here if the window changed. We can track this inside g_Vulkan. - // Actually, the window can't really change, can it? - g_Vulkan->DestroySurface(); - - // ========================================== - - g_Vulkan->ReinitSurface(); -#endif - + INFO_LOG(Log::G3D, "AndroidVulkanContext::Resize begin (oldsize: %dx%d)", vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); - g_Vulkan->InitSwapchain(presentMode); // This also destroys the old swapchain and makes a nice transition. - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - INFO_LOG(Log::G3D, "AndroidVulkanContext::Resize end (final size: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + vulkan_->InitSwapchain(presentMode); // This also destroys the old swapchain and makes a nice transition. + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + INFO_LOG(Log::G3D, "AndroidVulkanContext::Resize end (final size: %dx%d)", vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); } diff --git a/android/jni/AndroidVulkanContext.h b/android/jni/AndroidVulkanContext.h index 2400a18b93..649d6b1786 100644 --- a/android/jni/AndroidVulkanContext.h +++ b/android/jni/AndroidVulkanContext.h @@ -12,18 +12,18 @@ public: AndroidVulkanContext(); ~AndroidVulkanContext(); - bool InitAPI(); + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + void ShutdownAPI() override; - bool Init(ANativeWindow *wnd); - void ShutdownFromRenderThread() override; // Inverses InitFromRenderThread. + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; + void ShutdownSurface() override; - void Shutdown() override; void Resize() override; - void *GetAPIContext() override { return g_Vulkan; } + void *GetAPIContext() override { return vulkan_; } Draw::DrawContext *GetDrawContext() override { return draw_; } private: - VulkanContext *g_Vulkan = nullptr; + VulkanContext *vulkan_ = nullptr; Draw::DrawContext *draw_ = nullptr; }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index fe7b427ee4..da5a81988b 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -841,15 +841,16 @@ retry: // The Vulkan render manager manages its own thread. // We create and destroy the Vulkan graphics context in the app main thread though. AndroidVulkanContext *ctx = new AndroidVulkanContext(); - if (!ctx->InitAPI()) { - INFO_LOG(Log::System, "Failed to initialize Vulkan, switching to OpenGL"); + std::string errorMessage; + if (!ctx->InitAPI(nullptr, &g_Config.sVulkanDevice, &errorMessage)) { + ERROR_LOG(Log::System, "Failed to initialize Vulkan, switching to OpenGL: %s", errorMessage.c_str()); g_Config.iGPUBackend = (int)GPUBackend::OPENGL; SetGPUBackend(GPUBackend::OPENGL); delete ctx; goto retry; - } else { - graphicsContext = ctx; } + graphicsContext = ctx; + // Now, we wait until we get a surface to continue initializing Vulkan. break; } default: @@ -953,7 +954,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) { extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { INFO_LOG(Log::System, "NativeApp.shutdown() -- begin"); - if (renderer_inited && graphicsContext && graphicsContext->NeedsRenderThread()) { + if (renderer_inited && graphicsContext && graphicsContext->NeedsSeparateEmuThread()) { // Only used in Java EGL path. EmuThreadStop("shutdown"); // NOTE: We know that the GLSurfaceView render thread is stopped here, since we now @@ -963,7 +964,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { // Now we know that more frames won't be coming in. INFO_LOG(Log::System, "BeginShutdown"); - graphicsContext->BeginShutdown(); // Makes sure we don't actually perform draws. // Now, it could be that we had some frames queued up. Get through them. // We're on the render thread, so this is synchronous. @@ -975,14 +975,14 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { EmuThreadJoin(); INFO_LOG(Log::System, "ThreadEnd called."); - graphicsContext->ShutdownFromRenderThread(); + graphicsContext->ShutdownSurface(); INFO_LOG(Log::System, "Graphics context now shut down from NativeApp_shutdown"); } { if (graphicsContext) { INFO_LOG(Log::G3D, "Shutting down renderer"); - graphicsContext->Shutdown(); + graphicsContext->ShutdownAPI(); delete graphicsContext; graphicsContext = nullptr; renderer_inited = false; @@ -1009,7 +1009,7 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e return false; } - _assert_(graphicsContext->NeedsRenderThread()); + _assert_(graphicsContext->NeedsSeparateEmuThread()); INFO_LOG(Log::G3D, "NativeApp.displayInit()"); bool firstStart = !renderer_inited; @@ -1018,7 +1018,7 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e std::string errorMessage; if (!renderer_inited) { INFO_LOG(Log::G3D, "NativeApp.displayInit() first time"); - if (!graphicsContext->InitFromRenderThread(&errorMessage)) { + if (!graphicsContext->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr, &errorMessage)) { System_Toast("Graphics initialization failed. Quitting."); return false; } @@ -1038,9 +1038,9 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e INFO_LOG(Log::G3D, "NativeApp.displayInit() restoring"); EmuThreadStop("displayInit"); - graphicsContext->BeginShutdown(); - INFO_LOG(Log::G3D, "BeginAndroidShutdown. Looping until emu thread done..."); // Skipping GL calls here because the old context is lost. + INFO_LOG(Log::G3D, "Looping until emu thread done..."); + // TODO: Why can't we just join the EmuThread first? graphicsContext->ThreadFrameUntilCondition([]() -> bool { return emuThreadState == (int)EmuThreadState::STOPPED; }); @@ -1048,11 +1048,11 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e EmuThreadJoin(); graphicsContext->ThreadEnd(); - graphicsContext->ShutdownFromRenderThread(); + graphicsContext->ShutdownSurface(); INFO_LOG(Log::G3D, "Shut down both threads. Now let's bring it up again!"); - if (!graphicsContext->InitFromRenderThread(&errorMessage)) { + if (!graphicsContext->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr, &errorMessage)) { System_Toast(("Graphics initialization failed: Quitting: " + errorMessage).c_str()); return false; } @@ -1233,7 +1233,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, if (!graphicsContext) { return; } - _assert_(graphicsContext->NeedsRenderThread()); + _assert_(graphicsContext->NeedsSeparateEmuThread()); if (!graphicsContext->ThreadFrame(true)) { INFO_LOG(Log::G3D, "ThreadFrame returned false"); @@ -1707,7 +1707,7 @@ extern "C" jboolean JNICALL Java_org_ppsspp_ppsspp_PpssppActivity_runVulkanRende return false; } - _assert_(!graphicsContext->NeedsRenderThread()); + _assert_(!graphicsContext->NeedsSeparateEmuThread()); if (g_renderLoopThread.joinable()) { ERROR_LOG(Log::G3D, "runVulkanRenderLoop: Already running"); @@ -1763,21 +1763,20 @@ static void VulkanEmuThread(ANativeWindow *wnd, AndroidVulkanContext *graphicsCo WARN_LOG(Log::G3D, "runVulkanRenderLoop. display_xres=%d display_yres=%d desiredBackbufferSizeX=%d desiredBackbufferSizeY=%d", display_xres, display_yres, desiredBackbufferSizeX, desiredBackbufferSizeY); - if (!graphicsContext->Init(wnd)) { + std::string errorMessage; + if (!graphicsContext->InitSurface(WINDOWSYSTEM_ANDROID, wnd, nullptr, &errorMessage)) { // On Android, if we get here, really no point in continuing. // The UI is supposed to render on any device both on OpenGL and Vulkan. If either of those don't work // on a device, we blacklist it. Hopefully we should have already failed in InitAPI anyway and reverted to GL back then. - ERROR_LOG(Log::G3D, "Failed to initialize graphics context."); - System_Toast("Failed to initialize graphics context."); - - delete graphicsContext; - graphicsContext = nullptr; + ERROR_LOG(Log::G3D, "Failed to initialize graphics context for surface: %s", errorMessage.c_str()); + System_Toast("Failed to initialize graphics context for surface."); renderLoopRunning = false; ANativeWindow_release(wnd); return; } if (!exitRenderLoop) { + INFO_LOG(Log::G3D, "Calling NativeInitGraphics"); if (!NativeInitGraphics(graphicsContext)) { ERROR_LOG(Log::G3D, "Failed to initialize graphics."); // Gonna be in a weird state here.. @@ -1785,10 +1784,12 @@ static void VulkanEmuThread(ANativeWindow *wnd, AndroidVulkanContext *graphicsCo graphicsContext->ThreadStart(); renderer_inited = true; + // The main loop. while (!exitRenderLoop) { NativeFrame(graphicsContext); ProcessFrameCommands(); } + INFO_LOG(Log::G3D, "Leaving Vulkan main loop."); } else { INFO_LOG(Log::G3D, "Not entering main loop."); @@ -1801,7 +1802,10 @@ static void VulkanEmuThread(ANativeWindow *wnd, AndroidVulkanContext *graphicsCo // Shut the graphics context down to the same state it was in when we entered the render thread. INFO_LOG(Log::G3D, "Shutting down graphics context..."); - graphicsContext->ShutdownFromRenderThread(); + graphicsContext->ShutdownSurface(); + + // But we don't shut down the API. On some platforms like Android, we keep that around for later. + renderLoopRunning = false; exitRenderLoop = false; ANativeWindow_release(wnd); diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index 5d0d46829e..f6fa36509f 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -53,9 +53,11 @@ public: GLDummyGraphicsContext() {} ~GLDummyGraphicsContext() { delete draw_; } - bool InitFromRenderThread(std::string *errorMessage) override; + bool InitAPI(void *wnd, std::string *deviceNameSetting, std::string *errorMessage) override; + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) override; - void ShutdownFromRenderThread() override { + + void ShutdownSurface() override { delete draw_; draw_ = nullptr; @@ -83,7 +85,6 @@ public: renderManager_->ThreadEnd(); } - void Shutdown() override {} void Resize() override {} private: @@ -93,7 +94,7 @@ private: SDL_GLContext glContext_; }; -bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { +bool GLDummyGraphicsContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { SDL_Init(SDL_INIT_VIDEO); // TODO @@ -146,10 +147,10 @@ bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { #endif CheckGLExtensions(); + SetGPUBackend(GPUBackend::OPENGL); draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); - SetGPUBackend(GPUBackend::OPENGL); bool success = draw_->CreatePresets(); _assert_(success); renderManager_->SetSwapFunction([&]() { @@ -158,6 +159,11 @@ bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { return success; } +bool GLDummyGraphicsContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { + // Not used in this context. + return true; +} + bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext **ctx, GPUCore core) { GraphicsContext *graphicsContext = new GLDummyGraphicsContext(); *ctx = graphicsContext; @@ -165,13 +171,14 @@ bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext * std::thread th([&]{ SetCurrentThreadName("SDL-RenderThread"); - + std::string errorMessage; + gfx_->InitAPI(nullptr, nullptr, &errorMessage); while (threadState_ == RenderThreadState::IDLE) sleep_ms(1, "sdl-idle-poll"); threadState_ = RenderThreadState::STARTING; std::string err; - if (!gfx_->InitFromRenderThread(&err)) { + if (!gfx_->InitSurface(WINDOWSYSTEM_NONE, nullptr, nullptr, &err)) { threadState_ = RenderThreadState::START_FAILED; return; } @@ -182,7 +189,7 @@ bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext * threadState_ = RenderThreadState::STOPPING; gfx_->ThreadEnd(); - gfx_->ShutdownFromRenderThread(); + gfx_->ShutdownSurface(); threadState_ = RenderThreadState::STOPPED; }); th.detach(); @@ -199,7 +206,7 @@ void SDLHeadlessHost::ShutdownGraphics() { while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::START_FAILED) sleep_ms(1, "sdl-stop-poll"); - gfx_->Shutdown(); + gfx_->ShutdownAPI(); delete gfx_; gfx_ = nullptr; } diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 697cbbb338..02fd1e576e 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -75,7 +75,7 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte SetFocus(hWnd); } - WindowsGraphicsContext *graphicsContext = nullptr; + GraphicsContext *graphicsContext = nullptr; switch (gpuCore_) { case GPUCORE_GLES: #if PPSSPP_API(ANY_GL) @@ -95,9 +95,8 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte break; } - if (graphicsContext->Init(NULL, hWnd, error_message)) { - *ctx = graphicsContext; - gfx_ = graphicsContext; + if (graphicsContext->InitAPI(hWnd, nullptr, error_message)) { + // Success } else { delete graphicsContext; *ctx = nullptr; @@ -115,7 +114,7 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte threadState_ = RenderThreadState::STARTING; std::string err; - if (!gfx_->InitFromRenderThread(&err)) { + if (!gfx_->InitSurface(WINDOWSYSTEM_WIN32, nullptr, hWnd, &err)) { threadState_ = RenderThreadState::START_FAILED; return; } @@ -126,9 +125,19 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte threadState_ = RenderThreadState::STOPPING; gfx_->ThreadEnd(); - gfx_->ShutdownFromRenderThread(); + gfx_->ShutdownSurface(); threadState_ = RenderThreadState::STOPPED; }); + } else { + if (graphicsContext->InitSurface(WINDOWSYSTEM_WIN32, NULL, hWnd, error_message)) { + *ctx = graphicsContext; + gfx_ = graphicsContext; + } else { + delete graphicsContext; + *ctx = nullptr; + gfx_ = nullptr; + return false; + } } if (needRenderThread) { @@ -148,9 +157,12 @@ void WindowsHeadlessHost::ShutdownGraphics() { while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::IDLE) sleep_ms(1, "render-thread-stop-poll"); renderThread_.join(); + } else { + gfx_->ShutdownSurface(); } - gfx_->Shutdown(); + gfx_->ShutdownAPI(); + delete gfx_; gfx_ = nullptr; DestroyWindow(hWnd); diff --git a/ios/App.entitlements b/ios/App.entitlements index e65e4c3dcf..6631ffa6f2 100644 --- a/ios/App.entitlements +++ b/ios/App.entitlements @@ -2,7 +2,5 @@ - com.apple.InAppPurchase - diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 09bde9e981..b0ac390b19 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -45,27 +45,43 @@ class IOSGLESContext : public GraphicsContext { public: IOSGLESContext() { CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(false); - renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); - bool success = draw_->CreatePresets(); - _assert_msg_(success, "Failed to compile preset shaders"); } ~IOSGLESContext() { delete draw_; } - bool NeedsRenderThread() const override { return true; } + + bool InitAPI(void *wnd, std::string *deviceNameSetting, std::string *error_message) override { + // Nothing to do here, we already initialized in the constructor. + return true; + } + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override { + draw_ = Draw::T3DCreateGLContext(false); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager_->SetInflightFrames(g_Config.iInflightFrames); + bool success = draw_->CreatePresets(); + _assert_msg_(success, "Failed to compile preset shaders"); + return true; + } + + void ShutdownSurface() override { + // Nothing to do here, we already initialized in the constructor. + if (draw_) { + delete draw_; + draw_ = nullptr; + } + renderManager_ = nullptr; + } + void ShutdownAPI() override { + // Nothing to do here. + } + + bool NeedsSeparateEmuThread() const override { return true; } Draw::DrawContext *GetDrawContext() override { return draw_; } void Resize() override {} - void Shutdown() override {} - - void BeginShutdown() { - renderManager_->SetSkipGLCalls(); - } void ThreadStart() override { renderManager_->ThreadStart(draw_); @@ -83,6 +99,11 @@ public: renderManager_->StartThread(); } +protected: + void BeginShutdown() { + renderManager_->SetSkipGLCalls(); + } + private: Draw::DrawContext *draw_; GLRenderManager *renderManager_; @@ -223,9 +244,14 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) { graphicsContext = new IOSGLESContext(); - graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback"); - }, nullptr); + std::string errorMessage; + if (!graphicsContext->InitAPI(nullptr, nullptr, &errorMessage)) { + ERROR_LOG(Log::G3D, "InitAPI failed: %s", errorMessage.c_str()); + } + + if (!graphicsContext->InitSurface(WINDOWSYSTEM_NONE, nullptr, nullptr, &errorMessage)) { + ERROR_LOG(Log::G3D, "InitAPI failed: %s", errorMessage.c_str()); + } graphicsContext->ThreadStart(); @@ -350,14 +376,13 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) { [[NSNotificationCenter defaultCenter] removeObserver:self]; - graphicsContext->BeginShutdown(); // Skipping GL calls here because the old context is lost. graphicsContext->ThreadFrameUntilCondition([]() -> bool { return !renderLoopRunning; }); graphicsContext->ThreadEnd(); - - graphicsContext->Shutdown(); + graphicsContext->ShutdownSurface(); + graphicsContext->ShutdownAPI(); delete graphicsContext; graphicsContext = nullptr; INFO_LOG(Log::System, "Done shutting down GL"); diff --git a/ios/ViewControllerMetal.mm b/ios/ViewControllerMetal.mm index dfccad3aa2..22b238a1a4 100644 --- a/ios/ViewControllerMetal.mm +++ b/ios/ViewControllerMetal.mm @@ -17,6 +17,7 @@ #include "Common/System/Request.h" #include "Common/GraphicsContext.h" #include "Common/Thread/ThreadUtil.h" +#include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -38,94 +39,28 @@ class IOSVulkanContext : public GraphicsContext { public: IOSVulkanContext() {} ~IOSVulkanContext() { - delete g_Vulkan; - g_Vulkan = nullptr; + delete vulkan_; + vulkan_ = nullptr; } - bool InitAPI(); + bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; + void ShutdownAPI() override; - bool InitFromRenderThread(CAMetalLayer *layer, int desiredBackbufferSizeX, int desiredBackbufferSizeY); - void ShutdownFromRenderThread() override; // Inverses InitFromRenderThread. + bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; + void ShutdownSurface() override; - void Shutdown() override; void Resize() override {} - void *GetAPIContext() override { return g_Vulkan; } + void *GetAPIContext() override { return vulkan_; } Draw::DrawContext *GetDrawContext() override { return draw_; } private: - VulkanContext *g_Vulkan = nullptr; + VulkanContext *vulkan_ = nullptr; Draw::DrawContext *draw_ = nullptr; GraphicsContextState state_ = GraphicsContextState::PENDING; }; -bool IOSVulkanContext::InitFromRenderThread(CAMetalLayer *layer, int desiredBackbufferSizeX, int desiredBackbufferSizeY) { - INFO_LOG(Log::G3D, "IOSVulkanContext::InitFromRenderThread: desiredwidth=%d desiredheight=%d", desiredBackbufferSizeX, desiredBackbufferSizeY); - if (!g_Vulkan) { - ERROR_LOG(Log::G3D, "IOSVulkanContext::InitFromRenderThread: No Vulkan context"); - return false; - } - - VkResult res = g_Vulkan->InitSurface(WINDOWSYSTEM_METAL_EXT, (__bridge void *)layer, nullptr); - if (res != VK_SUCCESS) { - ERROR_LOG(Log::G3D, "g_Vulkan->InitSurface failed: '%s'", VulkanResultToString(res)); - return false; - } - - bool useMultiThreading = g_Config.bRenderMultiThreading; - if (g_Config.iInflightFrames == 1) { - useMultiThreading = false; - } - - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, useMultiThreading); - - VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); - - // This MUST run on the main thread. We're taking our chances with a dispatch_sync here. - g_Vulkan->InitSwapchain(presentMode); - - if (false) { - delete draw_; - ERROR_LOG(Log::G3D, "InitSwapchain failed"); - g_Vulkan->DestroySwapchain(); - g_Vulkan->DestroySurface(); - g_Vulkan->DestroyDevice(); - g_Vulkan->DestroyInstance(); - return false; - } - - SetGPUBackend(GPUBackend::VULKAN); - bool shaderSuccess = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. - _assert_msg_(shaderSuccess, "Failed to compile preset shaders"); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - - VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager->SetInflightFrames(g_Config.iInflightFrames); - return true; -} - -void IOSVulkanContext::ShutdownFromRenderThread() { - INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown"); - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - delete draw_; - draw_ = nullptr; - g_Vulkan->WaitUntilQueueIdle(); - g_Vulkan->PerformPendingDeletes(); - g_Vulkan->DestroySwapchain(); - g_Vulkan->DestroySurface(); - INFO_LOG(Log::G3D, "Done with ShutdownFromRenderThread"); -} - -void IOSVulkanContext::Shutdown() { - INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown"); - g_Vulkan->DestroyDevice(); - g_Vulkan->DestroyInstance(); - // We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app. - finalize_glslang(); - INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown completed"); -} - -bool IOSVulkanContext::InitAPI() { +bool IOSVulkanContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { INFO_LOG(Log::G3D, "IOSVulkanContext::Init"); init_glslang(); @@ -136,36 +71,92 @@ bool IOSVulkanContext::InitAPI() { INFO_LOG(Log::G3D, "Creating Vulkan context"); Version gitVer(PPSSPP_GIT_VERSION); - std::string errorStr; - if (!VulkanLoad(&errorStr)) { - ERROR_LOG(Log::G3D, "Failed to load Vulkan driver library: %s", errorStr.c_str()); + if (!VulkanLoad(errorMessage)) { + ERROR_LOG(Log::G3D, "Failed to load Vulkan driver library: %s", errorMessage->c_str()); state_ = GraphicsContextState::FAILED_INIT; return false; } - if (!g_Vulkan) { + if (!vulkan_) { // TODO: Assert if g_Vulkan already exists here? - g_Vulkan = new VulkanContext(); + vulkan_ = new VulkanContext(); } VulkanContext::CreateInfo info{}; InitVulkanCreateInfoFromConfig(&info); - if (!g_Vulkan->CreateInstanceAndDevice(info)) { - delete g_Vulkan; - g_Vulkan = nullptr; + if (!vulkan_->CreateInstanceAndDevice(info, &g_Config.sVulkanDevice)) { + delete vulkan_; + vulkan_ = nullptr; state_ = GraphicsContextState::FAILED_INIT; return false; } - g_Vulkan->SetCbGetDrawSize([]() { + INFO_LOG(Log::G3D, "Vulkan device created!"); + return true; +} + +bool IOSVulkanContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { + _dbg_assert_(winsys == WINDOWSYSTEM_METAL_EXT); + CAMetalLayer *layer = (__bridge CAMetalLayer *)data1; + + _assert_(vulkan_); + + VkResult res = vulkan_->InitSurface(winsys, data1, nullptr); + if (res != VK_SUCCESS) { + ERROR_LOG(Log::G3D, "g_Vulkan->InitSurface failed: '%s'", VulkanResultToString(res)); + *errorMessage = StringFromFormat("InitSurface failed: %s", VulkanResultToString(res)); + return false; + } + + bool useMultiThreading = g_Config.bRenderMultiThreading; + if (g_Config.iInflightFrames == 1) { + useMultiThreading = false; + } + + draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading); + + VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_); + + // This MUST run on the main thread. We're taking our chances with a dispatch_sync here. + vulkan_->InitSwapchain(presentMode); + + SetGPUBackend(GPUBackend::VULKAN); + bool shaderSuccess = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. + _assert_msg_(shaderSuccess, "Failed to compile preset shaders"); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->SetInflightFrames(g_Config.iInflightFrames); + + vulkan_->SetCbGetDrawSize([]() { return VkExtent2D {(uint32_t)g_display.pixel_xres, (uint32_t)g_display.pixel_yres}; }); - INFO_LOG(Log::G3D, "Vulkan device created!"); state_ = GraphicsContextState::INITIALIZED; return true; } +void IOSVulkanContext::ShutdownSurface() { + INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + delete draw_; + draw_ = nullptr; + vulkan_->WaitUntilQueueIdle(); + vulkan_->PerformPendingDeletes(); + vulkan_->DestroySwapchain(); + vulkan_->DestroySurface(); + INFO_LOG(Log::G3D, "Done with ShutdownFromRenderThread"); +} + +void IOSVulkanContext::ShutdownAPI() { + INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown"); + vulkan_->DestroyDevice(); + vulkan_->DestroyInstance(); + // We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app. + finalize_glslang(); + INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown completed"); +} + #pragma mark - #pragma mark PPSSPPViewControllerMetal @@ -189,16 +180,10 @@ static std::thread g_renderLoopThread; // Should be very similar to the Android one, probably mergeable. // This is the EmuThread for iOS. -void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLayer) { +static void VulkanRenderLoop(GraphicsContext *graphicsContext, CAMetalLayer *metalLayer) { SetCurrentThreadName("EmuThreadVulkan"); INFO_LOG(Log::G3D, "Entering EmuThreadVulkan"); - - if (!graphicsContext) { - ERROR_LOG(Log::G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context."); - renderLoopRunning = false; - exitRenderLoop = false; - return; - } + _assert_(graphicsContext); if (exitRenderLoop) { WARN_LOG(Log::G3D, "runVulkanRenderLoop: ExitRenderLoop requested at start, skipping the whole thing."); @@ -210,13 +195,10 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye // This is up here to prevent race conditions, in case we pause during init. renderLoopRunning = true; - int desiredBackbufferSizeX = g_display.pixel_xres; - int desiredBackbufferSizeY = g_display.pixel_yres; - //WARN_LOG(G3D, "runVulkanRenderLoop. desiredBackbufferSizeX=%d desiredBackbufferSizeY=%d", // desiredBackbufferSizeX, desiredBackbufferSizeY); - - if (!graphicsContext->InitFromRenderThread(metalLayer, desiredBackbufferSizeX, desiredBackbufferSizeY)) { + std::string errorMessage; + if (!graphicsContext->InitSurface(WINDOWSYSTEM_METAL_EXT, (__bridge void *)metalLayer, nullptr, &errorMessage)) { // On Android, if we get here, really no point in continuing. // The UI is supposed to render on any device both on OpenGL and Vulkan. If either of those don't work // on a device, we blacklist it. Hopefully we should have already failed in InitAPI anyway and reverted to GL back then. @@ -249,7 +231,7 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye // Shut the graphics context down to the same state it was in when we entered the render thread. INFO_LOG(Log::G3D, "Shutting down graphics context..."); - graphicsContext->ShutdownFromRenderThread(); + graphicsContext->ShutdownSurface(); renderLoopRunning = false; exitRenderLoop = false; @@ -284,6 +266,7 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye _assert_(g_renderLoopThread.joinable()); exitRenderLoop = true; g_renderLoopThread.join(); + _assert_(!g_renderLoopThread.joinable()); } @@ -312,8 +295,10 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye g_Config.Save("shutdown vk"); + // Hopefully requestExitVulkanRenderLoop has been called from willResignActive here... + if (graphicsContext) { - graphicsContext->Shutdown(); + graphicsContext->ShutdownAPI(); delete graphicsContext; graphicsContext = NULL; } @@ -344,13 +329,17 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye // self.view.clipsToBounds = YES; graphicsContext = new IOSVulkanContext(); + std::string errorMessage; + if (!graphicsContext->InitAPI(nullptr, &g_Config.sVulkanDevice, &errorMessage)) { + ERROR_LOG(Log::System, "Failed to initialize Vulkan, switching to OpenGL: %s", errorMessage.c_str()); + g_Config.iGPUBackend = (int)GPUBackend::OPENGL; + SetGPUBackend(GPUBackend::OPENGL); + delete graphicsContext; + // TODO: What to do here? + } [self updateResolutionWithView:self.view]; - if (!graphicsContext->InitAPI()) { - _assert_msg_(false, "Failed to init Vulkan"); - } - if ([[GCController controllers] count] > 0) { [self setupController:[[GCController controllers] firstObject]]; } diff --git a/libretro/LibretroD3D11Context.cpp b/libretro/LibretroD3D11Context.cpp index 5950e35222..dc784d1726 100644 --- a/libretro/LibretroD3D11Context.cpp +++ b/libretro/LibretroD3D11Context.cpp @@ -11,8 +11,8 @@ #define __uuidof(type) IID_##type #endif -bool LibretroD3D11Context::Init() { - if (!LibretroHWRenderContext::Init(true)) { +bool LibretroD3D11Context::InitAPI(void *wnd, std::string *deviceName, std::string *error_message) { + if (!LibretroHWRenderContext::InitHW(true)) { return false; } diff --git a/libretro/LibretroD3D11Context.h b/libretro/LibretroD3D11Context.h index b47755876c..6b195baa6b 100644 --- a/libretro/LibretroD3D11Context.h +++ b/libretro/LibretroD3D11Context.h @@ -7,7 +7,7 @@ class LibretroD3D11Context : public LibretroHWRenderContext { public: LibretroD3D11Context() : LibretroHWRenderContext(RETRO_HW_CONTEXT_D3D11, 11) {} - bool Init() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *error_message) override; void SwapBuffers() override; void GotBackbuffer() override; diff --git a/libretro/LibretroGLContext.cpp b/libretro/LibretroGLContext.cpp index 45a33eb69b..1f4bb6e5fc 100644 --- a/libretro/LibretroGLContext.cpp +++ b/libretro/LibretroGLContext.cpp @@ -7,8 +7,8 @@ #include "libretro/LibretroGLContext.h" -bool LibretroGLContext::Init() { - if (!LibretroHWRenderContext::Init(false)) +bool LibretroGLContext::InitAPI(void *wnd, std::string *deviceName, std::string *error_message) { + if (!LibretroHWRenderContext::InitHW(false)) return false; g_Config.iGPUBackend = (int)GPUBackend::OPENGL; diff --git a/libretro/LibretroGLContext.h b/libretro/LibretroGLContext.h index 6ca8691a28..296e6efd1c 100644 --- a/libretro/LibretroGLContext.h +++ b/libretro/LibretroGLContext.h @@ -16,9 +16,9 @@ public: hw_render_.bottom_left_origin = true; } - bool NeedsRenderThread() const override { return true; } + bool NeedsSeparateEmuThread() const override { return true; } - bool Init() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *error_message) override; void CreateDrawContext() override; void DestroyDrawContext() override; void SetRenderTarget() override { diff --git a/libretro/LibretroGLCoreContext.cpp b/libretro/LibretroGLCoreContext.cpp index 336e61db9a..968fcf2059 100644 --- a/libretro/LibretroGLCoreContext.cpp +++ b/libretro/LibretroGLCoreContext.cpp @@ -7,8 +7,8 @@ #include "libretro/LibretroGLCoreContext.h" -bool LibretroGLCoreContext::Init() { - if (!LibretroHWRenderContext::Init(false)) +bool LibretroGLCoreContext::InitAPI(void *wnd, std::string *deviceName, std::string *error_message) { + if (!LibretroHWRenderContext::InitHW(false)) return false; g_Config.iGPUBackend = (int)GPUBackend::OPENGL; diff --git a/libretro/LibretroGLCoreContext.h b/libretro/LibretroGLCoreContext.h index c84e741aa9..e97bb128e0 100644 --- a/libretro/LibretroGLCoreContext.h +++ b/libretro/LibretroGLCoreContext.h @@ -10,9 +10,9 @@ public: : LibretroHWRenderContext(RETRO_HW_CONTEXT_OPENGL_CORE, 3, 1) { hw_render_.bottom_left_origin = true; } - bool NeedsRenderThread() const override { return true; } + bool NeedsSeparateEmuThread() const override { return true; } - bool Init() override; + bool InitAPI(void *wnd, std::string *deviceName, std::string *error_message) override; void CreateDrawContext() override; void DestroyDrawContext() override; void SetRenderTarget() override { diff --git a/libretro/LibretroGraphicsContext.cpp b/libretro/LibretroGraphicsContext.cpp index cc7a272e27..310bb3ba39 100644 --- a/libretro/LibretroGraphicsContext.cpp +++ b/libretro/LibretroGraphicsContext.cpp @@ -22,7 +22,7 @@ void retro_set_video_refresh(retro_video_refresh_t cb) { LibretroGraphicsContext static void context_reset() { ((LibretroHWRenderContext *)Libretro::ctx)->ContextReset(); } static void context_destroy() { ((LibretroHWRenderContext *)Libretro::ctx)->ContextDestroy(); } -bool LibretroHWRenderContext::Init(bool cache_context) { +bool LibretroHWRenderContext::InitHW(bool cache_context) { hw_render_.cache_context = cache_context; if (!Libretro::environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render_)) return false; @@ -83,7 +83,7 @@ void LibretroHWRenderContext::ContextDestroy() { } if (!hw_render_.cache_context && !Libretro::useEmuThread) { - Shutdown(); + ShutdownAPI(); } } @@ -101,13 +101,15 @@ LibretroGraphicsContext *LibretroGraphicsContext::CreateGraphicsContext() { if (Libretro::backend != RETRO_HW_CONTEXT_DUMMY) preferred = Libretro::backend; + std::string errorMessage; #ifndef USING_GLES2 if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_OPENGL_CORE) { ctx = new LibretroGLCoreContext(); - if (ctx->Init()) { + if (ctx->InitAPI(nullptr, nullptr, &errorMessage)) { return ctx; } + // Failed? Try next. delete ctx; } #endif @@ -115,20 +117,24 @@ LibretroGraphicsContext *LibretroGraphicsContext::CreateGraphicsContext() { if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_OPENGL || preferred == RETRO_HW_CONTEXT_OPENGLES3) { ctx = new LibretroGLContext(); - if (ctx->Init()) { + if (ctx->InitAPI(nullptr, nullptr, &errorMessage)) { return ctx; } - delete ctx; + + // Failed? Try next. + delete ctx; } #ifndef HAVE_LIBNX if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_VULKAN) { ctx = new LibretroVulkanContext(); - if (ctx->Init()) { + if (ctx->InitAPI(nullptr, nullptr, &errorMessage)) { return ctx; } - delete ctx; + + // Failed? Try next. + delete ctx; } #endif @@ -136,15 +142,18 @@ LibretroGraphicsContext *LibretroGraphicsContext::CreateGraphicsContext() { if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_D3D11) { ctx = new LibretroD3D11Context(); - if (ctx->Init()) { + if (ctx->InitAPI(nullptr, nullptr, &errorMessage)) { return ctx; } + + // Failed? Try next. delete ctx; } #endif + // End of the line. ctx = new LibretroSoftwareContext(); - ctx->Init(); + ctx->InitAPI(nullptr, nullptr, &errorMessage); return ctx; } diff --git a/libretro/LibretroGraphicsContext.h b/libretro/LibretroGraphicsContext.h index 9dcdf74a8c..78016ab607 100644 --- a/libretro/LibretroGraphicsContext.h +++ b/libretro/LibretroGraphicsContext.h @@ -19,14 +19,13 @@ class LibretroGraphicsContext : public GraphicsContext { public: LibretroGraphicsContext() {} - ~LibretroGraphicsContext() override { Shutdown(); } + ~LibretroGraphicsContext() override { ShutdownAPI(); } - virtual bool Init() = 0; virtual void SetRenderTarget() {} virtual GPUCore GetGPUCore() = 0; virtual const char *Ident() = 0; - void Shutdown() override { + void ShutdownAPI() override { DestroyDrawContext(); } virtual void SwapBuffers() = 0; @@ -56,7 +55,7 @@ protected: class LibretroHWRenderContext : public LibretroGraphicsContext { public: LibretroHWRenderContext(retro_hw_context_type context_type, unsigned version_major = 0, unsigned version_minor = 0); - bool Init(bool cache_context); + bool InitHW(bool cache_context); void SetRenderTarget() override {} void SwapBuffers() override { video_cb(RETRO_HW_FRAME_BUFFER_VALID, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, 0); @@ -71,7 +70,6 @@ protected: class LibretroSoftwareContext : public LibretroGraphicsContext { public: LibretroSoftwareContext() {} - bool Init() override { return true; } void SwapBuffers() override { GPUDebugBuffer buf; u16 w = NATIVEWIDTH; diff --git a/libretro/LibretroVulkanContext.cpp b/libretro/LibretroVulkanContext.cpp index d222cd03cd..faffcecc69 100644 --- a/libretro/LibretroVulkanContext.cpp +++ b/libretro/LibretroVulkanContext.cpp @@ -90,8 +90,8 @@ static const VkApplicationInfo *GetApplicationInfo(void) { return &app_info; } -bool LibretroVulkanContext::Init() { - if (!LibretroHWRenderContext::Init(true)) { +bool LibretroVulkanContext::InitAPI(void *wnd, std::string *deviceName, std::string *error_message) { + if (!LibretroHWRenderContext::InitHW(true)) { return false; } @@ -149,7 +149,7 @@ void LibretroVulkanContext::CreateDrawContext() { SetGPUBackend(GPUBackend::VULKAN); } -void LibretroVulkanContext::Shutdown() { +void LibretroVulkanContext::ShutdownAPI() { if (!vk) { return; } @@ -157,7 +157,7 @@ void LibretroVulkanContext::Shutdown() { if (draw_) draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vk->GetBackbufferWidth(), vk->GetBackbufferHeight()); - LibretroHWRenderContext::Shutdown(); + LibretroHWRenderContext::ShutdownAPI(); vk->WaitUntilQueueIdle(); diff --git a/libretro/LibretroVulkanContext.h b/libretro/LibretroVulkanContext.h index 49227f6b3a..5fa83f1daf 100644 --- a/libretro/LibretroVulkanContext.h +++ b/libretro/LibretroVulkanContext.h @@ -5,9 +5,9 @@ class LibretroVulkanContext : public LibretroHWRenderContext { public: LibretroVulkanContext(); - ~LibretroVulkanContext() override { Shutdown(); } - bool Init() override; - void Shutdown() override; + ~LibretroVulkanContext() override { ShutdownAPI(); } + bool InitAPI(void *wnd, std::string *deviceName, std::string *error_message) override; + void ShutdownAPI() override; void SwapBuffers() override; void *GetAPIContext() override;