Open wiresketch opened 6 months ago
One obvious option that I see is to force the Draw2d/GEF applications to be always used with overlay scrollbars disabled to achieve optimum performance on Linux.
I'm leaning towards this approach. I fear that any attempt to change the native behavior within Draw2D will just result in a messy hack. It also has the effect that the controls would look the same across all platforms. Given the performance problem, I think it makes sense to have it be the default. But clients should still have the option to switch back to the current behavior via e.g. a system property or a static variable.
However this has the side effect of disabling overlay scrollbars for all views in the application, not only for FigureCanvas control.
I think that's the crucial part. Draw2D/GEF must not alter the behavior of other UI components. Otherwise this will ruin someones day, the moment something unrelated suddenly breaks.
An improved solution would be to disable overlay scrollbars just for the FigureCanvas. This is actually possible using GTK3/GTK4 API by using the gtk_scrolled_window_set_overlay_scrolling method. The symmetrical gtk_scrolled_window_get_overlay_scrolling is already being using by SWT's Scrollable.getScrollbarsMode() method, so perhaps it wouldn't be too much to ask from the SWT team to add a Scrollable.setScrollbarsMode(int) method that would allow to disable overlay scrollbars on a specific FigureCanvas.
That sounds like a good approach to me. If at all possible, I'd like to avoid calling any platform-specific methods in Draw2D. If the SWT team can provide an interface for this, then that would already help out a lot. Otherwise either Draw2D or downstream projects would have to compile their own native binary files and ship it with their jar. Both options I'm not overly excited about.
I see you've already created an issue in the SWT project. Awesome! 😄 I'll try to also have a look at (at least) the Draw2d side during the weekend.
I've created #432. My idea is to call setScrollbarsMode(...)
inside the constructor of FigureCanvas. By default, SWT.NONE
is used, which effectively disables overlay scrolling.
If clients want the old behavior, they can do so by calling the static method FigureCanvas.setScrollbarsMode(SWT.SCROLLBAR_OVERLAY)
inside e.g. the Activator.
I was more curios about the possibility of some kind of double buffering to optimize the paints. Is this something that could have some use? Or is the conclusion here that overlay scrollbars should never be used with FigureCanvas on Linux/GTK?
If Scrollable.setScrollbarsMode method gets added to SWT then it will be easy to change the scrollbars mode in RCP apps directly using something like this:
protected void configureGraphicalViewer() {
((Scrollable) getGraphicalViewer().getControl()).setScrollbarsMode(SWT.SCROLLBAR_OVERLAY);
}
With SWT versions checks and the use of reflection if needed. I have a lot of those in my RCP app to work around various bugs. I am not 100% convinced that GEF/Draw2d should contain those workarounds.
It's always risky to touch very basic classes like the FigureCanvas, which is why I'm a little hesitant to do so. Disabling overlay scrolling seems like a much safer think to do and at least in the short term, this should be the preferable solution.
We used to do double-buffering for a different project. In principle, one could create a prototype from that, but that would require a lot more testing.
I was more curios about the possibility of some kind of double buffering to optimize the paints. Is this something that could have some use?
There is probably no harm in trying this out. Though it has to be an opt-in feature, as to not disrupt other clients. I've created a PoC which applies double buffering for the entire LWS (see https://github.com/eclipse/gef-classic/pull/434).
Is this what you had in mind?
@ptziegler This is close to what I had in mind but not quite. What you've implemented is a classic double-buffering that should not be needed with SWT.DOUBLE_BUFFERING style, and is unlikely to help with performance. I believe that originally this method was meant to reduce flickering on paint.
What I had in mind is reusing the same image for multiple paint calls without redrawing the buffered image each time. It means there needs to be a flag like "bufferedImageIsStale" which would indicate that the buffered image needs to be updated. And even then we would only need to update the part of the buffered image that really changed and needs to be redrawn as indicated by the clipping of the GC.
I've experimented a little bit with this approach and I still need more time to see what's needed for a correct implementation. There are some issues with scrolling optimization that also needs to be propagated to buffered image.
I believe that this kind of optimization might help with performance not only on Linux, but on other platforms too. There are several cases where the paint method is being called multiple times when certain actions/event are performed:
I don't know if this idea is actually implementable. Perhaps there are some sync issues (as always with caching) that I am not seeing yet, but this is what I had in mind.
Hm... I see. Perhaps instead of using a "stale" flag, one could make it so that whenever there is a surge of paint events, the actual paint operation is only done once the "last" event has been received.
This would at least be something that works well together with the classic double-buffering approach. Until the last event has been received, the "old" image is painted. The paint event itself creates a "new" image, replacing the "old" one upon completion.
The Throttler does something similar. I wonder if one could re-use parts of it... The only real downside is that the update becomes asynchronous, but with a low enough latency, this probably wouldn't be noticable.
I'm a bit late to join this party. But I wanted to point out that during my performance measurements I noticed that the image copying is not as fast as it may seem. In 4diac IDE we have a thumbnail outline of the diagrams and the thumbnailer uses images with copying to create it and a significant amount of execution time is on copying the images.
I also noted that at least on Windows the repaints are really only on the regions that are given to the lightweight system, while on GTK always the full widget is repainted. You may want to consider this when introducing some buffering.
The Throttler does something similar. I wonder if one could re-use parts of it... The only real downside is that the update becomes asynchronous, but with a low enough latency, this probably wouldn't be noticable.
I just looked into the Throttler. This would exactly be needed for reducing the impact of the Thumbnailer outline. However I expect that jface is not suitable for draw2d isn't it?
I just looked into the Throttler. This would exactly be needed for reducing the impact of the Thumbnailer outline. However I expect that jface is not suitable for draw2d isn't it?
A dependency to JFace is not an option. However, the class only depends on SWT so if necessary, we could simply copy it and go from there.
I just looked into the Throttler. This would exactly be needed for reducing the impact of the Thumbnailer outline. However I expect that jface is not suitable for draw2d isn't it?
A dependency to JFace is not an option. However, the class only depends on SWT so if necessary, we could simply copy it and go from there.
In the mean-time I brushed up my Thumbnail
know how. It looks I would anyhow need to have a dedicated implementation for the Thumbnail
. So no quick win here.
Yes, copy and paste is not forbidden. It’s EPL.
This issue is stale because it has been open for 90 days with no activity.
I'd like to file another issue that I've observed while working on #425. Trying to debug performance issues in my application on Linux/GTK3 I am noticing another problem with overlay scrollbars that negatively impacts performance. I'd like to open this issue to have a discussion for possible solutions and workarounds.
What happens is that when overlay scrollbars are visible for a control, the GTK system will show/hide them automatically on user activity, specifically on mouse moves. For example if the mouse stops moving over the scrolled area, scrollbars will fade away. If the mouse starts moving again - scrollbars will fade in. This animation for scrollbars triggers hundreds of SWT.Paint events that are then handled by LightweightSytem/DeferredUpdateManager.
At first I thought it would be logical for the content to be redrawn under the scrollbars, as long as the paint event is sent with the right clipping that only includes the region under the scrollbar. However I was surprised to find out that this is not the case. The entire client area is being forced to be repainted.
Since this behavior is actually the native behavior, it can be observed with a simple SWT snippet. I've attached it here. ScrollbarsTest.java.txt
Try moving the mouse and observe the number of logged paint events.
The same example can be launched with GTK_OVERLAY_SCROLLING=0 to disable overlay scrollbars, in which case a very limited number of paint events can be observed, and none while moving the mouse.
Since it looks like this is the native behavior on Linux/GTK3 not specific to Draw2d/GEF I would like to explore here possible ways to solve this performance issue.
One obvious option that I see is to force the Draw2d/GEF applications to be always used with overlay scrollbars disabled to achieve optimum performance on Linux. This means using GTK_OVERLAY_SCROLLING=0 variable on GTK3 when launching the application. However this has the side effect of disabling overlay scrollbars for all views in the application, not only for FigureCanvas control. Another problem is that this is not future proof as this environment variable is no longer being used on GTK4.
An improved solution would be to disable overlay scrollbars just for the FigureCanvas. This is actually possible using GTK3/GTK4 API by using the gtk_scrolled_window_set_overlay_scrolling method. The symmetrical gtk_scrolled_window_get_overlay_scrolling is already being using by SWT's Scrollable.getScrollbarsMode() method, so perhaps it wouldn't be too much to ask from the SWT team to add a Scrollable.setScrollbarsMode(int) method that would allow to disable overlay scrollbars on a specific FigureCanvas.
Other possible solutions are less clear to me. Perhaps something in DeferredUpdatedManager could be done to improve these repaints. Perhaps some kind of custom double buffering could do the trick.
I'd like to hear from others what they think should be done here.