mono / xwt

A cross-platform UI toolkit for creating desktop applications with .NET and Mono
MIT License
1.36k stars 241 forks source link

[Wishlist] Give Canvas.OnDraw a way to find out what portion of the widget should be redrawn #98

Closed luiscubal closed 12 years ago

luiscubal commented 12 years ago

Currently, I believe there is no way for a Canvas control to figure out which parts should be redrawn.

In my personal tests, this seems to be making things too slow since in some cases an user widget may be unnecessarily drawing too much. This seems to be particularly bad when scrolling is considered, and makes XWT drawing practically unusable in my case - at least on GTK+.

Even platforms that do not have this feature should have no problems, since it's trivial to add a "fallback" result(a rectangle including the entire widget), while platforms that do have this would have its speed greatly improved.

As a potential alternative, one could pass this data to Context and have it ignore the useless drawing calls. This should also speed up the rendering, and require fewer(if any) changes to the API.

slluis commented 12 years ago

In GTK, the drawing context is automatically clipped when the OnDraw method is called, so drawing calls which fall outside the clipped region should already be ignored by Cairo.

In any case, I'm not sure this would improve the scrolling performance of a Canvas. AFAIR, GTK will invalidate the whole widget when it is scrolled, unless you use custom scrolling, in which case you have full control of what is redrawn (there is an example of custom scrolling in the scrolling sample).

lytico commented 12 years ago

Its not only a question if or if not the backend draws. preparation of drawing might be expensive too, and this could be avoided by the ClipRect-Feature of this proposal.

slluis commented 12 years ago

Can you elaborate on how would this help in the scrolling case?

lytico commented 12 years ago

Following your example in scrolling sample, one could calculate the VisibleRect out of the Canvas.Parent if its a ScrollView. I scetched this here: http://limada.git.sourceforge.net/git/gitweb.cgi?p=limada/limada;a=blob_plain;f=src/Limaki.View/Limaki.Widgets/Xwt/ScrollableCanvas.cs;hb=dfa202bfaf96f80f225fb143cef4af7842627d3c So one has the information in VisibleRect which parts should be redrawn, even outside of OnDraw or backend's draw. An other way could be to delegate the acess to (Canvas.Parent as ScrollView).VisibleRect

luiscubal commented 12 years ago

I created a new branch named "graphics-test" in my fork that shows that this problem happens with images(partial images).

In my test, drawing 1600 partial images is enough to make the program super-slow, even if only 100 partial images are actually visible. Additionally, rendering rectangles(via Rectangle/SetColor/Fill) seems to be very fast even with 1600.

If the code is modified so that only 100 partial images are rendered, then it seems to be fast enough(it is noticeably faster than the 1600 partial images case) This suggests that Cairo clipping doesn't help that much for images(or at least for partial images).

slluis commented 12 years ago

Just to be clear, I'm not against adding the dirty rect information, I plan to do it.

Following your example in scrolling sample, one could calculate the VisibleRect out of the Canvas.Parent if its a ScrollView.

When using custom scrolling, you have full control of how things are scrolled and drawn, so you can optimize your drawing operations like you did in your example, and this can be done without any additional information from XWT.

The problem is when you don't use custom scrolling. Let's say you have a canvas inside a ScrollView (not using custom scrolling), and the canvas is scrolled up 1 pixel. How things are redrawn depends on the platform. For example, the platform may be able to physically shift the pixels already drawn in the window up 1 pixel, and then request the redraw of the new 1 pixel-height rect now visible at the bottom. Or the platform may need to completely redrawn the widgets in their new shifted positions. In the first case, the dirty rect optimization will be useful. In the second case, it will be useless.

luiscubal commented 12 years ago

Seems to have been implemented in 3fc91bc40b93b2d3982746b5ed447eaa2354ef4b