Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement high-DPI support on Windows #332

Closed
wants to merge 3 commits into from
Closed

Implement high-DPI support on Windows #332

wants to merge 3 commits into from

Conversation

kryptan
Copy link
Contributor

@kryptan kryptan commented Oct 25, 2017

I tested this only on Windows 10. A also tested codepaths for older Windows versions by commenting out code for newer versions.

Currently it uses the DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE mode on Windows 10. There is also newer and better DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 but I had an issue with it. V2 enables correct scaling of window's non-client area (title bar and borders) but glViewport is then unable to correctly set viewport and works as if non-client area was not scaled. I see nothing wrong in winit or glutin so I suspect that this is a bug in either Windows or in OpenGL drivers (I have latest NVidia drivers installed). Before updating to the latest NVidia drivers I also had another issue where moving window from one monitor to another with different DPI would make it black (at least when using glutin), however it was fixed after driver update.

cc @retep998

@@ -79,13 +82,19 @@ unsafe extern "system" fn monitor_enum_proc(hmonitor: winapi::HMONITOR, _: winap
impl EventsLoop {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
unsafe {
// We need to enable DPI awareness to get correct resolution and DPI of each monitor.
dpi::become_dpi_aware();

Choose a reason for hiding this comment

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

You put tabs instead of spaces on these two lines only.

@chris-morgan
Copy link

Here’s a report from my environment (Windows 10 Insider Preview, i.e. Fall Creators Update; Surface Book 2× display plus two external 1× displays).

I can confirm that this seems to fire all the appropriate events and generally behave reasonably including coping with moving the window between screens. Multiple windows are handled appropriately, with each window scaling appropriately independently.

Sadly running the examples seems to want to open them on the primary display only, rather than on the last display they were open in (which is what Windows generally tries to do), so I can’t confirm what it’s like when it starts on a different display.

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE leads to the client dimensions of the window changing (the increased edges and title bar widths are taken out of the window size, and it seems to still be about ten pixels smaller horizontally and five vertically than I’d expect taking that into account, not certain what that’s about). Definitely not ideal. DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 doesn’t suffer from this weakness (but I haven’t tried it on anything that uses a glViewport).

Here are probably-unnecessary screenshots of moving the examples/window.rs window between screens. (This example doesn’t actually draw anything, of course.) I can run other examples too, I just haven’t yet.

Note the change in window size at 1×, as noted earlier.

  1. Starting on a 2× screen:

Screenshot showing everything correct at 2×

  1. Moving it to a 1× screen:

Screenshot showing the window correctly scaled, except for an oversized window title bar

  1. Moving back to a 2× screen:

Screenshot showing a white rectangle of the low DPI window size in the middle of an otherwise black window

The black I presume is a side-effect of the examples/window.rs not doing any drawing, since you get the same effect from increasing the window size. That the initial content is drawn white but any later areas become black has made me curious, but it’s hardly relevant.


Certainly it looks prettier with DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (the title bar being scaled appropriately; and the internal window dimensions then stay the same when moved from the 2× to the 1× display), but given that a glViewport is what you complained of and examples/window.rs doesn’t do that… yeah. Got something I could conveniently test or experiment with?

I was interested to note that with both regular and _V2 per-monitor awareness, when the window comes back to the 2× display it’s half an display pixel taller (i.e. 829px rather than 828px).

@tomaka
Copy link
Contributor

tomaka commented Nov 1, 2017

I don't like the windows-dont-enable-dpi-awareness feature thing. I'd prefer if this was done at runtime.
You could for example add a windows-specific trait named EventsLoopExt with a new_no_dpi_aware method, similar to what exists for unix if that's looks good for you: https://github.com/tomaka/winit/blob/master/src/os/unix.rs#L21-L39

@kryptan
Copy link
Contributor Author

kryptan commented Nov 2, 2017

@chris-morgan I've made an OpenGL app to test high-DPI support. It should correctly handle high-DPI and respond to DPI changes.

Turns out the issue with glViewport is limited to Intel GPU. I have both Nvidia and Intel GPUs and can switch between them, when using Nvidia everything works smoothly with V2 awareness, but with Intel I get this weird bug.

Intel:
wrong-dpi

Nvidia:
nvidia-dpi

@kryptan
Copy link
Contributor Author

kryptan commented Nov 2, 2017

@chris-morgan It seems that winit does not support specifying on which monitor to create window.

@kryptan
Copy link
Contributor Author

kryptan commented Nov 2, 2017

@tomaka This is a better solution indeed, I changed my implementation to use EventsLoopExt.

@chris-morgan
Copy link

@kryptan Thanks for that example; it messes with my eyes at 2×, well done! I’ll probably be able to play with it in a mixed-DPI environment on Saturday evening (GMT+11), as I’ll be away from my low-DPI monitors until then. Knowing how these things often go, that means don’t count on it until at least Monday… or next month…

My Surface Book has both Intel and nVidia GPUs, so I can also try it out with both. (You know what I’d really like? Apps that could switch between the two at will, so that they could use the nVidia GPU while the base was attached and seamlessly switch to the other when detaching the screen from the base. No idea how technically feasible that actually is. Anyway, that’s a digression.)

@kryptan
Copy link
Contributor Author

kryptan commented Nov 2, 2017

I updated my Intel drivers and now it is different. Now I don't have issues with V2 awareness when changing DPI scaling in Windows settings, but when I move window to another monitor I get garbage regardless of whether V2 or V1 awareness is used. We might want to report it to Intel, in my experience they are responsive on their forum.

@chris-morgan
Copy link

… and that’s a substantial part of why GPU rendering for general purpose applications hasn’t taken off. ☹

(The remainder is that without things like webrender and UI toolkits built on top of that, it’s harder!)

Also add call to EnableNonClientDpiScaling to enable non-client scaling even if V2 is not available
@kryptan
Copy link
Contributor Author

kryptan commented Nov 3, 2017

I changed PR to use DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 if available. I also added call to EnableNonClientDpiScaling to enable scaling of window's non client area if V2 is not available.

@chris-morgan
Copy link

Running your dpi-test against your latest patch from this PR, with the Intel GPU (renderer “Intel(R) HD Graphics 520”, version “4.4.0 - Build 20.19.15.4463”):

Initial 2× rendering with Intel GPU: perfect

And moved to the low-DPI display:

1× rendering with Intel GPU: ick.

(Shifting it to the low-DPI display with Win+Shift+Left changes its resolution from 1026×758 to 486×758, which corresponds to the different aspect ratio of that screen, but still surprises me.)

NVIDIA GPU (renderer “GeForce GPU/PCIe/SSE2”, version “4.5.0 NVIDIA 376.67”) at 2×:

NVIDIA GPU at 2×: perfect rendering

And at 1×:

NVIDIA GPU at 1×: perfect rendering

(On the Intel GPU, everything’s buttery-smooth; on the NVIDIA GPU which is generally 2–3× as powerful in my experience, initial rendering takes the best part of a second—presumably spinning the GPU up takes a short time—and resizing is a little slow, with rendering being at least a frame or two behind the resize with regular brief glitches. No idea whose fault that may be.)

@kryptan
Copy link
Contributor Author

kryptan commented Nov 9, 2017

Latest Intel driver released on November 7 fixes the issue: https://downloadcenter.intel.com/download/27266/Graphics-Intel-Graphics-Driver-for-Windows-15-60-?product=88355

@chris-morgan can you please test it on your system and confirm? For me it works fine.

@elinorbgr
Copy link
Contributor

Looks like this has stalled? I'd really like to see this and #341 go forward, if we could settle on the API question I raised in #341

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

5 participants