Fix window resize/fullscreen rendering, add ultrawide fill mode - #254
Open
KohlsAdrian wants to merge 1 commit into
Open
KohlsAdrian wants to merge 1 commit into
KohlsAdrian wants to merge 1 commit into
Conversation
The guest is given its render resolution once, when it constructs its renderer, and cannot re-create its render targets at a different size afterwards (the unimplemented "buffer resize" that Fullscreen, Monitor, WindowSize, AspectRatio and ResolutionScale are all disabled behind). Growing the viewport after that point made the guest draw a launch-sized image into the corner of a larger render target, which the present blit then copied 1:1, leaving the rest of the window black and the camera visibly off-centre. This happened on any post-startup size change: resizing the window, toggling fullscreen, or moving to another monitor. Pin the viewport to the guest's resolution once it has been handed over, and scale that image to the window when presenting instead: - Add Video::LockGuestResolution(), called where the guest receives its render config, so ComputeViewportDimensions() stops resizing the viewport afterwards while still tracking the real output size. - Add Video::ComputePresentRect() and make the gamma correction blit scale the guest's image into it, with manual bilinear filtering so no additional sampler binding is required. - Track the output size from SDL's main-thread pixel size on macOS. The Metal swap chain resolves its size from a cached window query on the render thread, so needsResize() can miss resizes, fullscreen toggles and monitor changes entirely; CheckSwapChain() now detects the change itself. - Map ImGui mouse input through the scaled destination rectangle. Config::AspectRatio now selects how the image is fit to the window and is enabled in the options menu, since it no longer needs a buffer resize: Auto fills the whole window so the HUD reaches the corners (best for ultrawide, alongside UIAlignmentMode::Edge and CutsceneAspectRatio:: Unlocked), Original preserves the aspect ratio and letterboxes. Note that the guest still renders at the aspect ratio it was launched with, so launching at the target resolution renders natively while a later change to a different aspect is scaled.
Author
|
Please support this PR with comments and changes, it's a must have fix for MacOS gaming with this recomp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The issue
Any change to the window size after startup broke rendering: the game kept drawing at its launch size in the top-left corner of the window, the rest of the window stayed black, and the 3D camera appeared off-centre relative to the window.
It reproduced on macOS/Metal by:
Launching directly at a given size was always correct — only later changes were affected.
Root cause
The guest is told its render resolution exactly once, in
Sonicteam::AppMarathon::AppMarathon(app.cpp), and it builds its render targets from that. It has no way to re-create them at a different size at runtime. This is the same unimplemented "buffer resize" thatWindowSize,Monitor,AspectRatio,ResolutionScaleandFullscreenare all disabled behind in the options menu.On a later size change:
ComputeViewportDimensions()grews_viewportWidth/Heightto the new window size,texture.Load, black outsideg_ViewportSize).Hence content in the corner, black elsewhere, and an apparently off-centre camera. ALT+ENTER and window-manager resizing bypass the disabled options, which is how the state is reached.
A second, macOS-specific problem compounded it: the Metal swap chain resolves its size from a cached window query performed on the render thread, so
needsResize()could miss the change entirely and report a stale size.What was done
Since the guest genuinely cannot re-render at a new size, its resolution is pinned and the result is scaled at present time.
Video::LockGuestResolution()— called inapp.cppright where the guest receives its render config. After this,ComputeViewportDimensions()no longer resizes the viewport, but still tracks the real output size and recomputes the aspect ratio offsets.Video::ComputePresentRect()— computes the destination rectangle for the guest's image inside the render output.gamma_correction_ps.metalandgamma_correction_ps.hlslnow scale the source into the destination rectangle using manual bilinear filtering, so no additional sampler binding was needed.GameWindowtracksSDL_GetWindowSizeInPixelson the main thread (s_pixelWidth/s_pixelHeight, updated onRESIZED,SIZE_CHANGED,DISPLAY_CHANGEDand per frame).CheckSwapChain()detects output-size changes itself rather than relying onneedsResize().Behaviour before the guest locks its resolution (installer, boot) is unchanged: in both
ComputeViewportDimensions()branches the viewport matches the output on one axis, so the fit scale is exactly1.0— identical 1:1 centring to before. Only the post-lock path changes.Nothing under
thirdparty/was modified.Feature: ultrawide fill mode
Config::AspectRationow selects how the image is fit to the window, and is enabled in the options menu because it no longer requires a buffer resize — it is applied when presenting and takes effect live:UIAlignmentMode::EdgeandCutsceneAspectRatio::Unlocked, which already extend the HUD to the edges and unlock in-game cutscenes.The existing option was reused rather than adding a new one, since
AutovsOriginalalready carries this meaning, and a new setting would need localisation across all supported languages.Limitations
Automode.Originalavoids the stretch at the cost of bars. SinceConfig::Fullscreenis persisted, toggling fullscreen and restarting yields the native path.CutsceneAspectRatio::Unlocked.// TODO: implement buffer resizeoptions stay disabled. Proper runtime resolution changes would still require the guest to re-create its render targets.Testing
Only D3D12/Vulkan were not exercised at runtime; the shared code path and the equivalent HLSL change mirror the Metal one, and the pre-lock behaviour is unchanged by construction.
Preview
Ultrawide Monitor:

Settings:


4K Monitor:

This was done using Claude Opus 5 with Kiro.