Skip to content

Conversation

amartya4256
Copy link
Contributor

With this PR, all the widgets scale themselves asynchronously independent of the order saving the wait time over their children to scale.
Earlier, the widgets would scale synchronously on a DPI_CHANGED event from the OS, i.e., the shell receives the callback from the OS and calls and waits for its children to execute their handleDPIChange (Over all the superclasses of course) and every widget on handleDPIChange method being triggered, would call handDPIChange for their children widgets or associated widgets and wait for them to finish and them execute he remaining scaling statements and pass the control to their parents. This whole flow would keep the thread busy for a significant amount of time making the UI freeze for sometime.

With this approach, the Shell receives the callback from the OS and executes its handleDPIChange (Over all the superclasses of course) and sends a notification asynchronously, i.e., queues the notifyListeners to be executed for its children when the thread is free. Once the first queued notifyListeners runs, it would invoke the handleDPIChange for that particular widget, which would then schedule DPI scaling calls for its children and associated widgets and move on with its handleDPIChange execution. This would definitely change the hierarchical order in which the widgets scale themselves. Considering that the parents need to resize them on the basis of the size of the children, there needs to be a recalibration of the bounds for the widgets once all the widgets have scaled themselves. Hence, we keep a note of when all the widgets have completed their scaling, the last widget to scale itself triggers a Resize event, leading every widget to recalibrate their bounds. This leads to a better UI freeze free scaling where all the widgets inside the shell are still operable/clickable amidst the scaling.

Only the last commit is relevant to this PR.
Depends on #2483

@amartya4256 amartya4256 linked an issue Sep 17, 2025 that may be closed by this pull request
Copy link
Contributor

github-actions bot commented Sep 17, 2025

Test Results

  118 files  +1    118 suites  +1   11m 54s ⏱️ + 3m 40s
4 583 tests ±0  4 547 ✅ ±0  36 💤 ±0  0 ❌ ±0 
  330 runs  ±0    307 ✅ ±0  23 💤 ±0  0 ❌ ±0 

Results for commit 1bda21e. ± Comparison against base commit b2b5d45.

♻️ This comment has been updated with latest results.

@HeikoKlare
Copy link
Contributor

What's the relation of this to ... ?

Changes look quite similar.

@amartya4256
Copy link
Contributor Author

#2465 was a draft or a POC with all the changes as a whole and then I divided bits from it into trailing PRs. We will not merge #2465 and I shall close it. This is the last PR that relates to #2465.

@HeikoKlare
Copy link
Contributor

Okay, then it would be good to close that other PR since it misses a title and description anyway.

This was referenced Sep 19, 2025
@amartya4256 amartya4256 force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch 8 times, most recently from f5e9146 to e65d64d Compare September 26, 2025 07:36
@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch 3 times, most recently from 32a70b6 to 3063414 Compare September 26, 2025 12:25
@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch 6 times, most recently from 8254b76 to c6bd75f Compare September 29, 2025 16:30
}

void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
private void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
Copy link
Contributor

Choose a reason for hiding this comment

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

WM_DPICHANGED does nothing if nativeZoom == newNativeZoom. Since all events are processed asynchronous (even handling the initial DPI change event for the shell) it can happen that a shell is moved back and forth and the second event is not processed because of the above condition, leading to the shell having a wrong scaling. As an example, imagine you move a shell from 100% to 125% monitor. WM_DPICHANGED forwards this event as 100 != 125 and the event is queued. Then the shell is immediately moved back from 125% to 100%. WM_DPICHANGED does not forward the event because the shell's native zoom is still 100 (the event to change it is only queued but not processed), just like the new zoom. So only the first event will be processed, leaving behind a 125% shell on a 100% monitor.

A solution would be to remove the condition such that every call to WM_DPICHANGED will result in an event to be processed. As an alternative, the initial event on shell level could be processed synchronously to immediately set the shell's native zoom.

Another effect of the above issue is that the shell can shrink or enlarge when rapidly moving around. That will also be fixed with the solution proposals.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point, I removed this condition. I don't see any harm in doing it, actually I just verified it by injecting the same multiple events for the same zoom change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since you now made the event processing at shell level synchronous, it would probably be possible to revert that change.

return;
}
if (handleDPIChangedScheduledTasksCount.decrementAndGet() <= 0) {
currentDpiChangeEvent = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this only be done when event.doit? Otherwise the event currently being processed could have been cancelled at the very end of processing it (thus counter is 0), so the current event would be cleared even though it is still being processed (as it's a different event than the current). Another option would be to only clear currentDpiChangeEvent if currentDpiChangeEvent == event.

Copy link
Contributor

Choose a reason for hiding this comment

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

Both conditions should be fine, as long as currentDpiChangeEvent is only changed when event.doit is falsified. I still took the second proposal as it seemed safer to me.

@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch 2 times, most recently from cb98f63 to cf46e6e Compare September 30, 2025 07:44
@akoch-yatta
Copy link
Contributor

@fedejeanne Could you have a second look on this PR. I noticed an issue when changing monitors quite fast so the second DPI change event was triggered before the first one was fully handled. This issue should not appear anymore, at least I could not reproduce it anyhow.

Copy link
Member

@fedejeanne fedejeanne left a comment

Choose a reason for hiding this comment

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

I tested with

  • 175% monitor on the left (primary)
  • 100% monitor on the right

And the menu looks odd on both monitors

100%

100

175%

175

@fedejeanne
Copy link
Member

btw. I'm on Windows 11 since this morning.

Everything looks good on the master branch.

@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch from cf46e6e to 7ea01d5 Compare September 30, 2025 15:25
@akoch-yatta
Copy link
Contributor

@fedejeanne Good catch with the dark theme. As light and dark theme menu bar is rendered differently this was caused as the call to getBoundsInPixels triggered a re-render of the menu bar, but the menu bar was not yet updated - the async call was not yet processed.
As I didn't find a way to re-trigger the resizing of the menu bar, the easiest solution was to still do the Shell DPI change handling synchronously and only process all children asynchronously.

Copy link
Member

@fedejeanne fedejeanne left a comment

Choose a reason for hiding this comment

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

@akoch-yatta I tested again and I still see some issues.

My setup (same as before)

  • 175% monitor on the left (primary)
  • 100% monitor on the right

Issue 1: Icons have the wrong size

image

To reproduce

  • Start the workbench on the 100% monitor (if necessary, move it there and restart it).

Issue Nr. 2: Editors are cut-off

image

To reproduce (only happens sporadically)

  • Either move back and forth really fast between the 2 monitors and then leave the workbench in the 175% monitor or
  • Restart the application in the 175% monitor

The last one is really hard to reproduce (I've only seen it once when moving back and forth and once when restarting).

@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch from 7ea01d5 to 323ee58 Compare October 1, 2025 13:57
@akoch-yatta
Copy link
Contributor

@fedejeanne the first issue was caused by ane interfering DPI change events triggered by moving the shell and changing the parent of ImageBasedFrame + the ZoomChange event listener in ImageBasedFrame. This listener currently relies on a Resize event from the Shell, which is not thrown in the scenario, when the parent is changed.
With this I wasn't able to produce Issue 2 anymore on my laptop.

Copy link
Member

@fedejeanne fedejeanne left a comment

Choose a reason for hiding this comment

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

LGTM (only an unnecessary empty line)

I wasn't able to reproduce error Nr. 2 either, thank you for the fix @akoch-yatta !

@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch 6 times, most recently from db80447 to 313bfcf Compare October 2, 2025 12:54
With this commit, all the widgets scale themselves asynchronously
independent of the order saving the wait time over their children to
scale.
@akoch-yatta akoch-yatta force-pushed the amartya4256/dpi_performance_improvement/async_dpi_scaling branch from 313bfcf to 1bda21e Compare October 2, 2025 13:28
@akoch-yatta akoch-yatta merged commit ae0153c into eclipse-platform:master Oct 2, 2025
21 of 22 checks passed
@akoch-yatta akoch-yatta deleted the amartya4256/dpi_performance_improvement/async_dpi_scaling branch October 2, 2025 13:54
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.

Make DPI change asynchronous
4 participants