Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[X11+GDK] Not painting when windows are covered, minimized, or hidden?

35 views
Skip to first unread message

L. David Baron

unread,
Feb 6, 2009, 2:08:05 AM2/6/09
to dev-pl...@lists.mozilla.org
So I was profiling a testcase recently where layout was spending a
lot of time painting, and surprised to (re?)discover that on Linux,
nothing suppresses all the work we do repainting when a browser
window is covered by another window, minimized, offscreen, or on
another virtual desktop (which to us looks just like minimized or
offscreen, depending on which of two drastically different
mechanisms the window manager in question uses for desktops).

My simple way of testing this in a cross platform way was to load a
browser window containing
http://blog.mozilla.com/dolske/2008/02/11/capital-n-small-y/
with the patch in
http://hg.mozilla.org/users/dbaron_mozilla.com/patches/raw-file/446c8c70ba5d/print-paint-region-info
(with the #if 0 changed to an #if 1), and see what areas were
getting repainted. So what I'm testing here is basically the
following sequence:
1. Imagelib tells layout that the image animation has changed, so
layout passes an invalidate to the widget code with the region
invalidated.
2. The widget code does something with it, perhaps interacting with
the windowing system.
3. At some point the widget code sends a paint event (which comes
from the OS / windowing system) with an area that needs to be
repainted back to view/gfx/layout and tells them to paint it.
I want to know if something, between steps (2) and (3), is adjusting
the area that needs to be repainted based on what is actually
visible to the user.

On Windows, when a window is minimized, partly offscreen, or covered
by another window, the way our widget code interacts with the
windowing system when it receives invalidates from layout and later
sends repaints to it causes us not to repaint the parts of the
window that are offscreen. When the area to be repainted (e.g., by
an animated GIF) is partly offscreen or partly covered, we're only
doing the work to repaint the area that's visible. When the area is
completely offscreen or covered, or the window is minimized, we
don't do any painting. This seems ideal.

On Mac, we stop repainting the animated images when we're minimized,
but not when we're covered or partially offscreen. So there's some
potential improvement there.

But on Linux, we *never* reduce the area under any of the above
circumstances. Our widget code does pass the invalidates to GDK,
but GDK doesn't do anything with them other than buffer them,
intersect them with a clip region that never clips anything useful,
and give them back to us.

That said, on Windows and Mac and Linux, opening another tab in the
same window and switching to it immediately stops all the repainting
in the hidden tab, since that's hiding that we know about internally.

And for the record, my Linux setup is Ubuntu 8.10, with metacity as
window manager (which is what you get from choosing System ->
Preferences -> Appearance -> Visual Effects -> None) and an Intel
video card.


Now I'm aware this gets a little more complicated when compositing
window managers are involved. (I'm still not using one, though.)
But it seems like we ought to be a way to avoid all this repainting
work that's not needed, and only do it when it is needed.

I don't know all that much about the various bits involved in X
(various X extensions, etc.), or, for that matter, even where to
find decent documentation of them. (I'm a Linux user, but most of
my Mozilla hacking is in cross-platform code.) Does this sound like
something that ought to "just work", and we're doing something wrong
to prevent it from working? Or even if it is expected, is there
some obvious way that we could improve things here? Or even some
less obvious way?

-David

--
L. David Baron http://dbaron.org/
Mozilla Corporation http://www.mozilla.com/

Neil

unread,
Feb 6, 2009, 5:18:00 AM2/6/09
to
L. David Baron wrote:

>On Windows, when a window is minimized, partly offscreen, or covered by another window, the way our widget code interacts with the windowing system when it receives invalidates from layout and later sends repaints to it causes us not to repaint the parts of the window that are offscreen.
>

Out of interest, was this Vista or a previous version? I'm interested in
whether Vista's double-buffering requires painting to continue in
covered windows so that it can do compositing or other tricks.

--
Warning: May contain traces of nuts.

Arivald

unread,
Feb 6, 2009, 8:05:43 AM2/6/09
to
L. David Baron pisze:

> So I was profiling a testcase recently where layout was spending a
> lot of time painting, and surprised to (re?)discover that on Linux,
> nothing suppresses all the work we do repainting when a browser
> window is covered by another window, minimized, offscreen, or on
> another virtual desktop (which to us looks just like minimized or
> offscreen, depending on which of two drastically different
> mechanisms the window manager in question uses for desktops).
[...]

> On Windows, when a window is minimized, partly offscreen, or covered
> by another window, the way our widget code interacts with the
> windowing system when it receives invalidates from layout and later
> sends repaints to it causes us not to repaint the parts of the
> window that are offscreen.
[...]

> But on Linux, we *never* reduce the area under any of the above
> circumstances. Our widget code does pass the invalidates to GDK,
> but GDK doesn't do anything with them other than buffer them,
> intersect them with a clip region that never clips anything useful,
> and give them back to us.

On Windows, this painting optimization is done by OS. It is two stage
process: first somebody (system or application) invalidate some part of
screen, and when there are invalid parts of screen windows sends
WM_ERASEBKGND, WM_NCPAINT and WM_PAINT messages, so application can
paint and "validate" screen.
This two stage process allow easy optimization, because there may me
many invalidates before system decide to repaint and validate some area.
Common error of new windows developers is forcing immediate repaint,
instead of just invalidating.

Did Linux X so something similar? Did any X client program is even able
to get from X server info about which parts of program window are
visible or need repaint?

--
Arivald

Arivald

unread,
Feb 6, 2009, 8:20:47 AM2/6/09
to
Neil pisze:

I don't know exactly, but I have some thoughts.

BeginBufferedPaint() will create memory bitmap, used as back-buffer for
window. Then when Vista sends painting messages, all HDC points to this
back-buffer instead of to real screen HDC. This way painting is much
faster (no need to copy with real device). After painting operation
completes, Vista just BitBlit backBuffer to screen.

So, because Vista always have allocated back-buffer and all drawing
operations go through this buffer, Vista does not need to force window
to always fully repaint. All optimizations related to update regions may
still work as in XP.

Vista double buffering may speed up painting, and for sure it reduce
flicker related to erase-paint cycle.

--
Arivald

Christopher Blizzard

unread,
Feb 6, 2009, 8:18:25 AM2/6/09
to Arivald, dev-pl...@lists.mozilla.org

On Feb 6, 2009, at 2:05 PM, Arivald wrote:

> Did Linux X so something similar? Did any X client program is even
> able to get from X server info about which parts of program window
> are visible or need repaint?

My memory is starting to get hazy but there are a few things here that
are worth knowing:

1. X does send "XExposeEvent" events for when parts of windows are
exposed and need to be redrawn.
2. X sends "XVisibilityNotifyEvents" events for when windows are
partially covered or fully covered.
3. Redraw requests are sent up from layout into the widget layer and
are combined with the native gtk redraw queue which is either cleared
(processed) during the idle loop or when there's an explicit flush of
the queue.

It might make sense to track the visibility notify events in the
widget layer and then not flush the queue or dump the queue (I can't
remember if you can do that) if the window and/or widget isn't
visible. I don't know if there's code in the widget layer to do this
or not, my memory is too hazy. I also have the distinct memory of
trying to do this in the past and finding that the visibility notify
events were extremely unreliable - especially with some window
managers. So you have to do some wide testing to be able to make a
change like this.

--Chris

Arivald

unread,
Feb 6, 2009, 8:35:47 AM2/6/09
to
Arivald pisze:

I just read some example code which use vista buffered paint...
It is not as good as I thought, it seems if program uses buffered
paints, it always must repaint window completely. So it is not very good
solution, it may works slower than manual double buffering.

Pros: window should not flicker, and painting to mem is faster.
Cons: Window must always do complete repaint.

--
Arivald

Arivald

unread,
Feb 6, 2009, 10:04:34 AM2/6/09
to
Christopher Blizzard pisze:

> too hazy. I also have the distinct memory of trying to do this in the
> past and finding that the visibility notify events were extremely
> unreliable - especially with some window managers. So you have to do
> some wide testing to be able to make a change like this.
>

unreliable...
So it it is not rock solid standard, then XULRunner can't use it for
optimization.

--
Arivald

Christopher Blizzard

unread,
Feb 9, 2009, 3:16:17 PM2/9/09
to Arivald, dev-pl...@lists.mozilla.org

I'm speaking here from memories that are several years old. What I
say about the reliability of visibility events might be lies. Testing
it would be the only way to say for sure.

--Chris

Robert O'Callahan

unread,
Feb 9, 2009, 3:47:15 PM2/9/09
to
On 7/2/09 2:20 AM, Arivald wrote:
> Vista double buffering may speed up painting, and for sure it reduce
> flicker related to erase-paint cycle.

We already do double-buffering internally on Windows. Maybe it's not as
efficient as Vista's, but if Vista's requires us to repaint the whole
window every time, that's not a win.

Rob

Robert O'Callahan

unread,
Feb 9, 2009, 3:50:48 PM2/9/09
to
On 6/2/09 8:08 PM, L. David Baron wrote:
> On Mac, we stop repainting the animated images when we're minimized,
> but not when we're covered or partially offscreen. So there's some
> potential improvement there.

Although that's hard because Mac is using a compositing window manager,
so we need to give it pixels so it can uncover or move our window
without calling back to our process.

> But on Linux, we *never* reduce the area under any of the above
> circumstances. Our widget code does pass the invalidates to GDK,
> but GDK doesn't do anything with them other than buffer them,
> intersect them with a clip region that never clips anything useful,
> and give them back to us.

It sounds like we should be able to do some visibility checks there.
Karl T might have some ideas.

Rob

Karl Tomlinson

unread,
Feb 11, 2009, 10:17:05 PM2/11/09
to
On Thu, 5 Feb 2009 23:08:05 -0800, L. David Baron wrote:

> But on Linux, we *never* reduce the area under any of the above
> circumstances. Our widget code does pass the invalidates to GDK,
> but GDK doesn't do anything with them other than buffer them,
> intersect them with a clip region that never clips anything useful,
> and give them back to us.

On Fri, 6 Feb 2009 14:18:25 +0100, Christopher Blizzard wrote:

> My memory is starting to get hazy but there are a few things here
> that are worth knowing:
>
> 1. X does send "XExposeEvent" events for when parts of windows are
> exposed and need to be redrawn.
> 2. X sends "XVisibilityNotifyEvents" events for when windows are
> partially covered or fully covered.
> 3. Redraw requests are sent up from layout into the widget layer
> and are combined with the native gtk redraw queue which is either
> cleared (processed) during the idle loop or when there's an
> explicit flush of the queue.
>
> It might make sense to track the visibility notify events in the
> widget layer and then not flush the queue or dump the queue (I
> can't remember if you can do that) if the window and/or widget
> isn't visible. I don't know if there's code in the widget layer
> to do this or not, my memory is too hazy. I also have the
> distinct memory of trying to do this in the past and finding that
> the visibility notify events were extremely unreliable -
> especially with some window managers. So you have to do some
> wide testing to be able to make a change like this.

Suppressing painting on fully obscured windows should be easy to
do, and nsWindow::mIsVisible even currently keeps track of
VisibilityNotify events.

I would check for visibility in nsWindow::Invalidate (before the
call to gdk_window_invalidate_rect rather than when
flushing/dumping the queue).

VisibilityNotify events, however, are only sent to "viewable"
windows
http://tronche.com/gui/x/xlib/events/window-state-change/visibility.html

but it seems that changing to an un"viewable" state can be
detected through UnmapNotify events
http://tronche.com/gui/x/xlib/events/window-state-change/unmap.html

I imagine these events are provided by the X server, and so would
only indirectly be dependent on window managers, so I'd be hopeful
of reliable behavior.

It would be difficult to optimize for partially obscured windows
as I don't know of a good way to determine the visible region.
Collecting rectangles from Expose events on the window since last
completely obscured could sometimes provide a useful upper bound,
but I doubt that would be worth the effort.

dbradley

unread,
Feb 12, 2009, 9:23:52 AM2/12/09
to

You have to explicitly use it, calling BeginBufferedPaint. So at least
Gecko isn't double double buffering painting. Hopefully Geck is
smartly painting only the invalidated areas. Some applications punt
and just repaint everything.

David

Karl Tomlinson

unread,
Feb 13, 2009, 9:44:46 PM2/13/09
to
I filed https://bugzilla.mozilla.org/show_bug.cgi?id=478519 and
attached a patch that works for me (with kwin).

If others are able to test, that would be much appreciated, thanks.

0 new messages