Skip to content

Commit f189ed5

Browse files
New Smart integer scaling implementation to address #18154 (#18296)
* New Smart integer scaling implementation to address #18154 * Fixed C89 compliance errors.
1 parent fc1acf4 commit f189ed5

File tree

2 files changed

+62
-38
lines changed

2 files changed

+62
-38
lines changed

gfx/video_driver.c

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,47 +2511,71 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp,
25112511
uint8_t max_scale_w = 1;
25122512
uint8_t max_scale_h = 1;
25132513

2514-
/* Overscale if less screen is lost by cropping instead of empty added by underscale */
2514+
/* Overscale if only overscan or a small number of rows get cropped.
2515+
* Otherwise, underscale if the smallest margin doesn't exceed 12%.
2516+
* Otherwise, fall back to non-integer scaling. */
25152517
if (scaling == VIDEO_SCALE_INTEGER_SCALING_SMART)
25162518
{
2517-
unsigned overscale_w = (width / content_width) + !!(width % content_width);
2518-
unsigned underscale_w = (width / content_width);
2519-
unsigned overscale_h = (height / content_height) + !!(height % content_height);
2520-
unsigned underscale_h = (height / content_height);
2521-
int overscale_w_diff = (content_width * overscale_w) - width;
2522-
int underscale_w_diff = width - (content_width * underscale_w);
2523-
int overscale_h_diff = (content_height * overscale_h) - height;
2524-
int underscale_h_diff = height - (content_height * underscale_h);
2525-
int scale_h_diff = overscale_h_diff - underscale_h_diff;
2526-
2527-
max_scale_w = underscale_w;
2528-
max_scale_h = underscale_h;
2529-
2530-
/* Prefer nearest scale */
2531-
if (overscale_w_diff <= underscale_w_diff)
2532-
max_scale_w = overscale_w;
2533-
2534-
if (overscale_h_diff <= underscale_h_diff)
2519+
unsigned max_scale_w = width / content_width;
2520+
unsigned underscale_h = height / content_height;
2521+
/* Always check if the next integer factor results in a usable
2522+
* overscale even if the underscale factor already fills the screen.
2523+
* This is particularly relevant when scaling 240p content to 4k -
2524+
* a x9 scale will fill the height, but a x10 overscale will only
2525+
* crop overscan and fills more of the screen. */
2526+
unsigned overscale_h = underscale_h + 1;
2527+
unsigned max_scale_h = underscale_h;
2528+
float width_utilization;
2529+
float height_utilization;
2530+
2531+
/* This is the minimum amount content that must be shown in order
2532+
* for overscan to be used.
2533+
*
2534+
* Handhelds don't have overscan, but there are noteworthy cases
2535+
* where overscaling crops so few pixels that it's worth allowing:
2536+
* - Atari Lynx @ 800p
2537+
* - GBA @ 1080p or 4k
2538+
* - PSP @ 800p, 1080p or 4k
2539+
* 6 pixels has no special significance, it's simply the smallest
2540+
* value that covers all of the above cases. */
2541+
unsigned overscale_min_height = content_height - 6;
2542+
/* Overscale 240p content if only the overscan area gets cropped.
2543+
* The core may have applied some cropping, or the emulated console
2544+
* may not use all 240 lines, so the content height may be lower.
2545+
* 192 is 80% of 240, and is the title safe area used by Nintendo
2546+
* for NES development. See https://www.nesdev.org/wiki/Overscan */
2547+
if (192 <= content_height && content_height <= 240)
2548+
overscale_min_height = 192;
2549+
/* Use the 240p thresholds for 480p, just doubled. */
2550+
else if (192 * 2 <= content_height && content_height <= 480)
2551+
overscale_min_height = 192 * 2;
2552+
2553+
if (height / overscale_h >= overscale_min_height)
25352554
max_scale_h = overscale_h;
2536-
2537-
/* Limit width overscale */
2538-
if (max_scale_w * content_width >= width + ((int)content_width / 2))
2539-
max_scale_w = underscale_w;
2540-
2541-
/* Allow overscale when it is close enough */
2542-
if (scale_h_diff > 0 && scale_h_diff < 64)
2543-
max_scale_h = overscale_h;
2544-
/* Overscale will be too much even if it is closer */
2545-
else if ((scale_h_diff < -140 && scale_h_diff >= (int)-content_height / 2)
2546-
|| (scale_h_diff < -30 && scale_h_diff > -50)
2547-
|| (scale_h_diff > 20))
2548-
max_scale_h = underscale_h;
2549-
2550-
/* Sensible limiting for small sources */
2551-
if (content_height <= 200)
2552-
max_scale_h = underscale_h;
2553-
25542555
max_scale = MIN(max_scale_w, max_scale_h);
2556+
2557+
/* Final step: check if the underscaling margins are too large.
2558+
*
2559+
* Sometimes there's no reasonable integer scale that can be used,
2560+
* such as when scaling 480p to 720p. Overscaling would crop 25% of
2561+
* the content height, but underscaling would only use 2/3 of the
2562+
* display's height.
2563+
*
2564+
* A threshold of 88% utilization (i.e. 12% margin) captures the
2565+
* majority of underscaling scenarios for HD resolutions while
2566+
* keeping the margins relatively small. Noteworthy use cases that
2567+
* straddle this cutoff include: GBA scaled to 720p; Nintendo DS
2568+
* scaled to 1080p; and 480p content scaled to 1080p. Past this,
2569+
* noticeable jumps in margin size would be needed to capture a
2570+
* relatively small number of additional use cases.
2571+
* TODO: Make this threshold configurable. */
2572+
width_utilization = (float)content_width * max_scale / width;
2573+
height_utilization = (float)content_height * max_scale / height;
2574+
if (MAX(height_utilization, width_utilization) < 0.88)
2575+
{
2576+
video_viewport_get_scaled_aspect(vp, width, height, y_down);
2577+
return;
2578+
}
25552579
}
25562580
else if (scaling == VIDEO_SCALE_INTEGER_SCALING_OVERSCALE)
25572581
max_scale = MIN((width / content_width) + !!(width % content_width),

intl/msg_hash_us.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2535,7 +2535,7 @@ MSG_HASH(
25352535
)
25362536
MSG_HASH(
25372537
MENU_ENUM_SUBLABEL_VIDEO_SCALE_INTEGER_SCALING,
2538-
"Round down or up to the next integer. 'Smart' drops to underscale when image is cropped too much."
2538+
"Round down or up to the next integer. 'Smart' drops to underscale when image is cropped too much, and finally falls back to non-integer scaling if the underscale margins are too large."
25392539
)
25402540
MSG_HASH(
25412541
MENU_ENUM_LABEL_VALUE_VIDEO_SCALE_INTEGER_SCALING_UNDERSCALE,

0 commit comments

Comments
 (0)