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

DPI for everyone #548

Merged
merged 29 commits into from
Jun 14, 2018
Merged

DPI for everyone #548

merged 29 commits into from
Jun 14, 2018

Conversation

francesca64
Copy link
Member

@francesca64 francesca64 commented Jun 3, 2018

Fixes #62
Fixes #105
Fixes #169
Fixes #315
Fixes #332 (big thanks to @kryptan for the initial work on Windows!)
Fixes #337
Fixes #469
Fixes rust-windowing/glutin#1025

Alright, it was quite the undertaking, but I believe I've finished this on X11, Windows, macOS, iOS, Android, and Emscripten.

Some caveats:

  • The Intel OpenGL driver on Windows still has some problems in mixed-DPI environments. It doesn't happen with DirectX or Vulkan (and OpenGL with Nvidia works fine), and it will only affect people who either A) have multiple monitors of differing scale factors, or B) change the scale factor after login. The issue itself is just that there's a small black/background-colored gap below the titlebar; this doesn't happen without DPI awareness, but I think this is an acceptable trade-off. Besides, if users disagree, it's still possible to disable DPI awareness using EventsLoopExt.
  • Android still returns a DPI factor of 1.0. As far as I can tell, getting the real value won't be possible until Initial JNI support rust-mobile/android-rs-glue#116 is sorted out. However, once that's filled in the code I've added here should do all the right conversions.
  • I couldn't really test Emscripten, since I was unable to get anything to actually run in my browser.
  • iOS was an adventure, and I ended up reworking glutin's iOS backend (it's apparently been broken for quite some time). I still never got anything to render, but I can at least say that it reported the correct sizes and DPI factor. I've unfortunately introduced a small amount of GL-specific code to the iOS backend here; I plan on doing some restructuring and fixing that before this makes it into a release, but I didn't want to make this PR any more huge and monolithic than it is already.
  • My solution for Wayland was just to comment things out in src/platform/linux/mod.rs and say "@vberger will figure this out later"

That said, this PR is functionally complete. The only reason I've labeled it WIP is because it's still completely undocumented! I just wanted an opportunity for feedback before I start fussing over doc comments. (I also noticed that I left a // TODO: Adjust min+max size in the Windows backend, whoops)

I won't reiterate what I wrote in CHANGELOG.md. Besides there, the other files I recommend looking at are src/dpi.rsand src/window.rs. You can obviously also test this, and I've provided some partial migrations of various things to help with that:

  • glutin - everything should work except for the examples, and note that GlWindow::resize doesn't yet explicitly take PhysicalSize.
  • gfx pre-ll - everything should work except for the examples.
  • alacritty - it seems fine on macOS, though on X11 it seems to "double upscale" on HiDPI displays. This is almost certainly a result of the math in the auto-resize Alacritty does after creation, but I couldn't find where it does that.

Migrating can be tedious, but isn't hard, and the more explicit API resulted in me fixing some bugs in my own applications. Overall, I'm pretty satisfied, but if anyone has objections, I'm in no rush to merge this. I want to release a 0.15.1 with various regression fixes before introducing any breaking changes like this.

@francesca64 francesca64 mentioned this pull request Jun 3, 2018
@elinorbgr
Copy link
Contributor

My solution for Wayland was just to comment things out in src/platform/linux/mod.rs and say "@vberger will figure this out later"

Do you want me to take my #341 PR and integrate these changes into a PR to your PR branch ?

@francesca64
Copy link
Member Author

I'm glad I merged #547, since this failed on 1.24...

@vberger that's probably a good idea; besides making things cleaner, you integrating the new types before this merges would be an effective form of review for them.

CHANGELOG.md Outdated
@@ -3,6 +3,16 @@
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
- `MouseCursor` and `CursorState` now implement `Default`.
- `WindowBuilder::with_resizable` implemented for Windows.
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
- **Breaking:** `Window::get_hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to ``WindowEvent::HiDpiFactorChanged``. DPI factors are always represented as `f64` instead of `f32` now.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it should be "Window::hidpi_factor has been renamed"

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

@francesca64
Copy link
Member Author

It turns out I'd already fully implemented min/max dimension DPI adjustment on Windows, but just forgot to remove the TODO comment.

@rukai
Copy link
Contributor

rukai commented Jun 4, 2018

@vberger I migrated pf sandbox to use this branch https://github.com/rukai/PF_Sandbox/tree/super-dpi
And since your commit I'm hitting this issue.

error[E0277]: the trait bound `*mut wayland_sys::client::wl_display: std::marker::Send` is not satisfied in `wayland_client::display::DisplayInner`
   --> pf_sandbox/src/vulkan/mod.rs:221:25
    |
221 |         let draw_text = DrawText::new(device.clone(), queue.clone(), swapchain.clone(), &images);
    |                         ^^^^^^^^^^^^^ `*mut wayland_sys::client::wl_display` cannot be sent between threads safely
    |
    = help: within `wayland_client::display::DisplayInner`, the trait `std::marker::Send` is not implemented for `*mut wayland_sys::client::wl_display`
    = note: required because it appears within the type `wayland_client::display::DisplayInner`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<wayland_client::display::DisplayInner>`
    = note: required because it appears within the type `wayland_client::display::Display`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<wayland_client::display::Display>`
    = note: required because it appears within the type `winit::platform::platform::wayland::window::Window`
    = note: required because it appears within the type `winit::platform::platform::Window`
    = note: required because it appears within the type `winit::Window`
    = note: required by `vulkano_text::DrawText::new`

The issue probably occurs with any vulkano project but I havnt had time to test.
It looks like a similar issue to #472

Edit

Just confirmed we get the same error with the vulkano triangle example:

error[E0277]: the trait bound `*mut wayland_sys::client::wl_display: std::marker::Send` is not satisfied in `wayland_client::display::DisplayInner`
   --> src/main.rs:406:14
    |
406 |             .begin_render_pass(framebuffers.as_ref().unwrap()[image_num].clone(), false,
    |              ^^^^^^^^^^^^^^^^^ `*mut wayland_sys::client::wl_display` cannot be sent between threads safely
    |
    = help: within `wayland_client::display::DisplayInner`, the trait `std::marker::Send` is not implemented for `*mut wayland_sys::client::wl_display`
    = note: required because it appears within the type `wayland_client::display::DisplayInner`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<wayland_client::display::DisplayInner>`
    = note: required because it appears within the type `wayland_client::display::Display`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<wayland_client::display::Display>`
    = note: required because it appears within the type `winit::platform::platform::wayland::window::Window`
    = note: required because it appears within the type `winit::platform::platform::Window`
    = note: required because it appears within the type `winit::Window`
    = note: required because it appears within the type `vulkano::swapchain::Surface<winit::Window>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<vulkano::swapchain::Surface<winit::Window>>`
    = note: required because it appears within the type `vulkano::swapchain::Swapchain<winit::Window>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<vulkano::swapchain::Swapchain<winit::Window>>`
    = note: required because it appears within the type `vulkano::image::SwapchainImage<winit::Window>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<vulkano::image::SwapchainImage<winit::Window>>`
    = note: required because it appears within the type `((), std::sync::Arc<vulkano::image::SwapchainImage<winit::Window>>)`
    = note: required because it appears within the type `vulkano::framebuffer::Framebuffer<std::sync::Arc<vulkano::framebuffer::RenderPass<main::scope::CustomRenderPassDesc>>, ((), std::sync::Arc<vulkano::image::SwapchainImage<winit::Window>>)>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<vulkano::framebuffer::Framebuffer<std::sync::Arc<vulkano::framebuffer::RenderPass<main::scope::CustomRenderPassDesc>>, ((), std::sync::Arc<vulkano::image::SwapchainImage<winit::Window>>)>>`

@francesca64
Copy link
Member Author

You have no idea how much of a relief it is when a notification is for something that isn't my fault.

Shouldn't this have been caught by this test? https://github.com/tomaka/winit/blob/19dd961752319276fa170c07e46963de9e3b589c/tests/send_objects.rs#L11-L15

@rukai
Copy link
Contributor

rukai commented Jun 4, 2018

running cargo test locally, it does actually fail although it fails in an example before it reaches your test:

error[E0277]: the trait bound `*mut wayland_sys::client::wl_display: std::marker::Send` is not satisfied in `wayland_client::display::DisplayInner`
  --> examples/proxy.rs:13:5
   |
13 |     std::thread::spawn(move || {
   |     ^^^^^^^^^^^^^^^^^^ `*mut wayland_sys::client::wl_display` cannot be sent between threads safely
   |
   = help: within `wayland_client::display::DisplayInner`, the trait `std::marker::Send` is not implemented for `*mut wayland_sys::client::wl_display`
   = note: required because it appears within the type `wayland_client::display::DisplayInner`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<wayland_client::display::DisplayInner>`
   = note: required because it appears within the type `wayland_client::display::Display`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Weak<wayland_client::display::Display>`
   = note: required because it appears within the type `winit::platform::platform::wayland::event_loop::EventsLoopProxy`
   = note: required because it appears within the type `winit::platform::platform::EventsLoopProxy`
   = note: required because it appears within the type `winit::EventsLoopProxy`
   = note: required because it appears within the type `[closure@examples/proxy.rs:13:24: 19:6 proxy:winit::EventsLoopProxy]`
   = note: required by `std::thread::spawn`

@francesca64
Copy link
Member Author

Weird, cargo test passes for me, and I can run the proxy example fine in Weston.

@vberger feel free to just push directly to this branch.

@rukai
Copy link
Contributor

rukai commented Jun 4, 2018

To confirm the issue, I ran the following commands on a Digital Ocean vps of mine

rustup default stable
rustup update
git clone -b super-dpi https://github.com/francesca64/winit
cd winit
cargo test

I still get the proxy.rs build failures I posted above.

Maybe you need to cargo update or maybe you have modified files that arnt checked in or maybe you are using the wrong branch?

Doesnt explain why the travis is passing though!?!

@elinorbgr
Copy link
Contributor

Aaaah, ok, this is an unforseen consequence of my last release of wayland-client, sorry for that. I definitly need better testing... ><

I'll correct this asap.

@elinorbgr
Copy link
Contributor

Alright, wayland-client 0.20.9 is yanked, wayland-client 0.20.10 is released with the fix, and I added tests for that. All should be good again.

@rukai
Copy link
Contributor

rukai commented Jun 4, 2018

Thanks, that fixes it.

@kryptan
Copy link
Contributor

kryptan commented Jun 5, 2018

The Intel OpenGL driver on Windows still has some problems in mixed-DPI environments. ... The issue itself is just that there's a small black/background-colored gap below the titlebar;

But I think coordinates of mouse events will still be computed correctly while rendering is incorrect. This will create a mismatch

unsafe {
let (width, height) = dimensions.unwrap_or((0, 0));
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
Copy link
Contributor

Choose a reason for hiding this comment

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

Hello, I think this line should be:

let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());

Otherwise window.set_min_dimensions(None) crashes the application by changing the window size to a huge value.

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops, good catch. Looks like I got carried away with copy-pasting.

@francesca64
Copy link
Member Author

@kryptan that's really unfortunate. Hopefully Intel fixes this soon, since AFAIK there's nothing we can do about it, and I don't think it makes sense to postpone this for something that hopefully only affects a small subset of users.

(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
// Quantize 1/12 step size
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really have a very deep understanding of this topic, but wouldn't the 96.0 here need to by the actual x11 screen DPI to properly support DPI on x11?

As far as I understand it 96.0 is only the default screen DPI on x11 and it could change based on a user's configuration. I'm not sure if in this context it's a different value that's always constant, but thought I'd just ask to properly understand this topic.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually it might be possible that this 96.0 here is just used to calculate the DPI factor you'd need to apply to a screen with 96 DPI to get the "proper" DPI scaling. So this is probably all working as intended.

Copy link
Member Author

Choose a reason for hiding this comment

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

I recommend reading the relevant issue #169 along with this blog post: http://wok.oblomov.eu/tecnologia/mixed-dpi-x11/

After that, you'll have an understanding with the same depth as mine, or probably better, since I know for a fact that I glossed over some things. I might not get a chance to look into that constant today, since I still have a Windows bug to investigate and a burning desire to relax for the rest of the day.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, GitHub only sometimes wants to update the page automatically, so I didn't see that. I do encourage being skeptical of this math, since I don't know who added it or when; I just know that it makes my eyes not hurt when using winit apps on my 1440p monitor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks a lot for the clarification and the helpful links to the documentation. Until now I've mainly just made guesses based on the math in alacritty and winit and took a short read through the freetype documentation.

Looks like this will give me some more insights, very much appreciated!

@francesca64 francesca64 changed the title [WIP] DPI for everyone DPI for everyone Jun 14, 2018
@francesca64 francesca64 merged commit 1b74822 into rust-windowing:master Jun 14, 2018
@elinorbgr
Copy link
Contributor

🎉 🎉

malikolivier added a commit to aflak-vis/aflak that referenced this pull request Aug 10, 2018
You may need to remove your Cargo.lock file to be able to build.
Thus you will start with fresh dependencies.

Require the WINIT_HIDPI_FACTOR=1 environment variable set to 1 to work
flawlessly on Linux X11.

This was caused by a fix in WINIT. It seems that until now, reporting
DPI was buggy on X11 [1]. DPI was already reported as 1.
aflak is actually a special case for which a DPI of 1 is desired (at
least for now with the current design).

[1] rust-windowing/winit#548
malikolivier added a commit to aflak-vis/aflak that referenced this pull request Aug 11, 2018
You may need to remove your Cargo.lock file to be able to build.
Thus you will start with fresh dependencies.

Require the WINIT_HIDPI_FACTOR=1 environment variable set to 1 to work
flawlessly on Linux X11.

This was caused by a fix in WINIT. It seems that until now, reporting
DPI was buggy on X11 [1]. DPI was already reported as 1.
aflak is actually a special case for which a DPI of 1 is desired (at
least for now with the current design).

[1] rust-windowing/winit#548
yvt added a commit to yvt/ngspades that referenced this pull request Apr 25, 2020
…#548

NgsPF benefits at great extent from this PR
<rust-windowing/winit#548> as it fixes various quirks
regarding the HiDPI handling and window resizing that prevail in the
current master branch of `winit`.
yvt added a commit to yvt/ngspades that referenced this pull request Apr 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment