Closed
Bug 19256
Opened 26 years ago
Closed 24 years ago
Excess frame redrawing during incremental reflow
Categories
(Core Graveyard :: GFX, defect, P2)
Tracking
(Not tracked)
VERIFIED
FIXED
mozilla0.9
People
(Reporter: sfraser_bugs, Assigned: kmcclusk)
References
()
Details
(Keywords: highrisk, perf, Whiteboard: Waiting for super-review/review)
Attachments
(2 files)
|
9.11 KB,
patch
|
Details | Diff | Splinter Review | |
|
12.93 KB,
patch
|
Details | Diff | Splinter Review |
While loading a long, simple document (just <Hn> and <P>), we continually redraw
frames that are visible in the window, while appending content which only affects
frames that are not visible.
It seems like this should be optimized to not redraw visible frames which have
not been affected by appended content.
Note: turn off double-buffering to see the frames being redrawn.
Assignee: troy → beard
Severity: normal → major
Component: Layout → Compositor
Priority: P3 → P2
I don't know why you think this is layout redrawing frames that aren't visible.
This is a view manager bug introduced in revision 1.31
It used to just update the newly exposed areas of the view, and now it has been
changed to update the entire view. Both the old and new areas
That means that as the document loads and the height of the scrolled view
increases the entire visible area is painted for each and every incremental
reflow
Updated•25 years ago
|
Status: NEW → RESOLVED
Closed: 25 years ago
Resolution: --- → FIXED
Comment 2•25 years ago
|
||
Troy, this should no longer be happening. I did accidentally add some code in
v1.31 of nsViewManager.cpp that would invalidate the entire parent view. I've
since changed it to only invalidate the view in question. There are two issues:
1. If a view is moved, the old bounds and new bounds are invalidated, so any
backgrounds, or exposed overlapping views are redrawn in the old position.
2. When a view is resized, the bounding box of the old and new sizes is
invalidated, for similar reasons to 1.
One other issue, we used to only invalidate the newly exposed area of a view
when it was resized. Does layout properly invalidate the area that was already
in view if a reflow occurs? Should the view manager only be invalidating
areas that are exposed?
I believe the state of things is better than before, but perhaps we can do
even better.
| Reporter | ||
Updated•25 years ago
|
Status: RESOLVED → REOPENED
| Reporter | ||
Comment 3•25 years ago
|
||
I don't see any improvement. Reopening
| Reporter | ||
Updated•25 years ago
|
Resolution: FIXED → ---
| Reporter | ||
Comment 4•25 years ago
|
||
To see the problem, turn OFF double buffering (developer only), and load a
longish HTML document that is just a bunch of <p>s. Turn off your content timer
pref, so that you get lots of reflows (so the problem is more apparent):
user_pref("content.notify.ontimer", false);
Now load the document. Note that each time content is appended, we repaint the
visible frames, even though they have not changed.
| Reporter | ||
Comment 5•25 years ago
|
||
The problem here is that nsViewManager::ResizeView() invalidates the entire view
when the size fo the view changes. It needs to be smarter, figure out of the
widget of the view has changed, and only invalidate the new area if so.
| Reporter | ||
Comment 6•25 years ago
|
||
Here's a diff (unified, line numbers not supplied becase mine will be a little
off). This changes will cause us to only invalidate the new area of the view if
the width does not change. This assumes that adding to the bottom of a view can
never change what is displayed in the existing area.
#if 1
- // refresh the bounding box of old and new areas.
- nscoord maxWidth = (oldWidth < width ? width : oldWidth);
- nscoord maxHeight = (oldHeight < height ? height : oldHeight);
- nsRect boundingArea(x, y, maxWidth, maxHeight);
- UpdateView(parentView, boundingArea, NS_VMREFRESH_NO_SYNC);
+ // did the width change?
+ if (oldWidth != width)
+ {
+ // refresh the bounding box of old and new areas.
+ nscoord maxWidth = (oldWidth < width ? width : oldWidth);
+ nscoord maxHeight = (oldHeight < height ? height : oldHeight);
+ nsRect boundingArea(x, y, maxWidth, maxHeight);
+ UpdateView(parentView, boundingArea, NS_VMREFRESH_NO_SYNC);
+ }
+ else if (height > oldHeight) // if we got longer
+ {
+ // refresh the new area
+ nsRect newArea(x, y + oldHeight, width, y + height);
+ UpdateView(parentView, newArea, NS_VMREFRESH_NO_SYNC);
+ }
+
#else
// brute force, invalidate old and new areas. I don't understand
| Reporter | ||
Comment 7•25 years ago
|
||
With this change, and the content.timer pref OFF (hence more reflows), loading a
test page drops from 9 to 5 seconds, because of the time saved painting.
Comment 8•25 years ago
|
||
I'll test thing out saturday / sunday on linux, so we can get it into
m12.
Comment 9•25 years ago
|
||
patch is working fine on linux, I've been running with it since last night, with
no problems.
sites I've been testing, to see the improvements:
http://tinderbox.mozilla.org/SeaMonkey/warnings.html
http://www.slashdot.org
http://cvs-mirror.mozilla.org/webtools/tinderbox/showbuilds.cgi?tree=SeaMonkey&hours=12&nocrap=1
Comment 10•25 years ago
|
||
another good test page:
http://cvs-mirror.mozilla.org/webtools/bonsai/showcheckins.cgi?&treeid=SeaMonkey
Updated•25 years ago
|
Status: REOPENED → ASSIGNED
Comment 11•25 years ago
|
||
(Reminder!)
Is the patch now in and the bug effectively closed?
--Adam
| Reporter | ||
Comment 12•25 years ago
|
||
No, the patch has not been checked in. The problem is that the patch makes
assumptions about the way layout works (that, if the width of the view does not
change, you don't need to repaint the visible area), and there is no way for the
view manager to know if these assumptions are valid. Ideally, layout needs to
propagate that information back to the view system, or layout itself needs to do
smarter view invalidation when the view changes size.
Keywords: perf
Summary: [Perf] Excess frame redrawing during incremental reflow → Excess frame redrawing during incremental reflow
Comment 13•25 years ago
|
||
Moving "perf" to keyword field. This is the are we use now :-)
Comment 14•25 years ago
|
||
Handing this to kevin, who's working on redraw optimizations.
Assignee: beard → kmcclusk
Status: ASSIGNED → NEW
| Assignee | ||
Updated•25 years ago
|
Status: NEW → ASSIGNED
Target Milestone: --- → M17
| Assignee | ||
Updated•25 years ago
|
Whiteboard: nsbeta3+ may re-evaluate based on what it will take to fix
| Assignee | ||
Comment 16•25 years ago
|
||
Marking nsbeta3+
| Assignee | ||
Updated•25 years ago
|
Whiteboard: nsbeta3+ may re-evaluate based on what it will take to fix → [nsbeta3+] may re-evaluate based on what it will take to fix
| Assignee | ||
Comment 17•25 years ago
|
||
I agree with Simon's analysis. The problem is that isn't clear whether resizing
a view should cause only the newly exposed portion to be invalidated or the
entire view should be invalidated. Layout sometimes assumes the entire view will
be invalidated by the view system when the view changes size. Layout needs to be
enhanced to pass a flag to view system to indicate whether all of the view or
only the newly expanded portion of the view really needs to be invalidated. I
can not see how to easily make this change within Layout. In general the view is
simply sync'ed (i.e width, height) are made to match the dimensions of a frame.
Currently, There isn't any info within the frames which indicates whether the
reflow operation actually changed the appearance of only a portion of the view.
Marking nsbeta3-. Need to look at first thing, post RTM. This is really a layout
issue, since it is in control of what gets invalidated.
Whiteboard: [nsbeta3+] may re-evaluate based on what it will take to fix → [nsbeta3-]
Target Milestone: M17 → Future
| Assignee | ||
Comment 20•24 years ago
|
||
The layout code which calls ResizeView needs to determine if only the vertical
size is changing and if so it should resize the view without causing a repaint.
This will require adding an aPaint flag to ResizeView and modifying the layout
code to check for a height change. It may also require determining if their are
any layout objects which depend on the vertical height of the document to
determine their height.
| Assignee | ||
Comment 21•24 years ago
|
||
I hacked the viewmanager resize code to prevent the invalidate from happening on
view resizes and I ran jrgm's timing tests. It had no effect on average page
load time.
| Assignee | ||
Comment 22•24 years ago
|
||
| Assignee | ||
Comment 24•24 years ago
|
||
| Assignee | ||
Comment 25•24 years ago
|
||
patch 27067 puts things back to way they were originally. Originally, the
viewmanager used to only invalidate the newly exposed area when it was resized.
In the patch the container frame can specify this behavior by passing a new flag
to when the view is resized.
I have not seen update problems running with the patch.
| Assignee | ||
Comment 26•24 years ago
|
||
Updated status.
Whiteboard: [nsbeta3-] → Waiting for super-review/review
The comment "//and left edge overlap" should read "//and bottom edge overlap".
If aView has a widget, then updating the parent view might not repaint anything
if the child view is growing in size. In that case we'd need to update the child
view instead (or as well). So to actually do what you're trying to do, I think
you have to do this:
if (increasing width) { repaint right-hand strip in child; }
else { repaint right-hand strip in parent; }
if (increasing height) { repaint bottom strip in child; }
else { repaint bottom strip in parent; }
Forget about optimizing the corner where the bottom and right-hand strips
overlap. The dirty rects are supposed to be merged into a single region anyway.
The change to nsContainerFrame.cpp looks like a grotesque hack. What reasonable
argument can there be that "frameSize.width == width" is the correct condition?
IMHO it should always be "TRUE", and if layout needs more repainting, it should
do that explicitly somewhere else.
| Assignee | ||
Comment 28•24 years ago
|
||
"If aView has a widget, then updating the parent view might not repaint anything
if the child view is growing in size. In that case we'd need to update the child
view instead (or as well). So to actually do..."
Good catch! I see there is call to InvalidateChildWidgets(widgetView,
widgetRect); in UpdateView which would take care of this, but its part of a
large chunk of code thats been #ifdef'ed out, probably when the code to batch
the invalidates was added.
I'll change the patch to explicitly invalidate the child if it has a widget in
the ResizeView method:
"Forget about optimizing the corner where the bottom and right-hand strips
overlap. The dirty rects are supposed to be merged into a single region anyway."
I agree that the dirty rects would be merged, but the optimization eliminates
some of the calls to platform's native invalidate which is expensive even if it
doesn't result in any extra painting. If only the width or height changes then
the native invalidate is suppressed by an optimization in
nsIViewManager::UpdateView which looks for 0 width or 0 height invalidates
(Which happen if only one dimension of the view is changed). This optimization
costs us nothing, the only difference is setting:
damageRect.height = shortHeight;
instead of
damageRect.height = longHeight;
"The change to nsContainerFrame.cpp looks like a grotesque hack. What reasonable
argument can there be that "frameSize.width == width" is the correct condition?
IMHO it should always be "TRUE", and if layout needs more repainting, it should
do that explicitly somewhere else."
I agree that the layout use of this flag is a hack. Unfortunetly, Layout is not
consistent in it's invalidation of views. Some frames always invalidate their
bounds when they change size, others rely on the invalidation comming from
container changing its size. This is why Simon's original patch doesn't work.
This was discussed in some length with buster before he left, karnaze, and Marc.
The passing of the flag for only height changes takes advantage of the nature of
flow based layout where height changes don't impact the layout of previously
positioned frames, while width changes do.
Until all of the frames invalidate themselves properly in response to their size
changing. We still need to pass this flag.
This pushes the intelligence for determining what sort of invalidation is
necessary back into layout where it belongs. The view system should be flexible
enough to handle either request; invalidate the entire view when changes size,
or just the newly exposed region patch. When we have fixed all of the frame's
invalidating issues we can choose to always pass PR_TRUE, or it may not need to
invalidate the container at all if all of the child frames have invalidated
themselves properly.
| Assignee | ||
Comment 29•24 years ago
|
||
I filed bug 73825 to cover the layout issue of not invalidating the views
consistently.
| Assignee | ||
Comment 30•24 years ago
|
||
Comment 31•24 years ago
|
||
sr=attinasi
| Assignee | ||
Comment 32•24 years ago
|
||
Robert: Could you review the lastest patch? Thanks.
Sorry Kevin, I'm a total crackhead. I was wrong; there is in fact no problem
calling UpdateView on the parent view to invalidate whatever areas you want. The
code that I myself wrote guarantees that all necessary widgets will be
invalidated. So, you can always invalidate the two strips in the context of the
parent without needing the extra conditionals. Sorry!!!!
The rest looks great.
| Assignee | ||
Comment 34•24 years ago
|
||
Fix checked in.
Status: ASSIGNED → RESOLVED
Closed: 25 years ago → 24 years ago
Resolution: --- → FIXED
Comment 36•24 years ago
|
||
I still see this in the table stress case. See bug 90503. I don't know if I
should reopen this and make it a duplicate or let it be a new issue.
Updated•16 years ago
|
Product: Core → Core Graveyard
You need to log in
before you can comment on or make changes to this bug.
Description
•