86Box/86Box

Fullscreen Integer Scaling is not working when Host-PC uses HiDPI scaling - despite Host-Screen-Resolution divisable by Guest-OS-Resolution.

Closed this issue · 20 comments

What happened?

Lets say the Host-Windows PC uses 1920x1080 as resolution and it is also the native resolution of the hosts monitor.
The 86Box Guest (Win98) is running at 640x480 Resolution. (HiDPI option is unchecked)

Switching 86Box Guest to Fullscreen while having filtering set to "nearest" and fullscreen scaling to "integer" does not work as expected, depending on the Hosts HiDPI settings:

Case 1: The Host has 100% DPI scaling
86Box fullscreen integer scaling works as expected: it scales (doubles) the 640x480 guest resolution to 1280x960 onto the fullHD Host screen.

Case 2: The Host has a custom DPI scaling of let's say 115%
86Box fullscreen integer scaling does NOT work as expected: it does not double the resolution and is still using 640x480 on the fullHD monitor.

Now, I have red the 86Box manual where it says:

If HiDPI scaling is disabled on a host with a HiDPI display, the emulated display’s size may be off by one pixel due to an integer scaling limitation.

so I checked the data and the Guest OS is actually using 639x478 instead of 640x480.
But doubling the 639x478 should never the less perfectly fit onto a 1920x1080 screen - with an integer scaled 1278x956 resolution on the fullHD monitor.

so why does it not scale to this resolution in this particular case?

(on a sidenode, I don't use HiDPI option in 86box on purpose, because it looks much worse on a Host with 115% dpi scaling)

That being said, I would expect the VGA guest-OS-resolution to be doubled in either case: Host-DPI 100% and Host-DPI 115% (or other customs) because it would still fit the Host resolution.

Configuration file

[General]
vid_renderer = qt_vulkan
host_cpu = Intel(R) Core(TM) i7-10870H CPU @ 2.20GHz
emu_build_num = 5700
uuid = c69bbaea-bf7b-53ed-9aa3-4a5eda09a7b0
video_fullscreen_first = 0
mouse_sensitivity = 1.41
video_fullscreen_scale = 2
dpi_scale = 0

[Machine]
machine = p54tp4xe
cpu_family = pentium_p55c_od
cpu_speed = 166000000
cpu_multi = 2.5
cpu_use_dynarec = 1
fpu_softfloat = 0
time_sync = local
fpu_type = internal
mem_size = 131072

[Video]
gfxcard = mystique

[Input devices]
mouse_type = ps2

[Sound]
fm_driver = nuked
sndcard = sbawe32_pnp

[Network]
net_01_link = 0
net_02_link = 0
net_03_link = 0
net_04_link = 0

[Storage controllers]
hdc = ide_pci
cassette_mode = load

[Matrox Mystique]
memory = 4

[3Dfx Voodoo Graphics]
type = 0
framebuffer_memory = 2
texture_memory = 2
bilinear = 1
dithersub = 1
dacfilter = 0
render_threads = 2
sli = 0
recompiler = 1

[Standard PS/2 Mouse]
buttons = 5

[Sound Blaster AWE32 PnP]
onboard_ram = 28672
control_pc_speaker = 0
receive_input = 1
receive_input401 = 0

[Hard disks]
hdd_01_parameters = 63, 16, 4111, 0, ide
hdd_01_fn = hd.img
hdd_01_speed = ramdisk
hdd_01_ide_channel = 1:1

[Floppy and CD-ROM drives]
fdd_01_type = 35_2hd
fdd_02_type = none
cdrom_01_speed = 32
cdrom_01_parameters = 1, atapi
cdrom_01_ide_channel = 0:1
cdrom_01_host_drive = 200
cdrom_01_type = 86BOX_CD-ROM_1.00

Operating system

Win10

CPU

Intel i7-10870H

86Box version

v4.2 b5700

Build architecture

Windows - x64 (64-bit)

Build type

  • New recompiler
  • Debug build

Download source

Official website (Jenkins, GitHub)

Additional context

No response

Ok I think I misunderstood the integer scaling a bit. while 1920 / 640 gives a perfect integer of 3, 1920 / 639 gives a non-integer value of 3,004694835680751something, hence it is not doubling the resolution.

This somehow turns the initial issue into something different: How can I keep a 640x480 resolution despite having HiDPI on the host computer turned on (like 115% dpi scaling)?

Or to put it in other words: is there any way to circumvent the integer limiation so that the emulated display’s size is not off by 1 or 2 pixels on HiDPI-Hosts? Or is that technically just impossible? (<- after thinking about that I guess it's just mathematically not possible because the host is already scaled on a fixed native resolution matrix :/)

The problem I get out of this issue is, that whenever I want to have the best possible display output with integer scaling, I would have to disable the Hosts HiDPI and turn it back on afterwards - that means 1 reboot before and 2 reboots after the session to get back to normal, which is kind of cumbersome.

PS: while thinking about it a little more, isn't it possible in windows to disable HiDPI mode / DPI scaling specific for one program only? like that:
1
and choosing on of the three options, f.i. Application, for the 86box.exe? (I already tested that, but it seems to ignore this setting)

The compatibility settings are for application menu interface, try it with an older application (not this one) and you will see what I mean. With application it will snap to pixel perfect for the fonts, with system it will have some blur and system (enhanced) a different blur. I don't use anti-aliasing for fonts so I notice the edges very much, and I do pay attention to things like hinting, etc. Out of the scope of this app though, 86Box is fine with the properties left at default.

As for the view output window, yes, I agree, it absolutely is capable of doing integer scaling. In #4379 I posted a link showing images with the last comment being a full vertical window correctly integer scaled, but it only works as a window not fullscreen.

To reproduce the difference:

  1. Settings: Hidpi scaling unchecked, force 4:3 display ratio checked, window scale factor 2x, filter method nearest, hide toolbar checked, hide status bar checked. (don't worry about the shrinking window, just go back to scale factor and set it back to 2x. The reason for hiding the toolbars is because we're making room.)
  2. There is now room to have a command prompt window fit on the screen at 2x. Just type these 3 letters twice "ee, oo, mm". Notice that both look the same. That's what you want to see.
  3. Settings: Fullscreen stretch mode 4:3 integer scale.
  4. Go into fullscreen mode at this point, and look again at the letters typed in 2, notice that they are now shaped differently.

I have a feeling that what OBlaster and us are talking about are two separate things.

We are talking about scaling integer upward from e.g. 2x2 to 4x4 after it has already been aspect ratio corrected, meaning it is fitting on a grid with a 1:1 aspect ratio and should render identically no matter how many multiples are added.

I suspect the other perspective is not that at all, and simply that it's not really possible to make a rectangular pixel fit neatly on a square pixel unless you have a much higher resolution monitor.. but this is a different thing..

So we are talking about scaling, after the conversion from rectangular to square has already been done at 1x. Not scaling first, then correcting the aspect ratio after.

I also noticed the windowed / fullscreen difference and that the (doubled res in) windowed mode is the one I expected, and I expected the same result in fullscreen.

so i guess it is obviously the old discussion about pixel-perfect-scaling and logical-scaling.

86box obviously does not scale (double) the resolution with integer scaling selected, when the width or height resolution ratio between host/guest is not an integer value. (1920 / 639 and 1080 / 478, while these deviations from 640x480 are just coming from Host-OS HiDPI scaling settings).

with fixed 4:3 aspect ratio, wouldn't it still be pixel-perfect if it is then doubled to 1278x956 on fullhd? I mean each doubled pixel would still fit a physical pixel on the host screen if the HiDPI setting is ignored - probably exactly that what's happening in window mode).

I still don't really understand why it doesn't completely ignore the HiDPI Host scaling in fullscreen and takes the original 640x480 resolution as base for integer scaling in fullscreen.

while these deviations from 640x480 are just coming from Host-OS HiDPI scaling settings).

No, they are not.

I still don't really understand why it doesn't completely ignore the HiDPI Host scaling in fullscreen and takes the original 640x480 resolution as base for integer scaling in fullscreen.

It does ignore the HiDPI host scaling in full screen and it does use the original 640x480 resolution as the base.

with fixed 4:3 aspect ratio, wouldn't it still be pixel-perfect if it is then doubled to 1278x956 on fullhd?

640x480 is already 4:3 (640 = 4 * 160 and 480 is 3 * 160), and doubled it's 1280x960, not 1278x956.

So we are talking about scaling, after the conversion from rectangular to square has already been done at 1x. Not scaling first, then correcting the aspect ratio after.

They are done in the same pass.

Go into fullscreen mode at this point, and look again at the letters typed in 2, notice that they are now shaped differently.

I can't fix that. Doing a regular pixel resize fom one aspect ratio to another WILL cause distortion. Using linear resampling mitigates that by making it blurry. The only better solution is to use the OpenGL 3.0 Core renderer and a shader.

Also, one thing to note is that QT applies HiDPI everywhere and there is no way to specify an absolute size, the only way is to specify a size that has been inverse HiDPI scaled and have the QT scale that to the desired size. So in case of 150% scale, I have to pass to QT the size divided by 1.5 (ie. 426.6666...x320) and have qt then multiply that back by 1.5 to 640x480... or 639x480 (426x320 multiplied by 1.5) because it has no provision to pass it the inverse scaled size as double, only as integer, so rounding errors are inevitable. This is a CANTFIX - it's a QT limitation I can do nothing about.

with fixed 4:3 aspect ratio, wouldn't it still be pixel-perfect if it is then doubled to 1278x956 on fullhd?

640x480 is already 4:3 (640 = 4 * 160 and 480 is 3 * 160), and doubled it's 1280x960, not 1278x956.

i wrote (the doubled) 1278x956 reso, because the 86Box window uses 639x478 resolution while the guest-resolution is 640x480 - when the host-os is using HiDPI scaling of 115%. the 1-2 pixel off due to hidpi integer limitations.

anyway, could you explain why I get a perfect result (pixel perfect, doubled 640x480->1280x960 resolution, fullscreen) with Host-OS dpi-scaling disabled (=100%)?
and why it is not possible to achieve the same while host-os dpi scaling being 115%? I mean I don't understand why it can't just deliver the same result by ignoring the dpi setting application wise. Is there no simple way to say: hey ignore OS level dpi for the program? does it have to be something complicated as reverse-hidpi-calculating?

Because with DPI scaling disabled, QT does not convert the sizes, otherwise, it does.

Is there no simple way to say: hey ignore OS level dpi for the program?

In QT, there is unfortunately not.

Is there no simple way to say: hey ignore OS level dpi for the program?

In QT, there is unfortunately not.

Ah damn.., so that is what it all comes down to then.

This can be closed as a CANTFIX then.

probably, I don't know the background and how it's built.. improving QT or using/adding alternatives is not in the scope of options I guess?

I'm not following the aspect ratio thing. The resolution of the display hasn't changed.

To me it makes sense that how it looks in a window is also how it should look in a fullscreen, just with black borders around it.

If I have a block of full pixels that are 640x480 pixels in a window, why can't it display as 640x480 in fullscreen with black borders all around it? Video players have done this for years. So if it can do that, why can't it just multiply this rectangle by two?

What am I missing here in my understanding?

I see talk about QT. Is all this because QT is unable to display something at 1x in fullscreen? I wrote 1x because I don't understand why if that's the issue, it's not possible to just internally multiply it 2x and then send the output of that to QT as a 1x.

Why can't it just be draw a borderless window?

@tokariu I have something here that will help you a bit for text mode, correct scaling.

The Compaq Portable 386 was a gas plasma display that had square pixels with a resolution of 640x400 (aspect 16:10). We can confirm they were square using math, (400*((16/10)/640))=1. If the number is other than 1, it isn't square.

In the BIOS it has an 8x16 font that was meant to be displayed on square pixels. I converted the raw bios font to a font TSR which I share with you here. I have it set to persist in 80 column mode so when you exit a game or whatever it'll still be resident until you reboot.

COMPAQP3.zip

3 more fonts from a Cirrus Logic GD610 videocard (8x16, 8x16 bold, 9x16), also for square pixels: CL-GD610.zip

86Box settings for correct rendering of the Compaq font.

Windowed mode-
HiDPI scaling: OFF
Window scale factor: 1x,2x etc.
Force 4:3 display ratio: OFF

Fullscreen mode- "Full screen stretch"

You would think "square pixels" is right, but no it is wrong. If you look at numbers like 5, 6, etc you can clearly see this, it won't match the rendering in windowed mode, but full screen stretch will match.

@Sunspark-007 thanks, I'll dive into this later and check it out.

@OBattler I researched a bit in the Qt forums and found many people asking the same question about disabling HiDPI for Qt apps.

what I take from it is, it is rather an all or nothing option, but one should be able to disable it completely with the help of a qt.conf like this:

qt.conf next to 86box.exe?

[Platforms]
WindowsArguments = dpiawareness=0

couldn't this work for 86box in fullscreen?

Also @Sunspark-007 has a valid point when he asks: why does it work in window mode, but not in fullscreen? just help me understand one more time, with this example:

Win98SE GuestOS running @ 640x480 resolution
Mode 1:
86Box running in windowed mode with nearest filter, HiDPI is DISABLED.
The image is crystal sharp and pixel-perfect.

Mode2:
then going to fullscreen with integer scaling, HiDPI still disabled, nearest filtering, fullscreen is also 640x480 (well, technically 639x478 due to host-hiDPI 115%)
The image becomes NOT pixel-perfect, icon-text becomes additional lines.

Mode3:
Back to windowed mode. 640x480 with nearest filter, but this time with the HiDPI option ENABLED.
The image becomes the same NOT pixel-perfect image as in Mode2 despite being in windowed mode.

Now, the million dollar question for me is:
When I can make the Window Pixel-Perfect just by deselecting the HiDPI option, why does the fullscreen mode not also draw a pixel perfect image when this HiDPI option is disabled? I mean it is the same 86Box instance, there was no all-or-nothing option changed that would require an app restart or the like. Also valid question: does Qt not provide a borderless fullscreen rendering option, which means it is actually a maximized window just without borders?

it just doesn't make sense to be satisfied with the current status of options available, there HAS to be better options for 86box.
I wouldn't consider this as CANTFIX therefor, might probably just be not that easy to achieve with Qt and the current toolset?
Or if Qt is unable to disable HiDPI in Fullscreen, wouldn't the best workaround be a borderless windowed mode aka borderless fullscreen? Obviously Qt is able to handle the HiDPI setting in window mode.

@tokariu A correction I need to make, I was mistaken about the fullscreen scaling mode. Using the square aspect ratio font and going fullscreen and hitting enter on a screen of text, I could see not all rows of pixels were being rendered. This is noticeable on a letter like "o" where you see a row of pixels disappear off the top or bottom of the character as you hit enter. Scaling is broken.

It makes sense. If a screen is 1080, 1152 or any other number that isn't evenly divisible then it must be scaled with black borders for correct rendering.

Our argument is that there is enough screen real estate present to do 2x scaling and have room for a black border.

I don't believe this is a QT limitation. But on the off chance that this is a QT bug, which is possible, as I have reported a QT bug before on their tracker, I could easily report a scaling bug there.. I would need to know what component to assign it to.

@tokariu Ok I have solved it different ways now. A windows app to scale works, but it keeps the menu bar.. we don't want that, but proof of concept is good. Moving on, I've been trying different QT variables.. pop open a command prompt, type "set QT_FONT_DPI=96". launch 86Box.exe from that same command prompt Window, have fullscreen stretch mode set to integer, et voila!!

@tokariu This variable here is a better one to use.. "set QT_AUTO_SCREEN_SCALE_FACTOR=0".

Either will work, but this one will keep the menu text size consistent with the rest of your Windows environment.

To have it automatically applied in a shortcut link:

C:\Windows\System32\cmd.exe /c "set QT_AUTO_SCREEN_SCALE_FACTOR=0 && START /D ^"C:\86Box^" 86Box.exe"

You can fix the icon by clicking on change icon, and navigating to 86Box.exe and selecting the icon.

To remove the flash of the cmd.exe opening, change run from "Normal" to "Minimised".

😊

@tokariu This variable here is a better one to use.. "set QT_AUTO_SCREEN_SCALE_FACTOR=0".

Either will work, but this one will keep the menu text size consistent with the rest of your Windows environment.

😊

holy sh!t, great find, this is awesome!
working exactly as expected in fullscreen.

I'll just write a batch file to start it like that then.

but from a 86box perspective.. it would be great if there would be a more convenient way, like a setting or menu toggle that does exactly this from within the app.

@tokariu No need for a batch file, just edit the shortcut link (and the path to where your .exe is located). I think I edited the post while you were writing your reply.

To remove the flash of the cmd.exe opening, change run from "Normal" to "Minimised".

@Sunspark-007 I work with multiple portable instances.. and you know, portable and shortcuts is not a good thing, so I'm fine with batch files :)