mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Prepare frontend for multiple graphics APIs (#6347)
* externals: Update dynarmic * settings: Introduce GraphicsAPI enum * For now it's OpenGL only but will be expanded upon later * citra_qt: Introduce backend agnostic context management * Mostly a direct port from yuzu * core: Simplify context acquire * settings: Add option to create debug contexts * renderer_opengl: Abstract initialization to Driver * This commit also updates glad and adds some useful extensions which we will use in part 2 * Rasterizer construction is moved to the specific renderer instead of RendererBase. Software rendering has been disable to achieve this but will be brought back in the next commit. * video_core: Remove Init/Shutdown methods from renderer * The constructor and destructor can do the same job * In addition move opengl function loading to Qt since SDL already does this. Also remove ErrorVideoCore which is never reached * citra_qt: Decouple software renderer from opengl part 1 * citra: Decouple software renderer from opengl part 2 * android: Decouple software renderer from opengl part 3 * swrasterizer: Decouple software renderer from opengl part 4 * This commit simply enforces the renderer naming conventions in the software renderer * video_core: Move RendererBase to VideoCore * video_core: De-globalize screenshot state * video_core: Pass system to the renderers * video_core: Commonize shader uniform data * video_core: Abstract backend agnostic rasterizer operations * bootmanager: Remove references to OpenGL for macOS OpenGL macOS headers definitions clash heavily with each other * citra_qt: Proper title for api settings * video_core: Reduce boost usage * bootmanager: Fix hide mouse option Remove event handlers from RenderWidget for events that are already handled by the parent GRenderWindow. Also enable mouse tracking on the RenderWidget. * android: Remove software from graphics api list * code: Address review comments * citra: Port per-game settings read * Having to update the default value for all backends is a pain so lets centralize it * android: Rename to OpenGLES --------- Co-authored-by: MerryMage <MerryMage@users.noreply.github.com> Co-authored-by: Vitor Kiguchi <vitor-kiguchi@hotmail.com>
This commit is contained in:
		
							parent
							
								
									9ef42040af
								
							
						
					
					
						commit
						b5d6f645bd
					
				
					 99 changed files with 3165 additions and 4501 deletions
				
			
		|  | @ -269,6 +269,10 @@ target_link_libraries(citra-qt PRIVATE audio_core common core input_common netwo | |||
| target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent) | ||||
| target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||||
| 
 | ||||
| if (NOT WIN32) | ||||
|     target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) | ||||
| endif() | ||||
| 
 | ||||
| if (UNIX AND NOT APPLE) | ||||
|     target_link_libraries(citra-qt PRIVATE Qt5::DBus) | ||||
| endif() | ||||
|  | @ -325,6 +329,10 @@ if (MSVC) | |||
|     endif() | ||||
| endif() | ||||
| 
 | ||||
| if (NOT APPLE) | ||||
|     target_compile_definitions(citra-qt PRIVATE HAS_OPENGL) | ||||
| endif() | ||||
| 
 | ||||
| if (CITRA_USE_PRECOMPILED_HEADERS) | ||||
|     target_precompile_headers(citra-qt PRIVATE precompiled_headers.h) | ||||
| endif() | ||||
|  |  | |||
|  | @ -2,31 +2,45 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <glad/glad.h> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QDragEnterEvent> | ||||
| #include <QHBoxLayout> | ||||
| #include <QKeyEvent> | ||||
| #include <QOffscreenSurface> | ||||
| #include <QOpenGLContext> | ||||
| #include <QOpenGLFunctions> | ||||
| #include <QOpenGLFunctions_4_3_Core> | ||||
| #include <QMessageBox> | ||||
| #include <QPainter> | ||||
| #include <fmt/format.h> | ||||
| #include "citra_qt/bootmanager.h" | ||||
| #include "citra_qt/main.h" | ||||
| #include "common/color.h" | ||||
| #include "common/microprofile.h" | ||||
| #include "common/scm_rev.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/3ds.h" | ||||
| #include "core/core.h" | ||||
| #include "core/frontend/scope_acquire_context.h" | ||||
| #include "core/frontend/framebuffer_layout.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" | ||||
| 
 | ||||
| #ifdef HAS_OPENGL | ||||
| #include <QOffscreenSurface> | ||||
| #include <QOpenGLContext> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__APPLE__) | ||||
| #include <objc/message.h> | ||||
| #include <objc/objc.h> | ||||
| #endif | ||||
| 
 | ||||
| #if !defined(WIN32) | ||||
| #include <qpa/qplatformnativeinterface.h> | ||||
| #endif | ||||
| 
 | ||||
| EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {} | ||||
| 
 | ||||
| EmuThread::~EmuThread() = default; | ||||
|  | @ -44,7 +58,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); | ||||
| 
 | ||||
|  | @ -55,6 +69,7 @@ void EmuThread::run() { | |||
|         }); | ||||
| 
 | ||||
|     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||||
|     emit HideLoadingScreen(); | ||||
| 
 | ||||
|     core_context.MakeCurrent(); | ||||
| 
 | ||||
|  | @ -113,90 +128,263 @@ void EmuThread::run() { | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, | ||||
|                            bool is_secondary) | ||||
|     : QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())), | ||||
|       event_handler(event_handler), is_secondary{is_secondary} { | ||||
| #ifdef HAS_OPENGL | ||||
| class OpenGLSharedContext : public Frontend::GraphicsContext { | ||||
| public: | ||||
|     /// Create the original context that should be shared from
 | ||||
|     explicit OpenGLSharedContext() { | ||||
|         QSurfaceFormat format; | ||||
| 
 | ||||
|     // disable vsync for any shared contexts
 | ||||
|     auto format = shared_context->format(); | ||||
|     format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0); | ||||
|     this->setFormat(format); | ||||
|         format.setVersion(4, 3); | ||||
|         format.setProfile(QSurfaceFormat::CoreProfile); | ||||
| 
 | ||||
|     context->setShareContext(shared_context); | ||||
|     context->setScreen(this->screen()); | ||||
|     context->setFormat(format); | ||||
|     context->create(); | ||||
|         if (Settings::values.renderer_debug) { | ||||
|             format.setOption(QSurfaceFormat::FormatOption::DebugContext); | ||||
|         } | ||||
| 
 | ||||
|     setSurfaceType(QWindow::OpenGLSurface); | ||||
|         // TODO: expose a setting for buffer value (ie default/single/double/triple)
 | ||||
|         format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||||
|         format.setSwapInterval(0); | ||||
| 
 | ||||
|     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 | ||||
|     // WA_DontShowOnScreen, WA_DeleteOnClose
 | ||||
| } | ||||
|         context = std::make_unique<QOpenGLContext>(); | ||||
|         context->setFormat(format); | ||||
|         if (!context->create()) { | ||||
|             LOG_ERROR(Frontend, "Unable to create main openGL context"); | ||||
|         } | ||||
| 
 | ||||
| OpenGLWindow::~OpenGLWindow() { | ||||
|     context->doneCurrent(); | ||||
| } | ||||
| 
 | ||||
| void OpenGLWindow::Present() { | ||||
|     if (!isExposed()) | ||||
|         return; | ||||
| 
 | ||||
|     context->makeCurrent(this); | ||||
|     if (VideoCore::g_renderer) { | ||||
|         VideoCore::g_renderer->TryPresent(100, is_secondary); | ||||
|         offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); | ||||
|         offscreen_surface->setFormat(format); | ||||
|         offscreen_surface->create(); | ||||
|         surface = offscreen_surface.get(); | ||||
|     } | ||||
|     context->swapBuffers(this); | ||||
|     auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | ||||
|     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) { | ||||
| 
 | ||||
|         // disable vsync for any shared contexts
 | ||||
|         auto format = share_context->format(); | ||||
|         format.setSwapInterval(main_surface ? Settings::values.use_vsync_new.GetValue() : 0); | ||||
| 
 | ||||
|         context = std::make_unique<QOpenGLContext>(); | ||||
|         context->setShareContext(share_context); | ||||
|         context->setFormat(format); | ||||
|         if (!context->create()) { | ||||
|             LOG_ERROR(Frontend, "Unable to create shared openGL context"); | ||||
|         } | ||||
| 
 | ||||
|         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<QOpenGLContext> context; | ||||
|     std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||||
|     QSurface* surface; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| class DummyContext : public Frontend::GraphicsContext {}; | ||||
| 
 | ||||
| class RenderWidget : public QWidget { | ||||
| public: | ||||
|     RenderWidget(GRenderWindow* parent) : QWidget(parent) { | ||||
|         setMouseTracking(true); | ||||
|     } | ||||
| 
 | ||||
|     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<QDropEvent*>(event)); | ||||
|         return true; | ||||
|     case QEvent::DragEnter: | ||||
|     case QEvent::DragMove: | ||||
|         GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); | ||||
|         return true; | ||||
|     default: | ||||
|         return QWindow::event(event); | ||||
|         update(); | ||||
|     } | ||||
| 
 | ||||
|     std::pair<unsigned, unsigned> GetSize() const { | ||||
|         return std::make_pair(width(), height()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAS_OPENGL | ||||
| class OpenGLRenderWidget : public RenderWidget { | ||||
| public: | ||||
|     explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary) | ||||
|         : RenderWidget(parent), is_secondary(is_secondary) { | ||||
|         setAttribute(Qt::WA_NativeWindow); | ||||
|         setAttribute(Qt::WA_PaintOnScreen); | ||||
|         windowHandle()->setSurfaceType(QWindow::OpenGLSurface); | ||||
|     } | ||||
| 
 | ||||
|     void SetContext(std::unique_ptr<Frontend::GraphicsContext>&& context_) { | ||||
|         context = std::move(context_); | ||||
|     } | ||||
| 
 | ||||
|     void Present() override { | ||||
|         if (!isVisible()) { | ||||
|             return; | ||||
|         } | ||||
|         if (!Core::System::GetInstance().IsPoweredOn()) { | ||||
|             return; | ||||
|         } | ||||
|         context->MakeCurrent(); | ||||
|         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||
|         VideoCore::g_renderer->TryPresent(100, is_secondary); | ||||
|         context->SwapBuffers(); | ||||
|         glFinish(); | ||||
|     } | ||||
| 
 | ||||
|     QPaintEngine* paintEngine() const override { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Frontend::GraphicsContext> context{}; | ||||
|     bool is_secondary; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| struct SoftwareRenderWidget : public RenderWidget { | ||||
|     explicit SoftwareRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} | ||||
| 
 | ||||
|     void Present() override { | ||||
|         if (!isVisible()) { | ||||
|             return; | ||||
|         } | ||||
|         if (!Core::System::GetInstance().IsPoweredOn()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const auto layout{Layout::DefaultFrameLayout(width(), height(), false, false)}; | ||||
|         QPainter painter(this); | ||||
| 
 | ||||
|         const auto draw_screen = [&](int fb_id) { | ||||
|             const auto rect = fb_id == 0 ? layout.top_screen : layout.bottom_screen; | ||||
|             const QImage screen = LoadFramebuffer(fb_id); | ||||
|             painter.drawImage(rect.left, rect.top, screen); | ||||
|         }; | ||||
| 
 | ||||
|         painter.fillRect(rect(), qRgb(Settings::values.bg_red.GetValue() * 255, | ||||
|                                       Settings::values.bg_green.GetValue() * 255, | ||||
|                                       Settings::values.bg_blue.GetValue() * 255)); | ||||
|         draw_screen(0); | ||||
|         draw_screen(1); | ||||
| 
 | ||||
|         painter.end(); | ||||
|     } | ||||
| 
 | ||||
|     QImage LoadFramebuffer(int fb_id) { | ||||
|         const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id]; | ||||
|         const PAddr framebuffer_addr = | ||||
|             framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; | ||||
| 
 | ||||
|         Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); | ||||
|         const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr); | ||||
| 
 | ||||
|         const int width = framebuffer.height; | ||||
|         const int height = framebuffer.width; | ||||
|         const int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); | ||||
| 
 | ||||
|         QImage image{width, height, QImage::Format_RGBA8888}; | ||||
|         for (int y = 0; y < height; y++) { | ||||
|             for (int x = 0; x < width; x++) { | ||||
|                 const u8* pixel = framebuffer_data + (x * height + height - y) * bpp; | ||||
|                 const Common::Vec4 color = [&] { | ||||
|                     switch (framebuffer.color_format) { | ||||
|                     case GPU::Regs::PixelFormat::RGBA8: | ||||
|                         return Common::Color::DecodeRGBA8(pixel); | ||||
|                     case GPU::Regs::PixelFormat::RGB8: | ||||
|                         return Common::Color::DecodeRGB8(pixel); | ||||
|                     case GPU::Regs::PixelFormat::RGB565: | ||||
|                         return Common::Color::DecodeRGB565(pixel); | ||||
|                     case GPU::Regs::PixelFormat::RGB5A1: | ||||
|                         return Common::Color::DecodeRGB5A1(pixel); | ||||
|                     case GPU::Regs::PixelFormat::RGBA4: | ||||
|                         return Common::Color::DecodeRGBA4(pixel); | ||||
|                     } | ||||
|                 }(); | ||||
| 
 | ||||
|                 image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||
|             } | ||||
|         } | ||||
|         return image; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static Frontend::WindowSystemType GetWindowSystemType() { | ||||
|     // Determine WSI type based on Qt platform.
 | ||||
|     const QString platform_name = QGuiApplication::platformName(); | ||||
|     if (platform_name == QStringLiteral("windows")) | ||||
|         return Frontend::WindowSystemType::Windows; | ||||
|     else if (platform_name == QStringLiteral("xcb")) | ||||
|         return Frontend::WindowSystemType::X11; | ||||
|     else if (platform_name == QStringLiteral("wayland")) | ||||
|         return Frontend::WindowSystemType::Wayland; | ||||
|     else if (platform_name == QStringLiteral("cocoa")) | ||||
|         return Frontend::WindowSystemType::MacOS; | ||||
| 
 | ||||
|     LOG_CRITICAL(Frontend, "Unknown Qt platform!"); | ||||
|     return Frontend::WindowSystemType::Windows; | ||||
| } | ||||
| 
 | ||||
| void OpenGLWindow::exposeEvent(QExposeEvent* event) { | ||||
|     QWindow::requestUpdate(); | ||||
|     QWindow::exposeEvent(event); | ||||
| static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { | ||||
|     Frontend::EmuWindow::WindowSystemInfo wsi; | ||||
|     wsi.type = GetWindowSystemType(); | ||||
| 
 | ||||
|     if (window) { | ||||
| #if defined(WIN32) | ||||
|         // Our Win32 Qt external doesn't have the private API.
 | ||||
|         wsi.render_surface = reinterpret_cast<void*>(window->winId()); | ||||
| #elif defined(__APPLE__) | ||||
|         wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)( | ||||
|             reinterpret_cast<id>(window->winId()), sel_registerName("layer")); | ||||
| #else | ||||
|         QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); | ||||
|         wsi.display_connection = pni->nativeResourceForWindow("display", window); | ||||
|         if (wsi.type == Frontend::WindowSystemType::Wayland) | ||||
|             wsi.render_surface = pni->nativeResourceForWindow("surface", window); | ||||
|         else | ||||
|             wsi.render_surface = reinterpret_cast<void*>(window->winId()); | ||||
| #endif | ||||
|         wsi.render_surface_scale = static_cast<float>(window->devicePixelRatio()); | ||||
|     } else { | ||||
|         wsi.render_surface = nullptr; | ||||
|         wsi.render_surface_scale = 1.0f; | ||||
|     } | ||||
| 
 | ||||
|     return wsi; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<Frontend::GraphicsContext> GRenderWindow::main_context; | ||||
| 
 | ||||
| GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_) | ||||
|     : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) { | ||||
| 
 | ||||
|  | @ -218,11 +406,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() { | ||||
|  | @ -295,8 +483,9 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| } | ||||
| 
 | ||||
| void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||
|         return; // touch input is handled in TouchBeginEvent
 | ||||
|     } | ||||
| 
 | ||||
|     auto pos = event->pos(); | ||||
|     if (event->button() == Qt::LeftButton) { | ||||
|  | @ -309,8 +498,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| } | ||||
| 
 | ||||
| void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||
|         return; // touch input is handled in TouchUpdateEvent
 | ||||
|     } | ||||
| 
 | ||||
|     auto pos = event->pos(); | ||||
|     const auto [x, y] = ScaleTouch(pos); | ||||
|  | @ -320,8 +510,9 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| } | ||||
| 
 | ||||
| void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||
|         return; // touch input is handled in TouchEndEvent
 | ||||
|     } | ||||
| 
 | ||||
|     if (event->button() == Qt::LeftButton) | ||||
|         this->TouchReleased(); | ||||
|  | @ -393,42 +584,61 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
|     OnFramebufferSizeChanged(); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::InitRenderTarget() { | ||||
|     ReleaseRenderTarget(); | ||||
| bool GRenderWindow::InitRenderTarget() { | ||||
|     { | ||||
|         // 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 auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||
|     switch (graphics_api) { | ||||
|     case Settings::GraphicsAPI::Software: | ||||
|         InitializeSoftware(); | ||||
|         break; | ||||
|     case Settings::GraphicsAPI::OpenGL: | ||||
|         if (!InitializeOpenGL() || !LoadOpenGL()) { | ||||
|             return false; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     // Update the Window System information with the new render target
 | ||||
|     window_info = GetWindowSystemInfo(child_widget->windowHandle()); | ||||
| 
 | ||||
|     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) { | ||||
|     if (res_scale == 0) | ||||
|     if (res_scale == 0) { | ||||
|         res_scale = VideoCore::GetResolutionScaleFactor(); | ||||
|     } | ||||
| 
 | ||||
|     const auto layout{Layout::FrameLayoutFromResolutionScale(res_scale, is_secondary)}; | ||||
|     screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); | ||||
|     VideoCore::RequestScreenshot( | ||||
|     VideoCore::g_renderer->RequestScreenshot( | ||||
|         screenshot_image.bits(), | ||||
|         [this, screenshot_path] { | ||||
|             const std::string std_screenshot_path = screenshot_path.toStdString(); | ||||
|  | @ -445,6 +655,59 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
|     setMinimumSize(minimal_size.first, minimal_size.second); | ||||
| } | ||||
| 
 | ||||
| bool GRenderWindow::InitializeOpenGL() { | ||||
| #ifdef HAS_OPENGL | ||||
|     // 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(); | ||||
| 
 | ||||
|     if (!main_context) { | ||||
|         main_context = std::make_shared<OpenGLSharedContext>(); | ||||
|     } | ||||
| 
 | ||||
|     auto child_context = CreateSharedContext(); | ||||
|     child->SetContext(std::move(child_context)); | ||||
| 
 | ||||
|     return true; | ||||
| #else | ||||
|     QMessageBox::warning(this, tr("OpenGL not available!"), | ||||
|                          tr("Citra has not been compiled with OpenGL support.")); | ||||
|     return false; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::InitializeSoftware() { | ||||
|     child_widget = new SoftwareRenderWidget(this); | ||||
|     main_context = std::make_unique<DummyContext>(); | ||||
| } | ||||
| 
 | ||||
| bool GRenderWindow::LoadOpenGL() { | ||||
|     auto context = CreateSharedContext(); | ||||
|     auto scope = context->Acquire(); | ||||
|     if (!gladLoadGL()) { | ||||
|         QMessageBox::warning( | ||||
|             this, tr("Error while initializing OpenGL!"), | ||||
|             tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver.")); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     const QString renderer = | ||||
|         QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); | ||||
| 
 | ||||
|     if (!GLAD_GL_VERSION_4_3) { | ||||
|         LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString()); | ||||
|         QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"), | ||||
|                              tr("Your GPU may not support OpenGL 4.3, or you do not have the " | ||||
|                                 "latest graphics driver.<br><br>GL Renderer:<br>%1") | ||||
|                                  .arg(renderer)); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | ||||
|     this->emu_thread = emu_thread; | ||||
| } | ||||
|  | @ -458,29 +721,15 @@ void GRenderWindow::showEvent(QShowEvent* event) { | |||
| } | ||||
| 
 | ||||
| std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||||
|     return std::make_unique<GLContext>(QOpenGLContext::globalShareContext()); | ||||
| } | ||||
| 
 | ||||
| GLContext::GLContext(QOpenGLContext* shared_context) | ||||
|     : context(std::make_unique<QOpenGLContext>(shared_context->parent())), | ||||
|       surface(std::make_unique<QOffscreenSurface>(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(); | ||||
| #ifdef HAS_OPENGL | ||||
|     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||
|     if (graphics_api == Settings::GraphicsAPI::OpenGL) { | ||||
|         auto gl_context = static_cast<OpenGLSharedContext*>(main_context.get()); | ||||
|         // Bind the shared contexts to the main surface in case the backend wants to take over
 | ||||
|         // presentation
 | ||||
|         return std::make_unique<OpenGLSharedContext>(gl_context->GetShareContext(), | ||||
|                                                      child_widget->windowHandle()); | ||||
|     } | ||||
| #endif | ||||
|     return std::make_unique<DummyContext>(); | ||||
| } | ||||
|  |  | |||
|  | @ -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<QOpenGLContext> context; | ||||
|     std::unique_ptr<QOffscreenSurface> 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<QOpenGLContext> 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<u32, u32> ScaleTouch(const QPointF pos) const; | ||||
| 
 | ||||
| public slots: | ||||
| 
 | ||||
|     void OnEmulationStarting(EmuThread* emu_thread); | ||||
|  | @ -211,29 +180,28 @@ signals: | |||
|     void MouseActivity(); | ||||
| 
 | ||||
| private: | ||||
|     std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||||
|     void TouchBeginEvent(const QTouchEvent* event); | ||||
|     void TouchUpdateEvent(const QTouchEvent* event); | ||||
|     void TouchEndEvent(); | ||||
| 
 | ||||
|     void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; | ||||
| 
 | ||||
|     std::unique_ptr<GraphicsContext> core_context; | ||||
|     bool InitializeOpenGL(); | ||||
|     void InitializeSoftware(); | ||||
|     bool LoadOpenGL(); | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     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
 | ||||
|     static std::shared_ptr<Frontend::GraphicsContext> main_context; | ||||
| 
 | ||||
|     /// Temporary storage of the screenshot taken
 | ||||
|     QImage screenshot_image; | ||||
|     QByteArray geometry; | ||||
|     bool first_frame = false; | ||||
|     bool has_focus = false; | ||||
| 
 | ||||
|  |  | |||
|  | @ -482,6 +482,7 @@ void Config::ReadDebuggingValues() { | |||
|         qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); | ||||
|     ReadBasicSetting(Settings::values.use_gdbstub); | ||||
|     ReadBasicSetting(Settings::values.gdbstub_port); | ||||
|     ReadBasicSetting(Settings::values.renderer_debug); | ||||
| 
 | ||||
|     qt_config->beginGroup(QStringLiteral("LLE")); | ||||
|     for (const auto& service_module : Service::service_module_map) { | ||||
|  | @ -625,7 +626,7 @@ void Config::ReadPathValues() { | |||
| void Config::ReadRendererValues() { | ||||
|     qt_config->beginGroup(QStringLiteral("Renderer")); | ||||
| 
 | ||||
|     ReadGlobalSetting(Settings::values.use_hw_renderer); | ||||
|     ReadGlobalSetting(Settings::values.graphics_api); | ||||
|     ReadGlobalSetting(Settings::values.use_hw_shader); | ||||
| #ifdef __APPLE__ | ||||
|     // Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
 | ||||
|  | @ -992,6 +993,7 @@ void Config::SaveDebuggingValues() { | |||
|     qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); | ||||
|     WriteBasicSetting(Settings::values.use_gdbstub); | ||||
|     WriteBasicSetting(Settings::values.gdbstub_port); | ||||
|     WriteBasicSetting(Settings::values.renderer_debug); | ||||
| 
 | ||||
|     qt_config->beginGroup(QStringLiteral("LLE")); | ||||
|     for (const auto& service_module : Settings::values.lle_modules) { | ||||
|  | @ -1103,7 +1105,7 @@ void Config::SavePathValues() { | |||
| void Config::SaveRendererValues() { | ||||
|     qt_config->beginGroup(QStringLiteral("Renderer")); | ||||
| 
 | ||||
|     WriteGlobalSetting(Settings::values.use_hw_renderer); | ||||
|     WriteGlobalSetting(Settings::values.graphics_api); | ||||
|     WriteGlobalSetting(Settings::values.use_hw_shader); | ||||
| #ifdef __APPLE__ | ||||
|     // Hardware shader is broken on macos thanks to poor drivers.
 | ||||
|  |  | |||
|  | @ -83,6 +83,21 @@ template <> | |||
| void SetPerGameSetting(QComboBox* combobox, | ||||
|                        const Settings::SwitchableSetting<std::string>* setting); | ||||
| 
 | ||||
| /// Given an index of a combobox setting extracts the setting taking into
 | ||||
| /// account per-game status
 | ||||
| template <typename Type, bool ranged> | ||||
| Type GetComboboxSetting(int index, const Settings::SwitchableSetting<Type, ranged>* setting) { | ||||
|     if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { | ||||
|         return static_cast<Type>(index); | ||||
|     } else if (!Settings::IsConfiguringGlobal()) { | ||||
|         if (index == 0) { | ||||
|             return setting->GetValue(); | ||||
|         } else { | ||||
|             return static_cast<Type>(index - ConfigurationShared::USE_GLOBAL_OFFSET); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Given a Qt widget sets the background color to indicate whether the setting
 | ||||
| /// is per-game overriden (highlighted) or global (non-highlighted)
 | ||||
| void SetHighlight(QWidget* widget, bool highlighted); | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) | |||
| 
 | ||||
|     const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); | ||||
|     ui->toggle_cpu_jit->setEnabled(!is_powered_on); | ||||
|     ui->toggle_renderer_debug->setEnabled(!is_powered_on); | ||||
| 
 | ||||
|     // Set a minimum width for the label to prevent the slider from changing size.
 | ||||
|     // This scales across DPIs. (This value should be enough for "xxx%")
 | ||||
|  | @ -62,6 +63,7 @@ void ConfigureDebug::SetConfiguration() { | |||
|     ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); | ||||
|     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); | ||||
|     ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); | ||||
|     ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); | ||||
| 
 | ||||
|     if (!Settings::IsConfiguringGlobal()) { | ||||
|         if (Settings::values.cpu_clock_percentage.UsingGlobal()) { | ||||
|  | @ -91,6 +93,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
|     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||
|     Log::SetGlobalFilter(filter); | ||||
|     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); | ||||
|     Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); | ||||
| 
 | ||||
|     ConfigurationShared::ApplyPerGameSetting( | ||||
|         &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, | ||||
|  |  | |||
|  | @ -23,5 +23,6 @@ public: | |||
|     void SetConfiguration(); | ||||
|     void SetupPerGameUI(); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Ui::ConfigureDebug> ui; | ||||
| }; | ||||
|  |  | |||
|  | @ -6,8 +6,8 @@ | |||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>443</width> | ||||
|     <height>358</height> | ||||
|     <width>523</width> | ||||
|     <height>447</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|  | @ -112,12 +112,34 @@ | |||
|       <string>CPU</string> | ||||
|      </property> | ||||
|      <layout class="QGridLayout" name="gridLayout_2"> | ||||
|       <item row="2" column="0"> | ||||
|        <widget class="QCheckBox" name="toggle_cpu_jit"> | ||||
|         <property name="toolTip"> | ||||
|          <string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Enable CPU JIT</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item row="1" column="0"> | ||||
|        <widget class="QWidget" name="clock_speed_widget" native="true"> | ||||
|         <layout class="QHBoxLayout" name="clock_speed_layout"> | ||||
|          <property name="spacing"> | ||||
|           <number>7</number> | ||||
|          </property> | ||||
|          <property name="leftMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="topMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="rightMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="bottomMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QComboBox" name="clock_speed_combo"> | ||||
|            <item> | ||||
|  | @ -180,13 +202,10 @@ | |||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item row="2" column="0"> | ||||
|        <widget class="QCheckBox" name="toggle_cpu_jit"> | ||||
|         <property name="toolTip"> | ||||
|          <string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string> | ||||
|         </property> | ||||
|       <item row="3" column="0"> | ||||
|        <widget class="QCheckBox" name="toggle_renderer_debug"> | ||||
|         <property name="text"> | ||||
|          <string>Enable CPU JIT</string> | ||||
|          <string>Enable debug renderer</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|  |  | |||
|  | @ -22,7 +22,9 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) | |||
| 
 | ||||
|     ui->layout_group->setEnabled(!Settings::values.custom_layout); | ||||
| 
 | ||||
|     ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue()); | ||||
|     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||
|     const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; | ||||
|     ui->resolution_factor_combobox->setEnabled(res_scale_enabled); | ||||
| 
 | ||||
|     connect(ui->render_3d_combobox, | ||||
|             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||||
|  |  | |||
|  | @ -16,22 +16,20 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
|     : QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) { | ||||
|     ui->setupUi(this); | ||||
| 
 | ||||
|     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()); | ||||
|     // Set the index to -1 to ensure the below lambda is called with setCurrentIndex
 | ||||
|     ui->graphics_api_combo->setCurrentIndex(-1); | ||||
| 
 | ||||
|     connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { | ||||
|         const bool checked = ui->toggle_hw_renderer->isChecked(); | ||||
|         ui->hw_renderer_group->setEnabled(checked); | ||||
|         ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked()); | ||||
|     }); | ||||
|     connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||
|             [this](int index) { | ||||
|                 const auto graphics_api = | ||||
|                     ConfigurationShared::GetComboboxSetting(index, &Settings::values.graphics_api); | ||||
|                 const bool is_software = graphics_api == Settings::GraphicsAPI::Software; | ||||
| 
 | ||||
|     ui->hw_shader_group->setEnabled(ui->toggle_hw_shader->isChecked()); | ||||
|     ui->toggle_disk_shader_cache->setEnabled(ui->toggle_hw_renderer->isChecked() && | ||||
|                                              ui->toggle_hw_shader->isChecked()); | ||||
|                 ui->hw_renderer_group->setEnabled(!is_software); | ||||
|                 ui->toggle_disk_shader_cache->setEnabled(!is_software && | ||||
|                                                          ui->toggle_hw_shader->isChecked()); | ||||
|             }); | ||||
| 
 | ||||
|     connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { | ||||
|         const bool checked = ui->toggle_hw_shader->isChecked(); | ||||
|  | @ -60,12 +58,24 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
|     // TODO(B3N30): Hide this for macs with none Intel GPUs, too.
 | ||||
|     ui->toggle_separable_shader->setVisible(false); | ||||
| #endif | ||||
| 
 | ||||
|     SetupPerGameUI(); | ||||
|     SetConfiguration(); | ||||
| } | ||||
| 
 | ||||
| ConfigureGraphics::~ConfigureGraphics() = default; | ||||
| 
 | ||||
| void ConfigureGraphics::SetConfiguration() { | ||||
|     ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue()); | ||||
|     if (!Settings::IsConfiguringGlobal()) { | ||||
|         ConfigurationShared::SetHighlight(ui->graphics_api_group, | ||||
|                                           !Settings::values.graphics_api.UsingGlobal()); | ||||
|         ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo, | ||||
|                                                &Settings::values.graphics_api); | ||||
|     } else { | ||||
|         ui->graphics_api_combo->setCurrentIndex( | ||||
|             static_cast<int>(Settings::values.graphics_api.GetValue())); | ||||
|     } | ||||
| 
 | ||||
|     ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); | ||||
|     ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue()); | ||||
|     ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); | ||||
|  | @ -78,8 +88,8 @@ void ConfigureGraphics::SetConfiguration() { | |||
| } | ||||
| 
 | ||||
| void ConfigureGraphics::ApplyConfiguration() { | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer, | ||||
|                                              ui->toggle_hw_renderer, use_hw_renderer); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, | ||||
|                                              ui->graphics_api_combo); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, | ||||
|                                              use_hw_shader); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader, | ||||
|  | @ -103,7 +113,7 @@ void ConfigureGraphics::RetranslateUI() { | |||
| void ConfigureGraphics::SetupPerGameUI() { | ||||
|     // Block the global settings if a game is currently running that overrides them
 | ||||
|     if (Settings::IsConfiguringGlobal()) { | ||||
|         ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal()); | ||||
|         ui->graphics_api_group->setEnabled(Settings::values.graphics_api.UsingGlobal()); | ||||
|         ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal()); | ||||
|         ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal()); | ||||
|         ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal()); | ||||
|  | @ -115,8 +125,10 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 
 | ||||
|     ui->toggle_shader_jit->setVisible(false); | ||||
| 
 | ||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer, | ||||
|                                             Settings::values.use_hw_renderer, use_hw_renderer); | ||||
|     ConfigurationShared::SetColoredComboBox( | ||||
|         ui->graphics_api_combo, ui->graphics_api_group, | ||||
|         static_cast<u32>(Settings::values.graphics_api.GetValue(true))); | ||||
| 
 | ||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, | ||||
|                                             use_hw_shader); | ||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader, | ||||
|  |  | |||
|  | @ -28,9 +28,9 @@ public: | |||
| 
 | ||||
|     void UpdateBackgroundColorButton(const QColor& color); | ||||
| 
 | ||||
| private: | ||||
|     void SetupPerGameUI(); | ||||
| 
 | ||||
|     ConfigurationShared::CheckState use_hw_renderer; | ||||
|     ConfigurationShared::CheckState use_hw_shader; | ||||
|     ConfigurationShared::CheckState separable_shader; | ||||
|     ConfigurationShared::CheckState shaders_accurate_mul; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>400</width> | ||||
|     <height>430</height> | ||||
|     <height>443</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="minimumSize"> | ||||
|  | @ -20,27 +20,65 @@ | |||
|    <string>Form</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="apiBox"> | ||||
|      <property name="title"> | ||||
|       <string>API Settings</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|       <item> | ||||
|        <widget class="QWidget" name="graphics_api_group" native="true"> | ||||
|         <layout class="QHBoxLayout" name="graphics_api_group_2"> | ||||
|          <property name="leftMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="topMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="rightMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="bottomMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="graphics_api_label"> | ||||
|            <property name="text"> | ||||
|             <string>Graphics API</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QComboBox" name="graphics_api_combo"> | ||||
|            <item> | ||||
|             <property name="text"> | ||||
|              <string>Software</string> | ||||
|             </property> | ||||
|            </item> | ||||
|            <item> | ||||
|             <property name="text"> | ||||
|              <string>OpenGL</string> | ||||
|             </property> | ||||
|            </item> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="rendererBox"> | ||||
|      <property name="title"> | ||||
|       <string>Renderer</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|       <item> | ||||
|        <widget class="QCheckBox" name="toggle_hw_renderer"> | ||||
|         <property name="toolTip"> | ||||
|          <string><html><head/><body><p>Use OpenGL to accelerate rendering.</p><p>Disable to debug graphics-related problem.</p></body></html></string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Enable Hardware Renderer</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QWidget" name="hw_renderer_group" native="true"> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|          <property name="leftMargin"> | ||||
|           <number>16</number> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="topMargin"> | ||||
|           <number>0</number> | ||||
|  | @ -157,8 +195,6 @@ | |||
|   </layout> | ||||
|  </widget> | ||||
|  <tabstops> | ||||
|   <tabstop>toggle_hw_renderer</tabstop> | ||||
|   <tabstop>toggle_hw_shader</tabstop> | ||||
|   <tabstop>toggle_separable_shader</tabstop> | ||||
|   <tabstop>toggle_accurate_mul</tabstop> | ||||
|   <tabstop>toggle_shader_jit</tabstop> | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ | |||
| #include <QFutureWatcher> | ||||
| #include <QLabel> | ||||
| #include <QMessageBox> | ||||
| #include <QOpenGLFunctions_4_3_Core> | ||||
| #include <QSysInfo> | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include <QtGui> | ||||
|  | @ -88,7 +87,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/cfg/cfg.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
|  | @ -1026,16 +1024,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<QOpenGLFunctions_4_3_Core>()) { | ||||
|         QMessageBox::critical(this, below_gl43_title, below_gl43_message); | ||||
|         return false; | ||||
|     } | ||||
|     const auto scope = render_window->Acquire(); | ||||
| 
 | ||||
|     Core::System& system{Core::System::GetInstance()}; | ||||
| 
 | ||||
|  | @ -1091,28 +1080,6 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
|                                   tr("GBA Virtual Console ROMs are not supported by Citra.")); | ||||
|             break; | ||||
| 
 | ||||
|         case Core::System::ResultStatus::ErrorVideoCore: | ||||
|             QMessageBox::critical( | ||||
|                 this, tr("Video Core Error"), | ||||
|                 tr("An error has occurred. Please <a " | ||||
|                    "href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see " | ||||
|                    "the " | ||||
|                    "log</a> for more details. " | ||||
|                    "Ensure that you have the latest graphics drivers for your GPU.")); | ||||
|             break; | ||||
| 
 | ||||
|         case Core::System::ResultStatus::ErrorVideoCore_ErrorGenericDrivers: | ||||
|             QMessageBox::critical( | ||||
|                 this, tr("Video Core Error"), | ||||
|                 tr("You are running default Windows drivers " | ||||
|                    "for your GPU. You need to install the " | ||||
|                    "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!"), | ||||
|  | @ -2786,14 +2753,6 @@ int main(int argc, char* argv[]) { | |||
|     QCoreApplication::setOrganizationName(QStringLiteral("Citra team")); | ||||
|     QCoreApplication::setApplicationName(QStringLiteral("Citra")); | ||||
| 
 | ||||
|     QSurfaceFormat format; | ||||
|     format.setVersion(4, 3); | ||||
|     format.setProfile(QSurfaceFormat::CoreProfile); | ||||
|     format.setSwapInterval(0); | ||||
|     // TODO: expose a setting for buffer value (ie default/single/double/triple)
 | ||||
|     format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||||
|     QSurfaceFormat::setDefaultFormat(format); | ||||
| 
 | ||||
|     SetHighDPIAttributes(); | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue