From 630952f36bdcf8b6f4d7f9618e1db88ceca9fcc7 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:35:38 +0300 Subject: [PATCH] SDL/Qt: Better resizing & fullscreen support --- include/renderer.hpp | 3 +++ include/renderer_gl/renderer_gl.hpp | 11 ++++++++- include/renderer_mtl/renderer_mtl.hpp | 10 ++++++++ src/core/renderer_gl/renderer_gl.cpp | 28 +++++++++++++++++++++- src/core/renderer_mtl/renderer_mtl.cpp | 32 ++++++++++++++++++++++++-- src/panda_qt/main_window.cpp | 8 ++++--- 6 files changed, 85 insertions(+), 7 deletions(-) diff --git a/include/renderer.hpp b/include/renderer.hpp index 0798184d..753bc319 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -53,10 +53,12 @@ class Renderer { // Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually bool hashTextures = false; + bool outputSizeChanged = true; EmulatorConfig* emulatorConfig = nullptr; void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap); + public: Renderer(GPU& gpu, const std::array& internalRegs, const std::array& externalRegs); virtual ~Renderer(); @@ -121,6 +123,7 @@ class Renderer { void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } void setOutputSize(u32 width, u32 height) { + outputSizeChanged = true; outputWindowWidth = width; outputWindowHeight = height; } diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index a862cd26..5476843f 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -40,7 +40,7 @@ class RendererGL final : public Renderer { OpenGL::VertexArray hwShaderVAO; OpenGL::VertexBuffer vbo; - // Data + // Data that will be uploaded to the ubershader struct { // TEV configuration uniform locations GLint textureEnvSourceLoc = -1; @@ -146,6 +146,15 @@ class RendererGL final : public Renderer { PICA::ShaderGen::FragmentGenerator fragShaderGen; OpenGL::Driver driverInfo; + // Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on + // the window's dimensions. + struct { + int destX = 0; + int destY = 0; + int destWidth = 400; + int destHeight = 480; + } blitInfo; + MAKE_LOG_FUNCTION(log, rendererLogger) void setupBlending(); void setupStencilTest(bool stencilEnable); diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp index 10fac7cd..add02d52 100644 --- a/include/renderer_mtl/renderer_mtl.hpp +++ b/include/renderer_mtl/renderer_mtl.hpp @@ -88,6 +88,16 @@ class RendererMTL final : public Renderer { MTL::Texture* lastColorTexture = nullptr; MTL::Texture* lastDepthTexture = nullptr; + // Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on + // the window's dimensions. + struct { + float topScreenX = 0; + float topScreenY = 0; + float bottomScreenX = 40; + float bottomScreenY = 240; + float scale = 1.0; + } blitInfo; + // Debug std::string nextRenderPassName; diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 828ca120..9dbc0434 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -582,7 +582,33 @@ void RendererGL::display() { if constexpr (!Helpers::isHydraCore()) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); screenFramebuffer.bind(OpenGL::ReadFramebuffer); - glBlitFramebuffer(0, 0, 400, 480, 0, 0, outputWindowWidth, outputWindowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + if (outputSizeChanged) { + outputSizeChanged = false; + + const float srcAspect = 400.0f / 480.0f; // 3DS aspect ratio + const float dstAspect = float(outputWindowWidth) / float(outputWindowHeight); + + blitInfo.destWidth = outputWindowWidth; + blitInfo.destHeight = outputWindowHeight; + blitInfo.destX = 0; + blitInfo.destY = 0; + + if (dstAspect > srcAspect) { + // Window is wider than source + blitInfo.destWidth = int(outputWindowHeight * srcAspect + 0.5f); + blitInfo.destX = (outputWindowWidth - blitInfo.destWidth) / 2; + } else { + // Window is taller than source + blitInfo.destHeight = int(outputWindowWidth / srcAspect + 0.5f); + blitInfo.destY = (outputWindowHeight - blitInfo.destHeight) / 2; + } + } + + glBlitFramebuffer( + 0, 0, 400, 480, blitInfo.destX, blitInfo.destY, blitInfo.destX + blitInfo.destWidth, blitInfo.destY + blitInfo.destHeight, + GL_COLOR_BUFFER_BIT, GL_LINEAR + ); } } diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp index e2e4c085..2b0f80c6 100644 --- a/src/core/renderer_mtl/renderer_mtl.cpp +++ b/src/core/renderer_mtl/renderer_mtl.cpp @@ -103,16 +103,44 @@ void RendererMTL::display() { renderCommandEncoder->setRenderPipelineState(displayPipeline); renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0); + if (outputSizeChanged) { + outputSizeChanged = false; + + const float srcAspect = 400.0 / 480.0; + const float destAspect = float(outputWindowWidth) / float(outputWindowHeight); + int destX = 0, destY = 0, destWidth = outputWindowWidth, destHeight = outputWindowHeight; + + if (destAspect > srcAspect) { + // Window is wider than source + destWidth = int(outputWindowHeight * srcAspect + 0.5f); + destX = (outputWindowWidth - destWidth) / 2; + } else { + // Window is taller than source + destHeight = int(outputWindowWidth / srcAspect + 0.5f); + destY = (outputWindowHeight - destHeight) / 2; + } + + blitInfo.scale = float(destWidth) / 400.0f; + blitInfo.topScreenX = float(destX); + blitInfo.topScreenY = float(destY + (destHeight - int(480 * blitInfo.scale)) / 2); + blitInfo.bottomScreenX = float(destX) + 40 * blitInfo.scale; + blitInfo.bottomScreenY = blitInfo.topScreenY + 240 * blitInfo.scale; + } + // Top screen if (topScreen) { - renderCommandEncoder->setViewport(MTL::Viewport{0, 0, 400, 240, 0.0f, 1.0f}); + renderCommandEncoder->setViewport( + MTL::Viewport{blitInfo.topScreenX, blitInfo.topScreenY + 240 * blitInfo.scale, 400 * blitInfo.scale, 240 * blitInfo.scale, 0.0f, 1.0f} + ); renderCommandEncoder->setFragmentTexture(topScreen->get().texture, 0); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); } // Bottom screen if (bottomScreen) { - renderCommandEncoder->setViewport(MTL::Viewport{40, 240, 320, 240, 0.0f, 1.0f}); + renderCommandEncoder->setViewport( + MTL::Viewport{blitInfo.bottomScreenX, blitInfo.bottomScreenY, 320 * blitInfo.scale, 240 * blitInfo.scale, 0.0f, 1.0f} + ); renderCommandEncoder->setFragmentTexture(bottomScreen->get().texture, 0); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); } diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index c060318e..ed6c2852 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -415,9 +415,8 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) { case MessageType::SetScreenSize: { const u32 width = message.screenSize.width; const u32 height = message.screenSize.height; - - emu->setOutputSize(width, height); screen->resizeSurface(width, height); + emu->setOutputSize(width, height); break; } @@ -566,7 +565,10 @@ void MainWindow::handleScreenResize(u32 width, u32 height) { message.screenSize.width = width; message.screenSize.height = height; - sendMessage(message); + if (messageQueueMutex.try_lock()) { + messageQueue.push_back(message); + messageQueueMutex.unlock(); + } } void MainWindow::initControllers() {