diff --git a/CMakeLists.txt b/CMakeLists.txt index 95db991510..bf9b4c99c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -776,7 +776,9 @@ elseif(TARGET SDL2::SDL2) set(nativeExtra ${nativeExtra} SDL/SDLJoystick.h SDL/SDLJoystick.cpp - SDL/SDLMain.cpp) + SDL/SDLMain.cpp + SDL/SDLGLGraphicsContext.cpp + SDL/SDLVulkanGraphicsContext.cpp) set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2) if(APPLE) set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index 1410d985a1..54c2cb6f46 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -359,7 +359,7 @@ void VulkanDeviceAllocator::ExecuteFree(FreeInfo *userdata) { slab.allocSizes.erase(it); } else { // Ack, a double free? - _assert_msg_(G3D, false, "Double free? Block missing at offset %d", userdata->offset); + _assert_msg_(G3D, false, "Double free? Block missing at offset %d", (int)userdata->offset); } found = true; break; diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index c9dc744843..5b80b23e79 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -395,8 +395,8 @@ void PSPSaveDialog::DisplaySaveDataInfo1() } else { char title[512]; char time[512]; - char saveTitle[512]; - char saveDetail[512]; + char saveTitle[1024]; + char saveDetail[1024]; char am_pm[] = "AM"; char hour_time[10] ; @@ -421,27 +421,27 @@ void PSPSaveDialog::DisplaySaveDataInfo1() snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm); } - snprintf(title, 512, "%s", param.GetFileInfo(currentSelectedSave).title); + snprintf(title, sizeof(title), "%s", param.GetFileInfo(currentSelectedSave).title); int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday; int month = param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1; int year = param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900; s64 sizeK = param.GetFileInfo(currentSelectedSave).size / 1024; switch (g_Config.iDateFormat) { case 1: - snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); + snprintf(time, sizeof(time), "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); break; case 2: - snprintf(time, 512, "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK); + snprintf(time, sizeof(time), "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK); break; case 3: - snprintf(time, 512, "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK); + snprintf(time, sizeof(time), "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK); break; default: - snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); + snprintf(time, sizeof(time), "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); } - snprintf(saveTitle, 512, "%s", param.GetFileInfo(currentSelectedSave).saveTitle); - snprintf(saveDetail, 512, "%s", param.GetFileInfo(currentSelectedSave).saveDetail); - + snprintf(saveTitle, sizeof(saveTitle), "%s", param.GetFileInfo(currentSelectedSave).saveTitle); + snprintf(saveDetail, sizeof(saveDetail), "%s", param.GetFileInfo(currentSelectedSave).saveDetail); + PPGeDrawRect(180, 136, 480, 137, CalcFadedColor(0xFFFFFFFF)); std::string titleTxt = title; std::string timeTxt = time; diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index ea364b08a5..16f3292a84 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -140,8 +140,7 @@ float CalculateDPIScale() #endif } -static int mainInternal(QApplication &a) -{ +static int mainInternal(QApplication &a) { #ifdef MOBILE_DEVICE emugl = new MainUI(); emugl->resize(pixel_xres, pixel_yres); @@ -186,15 +185,19 @@ static int mainInternal(QApplication &a) QScopedPointer audio(new MainAudio()); audio->run(); #endif - return a.exec(); + int retval = a.exec(); + delete emugl; + return retval; } void MainUI::EmuThreadFunc() { ILOG("In emu thread"); setCurrentThreadName("Emu"); - // There's no real requirement that NativeInit happen on this thread. - // We just call the update/render loop here. + // There's no real requirement that NativeInit happen on this thread, though it can't hurt... + // We just call the update/render loop here. NativeInitGraphics should be here though. + NativeInitGraphics(graphicsContext); + emuThreadState = (int)EmuThreadState::RUNNING; while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) { #ifdef SDL @@ -204,6 +207,8 @@ void MainUI::EmuThreadFunc() { time_update(); UpdateRunLoop(); } + NativeShutdownGraphics(); + emuThreadState = (int)EmuThreadState::STOPPED; } @@ -237,13 +242,14 @@ MainUI::MainUI(QWidget *parent): MainUI::~MainUI() { - if (useThread_) { + ILOG("MainUI::Destructor"); + if (emuThreadState != (int)EmuThreadState::DISABLED) { + ILOG("EmuThreadStop"); EmuThreadStop(); } #if defined(MOBILE_DEVICE) delete acc; #endif - NativeShutdownGraphics(); graphicsContext->Shutdown(); delete graphicsContext; graphicsContext = nullptr; @@ -365,34 +371,30 @@ void MainUI::initializeGL() glGetError(); #endif ILOG("Initializing graphics context"); - graphicsContext = new QtDummyGraphicsContext(); - NativeInitGraphics(graphicsContext); // OpenGL uses a background thread to do the main processing and only renders on the gl thread. - useThread_ = g_Config.iGPUBackend == (int)GPUBackend::OPENGL; + graphicsContext = new QtDummyGraphicsContext(); - if (useThread_) { + if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { ILOG("Using thread, starting emu thread"); EmuThreadStart(); - - graphicsContext->ThreadStart(); } else { ILOG("Not using thread, backend=%d", (int)g_Config.iGPUBackend); } + graphicsContext->ThreadStart(); } void MainUI::paintGL() { - if (useThread_) { + #ifdef SDL + SDL_PumpEvents(); + #endif + updateAccelerometer(); + if (emuThreadState == (int)EmuThreadState::DISABLED) { + UpdateRunLoop(); + } else { graphicsContext->ThreadFrame(); // Do the rest in EmuThreadFunc - } else { - #ifdef SDL - SDL_PumpEvents(); - #endif - updateAccelerometer(); - time_update(); - UpdateRunLoop(); } } @@ -518,8 +520,8 @@ int main(int argc, char *argv[]) g_Config.iGPUBackend = (int)GPUBackend::OPENGL; int ret = mainInternal(a); + ILOG("Left mainInternal here."); - NativeShutdownGraphics(); #ifdef SDL SDL_PauseAudio(1); SDL_CloseAudio(); diff --git a/Qt/QtMain.h b/Qt/QtMain.h index 8f0538f7c6..fc04446b00 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -131,8 +131,6 @@ private: std::thread emuThread; std::atomic emuThreadState; - - bool useThread_ = false; }; extern MainUI* emugl; diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 7e0b481f9b..c9469c971e 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -72,6 +72,17 @@ void MainWindow::newFrame() updateMenus(); } + + std::unique_lock lock(msgMutex_); + while (!msgQueue_.empty()) { + MainWindowMsg msg = msgQueue_.front(); + msgQueue_.pop(); + switch (msg) { + case MainWindowMsg::BOOT_DONE: + bootDone(); + break; + } + } } void MainWindow::updateMenus() @@ -128,8 +139,7 @@ void MainWindow::updateMenus() emit updateMenu(); } -/* SLOTS */ -void MainWindow::Boot() +void MainWindow::bootDone() { dialogDisasm = new Debugger_Disasm(currentDebugMIPS, this, this); if(g_Config.bShowDebuggerOnLoad) @@ -149,6 +159,7 @@ void MainWindow::Boot() updateMenus(); } +/* SIGNALS */ void MainWindow::openAct() { QString filename = QFileDialog::getOpenFileName(NULL, "Load File", g_Config.currentDirectory.c_str(), "PSP ROMs (*.pbp *.elf *.iso *.cso *.prx)"); diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index c52120820e..3e9c5469e8 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -1,5 +1,7 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#pragma once + +#include +#include #include #include @@ -21,6 +23,11 @@ extern bool g_TakeScreenshot; class MenuAction; class MenuTree; +// hacky, should probably use qt signals or something, but whatever.. +enum class MainWindowMsg { + BOOT_DONE +}; + class MainWindow : public QMainWindow { Q_OBJECT @@ -38,6 +45,11 @@ public: void ShowMemory(u32 addr); void updateMenus(); + void Notify(MainWindowMsg msg) { + std::unique_lock lock(msgMutex_); + msgQueue_.push(msg); + } + protected: void changeEvent(QEvent *e) { @@ -54,7 +66,6 @@ signals: void updateMenu(); public slots: - void Boot(); void newFrame(); private slots: @@ -136,6 +147,7 @@ private slots: void langChanged(QAction *action) { loadLanguage(action->data().toString(), true); } private: + void bootDone(); void SetWindowScale(int zoom); void SetGameTitle(QString text); void loadLanguage(const QString &language, bool retranslate); @@ -155,6 +167,9 @@ private: QActionGroup *anisotropicGroup, *screenGroup, *displayLayoutGroup, *defaultLogGroup, *g3dLogGroup, *hleLogGroup; + + std::queue msgQueue_; + std::mutex msgMutex_; }; class MenuAction : public QAction @@ -275,5 +290,3 @@ public slots: private: const char *_text; }; - -#endif // MAINWINDOW_H diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp new file mode 100644 index 0000000000..f8f69fd02c --- /dev/null +++ b/SDL/SDLGLGraphicsContext.cpp @@ -0,0 +1,142 @@ +#include "SDLGLGraphicsContext.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "base/NativeApp.h" +#include "base/display.h" +#include "gfx_es2/gpu_features.h" + +class GLRenderManager; + +// Returns 0 on success. +int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { + struct GLVersionPair { + int major; + int minor; + }; + GLVersionPair attemptVersions[] = { +#ifdef USING_GLES2 + {3, 2}, {3, 1}, {3, 0}, {2, 0}, +#else + {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, + {3, 3}, {3, 2}, {3, 1}, {3, 0}, +#endif + }; + +#ifdef USING_GLES2 + mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN; +#else + mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; +#endif + SDL_GLContext glContext = nullptr; + for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) { + const auto &ver = attemptVersions[i]; + // Make sure to request a somewhat modern GL context at least - the + // latest supported by MacOS X (really, really sad...) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, ver.major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, ver.minor); +#ifdef USING_GLES2 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SetGLCoreContext(false); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SetGLCoreContext(true); +#endif + + window = SDL_CreateWindow("PPSSPP", x,y, pixel_xres, pixel_yres, mode); + if (!window) { + NativeShutdown(); + fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + continue; + } + + glContext = SDL_GL_CreateContext(window); + if (glContext != nullptr) { + // Victory, got one. + break; + } + + // Let's keep trying. To be safe, destroy the window - docs say needed to change profile. + // in practice, it doesn't seem to matter, but maybe it differs by platform. + SDL_DestroyWindow(window); + } + + 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); + SetGLCoreContext(false); + + window = SDL_CreateWindow("PPSSPP", x,y, pixel_xres, pixel_yres, mode); + if (window == nullptr) { + NativeShutdown(); + fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + SDL_Quit(); + return 2; + } + + glContext = SDL_GL_CreateContext(window); + if (glContext == nullptr) { + NativeShutdown(); + fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + SDL_Quit(); + return 2; + } + } + +#ifdef USING_EGL + EGL_Init(); +#endif + +#ifndef USING_GLES2 + // Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc. + // glewExperimental allows us to force GLEW to search for the pointers anyway. + if (gl_extensions.IsCoreContext) { + glewExperimental = true; + } + if (GLEW_OK != glewInit()) { + printf("Failed to initialize glew!\n"); + return 1; + } + // Unfortunately, glew will generate an invalid enum error, ignore. + if (gl_extensions.IsCoreContext) + glGetError(); + + if (GLEW_VERSION_2_0) { + printf("OpenGL 2.0 or higher.\n"); + } else { + printf("Sorry, this program requires OpenGL 2.0.\n"); + return 1; + } +#endif + + // Finally we can do the regular initialization. + CheckGLExtensions(); + draw_ = Draw::T3DCreateGLContext(); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + SetGPUBackend(GPUBackend::OPENGL); + bool success = draw_->CreatePresets(); + assert(success); + renderManager_->SetSwapFunction([&]() { +#ifdef USING_EGL + eglSwapBuffers(g_eglDisplay, g_eglSurface); +#else + SDL_GL_SwapWindow(window_); +#endif + }); + window_ = window; + return 0; +} + +void SDLGLGraphicsContext::Shutdown() { +} + +void SDLGLGraphicsContext::ShutdownFromRenderThread() { + delete draw_; + draw_ = nullptr; + +#ifdef USING_EGL + EGL_Close(); +#else + SDL_GL_DeleteContext(glContext); +#endif +} diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h new file mode 100644 index 0000000000..71f130a4f7 --- /dev/null +++ b/SDL/SDLGLGraphicsContext.h @@ -0,0 +1,48 @@ +#if !defined(__APPLE__) +#include "SDL_syswm.h" +#endif +#include "SDL.h" + +#include "thin3d/GLRenderManager.h" +#include "gfx/gl_common.h" +#include "Common/GraphicsContext.h" + +class SDLGLGraphicsContext : public DummyGraphicsContext { +public: + SDLGLGraphicsContext() { + } + + // Returns 0 on success. + int Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message); + + void Shutdown() override; + void ShutdownFromRenderThread() override; + + void SwapBuffers() override { + renderManager_->Swap(); + } + + Draw::DrawContext *GetDrawContext() override { + return draw_; + } + + void ThreadStart() override { + renderManager_->ThreadStart(); + } + + bool ThreadFrame() override { + return renderManager_->ThreadFrame(); + } + + void ThreadEnd() override { + renderManager_->ThreadEnd(); + } + +private: + Draw::DrawContext *draw_ = nullptr; + SDL_Window *window_; + SDL_GLContext glContext = nullptr; + GLRenderManager *renderManager_ = nullptr; +}; + + diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index a3a15581e0..e6258fbe7d 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -24,20 +24,15 @@ SDLJoystick *joystick = NULL; #include "base/logging.h" #include "base/timeutil.h" #include "ext/glslang/glslang/Public/ShaderLang.h" -#include "gfx/gl_common.h" -#include "gfx_es2/gpu_features.h" #include "input/input_state.h" #include "input/keycodes.h" #include "net/resolve.h" #include "base/NKCodeFromSDL.h" #include "util/const_map.h" #include "util/text/utf8.h" -#include "util/text/parsers.h" #include "math/math_util.h" #include "thin3d/GLRenderManager.h" #include "thread/threadutil.h" -#include "Common/Vulkan/VulkanContext.h" -#include "Common/Vulkan/VulkanDebug.h" #include "math.h" #if !defined(__APPLE__) @@ -61,6 +56,9 @@ SDLJoystick *joystick = NULL; #include "Core/Core.h" #include "Core/Config.h" #include "Common/GraphicsContext.h" +#include "SDLGLGraphicsContext.h" +#include "SDLVulkanGraphicsContext.h" + GlobalUIState lastUIState = UISTATE_MENU; GlobalUIState GetUIState(); @@ -72,8 +70,6 @@ static int g_QuitRequested = 0; static int g_DesktopWidth = 0; static int g_DesktopHeight = 0; -static VulkanLogOptions g_LogOptions; - #if defined(USING_EGL) static EGLDisplay g_eglDisplay = NULL; @@ -202,318 +198,6 @@ void EGL_Close() { } #endif -class GLRenderManager; - -class SDLGLGraphicsContext : public DummyGraphicsContext { -public: - SDLGLGraphicsContext() { - } - ~SDLGLGraphicsContext() { - delete draw_; - } - - // Returns 0 on success. - int Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message); - - void Shutdown() override { -#ifdef USING_EGL - EGL_Close(); -#else - SDL_GL_DeleteContext(glContext); -#endif - } - - void SwapBuffers() override { - renderManager_->Swap(); - } - - Draw::DrawContext *GetDrawContext() override { - return draw_; - } - - void ThreadStart() override { - renderManager_->ThreadStart(); - } - - bool ThreadFrame() override { - return renderManager_->ThreadFrame(); - } - - void ThreadEnd() override { - renderManager_->ThreadEnd(); - } - -private: - Draw::DrawContext *draw_ = nullptr; - SDL_Window *window_; - SDL_GLContext glContext = nullptr; - GLRenderManager *renderManager_ = nullptr; -}; - -// Returns 0 on success. -int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { - struct GLVersionPair { - int major; - int minor; - }; - GLVersionPair attemptVersions[] = { -#ifdef USING_GLES2 - {3, 2}, {3, 1}, {3, 0}, {2, 0}, -#else - {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, - {3, 3}, {3, 2}, {3, 1}, {3, 0}, -#endif - }; - -#ifdef USING_GLES2 - mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN; -#else - mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; -#endif - SDL_GLContext glContext = nullptr; - for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) { - const auto &ver = attemptVersions[i]; - // Make sure to request a somewhat modern GL context at least - the - // latest supported by MacOS X (really, really sad...) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, ver.major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, ver.minor); -#ifdef USING_GLES2 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SetGLCoreContext(false); -#else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SetGLCoreContext(true); -#endif - - window = SDL_CreateWindow("PPSSPP", x,y, pixel_xres, pixel_yres, mode); - if (!window) { - NativeShutdown(); - fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); - continue; - } - - glContext = SDL_GL_CreateContext(window); - if (glContext != nullptr) { - // Victory, got one. - break; - } - - // Let's keep trying. To be safe, destroy the window - docs say needed to change profile. - // in practice, it doesn't seem to matter, but maybe it differs by platform. - SDL_DestroyWindow(window); - } - - 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); - SetGLCoreContext(false); - - window = SDL_CreateWindow("PPSSPP", x,y, pixel_xres, pixel_yres, mode); - if (window == nullptr) { - NativeShutdown(); - fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); - SDL_Quit(); - return 2; - } - - glContext = SDL_GL_CreateContext(window); - if (glContext == nullptr) { - NativeShutdown(); - fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); - SDL_Quit(); - return 2; - } - } - -#ifdef USING_EGL - EGL_Init(); -#endif - -#ifndef USING_GLES2 - // Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc. - // glewExperimental allows us to force GLEW to search for the pointers anyway. - if (gl_extensions.IsCoreContext) { - glewExperimental = true; - } - if (GLEW_OK != glewInit()) { - printf("Failed to initialize glew!\n"); - return 1; - } - // Unfortunately, glew will generate an invalid enum error, ignore. - if (gl_extensions.IsCoreContext) - glGetError(); - - if (GLEW_VERSION_2_0) { - printf("OpenGL 2.0 or higher.\n"); - } else { - printf("Sorry, this program requires OpenGL 2.0.\n"); - return 1; - } -#endif - - // Finally we can do the regular initialization. - CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); - renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - SetGPUBackend(GPUBackend::OPENGL); - bool success = draw_->CreatePresets(); - assert(success); - renderManager_->SetSwapFunction([&]() { -#ifdef USING_EGL - eglSwapBuffers(g_eglDisplay, g_eglSurface); -#else - SDL_GL_SwapWindow(window_); -#endif - }); - window_ = window; - return 0; -} - -class SDLVulkanGraphicsContext : public GraphicsContext { -public: - SDLVulkanGraphicsContext() {} - ~SDLVulkanGraphicsContext() { - delete draw_; - } - - bool Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message); - - void Shutdown() override; - - void SwapBuffers() override { - // We don't do it this way. - } - - void Resize() override { - /* - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - vulkan_->DestroyObjects(); - // TODO: Take from real window dimensions - int width = 1024; - int height = 768; - vulkan_->ReinitSurface(width, height); - vulkan_->InitObjects(); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - */ - } - - void SwapInterval(int interval) override { - } - void *GetAPIContext() override { - return vulkan_; - } - - Draw::DrawContext *GetDrawContext() override { - return draw_; - } -private: - Draw::DrawContext *draw_ = nullptr; - VulkanContext *vulkan_ = nullptr; -}; - -bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { - window = SDL_CreateWindow("Initializing Vulkan...", x, y, pixel_xres, pixel_yres, mode); - if (!window) { - fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); - exit(1); - } - - init_glslang(); - - g_LogOptions.breakOnError = true; - g_LogOptions.breakOnWarning = true; - g_LogOptions.msgBoxOnError = false; - - Version gitVer(PPSSPP_GIT_VERSION); - - vulkan_ = new VulkanContext(); - if (vulkan_->InitError().size()) { - *error_message = vulkan_->InitError(); - delete vulkan_; - vulkan_ = nullptr; - return false; - } - - int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; - // vulkanFlags |= VULKAN_FLAG_VALIDATE; - VulkanContext::CreateInfo info{}; - info.app_name = "PPSSPP"; - info.app_ver = gitVer.ToInteger(); - info.flags = vulkanFlags; - if (VK_SUCCESS != vulkan_->CreateInstance(info)) { - *error_message = vulkan_->InitError(); - delete vulkan_; - vulkan_ = nullptr; - return false; - } - vulkan_->ChooseDevice(vulkan_->GetBestPhysicalDevice()); - if (vulkan_->CreateDevice() != VK_SUCCESS) { - *error_message = vulkan_->InitError(); - delete vulkan_; - vulkan_ = nullptr; - return false; - } - -#if !defined(__APPLE__) - SDL_SysWMinfo sys_info{}; - SDL_VERSION(&sys_info.version); //Set SDL version - if (!SDL_GetWindowWMInfo(window, &sys_info)) { - fprintf(stderr, "Error getting SDL window wm info: %s\n", SDL_GetError()); - exit(1); - } - switch (sys_info.subsystem) { - case SDL_SYSWM_X11: -#if defined(VK_USE_PLATFORM_XLIB_KHR) - vulkan_->InitSurface(WINDOWSYSTEM_XLIB, (void*)sys_info.info.x11.display, - (void *)(intptr_t)sys_info.info.x11.window, pixel_xres, pixel_yres); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - vulkan_->InitSurface(WINDOWSYSTEM_XCB, (void*)XGetXCBConnection(sys_info.info.x11.display), - (void *)(intptr_t)sys_info.info.x11.window, pixel_xres, pixel_yres); -#endif - break; -#if defined(VK_USE_PLATFORM_WAYLAND_KHR) - case SDL_SYSWM_WAYLAND: - vulkan_->InitSurface(WINDOWSYSTEM_WAYLAND, (void*)sys_info.info.wl.display, - (void *)sys_info.info.wl.surface, pixel_xres, pixel_yres); - break; -#endif - default: - fprintf(stderr, "Vulkan subsystem %d not supported\n", sys_info.subsystem); - exit(1); - break; - } -#endif - - if (!vulkan_->InitObjects()) { - *error_message = vulkan_->InitError(); - Shutdown(); - return false; - } - - draw_ = Draw::T3DCreateVulkanContext(vulkan_, false); - SetGPUBackend(GPUBackend::VULKAN); - bool success = draw_->CreatePresets(); - assert(success); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - - return true; -} - -void SDLVulkanGraphicsContext::Shutdown() { - if (draw_) - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - delete draw_; - draw_ = nullptr; - vulkan_->WaitUntilQueueIdle(); - vulkan_->DestroyObjects(); - vulkan_->DestroyDevice(); - vulkan_->DestroyDebugMsgCallback(); - vulkan_->DestroyInstance(); - delete vulkan_; - vulkan_ = nullptr; - finalize_glslang(); -} int getDisplayNumber(void) { int displayNumber = 0; @@ -522,8 +206,7 @@ int getDisplayNumber(void) { //get environment displayNumberStr=getenv("SDL_VIDEO_FULLSCREEN_HEAD"); - if (displayNumberStr) - { + if (displayNumberStr) { displayNumber = atoi(displayNumberStr); } @@ -725,21 +408,26 @@ enum class EmuThreadState { static std::thread emuThread; static std::atomic emuThreadState((int)EmuThreadState::DISABLED); -static void EmuThreadFunc() { +static void EmuThreadFunc(GraphicsContext *graphicsContext) { setCurrentThreadName("Emu"); // There's no real requirement that NativeInit happen on this thread. // We just call the update/render loop here. emuThreadState = (int)EmuThreadState::RUNNING; + + NativeInitGraphics(graphicsContext); + while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) { UpdateRunLoop(); } emuThreadState = (int)EmuThreadState::STOPPED; + + NativeShutdownGraphics(); } -static void EmuThreadStart() { +static void EmuThreadStart(GraphicsContext *context) { emuThreadState = (int)EmuThreadState::START_REQUESTED; - emuThread = std::thread(&EmuThreadFunc); + emuThread = std::thread(&EmuThreadFunc, context); } static void EmuThreadStop() { @@ -923,6 +611,9 @@ int main(int argc, char *argv[]) { GraphicsContext *graphicsContext = nullptr; SDL_Window *window = nullptr; + + bool useEmuThread; + std::string error_message; if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { SDLGLGraphicsContext *ctx = new SDLGLGraphicsContext(); @@ -930,6 +621,7 @@ int main(int argc, char *argv[]) { printf("GL init error '%s'\n", error_message.c_str()); } graphicsContext = ctx; + useEmuThread = true; } else if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { SDLVulkanGraphicsContext *ctx = new SDLVulkanGraphicsContext(); if (!ctx->Init(window, x, y, mode, &error_message)) { @@ -943,6 +635,7 @@ int main(int argc, char *argv[]) { } else { graphicsContext = ctx; } + useEmuThread = false; } // Since we render from the main thread, there's nothing done here, but we call it to avoid confusion. @@ -956,9 +649,10 @@ int main(int argc, char *argv[]) { SDL_ShowCursor(SDL_DISABLE); #endif - NativeInitGraphics(graphicsContext); - - NativeResized(); + if (!useEmuThread) { + NativeInitGraphics(graphicsContext); + NativeResized(); + } SDL_AudioSpec fmt, ret_fmt; memset(&fmt, 0, sizeof(fmt)); @@ -996,8 +690,8 @@ int main(int argc, char *argv[]) { int framecount = 0; bool mouseDown = false; - if (GetGPUBackend() == GPUBackend::OPENGL) { - EmuThreadStart(); + if (useEmuThread) { + EmuThreadStart(graphicsContext); } graphicsContext->ThreadStart(); @@ -1198,14 +892,16 @@ int main(int argc, char *argv[]) { framecount++; } - graphicsContext->ThreadEnd(); - graphicsContext->ShutdownFromRenderThread(); - EmuThreadStop(); delete joystick; - NativeShutdownGraphics(); + + if (!useEmuThread) { + NativeShutdownGraphics(); + } graphicsContext->Shutdown(); + graphicsContext->ThreadEnd(); + graphicsContext->ShutdownFromRenderThread(); NativeShutdown(); delete graphicsContext; @@ -1217,5 +913,6 @@ int main(int argc, char *argv[]) { #endif glslang::FinalizeProcess(); + ILOG("Leaving main"); return 0; } diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp new file mode 100644 index 0000000000..2c868b2a37 --- /dev/null +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -0,0 +1,111 @@ +#include "Core/Config.h" +#include "base/NativeApp.h" +#include "base/display.h" +#include "thin3d/thin3d.h" +#include "util/text/parsers.h" + +#include "Core/System.h" +#include "SDLVulkanGraphicsContext.h" + +bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { + window = SDL_CreateWindow("Initializing Vulkan...", x, y, pixel_xres, pixel_yres, mode); + if (!window) { + fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); + exit(1); + } + + init_glslang(); + + g_LogOptions.breakOnError = true; + g_LogOptions.breakOnWarning = true; + g_LogOptions.msgBoxOnError = false; + + Version gitVer(PPSSPP_GIT_VERSION); + + vulkan_ = new VulkanContext(); + if (vulkan_->InitError().size()) { + *error_message = vulkan_->InitError(); + delete vulkan_; + vulkan_ = nullptr; + return false; + } + + int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; + // vulkanFlags |= VULKAN_FLAG_VALIDATE; + VulkanContext::CreateInfo info{}; + info.app_name = "PPSSPP"; + info.app_ver = gitVer.ToInteger(); + info.flags = vulkanFlags; + if (VK_SUCCESS != vulkan_->CreateInstance(info)) { + *error_message = vulkan_->InitError(); + delete vulkan_; + vulkan_ = nullptr; + return false; + } + vulkan_->ChooseDevice(vulkan_->GetBestPhysicalDevice()); + if (vulkan_->CreateDevice() != VK_SUCCESS) { + *error_message = vulkan_->InitError(); + delete vulkan_; + vulkan_ = nullptr; + return false; + } + +#if !defined(__APPLE__) + SDL_SysWMinfo sys_info{}; + SDL_VERSION(&sys_info.version); //Set SDL version + if (!SDL_GetWindowWMInfo(window, &sys_info)) { + fprintf(stderr, "Error getting SDL window wm info: %s\n", SDL_GetError()); + exit(1); + } + switch (sys_info.subsystem) { + case SDL_SYSWM_X11: +#if defined(VK_USE_PLATFORM_XLIB_KHR) + vulkan_->InitSurface(WINDOWSYSTEM_XLIB, (void*)sys_info.info.x11.display, + (void *)(intptr_t)sys_info.info.x11.window, pixel_xres, pixel_yres); +#elif defined(VK_USE_PLATFORM_XCB_KHR) + vulkan_->InitSurface(WINDOWSYSTEM_XCB, (void*)XGetXCBConnection(sys_info.info.x11.display), + (void *)(intptr_t)sys_info.info.x11.window, pixel_xres, pixel_yres); +#endif + break; +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + case SDL_SYSWM_WAYLAND: + vulkan_->InitSurface(WINDOWSYSTEM_WAYLAND, (void*)sys_info.info.wl.display, + (void *)sys_info.info.wl.surface, pixel_xres, pixel_yres); + break; +#endif + default: + fprintf(stderr, "Vulkan subsystem %d not supported\n", sys_info.subsystem); + exit(1); + break; + } +#endif + + if (!vulkan_->InitObjects()) { + *error_message = vulkan_->InitError(); + Shutdown(); + return false; + } + + draw_ = Draw::T3DCreateVulkanContext(vulkan_, false); + SetGPUBackend(GPUBackend::VULKAN); + bool success = draw_->CreatePresets(); + assert(success); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + + return true; +} + +void SDLVulkanGraphicsContext::Shutdown() { + if (draw_) + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + delete draw_; + draw_ = nullptr; + vulkan_->WaitUntilQueueIdle(); + vulkan_->DestroyObjects(); + vulkan_->DestroyDevice(); + vulkan_->DestroyDebugMsgCallback(); + vulkan_->DestroyInstance(); + delete vulkan_; + vulkan_ = nullptr; + finalize_glslang(); +} diff --git a/SDL/SDLVulkanGraphicsContext.h b/SDL/SDLVulkanGraphicsContext.h new file mode 100644 index 0000000000..42883b6700 --- /dev/null +++ b/SDL/SDLVulkanGraphicsContext.h @@ -0,0 +1,53 @@ +#include "SDL.h" +#if !defined(__APPLE__) +#include "SDL_syswm.h" +#endif + +#include "Common/GraphicsContext.h" +#include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanDebug.h" + +#include "thin3d/thin3d.h" + +class SDLVulkanGraphicsContext : public GraphicsContext { +public: + SDLVulkanGraphicsContext() {} + ~SDLVulkanGraphicsContext() { + delete draw_; + } + + bool Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message); + + void Shutdown() override; + + void SwapBuffers() override { + // We don't do it this way. + } + + void Resize() override { + /* + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + vulkan_->DestroyObjects(); + // TODO: Take from real window dimensions + int width = 1024; + int height = 768; + vulkan_->ReinitSurface(width, height); + vulkan_->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + */ + } + + void SwapInterval(int interval) override { + } + void *GetAPIContext() override { + return vulkan_; + } + + Draw::DrawContext *GetDrawContext() override { + return draw_; + } +private: + Draw::DrawContext *draw_ = nullptr; + VulkanContext *vulkan_ = nullptr; + VulkanLogOptions g_LogOptions; +}; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 7597dd7087..b6d25ae126 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -471,6 +471,9 @@ void SystemInfoScreen::CreateViews() { #else buildConfig->Add(new InfoItem("NDEBUG", "")); #endif +#ifdef USE_ADDRESS_SANITIZER + buildConfig->Add(new InfoItem("USE_ADDRESS_SANITIZER", "")); +#endif #ifdef USING_GLES2 buildConfig->Add(new InfoItem("USING_GLES2", "")); #endif diff --git a/UI/HostTypes.h b/UI/HostTypes.h index 102d583ce7..c236a1baac 100644 --- a/UI/HostTypes.h +++ b/UI/HostTypes.h @@ -110,7 +110,7 @@ public: // this is sent from EMU thread! Make sure that Host handles it properly! virtual void BootDone() { g_symbolMap->SortSymbols(); - mainWindow->Boot(); + mainWindow->Notify(MainWindowMsg::BOOT_DONE); } virtual bool IsDebuggingEnabled() { @@ -121,7 +121,10 @@ public: #endif } virtual bool AttemptLoadSymbolMap() { - return g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart)); + return false; + // TODO: Make this work with Qt and threaded GL... not sure what's so broken. + // auto fn = SymbolMapFilename(PSP_CoreParameter().fileToStart); + // return g_symbolMap->LoadSymbolMap(fn); } virtual void PrepareShutdown() { g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart)); diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index 7b523f47ab..7b6ca82561 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -140,6 +140,12 @@ void TextDrawerQt::ClearCache() { } cache_.clear(); sizeCache_.clear(); + // Also wipe the font map. + for (auto iter : fontMap_) { + delete iter.second; + } + fontMap_.clear(); + fontHash_ = 0; } void TextDrawerQt::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) { diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 6c335f422a..e4a4ab23ae 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -54,8 +54,11 @@ GLRenderManager::GLRenderManager() { GLRenderManager::~GLRenderManager() { for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - + _assert_(frameData_[i].deleter.IsEmpty()); } + // Was anything deleted during shutdown? + deleter_.Perform(); + // _assert_(deleter_.IsEmpty()); if (!useThread_) { // The main thread is also the render thread. @@ -69,10 +72,29 @@ void GLRenderManager::ThreadStart() { } void GLRenderManager::ThreadEnd() { + ILOG("ThreadEnd"); + // Wait for any shutdown to complete in StopThread(). std::unique_lock lock(mutex_); queueRunner_.DestroyDeviceObjects(); VLOG("PULL: Quitting"); + + // Good point to run all the deleters to get rid of leftover objects. + for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].deleter.Perform(); + for (int j = 0; j < (int)frameData_[i].steps.size(); j++) { + delete frameData_[i].steps[j]; + } + frameData_[i].steps.clear(); + frameData_[i].initSteps.clear(); + } + deleter_.Perform(); + + for (int i = 0; i < (int)steps_.size(); i++) { + delete steps_[i]; + } + steps_.clear(); + initSteps_.clear(); } bool GLRenderManager::ThreadFrame() { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index f4355155fe..670ecd72f7 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -10,6 +10,7 @@ #include #include +#include "base/logging.h" #include "gfx/gl_common.h" #include "math/dataconv.h" #include "Common/Log.h" @@ -152,9 +153,12 @@ class GLDeleter { public: void Perform(); + bool IsEmpty() const { + return shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty(); + } void Take(GLDeleter &other) { deleterMutex_.lock(); - _assert_msg_(G3D, shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty(), "Deleter already has stuff"); + _assert_msg_(G3D, IsEmpty(), "Deleter already has stuff"); shaders = std::move(other.shaders); programs = std::move(other.programs); buffers = std::move(other.buffers); @@ -193,6 +197,9 @@ public: int semanticsMask_ = 0; }; +// Note: The GLRenderManager is created and destroyed on the render thread, and the latter +// happens after the emu thread has been destroyed. Therefore, it's safe to run wild deleting stuff +// directly in the destructor. class GLRenderManager { public: GLRenderManager(); @@ -701,7 +708,7 @@ private: // Swapchain. bool hasBegun = false; uint32_t curSwapchainImage = -1; - + GLDeleter deleter; std::set activePushBuffers; }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 91868b4095..6d81a98c6e 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -297,6 +297,7 @@ public: OpenGLPipeline(GLRenderManager *render) : render_(render) { } ~OpenGLPipeline() { + ILOG("OpenGLPipeline released"); for (auto &iter : shaders) { iter->Release(); } @@ -477,7 +478,7 @@ private: OpenGLTexture *boundTextures_[8]{}; int maxTextures_ = 0; DeviceCaps caps_{}; - + // Bound state OpenGLPipeline *curPipeline_ = nullptr; OpenGLBuffer *curVBuffers_[4]{}; @@ -519,7 +520,7 @@ OpenGLContext::OpenGLContext() { default: caps_.vendor = GPUVendor::VENDOR_UNKNOWN; break; - } + } for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { frameData_[i].push = new GLPushBuffer(&renderManager_, GL_ARRAY_BUFFER, 64 * 1024); renderManager_.RegisterPushBuffer(i, frameData_[i].push);