Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Windows DPI scaling is causing the GameWindow size to scale, which is correct, but there is no way to find out the unscaled size of the GameWindow. #47

Open
OnlyRansom opened this Issue · 8 comments

6 participants

@OnlyRansom

Reported Width and Height of GameWindow is scaled by the Windows DPI scaling.
This is correct, but there is no way to know what the non-scaled Width and Height are set to. This is useful when saving the window size and position.

Example: Create a window with size 640 by 480. Windows desktop DPI scaling is set to 150%. GameWindow width is 960 and height is 720 which is correct. If we save those values and use them upon next start, the window will grow another 150% in size.

Maybe there could be new properties such as UnscaledWidth and UnscaledHeight.

@thefiddler
Owner

Thanks for the bug report, this is indeed an issue. Neither OpenTK nor SDL handle resolution independence correctly right now, but this is something I would like to fix for the 1.1 release. Unfortunately, finding an acceptable solution is a bit more challenging than it looks.

Before I dive into the details, a workaround for your use case: calculate the current DPI scale factor and divide Width and Height by that before saving to disk:

var gw = new GameWindow(width, height);
var scale = gw.Width / (float)width; // derive current DPI scale factor

This way, you save unscaled values to disk and the size is correct upon next start.

Now to the meat of the issue. The problem is that each platform behaves slightly differently under DPI scaling:

  • Windows APIs are defined in terms of unscaled pixels, i.e. desktop resolution, window size, mouse position refer to pixels directly. A 2560x1600 monitor at 200% DPI scaling has a resolution of 2560x1600. A 1280x800 window would take up 50% of this monitor.
  • Mac APIs (Cocoa), on the other hand, are defined in terms of scaled points: a 2560x1600 monitor at 200% DPI scaling has a reported resolution of 1280x800. A 1280x800 window would take up 100% of this monitor.
  • Linux behaves in a similar fashion to Windows.
  • SDL is even more complicated: 2.0.0 does not support scaling at all. 2.0.1 supports scaling on Mac OS but not Windows or Linux. Scaling for Window will probably be added in 2.0.2.

Here is how the current situation looks like in OpenTK:

  • On Windows, DisplayResolution, MouseState and GameWindow use unscaled pixels. The GameWindow constructor, however, uses scaled points (as suggested by MSDN.)
  • On Mac OS, DisplayResolution and MouseState use scaled points, as do the GameWindow constructor, GameWindow.Bounds and GameWindow.Location. GameWindow.ClientRectangle, Size, Width and Height use unscaled pixels (otherwise GL.Viewport() would give unexpected results.)
  • On Linux, it uses unscaled pixels throughout.
  • On SDL, it lets the unmanaged library handle the situation. As of SDL 2.0.1, only Mac OS is working correctly.

Considering the above, a hypothetical UnscaledWidth/Height API would work on Windows but fail horribly on Mac OS: a 200% 1280x800 window would return 2560x1600 unscaled size. Passing 2560x1600 to the GameWindow constructor would result in a window 4x bigger than desired!

I'm open to suggestions on how to solve this mess.

Possible solutions:

  1. Should we redefine DisplayResolution, GameWindow, MouseState to work in terms of points, as suggested by Microsoft and Apple? In that case, applications would be resolution-independent out of the box. However, a 1920x1080 monitor resolution at 125% scaling would be reported as 1536x864 points - which is correct but also totally unexpected to Windows users.
  2. Alternatively, should we use pixels throughout but add a ScaleFactor property to retrieve the current DPI scale? This would mean that applications would not be resolution-independent out of the box, unless the programmer took the steps to multiply everything by ScaleFactor. Chances are, few programmers would do that (given that HiDPI monitors are still uncommon.) 3 .Should we add properties for both cases? The API would become messy and the programmer would still have to know when to use each scaled vs unscaled values.

Maybe someone with more experience writing DPI-aware cross-platform apps could pitch in here.

@OnlyRansom

Thanks for the workaround! It is working very good. Also, thanks for the high DPI support. It has been on my wishlist for a while.

Cross platform scaling sounds like quite a quandary. I think there are benefits to both using points and pixels and both would work good. Given that OpenGL seems to work on a pixel level for many operations such as glViewport(int, int, int, int), pixels seem like a logical choice. This also works well with the current API where the mouse X & Y are integers. There is also the consideration that certain effects can be obtained by knowing the actual pixels on the screen such as sub-pixel aliasing.

@jeske

Yes, I think Option (1), which is basically to make all OpenTK HiDPI work like it does on the Mac today, is a sane option. I say this, because this probably produces the most sane "common case result" -- namely, applications which present themselves at the user-choosen scale size when the developer didn't handle HiDPI. I don't think this should affect full-screen options, so the "desktop resolution" being scaled shouldn't much matter as users won't see this anyhow.

The tradeoff here is that non-HiDPI aware apps may have slightly blurry 2d HUD fonts.. I think this is a more sane option than having the entire HUD be tiny in the case of 2x HiDPI displays.

@kaalus

IMHO leave it as is for compatibility and instead do 2 things:

  • Document which value each function takes/returns (something that's already mostly covered by this topic)
  • Allow users to access scaling value used, which AFAIK is currently unavailable. This will allow anybody to easily convert between scaled/unscaled units as needed. One multiplication/division in a few places is not the end of the world, if one knows what to multiply by!
@jeske

You cant "leave it as is" because right now you can only make hidpi opentk apps work properly on either mac or windows. The goal should be for the common opentk source patterns to work correctly out of the box with hidpi or not.

Option 1 will give opentk apps sane behavior on both mac and windows, hidpi or not. Windows recommendations should be ignored as their hidpi support is still a mess.

It's just important to also expose the raw unscaled desktop res, window res, and coords.. So apps that want to custom support hidpi can do it. (Best practice is to render 3d at a lower resolution, but 2d at native screen resolution.. Which takes sme custom cosing)

@amulware

We have been having some issues with this as well, but so far I have not been able to fix the problem apart from telling Windows not to use DPI scaling in the compatibility settings of the executable.

The workaround @thefiddler posted above also does not seem to do anything for us. The window reports to be the same size that I told it to be, even though I have scaling set to 200% right now. (And the window is ridiculously big and blurry accordingly.) And this is the same, whether I tell windows to enable or disable scaling for the application. (This is all on Windows 8.1.)

Regarding the two options given, from a game developer's perspective, I would much rather have control over my window on a per-pixel basis, so that I can render as pixel-perfect as I want. If there is up-scaling to be done, I would prefer being in charge of that myself (and in terms of games, especially on PC, exposing relevant settings to the user; a significant number of them prefers to be in full control of their games' settings).

I can see @jeske's point though and as long as enough info is exposed for me to do my own thing, I am fine.

One other point, that may or may not be related (if you think it is not, let me know and I'll make another issue):
We have a couple of customer complaints reporting that our game runs in less than full-screen size when they toggle on fullscreen mode. I use a bunch of custom code for that, but in essence I get the bounds of the DisplayDevice and set the bounds of my window to the same value.
What confuses me is that when I set my scaling to 200%, the window gets much larger than my screen. The users in question report that their window is too small though, which sounds like they have less than 100% scaling, which is not supported by Windows however. Further it makes little sense to me, since they would essentially be unable to read any text on their screen.
The only other bit of information I have right now is that the people in question all seem to use screens with larger than fullHD resolutions. I do not see what could go wrong with that though. While I cannot test on larger than fullHD myself, I can resize the window to be larger than that, and neither OpenTK nor OpenGL have any issues with that.

@Frassle
Collaborator

I've got a Win7 desktop and Win8.1 laptop at home so I can do some DPI tests on Windows. Looks like some MSDN reading is in order.

If others can help with the OSX and Linux stories (especially OSX, I have a Linux box at work I can test on but no access to OSX machines), we can see about trying to unify behaviour across all platforms.

@jeske

Agree with @amulware that it should be possible to see raw (unscaled) values, and control pixel perfect results.

The reason I support option-1 (using scaled pixels in all the default APIs) is that it produces the most sane behavior on the widest variety of systems and hardware "without extra work". When you have real hidpi displays, most games will not run reasonably with HiDPI 3d surface sizes, because of fill rate limits. For example, the MacBook Pro Retina is a laptop with 2880x1800. The mobile GPU can't push that many pixels in 3d rendering at reasonable FPS. The same is true of HiDPI windows laptops.

An important subtlety is how to detect HiDPI on windows (and eventually Linux), and when to treat everything as Native Pixels vs HiDPI scaled. For example, a user who sets the Windows UI Scale to (say 110% or 120%) should get a native-pixels OpenTK Game Window. However, one with 180% or 220% is most-likely on a HiDPI display and should get a scaled window. Alternatively, I believe windows has a DPI value you can query, independent of UI scaling. However, I'm not sure if that is reliable.

When developers want to do the extra work to support HiDPI, they should be able to see everything raw and also write cross-platform native pixels sizes with OpenTK Game Window. For example, my HiDPI plan is to render the 3d surface to an FBO at about 1/2 native HiDPI pixel count, but to render the 2d HUD at native HiDPI pixels ontop of the 3d surface. (For example, on MBP-retina, I'll render 3d at 1440x900, underneath HUD items with native 2880x1800 pixels. AFAIK, Arma and Diablo are two few titles to support separate 3d and 2d resolutions)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.