From 56c679595f9ba9f87c3bfd6bdea49f163cb8ff64 Mon Sep 17 00:00:00 2001 From: emufan4568 Date: Tue, 6 Sep 2022 19:05:43 +0300 Subject: [PATCH] citra_qt: Prepare GUI for Vulkan support --- src/android/app/src/main/jni/native.cpp | 1 - src/citra/citra.cpp | 3 +- src/citra/config.cpp | 3 +- src/citra/emu_window/emu_window_sdl2.cpp | 5 +- src/citra_qt/bootmanager.cpp | 380 ++++++++++++------ src/citra_qt/bootmanager.h | 59 +-- .../configuration/configure_graphics.cpp | 4 +- .../configuration/configure_graphics.ui | 30 ++ src/citra_qt/main.cpp | 36 +- src/core/CMakeLists.txt | 2 - src/core/core.cpp | 2 - src/core/core.h | 2 - src/core/frontend/emu_window.h | 26 +- src/core/frontend/scope_acquire_context.cpp | 17 - src/core/frontend/scope_acquire_context.h | 23 -- src/core/settings.cpp | 99 +++-- src/core/settings.h | 8 +- .../renderer_opengl/frame_dumper_opengl.cpp | 3 +- .../renderer_opengl/gl_shader_manager.cpp | 3 +- .../renderer_opengl/renderer_opengl.cpp | 4 +- src/video_core/video_core.cpp | 2 +- src/video_core/video_core.h | 4 +- 22 files changed, 413 insertions(+), 303 deletions(-) delete mode 100644 src/core/frontend/scope_acquire_context.cpp delete mode 100644 src/core/frontend/scope_acquire_context.h diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 04c38305e..d2c0f1d34 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -22,7 +22,6 @@ #include "core/frontend/applets/default_applets.h" #include "core/frontend/camera/factory.h" #include "core/frontend/mic.h" -#include "core/frontend/scope_acquire_context.h" #include "core/hle/service/am/am.h" #include "core/hle/service/nfc/nfc.h" #include "core/savestate.h" diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index cdae219e2..8f74aa627 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -35,7 +35,6 @@ #include "core/file_sys/cia_container.h" #include "core/frontend/applets/default_applets.h" #include "core/frontend/framebuffer_layout.h" -#include "core/frontend/scope_acquire_context.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/am/am.h" #include "core/hle/service/cfg/cfg.h" @@ -359,7 +358,7 @@ int main(int argc, char** argv) { Core::System::GetInstance().RegisterImageInterface(std::make_shared()); std::unique_ptr emu_window{std::make_unique(fullscreen)}; - Frontend::ScopeAcquireContext scope(*emu_window); + const auto scope = emu_window->Acquire(); Core::System& system = Core::System::GetInstance(); const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 477318ac0..14ca47401 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -109,7 +109,8 @@ void Config::ReadValues() { sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100); // Renderer - Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false); + Settings::values.graphics_api = + static_cast(sdl2_config->GetInteger("Renderer", "graphics_api", 0)); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true); #ifdef __APPLE__ diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index d42609a81..a537d1595 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -147,7 +147,8 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { SDL_SetMainReady(); - if (Settings::values.use_gles) { + const bool is_opengles = Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES; + if (is_opengles) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); @@ -201,7 +202,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { exit(1); } - auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; + auto gl_load_func = is_opengles ? gladLoadGLES2Loader : gladLoadGLLoader; if (!gl_load_func(static_cast(SDL_GL_GetProcAddress))) { LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 984514c87..3a86a70ba 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -6,10 +6,11 @@ #include #include #include +#include #include #include -#include #include +#include #include #include "citra_qt/bootmanager.h" #include "citra_qt/main.h" @@ -17,13 +18,10 @@ #include "common/scm_rev.h" #include "core/3ds.h" #include "core/core.h" -#include "core/frontend/scope_acquire_context.h" -#include "core/perf_stats.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" -#include "network/network.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -42,7 +40,7 @@ static GMainWindow* GetMainWindow() { void EmuThread::run() { MicroProfileOnThreadCreate("EmuThread"); - Frontend::ScopeAcquireContext scope(core_context); + const auto scope = core_context.Acquire(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); @@ -111,88 +109,195 @@ void EmuThread::run() { #endif } -OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) - : QWindow(parent), context(std::make_unique(shared_context->parent())), - event_handler(event_handler) { +class OpenGLSharedContext : public Frontend::GraphicsContext { +public: + /// Create the original context that should be shared from + explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { + QSurfaceFormat format; + format.setVersion(4, 3); + format.setProfile(QSurfaceFormat::CoreProfile); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + format.setSwapInterval(0); - // disable vsync for any shared contexts - auto format = shared_context->format(); - format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0); - this->setFormat(format); - - context->setShareContext(shared_context); - context->setScreen(this->screen()); - context->setFormat(format); - context->create(); - - setSurfaceType(QWindow::OpenGLSurface); - - // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, - // WA_DontShowOnScreen, WA_DeleteOnClose -} - -OpenGLWindow::~OpenGLWindow() { - context->doneCurrent(); -} - -void OpenGLWindow::Present() { - if (!isExposed()) - return; - - context->makeCurrent(this); - if (VideoCore::g_renderer) { - VideoCore::g_renderer->TryPresent(100); + context = std::make_unique(); + context->setFormat(format); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create main openGL context"); + } } - context->swapBuffers(this); - auto f = context->versionFunctions(); - f->glFinish(); - QWindow::requestUpdate(); -} -bool OpenGLWindow::event(QEvent* event) { - switch (event->type()) { - case QEvent::UpdateRequest: + /// Create the shared contexts for rendering and presentation + explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { + + // disable vsync for any shared contexts + auto format = share_context->format(); + format.setSwapInterval(main_surface ? Settings::values.use_vsync_new : 0); + + context = std::make_unique(); + context->setShareContext(share_context); + context->setFormat(format); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create shared openGL context"); + } + + if (!main_surface) { + offscreen_surface = std::make_unique(nullptr); + offscreen_surface->setFormat(format); + offscreen_surface->create(); + surface = offscreen_surface.get(); + } else { + surface = main_surface; + } + } + + ~OpenGLSharedContext() { + context->doneCurrent(); + } + + void SwapBuffers() override { + context->swapBuffers(surface); + } + + void MakeCurrent() override { + // We can't track the current state of the underlying context in this wrapper class because + // Qt may make the underlying context not current for one reason or another. In particular, + // the WebBrowser uses GL, so it seems to conflict if we aren't careful. + // Instead of always just making the context current (which does not have any caching to + // check if the underlying context is already current) we can check for the current context + // in the thread local data by calling `currentContext()` and checking if its ours. + if (QOpenGLContext::currentContext() != context.get()) { + context->makeCurrent(surface); + } + } + + void DoneCurrent() override { + context->doneCurrent(); + } + + QOpenGLContext* GetShareContext() const { + return context.get(); + } + +private: + // Avoid using Qt parent system here since we might move the QObjects to new threads + // As a note, this means we should avoid using slots/signals with the objects too + std::unique_ptr context; + std::unique_ptr offscreen_surface{}; + QSurface* surface; +}; + +class DummyContext : public Frontend::GraphicsContext {}; + +class RenderWidget : public QWidget { +public: + RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_PaintOnScreen); + } + + virtual ~RenderWidget() = default; + + virtual void Present() {} + + void paintEvent(QPaintEvent* event) override { Present(); - return true; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::FocusAboutToChange: - case QEvent::Enter: - case QEvent::Leave: - case QEvent::Wheel: - case QEvent::TabletMove: - case QEvent::TabletPress: - case QEvent::TabletRelease: - case QEvent::TabletEnterProximity: - case QEvent::TabletLeaveProximity: - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::InputMethodQuery: - case QEvent::TouchCancel: - return QCoreApplication::sendEvent(event_handler, event); - case QEvent::Drop: - GetMainWindow()->DropAction(static_cast(event)); - return true; - case QEvent::DragEnter: - case QEvent::DragMove: - GetMainWindow()->AcceptDropEvent(static_cast(event)); - return true; - default: - return QWindow::event(event); + update(); } -} -void OpenGLWindow::exposeEvent(QExposeEvent* event) { - QWindow::requestUpdate(); - QWindow::exposeEvent(event); -} + void resizeEvent(QResizeEvent* ev) override { + render_window->resize(ev->size()); + render_window->OnFramebufferSizeChanged(); + } + + void keyPressEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->PressKey(event->key()); + } + + void keyReleaseEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->ReleaseKey(event->key()); + } + + void mousePressEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchBeginEvent + + const auto pos{event->pos()}; + if (event->button() == Qt::LeftButton) { + const auto [x, y] = render_window->ScaleTouch(pos); + render_window->TouchPressed(x, y); + } else if (event->button() == Qt::RightButton) { + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); + } + } + + void mouseMoveEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchUpdateEvent + + const auto pos{event->pos()}; + const auto [x, y] = render_window->ScaleTouch(pos); + render_window->TouchMoved(x, y); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); + } + + void mouseReleaseEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchEndEvent + + if (event->button() == Qt::LeftButton) + render_window->TouchReleased(); + else if (event->button() == Qt::RightButton) + InputCommon::GetMotionEmu()->EndTilt(); + } + + std::pair GetSize() const { + return std::make_pair(width(), height()); + } + + QPaintEngine* paintEngine() const override { + return nullptr; + } + +private: + GRenderWindow* render_window; +}; + +class OpenGLRenderWidget : public RenderWidget { +public: + explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::OpenGLSurface); + } + + void SetContext(std::unique_ptr&& context_) { + context = std::move(context_); + } + + void Present() override { + if (!isVisible()) { + return; + } + if (!Core::System::GetInstance().IsPoweredOn()) { + return; + } + context->MakeCurrent(); + const auto f = context->GetShareContext()->extraFunctions(); + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + VideoCore::g_renderer->TryPresent(100); + context->SwapBuffers(); + f->glFinish(); + } + +private: + std::unique_ptr context{}; +}; + +class VulkanRenderWidget : public RenderWidget { +public: + explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::VulkanSurface); + } +}; GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) : QWidget(parent_), emu_thread(emu_thread) { @@ -218,11 +323,11 @@ GRenderWindow::~GRenderWindow() { } void GRenderWindow::MakeCurrent() { - core_context->MakeCurrent(); + main_context->MakeCurrent(); } void GRenderWindow::DoneCurrent() { - core_context->DoneCurrent(); + main_context->DoneCurrent(); } void GRenderWindow::PollEvents() { @@ -387,33 +492,67 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { OnFramebufferSizeChanged(); } -void GRenderWindow::InitRenderTarget() { +std::unique_ptr GRenderWindow::CreateSharedContext() const { + const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api; + if (graphics_api == Settings::GraphicsAPI::OpenGL || + graphics_api == Settings::GraphicsAPI::OpenGLES) { + auto c = static_cast(main_context.get()); + // Bind the shared contexts to the main surface in case the backend wants to take over + // presentation + return std::make_unique(c->GetShareContext(), + child_widget->windowHandle()); + } + + return std::make_unique(); +} + +bool GRenderWindow::InitRenderTarget() { ReleaseRenderTarget(); + { + // Create a dummy render widget so that Qt + // places the render window at the correct position. + const RenderWidget dummy_widget{this}; + } + first_frame = false; - GMainWindow* parent = GetMainWindow(); - QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; - child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); - child_window->create(); - child_widget = createWindowContainer(child_window, this); + const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api; + switch (graphics_api) { + case Settings::GraphicsAPI::OpenGL: + case Settings::GraphicsAPI::OpenGLES: + if (!InitializeOpenGL()) { + return false; + } + break; + case Settings::GraphicsAPI::Vulkan: + if (!InitializeVulkan()) { + return false; + } + break; + } + child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); layout()->addWidget(child_widget); + // Reset minimum required size to avoid resizing issues on the main window after restarting. + setMinimumSize(1, 1); - core_context = CreateSharedContext(); resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnFramebufferSizeChanged(); BackupGeometry(); + + return true; } void GRenderWindow::ReleaseRenderTarget() { if (child_widget) { layout()->removeWidget(child_widget); - delete child_widget; + child_widget->deleteLater(); child_widget = nullptr; } + main_context.reset(); } void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -438,6 +577,37 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair minimal setMinimumSize(minimal_size.first, minimal_size.second); } +bool GRenderWindow::InitializeOpenGL() { + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, + // WA_DontShowOnScreen, WA_DeleteOnClose + auto child = new OpenGLRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + auto context = std::make_shared(child->windowHandle()); + main_context = context; + child->SetContext( + std::make_unique(context->GetShareContext(), child->windowHandle())); + + // Check for OpenGL 4.3 support + if (!QOpenGLContext::globalShareContext()->versionFunctions()) { + QMessageBox::critical(this, tr("OpenGL 4.3 Unsupported"), + tr("Your GPU may not support OpenGL 4.3, or you do not " + "have the latest graphics driver.")); + return false; + } + + return true; +} + +bool GRenderWindow::InitializeVulkan() { + auto child = new VulkanRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + main_context = std::make_unique(); + + return true; +} + void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { this->emu_thread = emu_thread; } @@ -449,31 +619,3 @@ void GRenderWindow::OnEmulationStopping() { void GRenderWindow::showEvent(QShowEvent* event) { QWidget::showEvent(event); } - -std::unique_ptr GRenderWindow::CreateSharedContext() const { - return std::make_unique(QOpenGLContext::globalShareContext()); -} - -GLContext::GLContext(QOpenGLContext* shared_context) - : context(std::make_unique(shared_context->parent())), - surface(std::make_unique(nullptr)) { - - // disable vsync for any shared contexts - auto format = shared_context->format(); - format.setSwapInterval(0); - - context->setShareContext(shared_context); - context->setFormat(format); - context->create(); - surface->setParent(shared_context->parent()); - surface->setFormat(format); - surface->create(); -} - -void GLContext::MakeCurrent() { - context->makeCurrent(surface.get()); -} - -void GLContext::DoneCurrent() { - context->doneCurrent(); -} diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 58c15e4b9..67cc75da9 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -27,19 +27,6 @@ namespace VideoCore { enum class LoadCallbackStage; } -class GLContext : public Frontend::GraphicsContext { -public: - explicit GLContext(QOpenGLContext* shared_context); - - void MakeCurrent() override; - - void DoneCurrent() override; - -private: - std::unique_ptr context; - std::unique_ptr surface; -}; - class EmuThread final : public QThread { Q_OBJECT @@ -126,24 +113,6 @@ signals: void HideLoadingScreen(); }; -class OpenGLWindow : public QWindow { - Q_OBJECT -public: - explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context); - - ~OpenGLWindow(); - - void Present(); - -protected: - bool event(QEvent* event) override; - void exposeEvent(QExposeEvent* event) override; - -private: - std::unique_ptr context; - QWidget* event_handler; -}; - class GRenderWindow : public QWidget, public Frontend::EmuWindow { Q_OBJECT @@ -179,13 +148,15 @@ public: void focusOutEvent(QFocusEvent* event) override; - void InitRenderTarget(); + bool InitRenderTarget(); /// Destroy the previous run's child_widget which should also destroy the child_window void ReleaseRenderTarget(); void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); + std::pair ScaleTouch(const QPointF pos) const; + public slots: void OnEmulationStarting(EmuThread* emu_thread); @@ -205,29 +176,29 @@ signals: void MouseActivity(); private: - std::pair ScaleTouch(QPointF pos) const; void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); void OnMinimalClientAreaChangeRequest(std::pair minimal_size) override; - std::unique_ptr core_context; - - QByteArray geometry; - - /// Native window handle that backs this presentation widget - QWindow* child_window = nullptr; - - /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to - /// put the child_window into a widget then add it to the layout. This child_widget can be - /// parented to GRenderWindow and use Qt's lifetime system - QWidget* child_widget = nullptr; + bool InitializeOpenGL(); + bool InitializeVulkan(); EmuThread* emu_thread; + // Main context that will be shared with all other contexts that are requested. + // If this is used in a shared context setting, then this should not be used directly, but + // should instead be shared from + std::shared_ptr main_context; + /// Temporary storage of the screenshot taken QImage screenshot_image; + + QByteArray geometry; + + QWidget* child_widget = nullptr; + bool first_frame = false; protected: diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 75602a8b4..3d3eb9280 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -17,8 +17,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) ui->setupUi(this); SetConfiguration(); + const bool not_running = !Core::System::GetInstance().IsPoweredOn(); ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked()); - ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->toggle_vsync_new->setEnabled(not_running); + ui->graphics_api_combo->setEnabled(not_running); connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { auto checked = ui->toggle_hw_renderer->isChecked(); diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 97dc851d4..36a105452 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -26,6 +26,36 @@ Renderer + + + + + + Graphics API + + + + + + + + OpenGL + + + + + OpenGLES + + + + + Vulkan + + + + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index b10003dbd..3fb84e3a5 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -17,12 +17,6 @@ #include #include #include -#ifdef __APPLE__ -#include // for chdir -#endif -#ifdef _WIN32 -#include -#endif #include "citra_qt/aboutdialog.h" #include "citra_qt/applets/mii_selector.h" #include "citra_qt/applets/swkbd.h" @@ -76,7 +70,6 @@ #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_source_sd_savedata.h" #include "core/frontend/applets/default_applets.h" -#include "core/frontend/scope_acquire_context.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/nfc/nfc.h" @@ -90,6 +83,13 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" +#ifdef __APPLE__ +#include // for chdir +#endif +#ifdef _WIN32 +#include +#endif + #ifdef USE_DISCORD_PRESENCE #include "citra_qt/discord_impl.h" #endif @@ -914,20 +914,10 @@ bool GMainWindow::LoadROM(const QString& filename) { render_window->InitRenderTarget(); - Frontend::ScopeAcquireContext scope(*render_window); + const auto scope = render_window->Acquire(); - const QString below_gl43_title = tr("OpenGL 4.3 Unsupported"); - const QString below_gl43_message = tr("Your GPU may not support OpenGL 4.3, or you do not " - "have the latest graphics driver."); - - if (!QOpenGLContext::globalShareContext()->versionFunctions()) { - QMessageBox::critical(this, below_gl43_title, below_gl43_message); - return false; - } - - Core::System& system{Core::System::GetInstance()}; - - const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; + Core::System& system = Core::System::GetInstance(); + const Core::System::ResultStatus result = system.Load(*render_window, filename.toStdString()); if (result != Core::System::ResultStatus::Success) { switch (result) { @@ -976,7 +966,7 @@ bool GMainWindow::LoadROM(const QString& filename) { case Core::System::ResultStatus::ErrorVideoCore: QMessageBox::critical( this, tr("Video Core Error"), - tr("An error has occurred. Please see " "the " "log for more details. " @@ -991,10 +981,6 @@ bool GMainWindow::LoadROM(const QString& filename) { "proper drivers for your graphics card from the manufacturer's website.")); break; - case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL43: - QMessageBox::critical(this, below_gl43_title, below_gl43_message); - break; - default: QMessageBox::critical( this, tr("Error while loading ROM!"), diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e1738e9f6..a181d3c47 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -110,8 +110,6 @@ add_library(core STATIC frontend/input.h frontend/mic.cpp frontend/mic.h - frontend/scope_acquire_context.cpp - frontend/scope_acquire_context.h gdbstub/gdbstub.cpp gdbstub/gdbstub.h hle/applets/applet.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 1ed72a3d9..8450bb0a8 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -424,8 +424,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo switch (result) { case VideoCore::ResultStatus::ErrorGenericDrivers: return ResultStatus::ErrorVideoCore_ErrorGenericDrivers; - case VideoCore::ResultStatus::ErrorBelowGL43: - return ResultStatus::ErrorVideoCore_ErrorBelowGL43; default: return ResultStatus::ErrorVideoCore; } diff --git a/src/core/core.h b/src/core/core.h index 1ea2d9b55..c69c25a20 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -88,8 +88,6 @@ public: ErrorVideoCore, ///< Error in the video core ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having /// generic drivers installed - ErrorVideoCore_ErrorBelowGL43, ///< Error in the video core due to the user not having - /// OpenGL 4.3 or higher ErrorSavestate, ///< Error saving or loading ShutdownRequested, ///< Emulated program requested a system shutdown ErrorUnknown ///< Any other error diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 7841d3bac..add704207 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -60,11 +60,33 @@ class GraphicsContext { public: virtual ~GraphicsContext(); + /// Inform the driver to swap the front/back buffers and present the current image + virtual void SwapBuffers(){}; + /// Makes the graphics context current for the caller thread - virtual void MakeCurrent() = 0; + virtual void MakeCurrent(){}; /// Releases (dunno if this is the "right" word) the context from the caller thread - virtual void DoneCurrent() = 0; + virtual void DoneCurrent(){}; + + class Scoped { + public: + explicit Scoped(GraphicsContext& context_) : context(context_) { + context.MakeCurrent(); + } + ~Scoped() { + context.DoneCurrent(); + } + + private: + GraphicsContext& context; + }; + + /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value + /// ends + [[nodiscard]] Scoped Acquire() { + return Scoped{*this}; + } }; /** diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 3689483af..000000000 --- a/src/core/frontend/scope_acquire_context.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/frontend/emu_window.h" -#include "core/frontend/scope_acquire_context.h" - -namespace Frontend { - -ScopeAcquireContext::ScopeAcquireContext(Frontend::GraphicsContext& context) : context{context} { - context.MakeCurrent(); -} -ScopeAcquireContext::~ScopeAcquireContext() { - context.DoneCurrent(); -} - -} // namespace Frontend \ No newline at end of file diff --git a/src/core/frontend/scope_acquire_context.h b/src/core/frontend/scope_acquire_context.h deleted file mode 100644 index 12ab61ec6..000000000 --- a/src/core/frontend/scope_acquire_context.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace Frontend { - -class GraphicsContext; - -/// Helper class to acquire/release window context within a given scope -class ScopeAcquireContext : NonCopyable { -public: - explicit ScopeAcquireContext(Frontend::GraphicsContext& context); - ~ScopeAcquireContext(); - -private: - Frontend::GraphicsContext& context; -}; - -} // namespace Frontend \ No newline at end of file diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 3a26e7217..2903be9dc 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -7,7 +7,6 @@ #include "audio_core/dsp_interface.h" #include "core/core.h" #include "core/gdbstub/gdbstub.h" -#include "core/hle/kernel/shared_page.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir_rst.h" @@ -73,62 +72,62 @@ void Apply() { } void LogSettings() { - const auto log_setting = [](std::string_view name, const auto& value) { + const auto LogSetting = [](std::string_view name, const auto& value) { LOG_INFO(Config, "{}: {}", name, value); }; LOG_INFO(Config, "Citra Configuration:"); - log_setting("Core_UseCpuJit", values.use_cpu_jit); - log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage); - log_setting("Renderer_UseGLES", values.use_gles); - log_setting("Renderer_UseHwRenderer", values.use_hw_renderer); - log_setting("Renderer_UseHwShader", values.use_hw_shader); - log_setting("Renderer_SeparableShader", values.separable_shader); - log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul); - log_setting("Renderer_UseShaderJit", values.use_shader_jit); - log_setting("Renderer_UseResolutionFactor", values.resolution_factor); - log_setting("Renderer_FrameLimit", values.frame_limit); - log_setting("Renderer_UseFrameLimitAlternate", values.use_frame_limit_alternate); - log_setting("Renderer_FrameLimitAlternate", values.frame_limit_alternate); - log_setting("Renderer_VSyncNew", values.use_vsync_new); - log_setting("Renderer_PostProcessingShader", values.pp_shader_name); - log_setting("Renderer_FilterMode", values.filter_mode); - log_setting("Renderer_TextureFilterName", values.texture_filter_name); - log_setting("Stereoscopy_Render3d", values.render_3d); - log_setting("Stereoscopy_Factor3d", values.factor_3d); - log_setting("Layout_LayoutOption", values.layout_option); - log_setting("Layout_SwapScreen", values.swap_screen); - log_setting("Layout_UprightScreen", values.upright_screen); - log_setting("Utility_DumpTextures", values.dump_textures); - log_setting("Utility_CustomTextures", values.custom_textures); - log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache); - log_setting("Audio_EnableDspLle", values.enable_dsp_lle); - log_setting("Audio_EnableDspLleMultithread", values.enable_dsp_lle_multithread); - log_setting("Audio_OutputEngine", values.sink_id); - log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching); - log_setting("Audio_OutputDevice", values.audio_device_id); - log_setting("Audio_InputDeviceType", values.mic_input_type); - log_setting("Audio_InputDevice", values.mic_input_device); + LogSetting("Core_UseCpuJit", values.use_cpu_jit); + LogSetting("Core_CPUClockPercentage", values.cpu_clock_percentage); + LogSetting("Renderer_GraphicsAPI", values.graphics_api); + LogSetting("Renderer_UseHwRenderer", values.use_hw_renderer); + LogSetting("Renderer_UseHwShader", values.use_hw_shader); + LogSetting("Renderer_SeparableShader", values.separable_shader); + LogSetting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul); + LogSetting("Renderer_UseShaderJit", values.use_shader_jit); + LogSetting("Renderer_UseResolutionFactor", values.resolution_factor); + LogSetting("Renderer_FrameLimit", values.frame_limit); + LogSetting("Renderer_UseFrameLimitAlternate", values.use_frame_limit_alternate); + LogSetting("Renderer_FrameLimitAlternate", values.frame_limit_alternate); + LogSetting("Renderer_VSyncNew", values.use_vsync_new); + LogSetting("Renderer_PostProcessingShader", values.pp_shader_name); + LogSetting("Renderer_FilterMode", values.filter_mode); + LogSetting("Renderer_TextureFilterName", values.texture_filter_name); + LogSetting("Stereoscopy_Render3d", values.render_3d); + LogSetting("Stereoscopy_Factor3d", values.factor_3d); + LogSetting("Layout_LayoutOption", values.layout_option); + LogSetting("Layout_SwapScreen", values.swap_screen); + LogSetting("Layout_UprightScreen", values.upright_screen); + LogSetting("Utility_DumpTextures", values.dump_textures); + LogSetting("Utility_CustomTextures", values.custom_textures); + LogSetting("Utility_UseDiskShaderCache", values.use_disk_shader_cache); + LogSetting("Audio_EnableDspLle", values.enable_dsp_lle); + LogSetting("Audio_EnableDspLleMultithread", values.enable_dsp_lle_multithread); + LogSetting("Audio_OutputEngine", values.sink_id); + LogSetting("Audio_EnableAudioStretching", values.enable_audio_stretching); + LogSetting("Audio_OutputDevice", values.audio_device_id); + LogSetting("Audio_InputDeviceType", values.mic_input_type); + LogSetting("Audio_InputDevice", values.mic_input_device); using namespace Service::CAM; - log_setting("Camera_OuterRightName", values.camera_name[OuterRightCamera]); - log_setting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]); - log_setting("Camera_OuterRightFlip", values.camera_flip[OuterRightCamera]); - log_setting("Camera_InnerName", values.camera_name[InnerCamera]); - log_setting("Camera_InnerConfig", values.camera_config[InnerCamera]); - log_setting("Camera_InnerFlip", values.camera_flip[InnerCamera]); - log_setting("Camera_OuterLeftName", values.camera_name[OuterLeftCamera]); - log_setting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]); - log_setting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]); - log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); - log_setting("DataStorage_UseCustomStorage", values.use_custom_storage); + LogSetting("Camera_OuterRightName", values.camera_name[OuterRightCamera]); + LogSetting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]); + LogSetting("Camera_OuterRightFlip", values.camera_flip[OuterRightCamera]); + LogSetting("Camera_InnerName", values.camera_name[InnerCamera]); + LogSetting("Camera_InnerConfig", values.camera_config[InnerCamera]); + LogSetting("Camera_InnerFlip", values.camera_flip[InnerCamera]); + LogSetting("Camera_OuterLeftName", values.camera_name[OuterLeftCamera]); + LogSetting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]); + LogSetting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]); + LogSetting("DataStorage_UseVirtualSd", values.use_virtual_sd); + LogSetting("DataStorage_UseCustomStorage", values.use_custom_storage); if (values.use_custom_storage) { - log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); - log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); + LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); + LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); } - log_setting("System_IsNew3ds", values.is_new_3ds); - log_setting("System_RegionValue", values.region_value); - log_setting("Debugging_UseGdbstub", values.use_gdbstub); - log_setting("Debugging_GdbstubPort", values.gdbstub_port); + LogSetting("System_IsNew3ds", values.is_new_3ds); + LogSetting("System_RegionValue", values.region_value); + LogSetting("Debugging_UseGdbstub", values.use_gdbstub); + LogSetting("Debugging_GdbstubPort", values.gdbstub_port); } void LoadProfile(int index) { diff --git a/src/core/settings.h b/src/core/settings.h index b7d35e070..edfba8277 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -14,6 +14,12 @@ namespace Settings { +enum class GraphicsAPI { + OpenGL = 0, + OpenGLES = 1, + Vulkan = 2 +}; + enum class InitClock { SystemTime = 0, FixedTime = 1, @@ -163,7 +169,7 @@ struct Values { u64 init_time; // Renderer - bool use_gles; + GraphicsAPI graphics_api = GraphicsAPI::OpenGL; bool use_hw_renderer; bool use_hw_shader; bool separable_shader; diff --git a/src/video_core/renderer_opengl/frame_dumper_opengl.cpp b/src/video_core/renderer_opengl/frame_dumper_opengl.cpp index 53985823c..3122de2f7 100644 --- a/src/video_core/renderer_opengl/frame_dumper_opengl.cpp +++ b/src/video_core/renderer_opengl/frame_dumper_opengl.cpp @@ -4,7 +4,6 @@ #include #include "core/frontend/emu_window.h" -#include "core/frontend/scope_acquire_context.h" #include "video_core/renderer_opengl/frame_dumper_opengl.h" #include "video_core/renderer_opengl/renderer_opengl.h" @@ -39,7 +38,7 @@ void FrameDumperOpenGL::StopDumping() { } void FrameDumperOpenGL::PresentLoop() { - Frontend::ScopeAcquireContext scope{*context}; + const auto scope = context->Acquire(); InitializeOpenGLObjects(); const auto& layout = GetLayout(); diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index fa9398978..c2dac6451 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -7,7 +7,6 @@ #include #include #include -#include "core/frontend/scope_acquire_context.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -640,7 +639,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin, std::size_t end) { - Frontend::ScopeAcquireContext scope(*context); + const auto scope = context->Acquire(); for (std::size_t i = begin; i < end; ++i) { if (stop_loading || compilation_failed) { return; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index a2949ae9a..40ad42071 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -1234,7 +1234,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum VideoCore::ResultStatus RendererOpenGL::Init() { #ifndef ANDROID if (!gladLoadGL()) { - return VideoCore::ResultStatus::ErrorBelowGL43; + return VideoCore::ResultStatus::ErrorRendererInit; } // Qualcomm has some spammy info messages that are marked as errors but not important @@ -1264,7 +1264,7 @@ VideoCore::ResultStatus RendererOpenGL::Init() { } if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) { - return VideoCore::ResultStatus::ErrorBelowGL43; + return VideoCore::ResultStatus::ErrorRendererInit; } InitOpenGLObjects(); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 87310c5ce..10f3ebc54 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -43,7 +43,7 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Memory::MemorySystem& memory) g_memory = &memory; Pica::Init(); - OpenGL::GLES = Settings::values.use_gles; + OpenGL::GLES = Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES; g_renderer = std::make_unique(emu_window); ResultStatus result = g_renderer->Init(); diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index dddf7e3bb..16a1decf0 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -49,8 +49,8 @@ extern Memory::MemorySystem* g_memory; enum class ResultStatus { Success, - ErrorGenericDrivers, - ErrorBelowGL43, + ErrorRendererInit, + ErrorGenericDrivers }; /// Initialize the video core