Skip to content

[mir:wayland] Support wp_fractional_scale_v1 for correct HiDPI scaling#4932

Draft
Copilot wants to merge 7 commits into
mainfrom
copilot/update-mir-wayland-fractional-scale
Draft

[mir:wayland] Support wp_fractional_scale_v1 for correct HiDPI scaling#4932
Copilot wants to merge 7 commits into
mainfrom
copilot/update-mir-wayland-fractional-scale

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 7, 2026

The mir:wayland nested compositor platform only used the integer wl_output.scale event, so on hosts using fractional scaling (e.g. 1.75×) the nested session always rendered at scale 1 — sending undersized, upscaled buffers to the host.

What's new?

  • wp_fractional_scale_manager_v1 + wp_viewporter client support — generated and integrated wayland-scanner client bindings for both protocols
  • Per-output fractional scale tracking — each Output now creates a wp_fractional_scale_v1 object; the preferred_scale event (scale as uint32 / 120) updates host_scale (now float)
  • Correct buffer sizingtoplevel_configure sizes the EGL buffer as round(host_scale × logical_size); wp_viewport::set_destination is used so the host compositor maps the buffer to the correct logical footprint
  • Correct logical/pixel size separationdcout.custom_logical_size is set to the host's logical window size (from xdg_toplevel.configure), while output_size tracks the physical pixel buffer size separately. view_area() returns the pixel buffer size for the GL renderer's glViewport, while dcout.extents() returns the logical size used by the nested compositor for surface placement
  • dcout.scale propagation — the fractional scale is written to DisplayConfigurationOutput::scale so the nested session's display configuration reflects the true host scale, and nested clients receive correct wl_output.scale events
  • Live reconfiguration — if preferred_scale fires after initialization (e.g. after the first buffer is committed), the EGL surface is recreated at the new pixel size and on_display_config_changed is always triggered on any scale change so all connected clients are updated
  • Graceful fallback — when the host does not advertise either protocol the existing integer wl_output.scale path is preserved unchanged

How to test

Run a Mir nested session on a HiDPI host configured with a fractional scale (e.g. scale: 1.75 in a GNOME or Mir session):

miral-app --wayland-host $WAYLAND_DISPLAY

Previously the nested session rendered at 1× (blurry) and nested clients (e.g. the miral-shell background) had text rendered at proportionally reduced size due to incorrect logical size reporting. With this change:

  • The nested session renders at the fractional scale (sharp output)
  • The nested session reports the correct scale to its clients via wl_output.scale and wp_fractional_scale_v1
  • Clients correctly size and render their content for the HiDPI output

Checklist

  • Tests added and pass
  • Adequate documentation added
  • (optional) Added Screenshots or videos

Copilot AI and others added 3 commits May 7, 2026 10:06
Copilot AI changed the title [WIP] Update mir:wayland platform to support fractional_scale_v1 [mir:wayland] Support wp_fractional_scale_v1 for correct HiDPI scaling May 7, 2026
Copilot AI requested a review from AlanGriffiths May 7, 2026 10:25
@AlanGriffiths
Copy link
Copy Markdown
Contributor

@copilot testing this exposes existing problems with the scale handling:

The internal background client used by miral-shell has the text reduced in size in proportion to the scale.
External clients appear the same size regardless of the scale.

What appears to be happening is that the scale is not being propagated from the platform and therefore not seen by clients. Here's a comparison of a debug trace with the mir:x11 platform:

$ env -u DISPLAY WAYLAND_DEBUG=server miral-app --wayland-host $WAYLAND_DISPLAY 2>&1 | grep scale
[3904893.560]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3904893.740]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3904893.940]  -> wl_output@8.scale(1)
[3904894.062]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3904894.184]  -> wl_output@8.scale(1)
[3904894.230]  -> wl_output@8.scale(1)
[3904894.708]  -> wl_registry@3.global(31, "wp_fractional_scale_manager_v1", 1)
$ WAYLAND_DEBUG=server miral-app --x11-output=800x600^2 2>&1 | grep scale
[3926147.312]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3926147.498]  -> wl_output@8.scale(2)
[3926147.548]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3926147.791]  -> wl_output@8.scale(2)
[3926147.795]  -> wl_output@8.scale(2)
[3926147.848]  -> wl_registry@2.global(31, "wp_fractional_scale_manager_v1", 1)
[3926148.421]  -> wl_registry@3.global(31, "wp_fractional_scale_manager_v1", 1)
[3926152.164] wl_surface@9.set_buffer_scale(2)

…atform

Agent-Logs-Url: https://github.com/canonical/mir/sessions/ed6ce9e0-7914-48ea-9756-9a1b1a82877e

Co-authored-by: AlanGriffiths <9048879+AlanGriffiths@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 8, 2026

@copilot testing this exposes existing problems with the scale handling:

The internal background client used by miral-shell has the text reduced in size in proportion to the scale.
...

Fixed in c6f3b34. There were two bugs causing this:

  1. Wrong dcout.custom_logical_size: It was set to the physical pixel buffer size (e.g. 1920×1080) instead of the logical window size (e.g. 1097×617 at 1.75×). This caused the nested compositor to lay out surfaces in the full pixel-sized logical area, but wl_output.scale(2) told clients they had a 960×540 logical output — so clients rendered content sized for 960×540 but the compositor placed it in a 1920×1080 area, making it appear at ~50% = "reduced in size in proportion to the scale".

  2. view_area() returning logical size instead of pixel size: The GL renderer uses view_area() for glViewport(), which needs pixel coordinates. Since dcout.custom_logical_size was the pixel size before, the two happened to coincide. After fixing (1), dcout.extents() now correctly returns the logical size, so view_area() now explicitly returns {dcout.top_left, output_size} (the physical pixel buffer size).

Also fixed: on_display_config_changed() is now always called when the scale changes (not just when pixel buffer size changes), so the wl_output.scale update is reliably propagated to all connected clients.

Agent-Logs-Url: https://github.com/canonical/mir/sessions/ed6ce9e0-7914-48ea-9756-9a1b1a82877e

Co-authored-by: AlanGriffiths <9048879+AlanGriffiths@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[mir:wayland] platform does not scale correctly

2 participants