diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 78907ea4a..c22f06c39 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -23,7 +23,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 acd3092cc..7a7ed58a7 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -29,7 +29,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" @@ -368,7 +367,7 @@ int main(int argc, char** argv) { const auto secondary_window = use_secondary_window ? std::make_unique(false, true) : nullptr; - Frontend::ScopeAcquireContext scope(*emu_window); + const auto scope = emu_window->Acquire(); LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 64d33a930..8e0785519 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 9e7b7924f..45ad14626 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -137,7 +137,8 @@ void EmuWindow_SDL2::Fullscreen() { EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) { // Initialize the window - 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); @@ -192,7 +193,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(i } render_window_id = SDL_GetWindowID(render_window); - 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 ef89f890b..8f692a2fd 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" @@ -18,12 +19,9 @@ #include "common/settings.h" #include "core/3ds.h" #include "core/core.h" -#include "core/frontend/scope_acquire_context.h" -#include "core/perf_stats.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" @@ -44,7 +42,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); @@ -113,89 +111,197 @@ void EmuThread::run() { #endif } -OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, - bool is_secondary) - : QWindow(parent), context(std::make_unique(shared_context->parent())), - event_handler(event_handler), is_secondary{is_secondary} { +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, is_secondary); + 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, bool is_secondary) : + RenderWidget(parent), is_secondary(is_secondary) { + 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, is_secondary); + context->SwapBuffers(); + f->glFinish(); + } + +private: + std::unique_ptr context{}; + bool is_secondary; +}; + +class VulkanRenderWidget : public RenderWidget { +public: + explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::VulkanSurface); + } +}; GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_) : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) { @@ -218,11 +324,11 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_se GRenderWindow::~GRenderWindow() = default; void GRenderWindow::MakeCurrent() { - core_context->MakeCurrent(); + main_context->MakeCurrent(); } void GRenderWindow::DoneCurrent() { - core_context->DoneCurrent(); + main_context->DoneCurrent(); } void GRenderWindow::PollEvents() { @@ -393,34 +499,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(), - is_secondary); - 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) { @@ -445,6 +584,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, is_secondary); + 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; } @@ -456,31 +626,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 0dc550555..57d6e6f0a 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,26 +113,6 @@ signals: void HideLoadingScreen(); }; -class OpenGLWindow : public QWindow { - Q_OBJECT -public: - explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, - bool is_secondary = false); - - ~OpenGLWindow(); - - void Present(); - -protected: - bool event(QEvent* event) override; - void exposeEvent(QExposeEvent* event) override; - -private: - std::unique_ptr context; - QWidget* event_handler; - bool is_secondary; -}; - class GRenderWindow : public QWidget, public Frontend::EmuWindow { Q_OBJECT @@ -185,13 +152,15 @@ public: return has_focus; } - 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); @@ -211,29 +180,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; bool has_focus = false; diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 58c281490..a39ea256d 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -19,9 +19,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) SetupPerGameUI(); SetConfiguration(); - ui->hw_renderer_group->setEnabled(ui->hw_renderer_group->isEnabled() && - ui->toggle_hw_renderer->isChecked()); - ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + const bool not_running = !Core::System::GetInstance().IsPoweredOn(); + ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked()); + ui->toggle_vsync_new->setEnabled(not_running); + ui->graphics_api_combo->setEnabled(not_running); connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { const bool 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 a3273cdb6..3be209f70 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -18,12 +18,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" @@ -82,7 +76,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" @@ -96,6 +89,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 @@ -954,16 +954,7 @@ bool GMainWindow::LoadROM(const QString& filename) { render_window->InitRenderTarget(); secondary_window->InitRenderTarget(); - Frontend::ScopeAcquireContext scope(*render_window); - - 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; - } + const auto scope = render_window->Acquire(); Core::System& system{Core::System::GetInstance()}; @@ -1017,7 +1008,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. " @@ -1032,10 +1023,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/common/settings.cpp b/src/common/settings.cpp index 44627a6b7..17bb637cf 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -8,7 +8,6 @@ #include "common/settings.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" @@ -95,7 +94,7 @@ void LogSettings() { LOG_INFO(Config, "Citra Configuration:"); log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); - log_setting("Renderer_UseGLES", values.use_gles.GetValue()); + log_setting("Renderer_GraphicsAPI", values.graphics_api.GetValue()); log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue()); log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index 7fec09ba0..9b0559185 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -15,6 +15,12 @@ namespace Settings { +enum class GraphicsAPI { + OpenGL = 0, + OpenGLES = 1, + Vulkan = 2, +}; + enum class InitClock : u32 { SystemTime = 0, FixedTime = 1, @@ -440,7 +446,7 @@ struct Values { Setting allow_plugin_loader{true, "allow_plugin_loader"}; // Renderer - Setting use_gles{false, "use_gles"}; + SwitchableSetting graphics_api{GraphicsAPI::OpenGL, "graphics_api"}; SwitchableSetting use_hw_renderer{true, "use_hw_renderer"}; SwitchableSetting use_hw_shader{true, "use_hw_shader"}; SwitchableSetting separable_shader{false, "use_separable_shader"}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ddf905577..9789c39d5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -113,8 +113,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 2249a41aa..a29cc8382 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -431,8 +431,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, 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 79cbc3f85..61b1005a1 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 81cc930d2..d0cd341ff 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -62,11 +62,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/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 9b8033b41..5b593dc03 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" @@ -641,7 +640,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 5b8b7f060..3afec6d4c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -1259,7 +1259,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 @@ -1289,7 +1289,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 fdf7bcc3e..a08081c29 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -44,7 +44,7 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondar g_memory = &memory; Pica::Init(); - OpenGL::GLES = Settings::values.use_gles.GetValue(); + OpenGL::GLES = Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::OpenGLES; g_renderer = std::make_unique(emu_window, secondary_window); ResultStatus result = g_renderer->Init(); diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 5e0fc3bb5..b4ed12383 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