-
Notifications
You must be signed in to change notification settings - Fork 567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Partial invalidation #402
Comments
|
I'd like to take a stab at this, because I'm running into this performance issue. It seems to me that there are two parts here. The first is to report the invalidated regions to the windowing system. The second is to allow widgets to access those invalidated regions in their paint functions. Now, I only have access to gtk/cairo, so what I'm saying next might not be fully cross-platform (but I did have a quick peek at the mac/win API docs). In gtk, the paint callback receives a context with the clip region already set to the invalidated rects. Therefore, there should be some performance improvement just from the first step, because (at least in cairo, according to my tests) drawing is faster with a small clip, even if I'm not smart enough to ignore shapes outside the clip region. The second step should be more significant, however, because it will allow us to avoid recursing into un-invalidated children while drawing. So anyway, I was planning to look into the first part first. As far as I can tell, all three platforms like to do their invalidation one rectangle at a time (as opposed to batching up those rectangles and submitting them once). This seems to be the opposite to what druid is currently doing (storing Regarding the second part (propagating the invalidated regions back to the widgets). Cairo hands the window a list of rectangles, but as far as I can tell windows and mac only give a single rectangle. So maybe a single rectangle is the best approach? |
Having every widget do their own platform invaldiation call seems potentially inefficient, although I haven't looked at the details at all here. I think there might be performance benefits to bubbling up the rects and then sending the platform only the rects that are required to describe the total complex shape. For example let's consider a scenario where a container widget wants to repaint, and so do 50 of its children widgets. All of those children widgets are within the container's repaint request bounds anyway. |
That's a good point, although I'm also worried about the other extreme, where 50 children want to repaint but none of its ancestors does, and so we end up copying a length-50 Maybe a compromise would be to keep the |
I have more thoughts about this but for now encourage you to think about scrolling. |
I wouldn't worry about the About scrolling: this is deep water. At least on the mac, doing this really well involves a bunch of platform-specific conventions which will not generalize well. I think to get serious performance in a cross platform way we're going to need gpu rendering; then we can control the whole thing. It's going to be very hard to come up with a single abstraction that takes advantage of all the different platform fast paths. |
One opportunity for performance optimization is partial invalidation: only repainting the region of the screen that has actually changed. This is a fairly far-reaching change, as it requires platform-specific plumbing to the drawing pipeline. Some platforms (mac) don't have good support for partial invalidation, so if we're going to do things to improve performance, we have to fake it (see #16 for a bit of discussion of the issues at the druid-shell level).
This issue is primarily concerned with how widgets can express finer grained invalidation. The proposal is very simple: in context with an
invalidate
method (currentlyevent
andupdate
), we also have aninvalidate_rect
method that takes aRect
. To invalidate a more complex region, a widget could callinvalidate_rect
multiple times.We will keep the existing
invalidate
function as a convenience, and it will be defined as invalidating the layout rect. (We could consider invalidating the paint bounds instead, see #401, but layout bounds seems simpler; it's likely that a widget with fancy paint bounds should probably be implementing more precise invalidation in any case).This is quite similar to other APIs: on Windows it's called InvalidateRect and on macOS it's setNeedsDisplay.
The correctness criterion for widgets is basically that all pixels outside the requested region are identical to their value on previous paint. This only applies if there is a previous paint, so on first paint for a widget, no such requirement applies. Changing the layout size should also be considered a "free" invalidation. Lastly, when we implement show/hide lifecycle events (also a future architectural change), then the parent should be responsible for repainting the entire widget, so there is no requirement in that case.
As a minor style point, we might consider renaming "invalidate" to "request_paint", as we have a general naming scheme for requesting things.
The text was updated successfully, but these errors were encountered: