mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge pull request #165 from neobrain/viewport-scaling
Stretch emulation output to render window and be display density independent
This commit is contained in:
		
						commit
						40acd63631
					
				| @ -2,6 +2,8 @@ | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <GLFW/glfw3.h> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| #include "video_core/video_core.h" | ||||
| @ -10,22 +12,21 @@ | ||||
| 
 | ||||
| #include "citra/emu_window/emu_window_glfw.h" | ||||
| 
 | ||||
| EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) { | ||||
|     return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win)); | ||||
| } | ||||
| 
 | ||||
| /// Called by GLFW when a key event occurs
 | ||||
| void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { | ||||
| 
 | ||||
|     if (!VideoCore::g_emu_window) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id; | ||||
|     int keyboard_id = GetEmuWindow(win)->keyboard_id; | ||||
| 
 | ||||
|     if (action == GLFW_PRESS) { | ||||
|         EmuWindow::KeyPressed({key, keyboard_id}); | ||||
|     } | ||||
| 
 | ||||
|     if (action == GLFW_RELEASE) { | ||||
|     } else if (action == GLFW_RELEASE) { | ||||
|         EmuWindow::KeyReleased({key, keyboard_id}); | ||||
|     } | ||||
| 
 | ||||
|     HID_User::PadUpdateComplete(); | ||||
| } | ||||
| 
 | ||||
| @ -34,6 +35,23 @@ const bool EmuWindow_GLFW::IsOpen() { | ||||
|     return glfwWindowShouldClose(m_render_window) == 0; | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
| 
 | ||||
|     GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
| 
 | ||||
|     // NOTE: GLFW provides no proper way to set a minimal window size.
 | ||||
|     //       Hence, we just ignore the corresponding EmuWindow hint.
 | ||||
| 
 | ||||
|     GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||||
| } | ||||
| 
 | ||||
| /// EmuWindow_GLFW constructor
 | ||||
| EmuWindow_GLFW::EmuWindow_GLFW() { | ||||
|     keyboard_id = KeyMap::NewDeviceId(); | ||||
| @ -50,19 +68,31 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | ||||
|     // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
 | ||||
|     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | ||||
|     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | ||||
| 	 | ||||
|     m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,  | ||||
|         (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),  | ||||
|         m_window_title.c_str(), NULL, NULL); | ||||
| 
 | ||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||
|     m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, | ||||
|         (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | ||||
|         window_title.c_str(), NULL, NULL); | ||||
| 
 | ||||
|     if (m_render_window == NULL) { | ||||
|         printf("Failed to create GLFW window! Exiting..."); | ||||
|         exit(1); | ||||
|     } | ||||
|      | ||||
|     // Setup callbacks
 | ||||
| 
 | ||||
|     glfwSetWindowUserPointer(m_render_window, this); | ||||
| 
 | ||||
|     // Notify base interface about window state
 | ||||
|     int width, height; | ||||
|     glfwGetFramebufferSize(m_render_window, &width, &height); | ||||
|     OnFramebufferResizeEvent(m_render_window, width, height); | ||||
| 
 | ||||
|     glfwGetWindowSize(m_render_window, &width, &height); | ||||
|     OnClientAreaResizeEvent(m_render_window, width, height); | ||||
| 
 | ||||
|     // Setup callbacks
 | ||||
|     glfwSetKeyCallback(m_render_window, OnKeyEvent); | ||||
|     glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); | ||||
|     glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); | ||||
| 
 | ||||
|     DoneCurrent(); | ||||
| } | ||||
| @ -110,3 +140,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() { | ||||
|     KeyMap::SetKeyMapping({Settings::values.pad_sup_key,    keyboard_id}, HID_User::PAD_CIRCLE_UP); | ||||
|     KeyMap::SetKeyMapping({Settings::values.pad_sdown_key,  keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||||
|     std::pair<int,int> current_size; | ||||
|     glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second); | ||||
| 
 | ||||
|     _dbg_assert_(GUI, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); | ||||
|     int new_width  = std::max(current_size.first,  (int)minimal_size.first); | ||||
|     int new_height = std::max(current_size.second, (int)minimal_size.second); | ||||
| 
 | ||||
|     if (current_size != std::make_pair(new_width, new_height)) | ||||
|         glfwSetWindowSize(m_render_window, new_width, new_height); | ||||
| } | ||||
|  | ||||
| @ -4,10 +4,10 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <GLFW/glfw3.h> | ||||
| 
 | ||||
| #include "common/emu_window.h" | ||||
| 
 | ||||
| struct GLFWwindow; | ||||
| 
 | ||||
| class EmuWindow_GLFW : public EmuWindow { | ||||
| public: | ||||
|     EmuWindow_GLFW(); | ||||
| @ -21,7 +21,7 @@ public: | ||||
| 
 | ||||
|     /// Makes the graphics context current for the caller thread
 | ||||
|     void MakeCurrent() override; | ||||
|      | ||||
| 
 | ||||
|     /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
 | ||||
|     void DoneCurrent() override; | ||||
| 
 | ||||
| @ -30,9 +30,17 @@ public: | ||||
|     /// Whether the window is still open, and a close request hasn't yet been sent
 | ||||
|     const bool IsOpen(); | ||||
| 
 | ||||
|     static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height); | ||||
| 
 | ||||
|     static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height); | ||||
| 
 | ||||
|     void ReloadSetKeymaps() override; | ||||
| 
 | ||||
| private: | ||||
|     void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||||
| 
 | ||||
|     static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win); | ||||
| 
 | ||||
|     GLFWwindow* m_render_window; ///< Internal GLFW render window
 | ||||
| 
 | ||||
|     /// Device id of keyboard for use with KeyMap
 | ||||
|  | ||||
| @ -2,6 +2,12 @@ | ||||
| #include <QKeyEvent> | ||||
| #include <QApplication> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||
| // Required for screen DPI information
 | ||||
| #include <QScreen> | ||||
| #include <QWindow> | ||||
| #endif | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "bootmanager.hxx" | ||||
| 
 | ||||
| @ -82,20 +88,20 @@ void EmuThread::Stop() | ||||
| class GGLWidgetInternal : public QGLWidget | ||||
| { | ||||
| public: | ||||
|     GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent) | ||||
|     { | ||||
|         parent_ = parent; | ||||
|     GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) | ||||
|                      : QGLWidget(fmt, parent), parent(parent) { | ||||
|     } | ||||
| 
 | ||||
|     void paintEvent(QPaintEvent* ev) override | ||||
|     { | ||||
|     void paintEvent(QPaintEvent* ev) override { | ||||
|     } | ||||
| 
 | ||||
|     void resizeEvent(QResizeEvent* ev) override { | ||||
|         parent_->SetClientAreaWidth(size().width()); | ||||
|         parent_->SetClientAreaHeight(size().height()); | ||||
|         parent->OnClientAreaResized(ev->size().width(), ev->size().height()); | ||||
|         parent->OnFramebufferSizeChanged(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     GRenderWindow* parent_; | ||||
|     GRenderWindow* parent; | ||||
| }; | ||||
| 
 | ||||
| EmuThread& GRenderWindow::GetEmuThread() | ||||
| @ -105,6 +111,9 @@ EmuThread& GRenderWindow::GetEmuThread() | ||||
| 
 | ||||
| GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) | ||||
| { | ||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||
|     setWindowTitle(QString::fromStdString(window_title)); | ||||
| 
 | ||||
|     keyboard_id = KeyMap::NewDeviceId(); | ||||
|     ReloadSetKeymaps(); | ||||
| 
 | ||||
| @ -114,16 +123,25 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this | ||||
|     fmt.setProfile(QGLFormat::CoreProfile); | ||||
|     // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
 | ||||
|     fmt.setOption(QGL::NoDeprecatedFunctions); | ||||
|      | ||||
| 
 | ||||
|     child = new GGLWidgetInternal(fmt, this); | ||||
|     QBoxLayout* layout = new QHBoxLayout(this); | ||||
|     resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); | ||||
|     layout->addWidget(child); | ||||
|     layout->setMargin(0); | ||||
|     setLayout(layout); | ||||
|     QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); | ||||
|     connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); | ||||
| 
 | ||||
|     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||||
| 
 | ||||
|     OnFramebufferSizeChanged(); | ||||
|     NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height())); | ||||
| 
 | ||||
|     BackupGeometry(); | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||
|     connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged())); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::moveContext() | ||||
| @ -166,14 +184,28 @@ void GRenderWindow::DoneCurrent() | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::PollEvents() { | ||||
|     // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title
 | ||||
|     //  from the main thread, but this should probably be in an event handler...
 | ||||
|     /*
 | ||||
|     static char title[128]; | ||||
|     sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(),  | ||||
|         video_core::g_renderer->current_fps()); | ||||
|     setWindowTitle(title); | ||||
|     */ | ||||
| } | ||||
| 
 | ||||
| // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | ||||
| //
 | ||||
| // Older versions get the window size (density independent pixels),
 | ||||
| // and hence, do not support DPI scaling ("retina" displays).
 | ||||
| // The result will be a viewport that is smaller than the extent of the window.
 | ||||
| void GRenderWindow::OnFramebufferSizeChanged() | ||||
| { | ||||
|     // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||
|     // windowHandle() might not be accessible until the window is displayed to screen.
 | ||||
|     auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0; | ||||
| 
 | ||||
|     unsigned width = child->QPaintDevice::width() * pixel_ratio; | ||||
|     unsigned height = child->QPaintDevice::height() * pixel_ratio; | ||||
| #else | ||||
|     unsigned width = child->QPaintDevice::width(); | ||||
|     unsigned height = child->QPaintDevice::height(); | ||||
| #endif | ||||
| 
 | ||||
|     NotifyFramebufferSizeChanged(std::make_pair(width, height)); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::BackupGeometry() | ||||
| @ -236,3 +268,11 @@ void GRenderWindow::ReloadSetKeymaps() | ||||
|     KeyMap::SetKeyMapping({Settings::values.pad_sdown_key,  keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) | ||||
| { | ||||
|     NotifyClientAreaSizeChanged(std::make_pair(width, height)); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||||
| 	setMinimumSize(minimal_size.first, minimal_size.second); | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,16 @@ | ||||
| #include <atomic> | ||||
| 
 | ||||
| #include <QThread> | ||||
| #include <QGLWidget> | ||||
| #include <atomic> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/emu_window.h" | ||||
| 
 | ||||
| class GRenderWindow; | ||||
| class QScreen; | ||||
| class QKeyEvent; | ||||
| 
 | ||||
| class GRenderWindow; | ||||
| 
 | ||||
| class EmuThread : public QThread | ||||
| { | ||||
|     Q_OBJECT | ||||
| @ -74,7 +78,7 @@ private: | ||||
| signals: | ||||
|     /**
 | ||||
|      * Emitted when CPU when we've finished processing a single Gekko instruction | ||||
|      *  | ||||
|      * | ||||
|      * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) | ||||
|      * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) | ||||
|      */ | ||||
| @ -100,7 +104,7 @@ public: | ||||
|     void BackupGeometry(); | ||||
|     void RestoreGeometry(); | ||||
|     void restoreGeometry(const QByteArray& geometry); // overridden
 | ||||
|     QByteArray saveGeometry(); // overridden
 | ||||
|     QByteArray saveGeometry();  // overridden
 | ||||
| 
 | ||||
|     EmuThread& GetEmuThread(); | ||||
| 
 | ||||
| @ -109,10 +113,16 @@ public: | ||||
| 
 | ||||
|     void ReloadSetKeymaps() override; | ||||
| 
 | ||||
|     void OnClientAreaResized(unsigned width, unsigned height); | ||||
| 
 | ||||
|     void OnFramebufferSizeChanged(); | ||||
| 
 | ||||
| public slots: | ||||
|     void moveContext(); | ||||
|     void moveContext();  // overridden
 | ||||
| 
 | ||||
| private: | ||||
|     void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||||
| 
 | ||||
|     QGLWidget* child; | ||||
| 
 | ||||
|     EmuThread emu_thread; | ||||
|  | ||||
| @ -117,7 +117,8 @@ GMainWindow::GMainWindow() | ||||
|     connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); | ||||
|     connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); | ||||
| 
 | ||||
|     setWindowTitle(render_window->GetWindowTitle().c_str()); | ||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||
|     setWindowTitle(window_title.c_str()); | ||||
| 
 | ||||
|     show(); | ||||
| 
 | ||||
|  | ||||
| @ -9,17 +9,33 @@ | ||||
| #include "common/string_util.h" | ||||
| #include "common/key_map.h" | ||||
| 
 | ||||
| // Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL, 
 | ||||
| //  QGLWidget, GLFW, etc...)
 | ||||
| /**
 | ||||
|  * Abstraction class used to provide an interface between emulation code and the frontend | ||||
|  * (e.g. SDL, QGLWidget, GLFW, etc...). | ||||
|  * | ||||
|  * Design notes on the interaction between EmuWindow and the emulation core: | ||||
|  * - Generally, decisions on anything visible to the user should be left up to the GUI. | ||||
|  *   For example, the emulation core should not try to dictate some window title or size. | ||||
|  *   This stuff is not the core's business and only causes problems with regards to thread-safety | ||||
|  *   anyway. | ||||
|  * - Under certain circumstances, it may be desirable for the core to politely request the GUI | ||||
|  *   to set e.g. a minimum window size. However, the GUI should always be free to ignore any | ||||
|  *   such hints. | ||||
|  * - EmuWindow may expose some of its state as read-only to the emulation core, however care | ||||
|  *   should be taken to make sure the provided information is self-consistent. This requires | ||||
|  *   some sort of synchronization (most of this is still a TODO). | ||||
|  * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | ||||
|  *   re-read the upper points again and think about it if you don't see this. | ||||
|  */ | ||||
| class EmuWindow | ||||
| { | ||||
| 
 | ||||
| public: | ||||
|     /// Data structure to store an emuwindow configuration
 | ||||
|     /// Data structure to store emuwindow configuration
 | ||||
|     struct WindowConfig { | ||||
|         bool    fullscreen; | ||||
|         int     res_width; | ||||
|         int     res_height; | ||||
|         std::pair<unsigned,unsigned> min_client_area_size; | ||||
|     }; | ||||
| 
 | ||||
|     /// Swap buffers to display the next frame
 | ||||
| @ -42,52 +58,96 @@ public: | ||||
|     /// Signals a key release action to the HID module
 | ||||
|     static void KeyReleased(KeyMap::HostDeviceKey key); | ||||
| 
 | ||||
|     WindowConfig GetConfig() const { | ||||
|         return m_config; | ||||
|     /**
 | ||||
|      * Returns currently active configuration. | ||||
|      * @note Accesses to the returned object need not be consistent because it may be modified in another thread | ||||
|      */ | ||||
|     const WindowConfig& GetActiveConfig() const { | ||||
|         return active_config; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Requests the internal configuration to be replaced by the specified argument at some point in the future. | ||||
|      * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active. | ||||
|      */ | ||||
|     void SetConfig(const WindowConfig& val) { | ||||
|         m_config = val; | ||||
|     } | ||||
|      | ||||
|     int GetClientAreaWidth() const {  | ||||
|         return m_client_area_width; | ||||
|         config = val; | ||||
|     } | ||||
| 
 | ||||
|     void SetClientAreaWidth(const int val) { | ||||
|         m_client_area_width = val; | ||||
|     /**
 | ||||
|       * Gets the framebuffer size in pixels. | ||||
|       * @note This method is thread-safe | ||||
|       */ | ||||
|     const std::pair<unsigned,unsigned> GetFramebufferSize() const { | ||||
|         return framebuffer_size; | ||||
|     } | ||||
| 
 | ||||
|     int GetClientAreaHeight() const { | ||||
|         return m_client_area_height; | ||||
|     } | ||||
| 
 | ||||
|     void SetClientAreaHeight(const int val) { | ||||
|         m_client_area_height = val; | ||||
|     } | ||||
| 
 | ||||
|     std::string GetWindowTitle() const {  | ||||
|         return m_window_title; | ||||
|     } | ||||
|      | ||||
|     void SetWindowTitle(std::string val) { | ||||
|         m_window_title = val; | ||||
|     /**
 | ||||
|      * Gets window client area width in logical coordinates. | ||||
|      * @note For high-DPI systems, this is smaller than the framebuffer size. | ||||
|      * @note This method is thread-safe | ||||
|      */ | ||||
|     std::pair<unsigned,unsigned> GetClientAreaSize() const { | ||||
|         return std::make_pair(client_area_width, client_area_height); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     EmuWindow(): | ||||
|         m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)), | ||||
|         m_client_area_width(640), | ||||
|         m_client_area_height(480) | ||||
|     {} | ||||
|     EmuWindow() | ||||
|     { | ||||
|         // TODO: Find a better place to set this.
 | ||||
|         config.min_client_area_size = std::make_pair(400u, 480u); | ||||
|         active_config = config; | ||||
|     } | ||||
|     virtual ~EmuWindow() {} | ||||
| 
 | ||||
|     std::string m_window_title;     ///< Current window title, should be used by window impl.
 | ||||
|     /**
 | ||||
|      * Processes any pending configuration changes from the last SetConfig call. | ||||
|      * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration | ||||
|      * field changed. | ||||
|      * @note Implementations will usually want to call this from the GUI thread. | ||||
|      * @todo Actually call this in existing implementations. | ||||
|      */ | ||||
|     void ProcessConfigurationChanges() { | ||||
|         // TODO: For proper thread safety, we should eventually implement a proper
 | ||||
|         // multiple-writer/single-reader queue...
 | ||||
| 
 | ||||
|     int m_client_area_width;        ///< Current client width, should be set by window impl.
 | ||||
|     int m_client_area_height;       ///< Current client height, should be set by window impl.
 | ||||
|         if (config.min_client_area_size != active_config.min_client_area_size) { | ||||
|             OnMinimalClientAreaChangeRequest(config.min_client_area_size); | ||||
|             config.min_client_area_size = active_config.min_client_area_size; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Update internal framebuffer size with the given parameter. | ||||
|      * @note EmuWindow implementations will usually use this in window resize event handlers. | ||||
|      */ | ||||
|     void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) { | ||||
|         framebuffer_size = size; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Update internal client area size with the given parameter. | ||||
|      * @note EmuWindow implementations will usually use this in window resize event handlers. | ||||
|      */ | ||||
|     void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) { | ||||
|         client_area_width = size.first; | ||||
|         client_area_height = size.second; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     WindowConfig m_config;                ///< Internal configuration
 | ||||
|     /**
 | ||||
|      * Handler called when the minimal client area was requested to be changed via SetConfig. | ||||
|      * For the request to be honored, EmuWindow implementations will usually reimplement this function. | ||||
|      */ | ||||
|     virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||||
|         // By default, ignore this request and do nothing.
 | ||||
|     } | ||||
| 
 | ||||
|     std::pair<unsigned,unsigned> framebuffer_size; | ||||
| 
 | ||||
|     unsigned client_area_width;    ///< Current client width, should be set by window impl.
 | ||||
|     unsigned client_area_height;   ///< Current client height, should be set by window impl.
 | ||||
| 
 | ||||
|     WindowConfig config;         ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
 | ||||
|     WindowConfig active_config;  ///< Internal active configuration
 | ||||
| }; | ||||
|  | ||||
| @ -69,6 +69,7 @@ enum LOG_TYPE { | ||||
|     HW, | ||||
|     TIME, | ||||
|     NETPLAY, | ||||
|     GUI, | ||||
| 
 | ||||
|     NUMBER_OF_LOGS // Must be last
 | ||||
| }; | ||||
|  | ||||
| @ -75,6 +75,7 @@ LogManager::LogManager() | ||||
|     m_Log[LogTypes::ACTIONREPLAY]       = new LogContainer("ActionReplay",      "ActionReplay"); | ||||
|     m_Log[LogTypes::MEMCARD_MANAGER]    = new LogContainer("MemCard Manager",   "MemCard Manager"); | ||||
|     m_Log[LogTypes::NETPLAY]            = new LogContainer("NETPLAY",           "Netplay"); | ||||
|     m_Log[LogTypes::GUI]                = new LogContainer("GUI",               "GUI"); | ||||
| 
 | ||||
|     m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); | ||||
|     m_consoleLog = new ConsoleListener(); | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <type_traits> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace MathUtil | ||||
| @ -109,11 +110,11 @@ struct Rectangle | ||||
|     Rectangle(T theLeft, T theTop, T theRight, T theBottom) | ||||
|         : left(theLeft), top(theTop), right(theRight), bottom(theBottom) | ||||
|     { } | ||||
|      | ||||
| 
 | ||||
|     bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } | ||||
| 
 | ||||
|     T GetWidth() const { return abs(right - left); } | ||||
|     T GetHeight() const { return abs(bottom - top); } | ||||
|     T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); } | ||||
|     T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); } | ||||
| 
 | ||||
|     // If the rectangle is in a coordinate system with a lower-left origin, use
 | ||||
|     // this Clamp.
 | ||||
| @ -127,7 +128,7 @@ struct Rectangle | ||||
| 
 | ||||
|     // If the rectangle is in a coordinate system with an upper-left origin,
 | ||||
|     // use this Clamp.
 | ||||
|     void ClampUL(T x1, T y1, T x2, T y2)  | ||||
|     void ClampUL(T x1, T y1, T x2, T y2) | ||||
|     { | ||||
|         if (left < x1) left = x1; | ||||
|         if (right > x2) right = x2; | ||||
|  | ||||
| @ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x | ||||
|  * Draws the emulated screens to the emulator window. | ||||
|  */ | ||||
| void RendererOpenGL::DrawScreens() { | ||||
|     glViewport(0, 0, resolution_width, resolution_height); | ||||
|     auto viewport_extent = GetViewportExtent(); | ||||
|     glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom?
 | ||||
|     glClear(GL_COLOR_BUFFER_BIT); | ||||
| 
 | ||||
|     glUseProgram(program_id); | ||||
| @ -228,6 +229,34 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { | ||||
|     render_window = window; | ||||
| } | ||||
| 
 | ||||
| MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { | ||||
|     unsigned framebuffer_width; | ||||
|     unsigned framebuffer_height; | ||||
|     std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize(); | ||||
| 
 | ||||
|     float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width; | ||||
|     float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width; | ||||
| 
 | ||||
|     MathUtil::Rectangle<unsigned> viewport_extent; | ||||
|     if (window_aspect_ratio > emulation_aspect_ratio) { | ||||
| 		// Window is narrower than the emulation content => apply borders to the top and bottom
 | ||||
|         unsigned viewport_height = emulation_aspect_ratio * framebuffer_width; | ||||
|         viewport_extent.left = 0; | ||||
|         viewport_extent.top = (framebuffer_height - viewport_height) / 2; | ||||
|         viewport_extent.right = viewport_extent.left + framebuffer_width; | ||||
|         viewport_extent.bottom = viewport_extent.top + viewport_height; | ||||
|     } else { | ||||
|         // Otherwise, apply borders to the left and right sides of the window.
 | ||||
|         unsigned viewport_width = framebuffer_height / emulation_aspect_ratio; | ||||
|         viewport_extent.left = (framebuffer_width - viewport_width) / 2; | ||||
|         viewport_extent.top = 0; | ||||
|         viewport_extent.right = viewport_extent.left + viewport_width; | ||||
|         viewport_extent.bottom = viewport_extent.top + framebuffer_height; | ||||
|     } | ||||
| 
 | ||||
|     return viewport_extent; | ||||
| } | ||||
| 
 | ||||
| /// Initialize the renderer
 | ||||
| void RendererOpenGL::Init() { | ||||
|     render_window->MakeCurrent(); | ||||
|  | ||||
| @ -4,13 +4,15 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| 
 | ||||
| #include "generated/gl_3_2_core.h" | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "core/hw/gpu.h" | ||||
| #include "video_core/renderer_base.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| #include <array> | ||||
| #include "core/hw/gpu.h" | ||||
| 
 | ||||
| #include "video_core/renderer_base.h" | ||||
| 
 | ||||
| class EmuWindow; | ||||
| 
 | ||||
| @ -52,6 +54,9 @@ private: | ||||
|     static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, | ||||
|                                         const TextureInfo& texture); | ||||
| 
 | ||||
|     /// Computes the viewport rectangle
 | ||||
|     MathUtil::Rectangle<unsigned> GetViewportExtent(); | ||||
| 
 | ||||
|     EmuWindow*  render_window;                    ///< Handle to render window
 | ||||
|     u32         last_mode;                        ///< Last render mode
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei