Skip to content

Integer Scaling + Screen-Level Linear Interpolation Options#1976

Merged
flyinghead merged 10 commits intoflyinghead:devfrom
xander-will:integer_scale
Jun 13, 2025
Merged

Integer Scaling + Screen-Level Linear Interpolation Options#1976
flyinghead merged 10 commits intoflyinghead:devfrom
xander-will:integer_scale

Conversation

@xander-will
Copy link
Contributor

Standalone Flycast currently applies linear interpolation to the entire screen when the Texture Filtering option is set to "Default," which may be undesirable to those hoping for crisp output. Using the "Force Nearest Neighbor" option will alleviate this, but at the cost of removing linear filtering for in-game textures, which were intentional uses of filtering by the developers. I have decoupled the Texturing Filtering option from the screen scaling implementation by introducing a second option for Linear Interpolation. When unchecked, the screen is interpolated using Nearest Neighbor. This favors perfectly whole multiples of the original image to avoid misshaped pixels, and so I have also added an Integer Scale feature. Using this will automatically scale the image to the largest integer multiple it can within the display size. These features have been requested before in issues #1415 and #1759.

The implementation adds two Boolean options: Integer Scaling and Linear Interpolation. Each resides in the Video tab under Internal Resolution within the Rendering Options section. A new inline function in rend/transform_matrix.h called getWindowboxDimensions() has been added. This now contains the old letter/pillarboxing code used for screen scaling, which still runs when Integer Scaling is false (which it is by default). Locations containing this old code in each of the four renderer backends have been altered to contain the new function. Additionally, function calls for rendering the screen post-windowbox calculation have been changed to use the Linear Interpolation option instead of Texture Filtering, while the Texture Filtering option has been retained for all internal texture checks.

This code has been confirmed to work with all internal resolution options, Widescreen mode, and Rotate 90° mode. Super Widescreen mode is now disabled when the Integer Scaling option is true. These options have been tested in all four rendering backends. I was able to verify that the additions build and work on both Windows 11 and Linux Mint 21.

Bangai-O with default settings (native res, no integer scaling, linear interpolation).
bo1

Bangai-O with new settings (native res, integer scaling, nearest neighbor interpolation).
bo2

Psyvariar 2 with new settings in TATE mode (integer scaling, nearest neighbor, rotate 90).
psy

@flyinghead
Copy link
Owner

Thank you for this contribution. This looks great.

One issue I found is for games that render at odd non-4:3 resolutions.
Some games use horizontal scaling and render at 1280x480, which is then downscaled to 640x480 by the dreamcast GPU.
Some other games render at 640x240p instead of the usual 640x480i.

When using Integer Scaling the aspect ratio is ignored so the image is compressed vertically in both cases.
1280x480: Wacky Races
image

640x240p: Black Matrix A/D
image

Another issue is the use of the global variable _pvrrc in getWindowboxDimensions(). When this function is called, there's no guarantee that it's still valid. You should instead pass the actual dimensions of the rendered frame, which are known by the renderer (extent parameter in vulkan, GlFramebuffer::getWidth/Height in OpenGL, etc.)

@xander-will
Copy link
Contributor Author

Thanks for the feedback! Great catch on the games with unusual framebuffer sizes, that dovetails well with removing _pvrrc (which agreed, I didn't feel great about reaching behind the macro to check the pointer), since it sounds like grabbing the PVR framebuffer dimensions isn't what is needed anyway. I'll rewrite using the variables you mentioned above. I'll also check on this linker issue on libretro builds. I should've checked that locally prior, and I probably need to add guards around some of this code, since RetroArch should handle integer scale/filtering on its own anyway.

Option<int> MaxThreads("pvr.MaxThreads", 3);
Option<int> AutoSkipFrame("pvr.AutoSkipFrame", 0);
Option<int> RenderResolution("rend.Resolution", 480);
Option<bool> AnamorphicWidescreen("rend.AnamorphicWidescreen", false);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather avoid adding yet another option. The usual way to handle games that support anamorphic widescreen is to manually set Horizontal Stretching to 133%. And this setting should go in the Aspect Ratio section where there are already two distinct widescreen options...

return aspectRatio * config::ScreenStretching / 100.f;
}

#include <stdio.h>
Copy link
Owner

@flyinghead flyinghead Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover debug printf

@flyinghead
Copy link
Owner

Regarding the libretro build failures, you can define (dummy) options in shell/libretro/option.cpp if it helps.

@xander-will
Copy link
Contributor Author

Totally fine with the removal on the anamorphic widescreen option, and thanks for the tip about dummy options for libretro. I got the libretro build working on my computer, and I tested the two games you mentioned. I ended up just reading the RenderResolution config option directly to get the height value; if that feels wrong, I can resubmit with an argument that takes the height value from the caller.

wacky races (integer scaling, nearest neighbor)
Screenshot 2025-06-12 151556

wacky races anamorphic widescreen (integer scaling, nearest neighbor, horizontal stretching 133%)
Screenshot 2025-06-12 151703

black/matrix ad (integer scaling, nearest neighbor)
Screenshot 2025-06-12 151623

Pending any further build failures (not sure why NetBSD was throwing a fit on the last review?), this should be final.

@flyinghead flyinghead merged commit 71341a2 into flyinghead:dev Jun 13, 2025
16 checks passed
kosekmi pushed a commit to kosekmi/flycast that referenced this pull request Jul 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants