diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 02f524e03..7c43214cc 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -18,6 +18,10 @@ static void OnWindowSizeEvent(GLFWwindow* win, int width, int height) { emu_window->SetClientAreaHeight(height); } +void EmuWindow_GLFW::GetFramebufferSize(int* fbWidth, int* fbHeight) { + glfwGetFramebufferSize(m_render_window, fbWidth, fbHeight); +} + /// EmuWindow_GLFW constructor EmuWindow_GLFW::EmuWindow_GLFW() { // Initialize the window @@ -48,6 +52,7 @@ EmuWindow_GLFW::EmuWindow_GLFW() { //glfwSetKeyCallback(m_render_window, OnKeyEvent); //glfwSetWindowSizeCallback(m_render_window, OnWindowSizeEvent); + DoneCurrent(); } diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index c1b41203b..ee7c03251 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -17,7 +17,7 @@ public: void SwapBuffers(); /// Polls window events - void PollEvents(); + void PollEvents(); /// Makes the graphics context current for the caller thread void MakeCurrent(); @@ -25,6 +25,9 @@ public: /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread void DoneCurrent(); + /// Gets the size of the window in pixels + void GetFramebufferSize(int* fbWidth, int* fbHeight); + GLFWwindow* m_render_window; ///< Internal GLFW render window private: diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 573060d30..8a770925b 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -179,6 +179,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() { geometry = ((QGLWidget*)this)->saveGeometry(); diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index 51cb781e9..0cd9b3ed5 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx @@ -96,6 +96,7 @@ public: void MakeCurrent(); void DoneCurrent(); void PollEvents(); + void GetFramebufferSize(int* fbWidth, int* fbHeight); void BackupGeometry(); void RestoreGeometry(); diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 5e2c33d7a..bb1f5aecf 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -32,6 +32,9 @@ public: /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread virtual void DoneCurrent() = 0; + /// Gets the size of the window in pixels + virtual void GetFramebufferSize(int* fbWidth, int* fbHeight) = 0; + Config GetConfig() const { return m_config; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 0e4e06517..4f0eb8213 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -191,7 +191,8 @@ void RendererOpenGL::InitFramebuffer() { } void RendererOpenGL::RenderFramebuffer() { - 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); glUseProgram(program_id); @@ -243,6 +244,43 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { render_window = window; } +// TODO: This should happen on window resize callback from EmuWindow. +// It should be done when EmuWindow is refactored. +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; + } + + // Letterbox the emulation content into the window + framebuffer_size.width = width_in_pixels; + framebuffer_size.height = height_in_pixels; + + float windowRatio = static_cast(height_in_pixels) / width_in_pixels; + float emulationRatio = static_cast(resolution_height) / resolution_width; + + // If the window is more narrow than the emulation content, borders are applied on the + // top and bottom of the window + if (windowRatio > emulationRatio) { + viewport_extent.width = width_in_pixels; + viewport_extent.height = emulationRatio * viewport_extent.width; + viewport_extent.x = 0; + viewport_extent.y = (height_in_pixels - viewport_extent.height) / 2; + + // Otherwise, borders are applied on the left and right sides of the window + } else { + viewport_extent.height = height_in_pixels; + viewport_extent.width = (1 / emulationRatio) * viewport_extent.height; + viewport_extent.x = (width_in_pixels - viewport_extent.width) / 2; + viewport_extent.y = 0; + } +} + /// Initialize the renderer void RendererOpenGL::Init() { render_window->MakeCurrent(); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index eac91df51..fe7375fca 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -52,6 +52,9 @@ private: /// Updates the framerate void UpdateFramerate(); + /// Updates the viewport rectangle + void UpdateViewportExtent(); + /// Structure used for storing information for rendering each 3DS screen struct ScreenInfo { // Properties @@ -81,6 +84,18 @@ private: int resolution_width; ///< Current resolution width 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 global object IDs GLuint vertex_array_id; GLuint program_id;