News:

Use the "Forum Search"
It may help you to find anything in the forum ;).

HiDPI GUI scaling prototype — ready for maintainer review

Started by victor_18993, October 04, 2026, 10:22:26 AM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

victor_18993

Hi everyone,

I've finished preparing the HiDPI GUI scaling prototype for maintainer review.

https://files.simutrans-germany.com/upload/HIDPI-GUI-AUTOSCALE.rar

This is not an integration proposal yet. There are still a few real-hardware tests missing, especially Windows mixed-DPI/multi-monitor setups and macOS Retina, but the core implementation is now stable enough to review properly.

The main idea is simple:

- keep the map/world rendering unchanged;
- scale only the GUI;
- rerender fonts at the proper size;
- scale skins/icons with the existing pixel-art scaler;
- keep click areas exactly aligned with what is drawn.

At 100% GUI scale, the result is pixel-identical to current trunk.

From 100% to 300% GUI scale, the map itself remains unchanged, so this avoids the blur and oversized world graphics produced by the existing whole-screen scaling approach.

The review package is based on r12329 and contains:

- the complete HiDPI candidate;
- the independent font reload leak fix;
- the independent preferences.tab concurrent-write fix;
- the HiDPI-specific part separately, to make the feature easier to review;
- short review notes with the remaining decisions.

A few measured results:

- automated tests: 310/310 at 100%, 150% and 200%;
- 100% rendering: pixel-identical to trunk;
- live display-scale changes under Wayland: 800 changes with stable memory;
- no map scaling from GUI scale;
- no savegame/network version change;
- current scale change to 200% costs about 85 ms once, not per frame.

There are three things where I would particularly like maintainer feedback:

1. Default behaviour
  - current candidate stays at 100%;
  - Auto is opt-in;
  - I also have an optional patch for Auto on new installations only.

2. Station and town labels
  - currently they scale with the GUI font;
  - an optional version keeps them at world size.

3. Large themes
  - currently they remain supported and multiply with GUI scale.

One important note: the package still contains the r12329 preferences.tab integration because that is the exact tree that was certified.

After the recent discussion about configuration files, I am not treating that part as final architecture. If r12329 is reverted or another local-settings design is preferred, I will adapt the persistence layer separately.

The font ownership fix is completely independent from both HiDPI and preferences.tab.

The remaining release gates are:

- real Windows DPI at 125–200%;
- real mixed-DPI multi-monitor movement;
- macOS Retina.

So at this stage I'm mainly asking for review of the GUI-scaling direction and the three policy questions above.

I've attached the review package with the full diff and the split patches.

Thanks!
En la vida todo son vivencias y cada una de ellas nos hace mas grandes,¿Como de grande eres tu? :)

prissi

When scaling the GUI, usually the pak images also become too small. Even zooming in pak64 became tiny, hence the scaling of everything. This is especially serious on phones, with their insane DPI values. Unscaled maps on run of the mill 4k displays on middle end phone just don't work well without map zoom (and on early retina macs), both getting very laggy as too much data has to be moved.

Moreover, proportional scaling on some things was ugly on some things, like huge scrollbars, hence the use of themes. But it is good to have an additional option, at least for more high-end hardware.

On the implementation:
Why two env_t values? One should be enough. The second is only needed temporary, see below.

The code in poll_event is very hackish. Rather sent a size event with an extra parameter with the requested new dpi. This the can call gui_themes etc. Also, one could scale window positions in rdwr_all_win and gui_frame_t(), not in the event routine (maybe).

win_reload_windows_after_gui_scale is just used one time, so no need for a new routine. Also, when gui_themes_init is called from an event, just queue the event once from the dialog.

What is the font_t::swap routine needed for? Font scaling worked already without ...

The offset of the tooltip should be relative to LINE_SPACE and D_H_SPACE. The fixed values were rather ancient left-over.

On gui_themes_t. Why the skinverwaltung_t::restore_all_skins() was removed? Maybe a comment would help?

Also the scaling is always done with the two zoom values. Hence, the gui_init could at first determine those two values and then scaling could use a macro
SCALE(i) (((i)*gui_themes_t::zoom_up)/gui_themes_t::zoom_down)Now, this is done via MACRO plus two functions, not very clear and much more code. Not to mention, those two give anyway the factor the image are finally scaled ...

gfx->get_image_offset should probably return the image size on simgraph0.cc since it is loaded nowadays anyway. Would remove the if condition.

victor_18993

Thanks for the detailed review, prissi. I reworked the HiDPI prototype following your suggestions, and the implementation is now quite a bit simpler.

https://files.simutrans-germany.com/upload/autoescaler.rar

The main changes are:

- only one persistent value remains, env_t::gui_scale; the effective scale is just zoom_up/zoom_down;
- manual and display scale changes now go through the normal SYSTEM_RESIZE event path;
- window positions/sizes are rescaled through rdwr_all_win;
- the old GUI-scale reload helper is gone;
- font_t::swap is not part of the HiDPI patch anymore — it belongs only to the separate font ownership fix;
- restore_all_skins() is still used for real theme changes, but not for scale-only changes, because reloading the skins there only registered extra images without changing the result;
- scaling now uses one SCALE() macro based on zoom_up/zoom_down. I kept round-to-nearest rather than truncation because the truncated form changes many pixel positions compared with the already tested behaviour.

The refactored version is based on r12333.

At 100% it is still pixel-identical to trunk in SDL3, SDL2 and GDI, the map remains unchanged from 100% to 300%, and the test suite is still 310/310.

I kept two of your suggestions as separate optional patches because they change existing behaviour even at 100%:

1. Tooltip offset using LINESPACE / D_H_SPACE — this moves the tooltip by 3 pixels at 100%.
2. The simgraph0 get_image_offset cleanup — this also changes how empty images are represented.

Would you prefer these two cleanups to be included with the HiDPI patch, or kept separate?

There is also one remaining usability question: at very large manual scales, e.g. 400% in a 1280x800 window, the Display dialog can become too large to use to change the scale back. Would you prefer to clamp the selectable scale, or allow large scales but make critical dialogs always recoverable?

No code has been committed or pushed yet. Real Windows mixed-DPI and macOS Retina testing are still pending.
En la vida todo son vivencias y cada una de ellas nos hace mas grandes,¿Como de grande eres tu? :)

prissi

I think if the scaling scales a number, it is likely an overlooked GUI_theme constant.
like in banner/chart/... the "10" is probably "D_MARGIN_LEFT"

The 18 in char is linspace + D_V_SPACE, I think.
4 is D_H_SPACE, 5 is D_V_SPACE in almost any of the them for x/y offsets
SCALE(4) is probably always D_H_SPACE

Is the chart code really correct? 1/6 of the size should not scale, as the size should be scaled already. Maybe I overlooked something.

THe comment for dr_get_display_scale() makes no sense. What is the meaning of "A screen scale already draws every game pixel x_scale/32 pixels wide"? Actually, the whole comment is many words but not really enlightning.

Same the comment on "laid_out_at" (maybe meaning is "scale_at"?)

And apparently I was unclear on the zoom amount. GUI images can only take certain sizes, see the g_simgraph16.zoom_num[] ..._den[] in simgraph16.cc
With these factors, no rounding is needed as the pixel sizes always match. Any other given percent value should probably better be rounded to the next of these values, or the relative GUI sizes do not match at all. Also, for scaling each image, the calculation is repeated, instead of asking for scaling to a zoom factor.

Since there were not enough fine grained zoom factors for zooming out, I added a few more. Now possible factors are 2/1 7/4 3/2 11/8 4/3 5/4 9/8 1/1 3/4 5/8 1/2 3/8 1/4 1/8  (2/3 does not work, tile no longer isometric). One could in principle also add 7/8 but it is very close to 3/4 for zooming out. See display_settings.cc void gui_settings_t::draw(scr_coord offset) in r12336 on convert them to percent.