SDL/Qt: Better resizing & fullscreen support

This commit is contained in:
wheremyfoodat 2025-06-28 20:35:38 +03:00
parent bfeee04d3e
commit 630952f36b
6 changed files with 85 additions and 7 deletions

View file

@ -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 // Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually
bool hashTextures = false; bool hashTextures = false;
bool outputSizeChanged = true;
EmulatorConfig* emulatorConfig = nullptr; EmulatorConfig* emulatorConfig = nullptr;
void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap); void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap);
public: public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs); Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
virtual ~Renderer(); virtual ~Renderer();
@ -121,6 +123,7 @@ class Renderer {
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
void setOutputSize(u32 width, u32 height) { void setOutputSize(u32 width, u32 height) {
outputSizeChanged = true;
outputWindowWidth = width; outputWindowWidth = width;
outputWindowHeight = height; outputWindowHeight = height;
} }

View file

@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexArray hwShaderVAO; OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo; OpenGL::VertexBuffer vbo;
// Data // Data that will be uploaded to the ubershader
struct { struct {
// TEV configuration uniform locations // TEV configuration uniform locations
GLint textureEnvSourceLoc = -1; GLint textureEnvSourceLoc = -1;
@ -146,6 +146,15 @@ class RendererGL final : public Renderer {
PICA::ShaderGen::FragmentGenerator fragShaderGen; PICA::ShaderGen::FragmentGenerator fragShaderGen;
OpenGL::Driver driverInfo; 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) MAKE_LOG_FUNCTION(log, rendererLogger)
void setupBlending(); void setupBlending();
void setupStencilTest(bool stencilEnable); void setupStencilTest(bool stencilEnable);

View file

@ -88,6 +88,16 @@ class RendererMTL final : public Renderer {
MTL::Texture* lastColorTexture = nullptr; MTL::Texture* lastColorTexture = nullptr;
MTL::Texture* lastDepthTexture = 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 // Debug
std::string nextRenderPassName; std::string nextRenderPassName;

View file

@ -582,7 +582,33 @@ void RendererGL::display() {
if constexpr (!Helpers::isHydraCore()) { if constexpr (!Helpers::isHydraCore()) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer); 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
);
} }
} }

View file

@ -103,16 +103,44 @@ void RendererMTL::display() {
renderCommandEncoder->setRenderPipelineState(displayPipeline); renderCommandEncoder->setRenderPipelineState(displayPipeline);
renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0); 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 // Top screen
if (topScreen) { 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->setFragmentTexture(topScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
} }
// Bottom screen // Bottom screen
if (bottomScreen) { 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->setFragmentTexture(bottomScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
} }

View file

@ -415,9 +415,8 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) {
case MessageType::SetScreenSize: { case MessageType::SetScreenSize: {
const u32 width = message.screenSize.width; const u32 width = message.screenSize.width;
const u32 height = message.screenSize.height; const u32 height = message.screenSize.height;
emu->setOutputSize(width, height);
screen->resizeSurface(width, height); screen->resizeSurface(width, height);
emu->setOutputSize(width, height);
break; break;
} }
@ -566,7 +565,10 @@ void MainWindow::handleScreenResize(u32 width, u32 height) {
message.screenSize.width = width; message.screenSize.width = width;
message.screenSize.height = height; message.screenSize.height = height;
sendMessage(message); if (messageQueueMutex.try_lock()) {
messageQueue.push_back(message);
messageQueueMutex.unlock();
}
} }
void MainWindow::initControllers() { void MainWindow::initControllers() {