Closed
Bug 107827
Opened 23 years ago
Closed 23 years ago
Mac is over-painting during page load
Categories
(Core :: XUL, defect)
Tracking
()
RESOLVED
FIXED
mozilla0.9.8
People
(Reporter: bryner, Assigned: sfraser_bugs)
Details
(Keywords: perf)
Attachments
(2 files)
Spun off from bug 97895.
Invalidates happening on hidden nsWindows during paint suppression are causing
painting to happen on visible widgets. This is because of the way Invalidates
are translated to coordinates relative to the toplevel window, losing the key
information of exactly which widget was invalidated.
Simon, feel free to reassign as needed (but not to me, I don't have a Mac).
Assignee | ||
Comment 1•23 years ago
|
||
So I'm trying to figure out how we can make this work on Mac, and not having much
success. Each widget could maintain a dirty region, and we could intersect this
with incoming updates to decide what to paint. But that doesn't do much for
widgets with large overlapping areas, which is most of the problem.
I really think a cleaner solution to this is to keep widgets/views for hidden
content viewers really hidden, as I described in bug 97895.
Status: NEW → ASSIGNED
Assignee | ||
Comment 3•23 years ago
|
||
I don't think so.
I did think of one thing I could do. I could give each widget a 'pending update'
flag, and then only draw a widget in response to an update event if the flag is
set. That way, we may invalidate large areas of the screen, but we could avoid
repainting it all unnecessarily.
Assignee | ||
Comment 4•23 years ago
|
||
Well, the 'pending update' flag doesn't work, because I can't tell if the
incoming update event was OS-generated (e.g. window exposed) or is the result of
an earlier Invalidate() call. So I can't ignore updates based on whether the
window has had any known invalidates.
Comment 5•23 years ago
|
||
If you need to know whether it is an OS event, you can pass a boolean to
HandleUpdateEvent(), false by default and true when called from
nsMacEventHandler::UpdateEvent().
Assignee | ||
Comment 6•23 years ago
|
||
That's not it; we'll get update events from the OS as a result of two kinds of
invalidates:
1. Internal calls to InvalRect/Rgn
2. OS-level events like window expose (Windowshade, user unobscuring part of the
window).
and, when we receive the update event, we can't distinguish its cause(s). This
means that we can't know to ignore udpate events that happen as a result of
invalidates on certain widgets.
Comment 7•23 years ago
|
||
I tried a solution where when we call Invalidate(rect) of Invalidate(rgn) for an
invisible widget, we accumulate the rect or the rgn into a member variable. When
the widget is made visible, the whole area is really invalidated.
It doesn't work. When loading a simple page, we have only 3 such calls. They
happen right before "Document loaded successfully" but by that time we have
already repainted the old page. For those familiar with the traces from bug
69010, here is what it looks like (formatted to 80 chars/line):
----
UpdateWidget() parent rect top=68 left=10 width=737 height=372
New code updating visible level 1 rect top=68 left=10 width=737 height=372
New code NOT updating visible level 2 rect top=15 left=273 width=16 height=16
New code updating visible level 2 rect top=0 left=0 width=737 height=372
New code updating visible level 3 rect top=0 left=0 width=737 height=372
New code updating INVISIBLE level 4 rect top=0 left=0 width=737 height=372
New code updating visible level 4 rect top=0 left=0 width=737 height=372
New code updating visible level 5 rect top=0 left=0 width=722 height=372
New code NOT updating INVISIBLE level 2 rect top=15 left=273 width=0 height=0
Invalidating invisible rect top=68 left=10 width=738 height=373
Invalidating invisible rect top=68 left=732 width=16 height=343
Invalidating invisible rect top=68 left=732 width=16 height=373
Document resource:///res/samples/test0.html loaded successfully
----
These 3 calls corresponds to (1) the entire content area, (2) the scrollbar
without the arrows and (3) the scrollbar with the arrows. The old page is
repainted when we update the "level 5" widget, before these 3 calls are made.
I'll look into it again after the holydays.
Comment 8•23 years ago
|
||
If someone wants to play with it between Xmas and new year's eve...
Note: this patch contains the test code from bug 69010
Assignee | ||
Comment 9•23 years ago
|
||
This patch adds code to not do invalidates on widgets which are themselves
invisible, or which have a parent that is not visible. This avoids invalidating
widgets in a hierarchy for which paint suppression is active.
My tests show that this patch reduces page load time from 1470ms to 1280ms
(about 7%)
Assignee | ||
Comment 10•23 years ago
|
||
Ooops, patch contains some paint debugging changes too. Ignore those, and the
whacky spacing.
Comment 11•23 years ago
|
||
a cleaner way to write this would be:
PRBool
nsWindow::ContainerHierarchyIsVisible()
{
nsCOMPtr<nsIWidget> theParent = dont_AddRef(GetParent());
while ( theParent ) {
PRBool visible;
theParent->IsVisible(visible);
if (!visible)
return PR_FALSE;
nsIWidget* grandparent = theParent->GetParent();
theParent = dont_AddRef(grandparent);
}
return PR_TRUE;
}
right?
Comment 12•23 years ago
|
||
IMO, the patch works because the View manager keeps a separate list of the
invalidates that must be done on invisible widgets (if I understood correctly
what Kevin explained me last week). However to make the Mac toolkit really
behave like other platforms, we probably should mix the 2 patches above and
accumulate invalidates on invisible widgets like in attachment #62601 [details] [diff] [review] but using
the definition of invisible widget from attachment #65398 [details] [diff] [review].
Assignee | ||
Comment 13•23 years ago
|
||
Update on perf numbers:
Current trunk build page load: ~1500
Trunk with patch in attachment 65398 [details] [diff] [review]: ~1260
That's about 6%.
Comment 14•23 years ago
|
||
You're too modest: 1260/1500 = 0.84, that's a 16% (sixteen!) percent improvement.
What a beautiful day.
I'll attach a patch that does what I described in my previous comments and see if
it doesn't spoil the fun (and if it does, I guess you can check in your patch as-
is because the View manager does the right thing for us anyhow).
Assignee | ||
Comment 15•23 years ago
|
||
Time with the second patch, plus the dcone patched fixed to account
for invisible widgets (cleaned version of attachment 62519 [details] [diff] [review]) is in the 1260
ballpark, so that patch doesn't really help performance for the pages we're
testing.
Assignee | ||
Comment 16•23 years ago
|
||
If I can get an sr, I've love to check this in for 0.9.8. Hyatt?
Keywords: perf
Target Milestone: --- → mozilla0.9.8
Comment 17•23 years ago
|
||
Probably obvious, but I assume you don't want to leave these uncommented:
-//#define PAINT_DEBUGGING // flash areas as they are painted
-//#define INVALIDATE_DEBUGGING // flash areas as they are invalidated
+#define PAINT_DEBUGGING // flash areas as they are painted
+#define INVALIDATE_DEBUGGING // flash areas as they are invalidated
Fix the indentation in
ContainerHierarchyIsVisible()
sr=hyatt
Assignee | ||
Comment 18•23 years ago
|
||
This patch takes pageload time from 960 to 768ms on an optimized Mach-O build
(20%)
Comment 19•23 years ago
|
||
with hyatt's suggestion a=asa (on behalf of drivers).
Keywords: mozilla0.9.8+
Assignee | ||
Comment 20•23 years ago
|
||
Checked in.
Status: ASSIGNED → RESOLVED
Closed: 23 years ago
Resolution: --- → FIXED
Comment 21•23 years ago
|
||
Simon, your change affects only two functions:
nsWindow::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
I think you should look into these ones too to see if they are sometimes called
when the container hierarchy is not visible:
nsWindow::Validate()
nsWindow::Update()
nsWindow::HandleUpdateEvent(RgnHandle regionToValidate)
nsWindow::UpdateWidget(nsRect& aRect, nsIRenderingContext* aContext)
nsWindow::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
nsWindow::FindWidgetHit(Point aThePoint)
nsWindow::Show(PRBool bState)
Note: nsWindow::Show() should still update |mVisible| if the container hierarchy
is not visible
Otherwise I talked to Kevin again and there is no need to merge my patch that
accumulates invalidates for invisible widgets, even for the sake of correctness.
Assignee | ||
Comment 22•23 years ago
|
||
In my build I added an assertion in UpdateWidget() if it was ever called on a
widget with an invisible ancestor, and that was never the case. I didn't test the
other methods you list; we could throw some assertions in for testing, I suppose.
Comment 23•23 years ago
|
||
I suggested to check the visibility of the hierarchy not as much for a question
of performance (I guess there isn't anything else to grab beyond 16-20% :-) as
for a question of correctness. We are a bit too exposed to changes in the
Compositor. Validate() especially could be a problem, and maybe FindWidgetHit()
too if a UI widget someday does weird things with widget visibility.
In all the above functions, we should really be using
ContainerHierarchyIsVisible() instead of mVisible. Some assertions would be nice
to have indeed. It is not worth replacing mVisible everywhere if we know that
the function always returns true in the current code base.
Assignee | ||
Comment 24•23 years ago
|
||
Note that ContainerHierarchyIsVisible() doesn't check "this->mVisible"; it just
starts looking at the parent. Maybe it should be changed to check mVisible too.
Comment 25•23 years ago
|
||
Agreed. A small patch for this cleanup + add some assertions would be nice. No
need to open a new bug report.
Comment 26•23 years ago
|
||
Important note for the Mac folks....
Kevin warned me that future performance improvements with regard to the painting
on the Mac may no longer be noticeable if we beat the paint suppression timer on
most pages. It would be interesting to do the same test as jrgm did in bug 34887
comment #75 and see how many pages now beat the timer on an average machine. It
used to be 33 out of 40.
Comment 27•23 years ago
|
||
i would hardly call any machine hooked up to the netscape network an average
machine. Our pipeline is one of the largest and fastest in the world.
think about modem users who will most definately _never_ beat the paint
suppression timer.
Comment 28•23 years ago
|
||
End-users don't run the page load tests. I should have written "an average
Netscape developer machine". In fact, it would be even more interesting to check
it on a super-duper Netscape developer machine because if the super-duper machine
shows that we always beat the timer, we'll know that we can no longer rely on the
page load tests to measure performance in painting.
Comment 29•23 years ago
|
||
I checked this with current trunk builds on win2k and Mac, running with 0ms
page to page delay time:
"Cached" case:
Mac OS9.1, 450MHz, 256MB, page times: avg 2.2s, max 6.0s, painted: 15 of 40
win2k, 500MHz, 128MB, page times: avg 1.2s, max 3.5s, painted: 8 of 40
"Fully Uncached" case:
Mac OS9.1, 450MHz, 256MB, page times: avg 2.2s, max 6.0s, painted: 17 of 40
win2k, 500MHz, 128MB, page times: avg 1.2s, max 3.5s, painted: 11 of 40
Note: the win2k build formerly did not paint any pages. That was wrong at the
time since some of the pages clearly take longer than the paint suppression
threshold time to elapse. I don't know if this was specifically fixed in the
interim, or if it is just better behaved now for some unknown reason.
You need to log in
before you can comment on or make changes to this bug.
Description
•