mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-26 09:02:44 -06:00
Viewport scaling and display density independence
The view is scaled to be as large as possible, without changing the aspect, within the bounds of the window. On "retina" displays, or other displays where window units != pixels, the view should no longer draw incorrectly.
This commit is contained in:
parent
c8d933a142
commit
221a9b023d
7 changed files with 89 additions and 4 deletions
|
@ -34,6 +34,10 @@ const bool EmuWindow_GLFW::IsOpen() {
|
||||||
return glfwWindowShouldClose(m_render_window) == 0;
|
return glfwWindowShouldClose(m_render_window) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuWindow_GLFW::GetFramebufferSize(int* fbWidth, int* fbHeight) {
|
||||||
|
glfwGetFramebufferSize(m_render_window, fbWidth, fbHeight);
|
||||||
|
}
|
||||||
|
|
||||||
/// EmuWindow_GLFW constructor
|
/// EmuWindow_GLFW constructor
|
||||||
EmuWindow_GLFW::EmuWindow_GLFW() {
|
EmuWindow_GLFW::EmuWindow_GLFW() {
|
||||||
keyboard_id = KeyMap::NewDeviceId();
|
keyboard_id = KeyMap::NewDeviceId();
|
||||||
|
@ -64,6 +68,7 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
|
||||||
glfwSetWindowUserPointer(m_render_window, this);
|
glfwSetWindowUserPointer(m_render_window, this);
|
||||||
glfwSetKeyCallback(m_render_window, OnKeyEvent);
|
glfwSetKeyCallback(m_render_window, OnKeyEvent);
|
||||||
|
|
||||||
|
|
||||||
DoneCurrent();
|
DoneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
|
|
||||||
/// Makes the graphics context current for the caller thread
|
/// Makes the graphics context current for the caller thread
|
||||||
void MakeCurrent() override;
|
void MakeCurrent() override;
|
||||||
|
|
||||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
||||||
void DoneCurrent() override;
|
void DoneCurrent() override;
|
||||||
|
|
||||||
|
@ -32,6 +32,9 @@ public:
|
||||||
|
|
||||||
void ReloadSetKeymaps() override;
|
void ReloadSetKeymaps() override;
|
||||||
|
|
||||||
|
/// Gets the size of the window in pixels
|
||||||
|
void GetFramebufferSize(int* fbWidth, int* fbHeight);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLFWwindow* m_render_window; ///< Internal GLFW render window
|
GLFWwindow* m_render_window; ///< Internal GLFW render window
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QApplication>
|
#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 "common/common.h"
|
||||||
#include "bootmanager.hxx"
|
#include "bootmanager.hxx"
|
||||||
|
|
||||||
|
@ -176,6 +182,24 @@ void GRenderWindow::PollEvents() {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On Qt 5.1+, 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::GetFramebufferSize(int* fbWidth, int* fbHeight)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
||||||
|
int pixelRatio = child->QPaintDevice::devicePixelRatio();
|
||||||
|
|
||||||
|
*fbWidth = child->QPaintDevice::width() * pixelRatio;
|
||||||
|
*fbHeight = child->QPaintDevice::height() * pixelRatio;
|
||||||
|
#else
|
||||||
|
*fbWidth = child->QPaintDevice::width();
|
||||||
|
*fbHeight = child->QPaintDevice::height();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::BackupGeometry()
|
void GRenderWindow::BackupGeometry()
|
||||||
{
|
{
|
||||||
geometry = ((QGLWidget*)this)->saveGeometry();
|
geometry = ((QGLWidget*)this)->saveGeometry();
|
||||||
|
|
|
@ -96,6 +96,7 @@ public:
|
||||||
void MakeCurrent() override;
|
void MakeCurrent() override;
|
||||||
void DoneCurrent() override;
|
void DoneCurrent() override;
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
|
void GetFramebufferSize(int* fbWidth, int* fbHeight) override;
|
||||||
|
|
||||||
void BackupGeometry();
|
void BackupGeometry();
|
||||||
void RestoreGeometry();
|
void RestoreGeometry();
|
||||||
|
|
|
@ -49,8 +49,11 @@ public:
|
||||||
void SetConfig(const WindowConfig& val) {
|
void SetConfig(const WindowConfig& val) {
|
||||||
m_config = val;
|
m_config = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetClientAreaWidth() const {
|
/// Gets the size of the window in pixels
|
||||||
|
virtual void GetFramebufferSize(int* fbWidth, int* fbHeight) = 0;
|
||||||
|
|
||||||
|
int GetClientAreaWidth() const {
|
||||||
return m_client_area_width;
|
return m_client_area_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x
|
||||||
* Draws the emulated screens to the emulator window.
|
* Draws the emulated screens to the emulator window.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawScreens() {
|
void RendererOpenGL::DrawScreens() {
|
||||||
glViewport(0, 0, resolution_width, resolution_height);
|
UpdateViewportExtent();
|
||||||
|
glViewport(viewport_extent.x, viewport_extent.y, viewport_extent.width, viewport_extent.height);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
glUseProgram(program_id);
|
glUseProgram(program_id);
|
||||||
|
@ -228,6 +229,39 @@ void RendererOpenGL::SetWindow(EmuWindow* window) {
|
||||||
render_window = window;
|
render_window = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::UpdateViewportExtent() {
|
||||||
|
int width_in_pixels;
|
||||||
|
int height_in_pixels;
|
||||||
|
|
||||||
|
render_window->GetFramebufferSize(&width_in_pixels, &height_in_pixels);
|
||||||
|
|
||||||
|
// No update needed if framebuffer size hasn't changed
|
||||||
|
if (width_in_pixels == framebuffer_size.width && height_in_pixels == framebuffer_size.height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
framebuffer_size.width = width_in_pixels;
|
||||||
|
framebuffer_size.height = height_in_pixels;
|
||||||
|
|
||||||
|
float window_aspect_ratio = static_cast<float>(height_in_pixels) / width_in_pixels;
|
||||||
|
float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width;
|
||||||
|
|
||||||
|
if (window_aspect_ratio > emulation_aspect_ratio) {
|
||||||
|
// If the window is more narrow than the emulation content, borders are applied on the
|
||||||
|
// top and bottom of the window.
|
||||||
|
viewport_extent.width = width_in_pixels;
|
||||||
|
viewport_extent.height = emulation_aspect_ratio * viewport_extent.width;
|
||||||
|
viewport_extent.x = 0;
|
||||||
|
viewport_extent.y = (height_in_pixels - viewport_extent.height) / 2;
|
||||||
|
} else {
|
||||||
|
// Otherwise, borders are applied on the left and right sides of the window.
|
||||||
|
viewport_extent.height = height_in_pixels;
|
||||||
|
viewport_extent.width = (1 / emulation_aspect_ratio) * viewport_extent.height;
|
||||||
|
viewport_extent.x = (width_in_pixels - viewport_extent.width) / 2;
|
||||||
|
viewport_extent.y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the renderer
|
/// Initialize the renderer
|
||||||
void RendererOpenGL::Init() {
|
void RendererOpenGL::Init() {
|
||||||
render_window->MakeCurrent();
|
render_window->MakeCurrent();
|
||||||
|
|
|
@ -52,12 +52,27 @@ private:
|
||||||
static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
|
static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
|
||||||
const TextureInfo& texture);
|
const TextureInfo& texture);
|
||||||
|
|
||||||
|
/// Updates the viewport rectangle
|
||||||
|
void UpdateViewportExtent();
|
||||||
|
|
||||||
EmuWindow* render_window; ///< Handle to render window
|
EmuWindow* render_window; ///< Handle to render window
|
||||||
u32 last_mode; ///< Last render mode
|
u32 last_mode; ///< Last render mode
|
||||||
|
|
||||||
int resolution_width; ///< Current resolution width
|
int resolution_width; ///< Current resolution width
|
||||||
int resolution_height; ///< Current resolution height
|
int resolution_height; ///< Current resolution height
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} framebuffer_size; ///< Current framebuffer size
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} viewport_extent; ///< Current viewport rectangle
|
||||||
|
|
||||||
// OpenGL object IDs
|
// OpenGL object IDs
|
||||||
GLuint vertex_array_handle;
|
GLuint vertex_array_handle;
|
||||||
GLuint vertex_buffer_handle;
|
GLuint vertex_buffer_handle;
|
||||||
|
|
Loading…
Reference in a new issue