Closed
Bug 185977
Opened 22 years ago
Closed 22 years ago
xul tree paint way too much, way too often
Categories
(Core :: XUL, defect)
Core
XUL
Tracking
()
VERIFIED
FIXED
mozilla1.4alpha
People
(Reporter: rp.moz, Assigned: janv)
Details
(Keywords: perf, Whiteboard: [adt2])
Attachments
(5 files, 4 obsolete files)
(deleted),
application/vnd.mozilla.xul+xml
|
Details | |
(deleted),
text/plain
|
Details | |
(deleted),
patch
|
Details | Diff | Splinter Review | |
(deleted),
patch
|
neil
:
review+
bryner
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
Details | Diff | Splinter Review |
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.3a) Gecko/20021216
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.3a) Gecko/20021216
It seems that the xul tree paint way too often and way too much. I discovered
this by putting some printf in
mozilla\layout\xul\base\src\tree\src\nsTreeBodyFrame.cpp in PaintCell, PaintRow
and Paint.
First of all it looks as if everything (and I mean everything) in PaintCell is
drawn twice, once on the backgroundlayer and once on the foregroundlayer. It
seemed somewhat awkward so I solved this by changing the code
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect,
aDirtyRect);
in the file nsTreeBodyFrame.cpp into
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect,
aDirtyRect);
//return;
}
in a trunk of about half a year old which I still had on my harddisk. This made
the horizontal splitter in the mailform work *really* smooth and the vertical
splitter and resizing the window were also a lot faster. So I downloaded the
latest trunk and applied the same patch but now it had only little effect. It
seems that the current trunk paint way too much and which makes this fix less
noticable.
I discovered that by adding the printf's again. The entire tree is repainted
when mouse enters and leaves the tree. You easily see this by moving your mouse
in and out of e.g. the download manager, the cpu goes high. When moving a
splitter in the mailwindow, you'll see the trees are painted *many* times,
probably because the mousecursor first enteres the tree then leaves it again and
then has to be repainted for its new size. I'm not sure if the following is the
cause, but I noticed that in mozilla\widget\src\nsWindow.cpp in ProcessMessage
on every WM_MOUSEMOVE DispatchPendingEvents() is called which results in a new
WM_PAINT message.
Reproducible: Always
Steps to Reproduce:
Assignee | ||
Comment 1•22 years ago
|
||
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect,
aDirtyRect);
return;
}
you should not do this, otherwise background layer of the twisty, image and text
will be not painted
Assignee | ||
Comment 2•22 years ago
|
||
anyway, bug 124478 may help here
Assignee | ||
Comment 3•22 years ago
|
||
Another problem here is that treebody is just one frame
So frame specific invalidations cause entire treebody to repaint.
It means that if mousein/mouseout event causes a frame to repaint
it repaints entire treebody.
It would be probably better to fix splitter to not update frames after a move
immediately and rather coalesce moves and then update at once.
Reporter | ||
Comment 4•22 years ago
|
||
Several questions:
> you should not do this, otherwise background layer of the twisty,
> image and text will be not painted
Is there an example where I can see this go wrong? I can't see any difference.
As far as I can see this is what is happening in PaintCell:
1. if backgroundlayer then paint background
2. if cell is primary then paint twisty and lines
3. paint image (which in turn paints the background again(!))
4. if cel is not a cycler then paint text or progress meter in cell
This is done for both foreground and background layer so if I don't bailout
after step 1 for the backgroundlayer, it does step 2, 3 and 4 again for the
foreground layer. That's why you don't see any difference if we return after
step 1: it's painted again on the foreground layer.
Is there a design document on why xul works the way it does? I can't really see
why it would be usefull to have 3 layers. (are these layers merged and painted
or something like that?)
Why should a frame be repainted on a mousein event in the first place? Isn't
that up the frame itself to decide whether it needs painting on a mousein? I
don't understand why the splitter is involved here. The problem also occurs in
e.g. the Download Manager where there is no splitter.
Assignee | ||
Comment 5•22 years ago
|
||
Assignee | ||
Comment 6•22 years ago
|
||
try and see by yourself
Reporter | ||
Comment 7•22 years ago
|
||
Ah thanks. I see now that some of my remarks are not true. Sorry for that.
Nevertheless, it seems rather inefficient to me that many things are calculated
twice, e.g. the metrics for each cell text, which is calculated in PaintText,
once for drawing the background and once for drawing the text. I guess that
leaves us with that and the repaint on the mousein and mouseout.
Reporter | ||
Comment 8•22 years ago
|
||
Since all the calls to nsTreeBodyFrame::Paint seem to originate from
PresShell::Paint and PresShell paint all layers one after the other:
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_BACKGROUND);
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_FLOATERS);
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_FOREGROUND);
it seemed worth a try to merge these in a single paint. I've changed the paint
methods in nsTreeBodyFrame such that they now paint everything when the
backgroundlayer is painted. This gives a small but noticable speedup.
I'm trying to find out why the mousein/mouseout causes a repaint. I have found
out that a WM_MOUSE_MOVE in nsWindow::ProcessMessage somehow gets translated
into a WM_PAINT in nsWindow::ProcessMessage, but the callstack in between only
shows some user32 calls. Is there any option I can add to the compiler that
would show the callstack correctly?
Comment 9•22 years ago
|
||
It does look like nsTreeBodyFrame::Paint could move more of the work into a
single layer, but probably foreground would be better.
The paint message is probably coming from an event enqueued during the
processing of the previous message. There's probably a style change that causes
invalidates. Break in nsViewManager::UpdateView to see what's causing it.
Assignee | ||
Comment 10•22 years ago
|
||
Rene, do you volunteer to refactor nsTreeBodyFrame to remove layers from all
paint methods ?
We should also ask hyatt what he thinks of it.
If we can eliminate layers in nsTreeBodyFrame completely it should give us real
speed boost.
Thanks for catching this.
Nominating for Buffy.
Reporter | ||
Comment 11•22 years ago
|
||
I would be delighted. This patch puts all the drawing of the tree in the
foreground layer. The patch might seem rather large but I've corrected the
indentation; what I've basically done is just remove the if (aWhichLayer ==
...) in the paint functions. The speedup is reasonable.
Comment 12•22 years ago
|
||
diff -uw would be much easier to review
Assignee | ||
Comment 13•22 years ago
|
||
I thought that we can remove aWhichLayer parameter from paint methods completely.
Comments in the code should be fixed too.
Reporter | ||
Comment 14•22 years ago
|
||
New patch
- diff -uw
- aWhichLayer parameters removed, were obsolete
- comments fixed
Attachment #109947 -
Attachment is obsolete: true
Comment 15•22 years ago
|
||
PaintDropFeedback looks like it might be a problem there, actually, since before
it painted between the two layers of the rows. Also nsLeafFrame::Paint probably
needs to be called for the background layer.
If these are problems, then there might be a need to keep layers here. If so,
you could at least get a performance win by fixing the numerous places in the
current code where we paint in both layers (e.g., in PaintCell). (However, if
layers aren't needed, it's probably better to remove them, since I may want to
remove them from the CSS rendering objects at some point.)
Reporter | ||
Comment 16•22 years ago
|
||
If my assumption is correct that the nsTreeBodyFrame::Paint is only called from
PresShell::Paint with the code in #8 it looks to me more as if the
nsLeafTree::Paint was accidently called twice, once for background and once for
foreground. Either way, nsLeafTree shouldn't be a problem.
PaintDropFeedback could indeed be a problem. Jan, do you have an example that
calls this PaintDropFeedback? Normal drag&drop doesn't call it (which works fine
by the way) and I can't figure when it is called.
Assignee | ||
Comment 17•22 years ago
|
||
I'll try to check with hyatt and make sure he is ok with this change.
Reporter | ||
Comment 18•22 years ago
|
||
This is a new patch. In PaintImage and PaintTwisty we were resolving the image
twice. This now done only once. We were also always drawing the background and
borders for an image, even if there is was none (that is in most cells the
case). Now the background and borders are only drawn if there is an image. This
makes sense since we draw no background either when there is no text.
In 99% of the cases the celltextbackgroundcolor is the same as the
cellbackgroundcolor and the same as the rowbackgroundcolor and the same as the
treebackgroundcolor. Indeed, right now we're painting the background of each
cell 4 times. If the backgroundcolor is inherited, I would think we don't need
to draw it again. Is there a way to see if we need to call
PaintBackgroundLayer?
I noticed that PaintBackgroundLayer and PaintImage both take up a big part of
the time needed to draw the tree (which is not surprising since they're both
called at least once per cell). In each of these functions a lot of information
is resolved, very time consuming. Isn't it possible to avoid calls by storing
some information closer to the tree object? E.g. if each row in the tree could
just walk through a linked list of cells containing e.g. the text,
imageContainer, image size and color. That would save a lot of time. These
values could be resolved the first time they are needed from the corresponding
DOM element. An observer could watch the DOM element and invalidate the cache
if it changes. This should be, if possible of course, in a new bug. I don't
volunteer for this one since I don't consider myself qualified for the job: I
know too little about Mozilla's internals right now and have some exams coming
up.
Any word of Hyatt yet or is he still busy with his 314 Christmas dinners? :-)
Attachment #109950 -
Attachment is obsolete: true
Comment 19•22 years ago
|
||
Nav triage team: nsbeta1+/adt3
Assignee | ||
Comment 20•22 years ago
|
||
hyatt confirmed that he is ok with this change. I'll take a look at the latest
patch once I complete high priority items on my list.
Comment 21•22 years ago
|
||
Nav triage team: Jan, under what components will users see this performance
improvement? Mail, address book, bookmarks? What else? Also, what percentage
performance improvement do you expect to see? Thanks.
Assignee | ||
Comment 22•22 years ago
|
||
Currently we paint twice, background and foreground layer separately.
When we paint we need to compute the position and size of tree pseudo elements.
While computing these values we ask a tree view to return e.g. row and cell
properties, then resolving style, etc.
The computation is many times redundant because we do it separately for
background and foreground layer.
This patch could speed up painting by 50-100% I guess. I haven't measured it
yet, but I'm sure it will help performance of all tree widgets in the product.
I was just not sure if we can make such a radical change, but I talked to hyatt
and he is ok with it too.
Assignee | ||
Comment 23•22 years ago
|
||
Ok I measured it.
Currently it takes 10 + 18 msec to paint folder pane.
It takes just 18 msec to paint folder pane with the v2 patch.
So we can save 36%
Assignee | ||
Comment 24•22 years ago
|
||
components which use the tree widget:
- mail
- addressbook
- bookmarks
- history
- preferences
- help
- JS Debugger
- DOM Inspector
- file picker
- component view
- wallet
- security
- page info
- autocomplete
Reporter | ||
Comment 25•22 years ago
|
||
And
- Download manager
- about:config
- password/cookie/image/popup/password manager
With patch v3 the gain should be even more since some expensive paints in
PaintImage are removed when there is no image.
With patch v3, the majority of the time is consumed by calls to PaintBackground.
It is called once for each tree, then for each row, then for each cell and then
for each celltext. It would be nice of we could get this out but I haven't
figured out how. Anyone a clue?
Assignee | ||
Comment 26•22 years ago
|
||
Rene, optimization of GetImage and GetImageSize sounds good, but the
implementation looks weird. That's why I didn't use that one, it even didn't
compile on my linux box. I think you should take a look at the XPCOM tutorial on
mozilla.org, especially the reference counting part and how to avoid leaks.
Assignee | ||
Comment 27•22 years ago
|
||
>With patch v3 the gain should be even more since some expensive paints in
>PaintImage are removed when there is no image.
actually, which expensive paints do you mean ?
if I'm reading your patch correctly, you just calling GetImageAndSize() instead of
GetImageSize() and GetImage()
Anyway, I think this is not so expensive since images are cached.
I'm not sure if optimization of PaintBackgroundLayer is worth a try.
Other frames in the layout call PaintSelf() which is very similar to our
PaintBackgroundLayer().
We should rather try to fix bug 113528, that would help performance even more.
Assignee | ||
Comment 28•22 years ago
|
||
ah I see, PaintImage() with your patch v3 checks for null image
yeah that will help performace too.
Reporter | ||
Comment 29•22 years ago
|
||
I'll take a look at the XPCOM tutorial soon. I admin that I'm not very familiar
with it.
I didn't mean optimizing PaintBackgroundLayer but reducing the number of calls
it. For example, for a normal all-cells-white table of 15 rows and 5 columns we
do the following:
- call PaintBackgroundLayer for the table (1x)
- call PaintBackgroundLayer for each row (15x)
- call PaintBackgroundLayer for each cell (75x)
- call PaintBackgroundLayer for each cell-text (75x)
- optionally we call PaintBackgrounLayer for every image, twisty and progressmeter
We're doing a total of 166 calls to PaintBackgroundLayer where only 1 should be
enough since all cells are white and have no border or outline. If the cell-text
knowns it has the same backgroundcolor as the cell and no border and no outline,
which is in 99% the case, it can skip PaintBackgroundLayer. That's already half
the work. If each cell would know it has the same backgroundcolor as its row and
no border and no outline then can save another 75 calls. If I have the
downloadmanager maximized, I have a 36 rows and 6 columns, totalling 1 + 36 +
36*6 + 36*6 + 36(for the images)=505 calls to PaintBackgroundLayer where 1
should be sufficient.
Jan, could you perhaps measure the potential speedup by inserting "return
NS_OK;" at the beginning of PaintBackgroundLayer and then measure the time
needed to paint the tree. The real speedup would of course be less, but just to
know if it's worth it.
With respect to the GetImageAndSize, now we are first calling GetImageSize which
internally get the image (and needs to resolve a couple of thing for that) and
then we get image ourselves. It seemed double work to, that's why the
GetImageAndSize. I'm not sure how much we win here though.
Assignee | ||
Comment 30•22 years ago
|
||
Reporter | ||
Comment 31•22 years ago
|
||
Comment on attachment 110228 [details] [diff] [review]
patch v3
Forget about that third patch. I don't know how that got so messed up. Probably
mixed up some trees I have here.
I hope to come up with a new patch and some numbers on this soon. With patch v2
I get a speedup of 33% when viewing the messagepane maximized. This value
depends somewhat on the tree you're looking at, it might be more, it might be
less.
Attachment #110228 -
Attachment is obsolete: true
Comment 32•22 years ago
|
||
Nav triage team: nsbeta1+/adt2
Reporter | ||
Comment 33•22 years ago
|
||
New patch. This avoids some unnecessary calculations.
I joined the backgroundlayer and foregroundlayer in a single run. This gave the
biggest gain. Besides that I moved some stuff to outer Paint function and
cached some stuff that was worth caching. This patch also contains a 1 byte fix
in EnsureRowIsVisible that avoids flickering when 1 line is selected and the
tree is made as small as 1 line.
Here are some numbers. I did this testing with the mailwindow maximized and the
threadpane maximized:
Without the patch:
Paint foreground layer: 57ms
Paint background layer: 75ms
Total: 132ms
With patch:
Paint: 83ms
This reduces the time needed to paint the tree by 39% (or speeds it up by 60%,
it's the same but sounds better :-).
This seems to be as good as it gets. We could scrape off a few more
milliseconds by having GetImageSize also return the image but I couldn't get
that to work. The majority of the 83ms is now consumed by the following calls:
10 ms bidiUtils->RenderText
I found that rather slow especially is you know that the call to DrawString is
so fast that I can't even measure it.
20 ms mView->GetCellText
This call is outside the tree so we can't do much about it here.
13 ms aRenderingContext->DrawImage
Seems reasonable
7 ms mView->GetCellProperties
Also outside the tree. However, this seems like a lot to me, especially if you
find out that in case of the threadpane it does nothing usefull. Perhaps I'll
fix it in another bug if possible.
7 ms in PainBackgroundLayer
This calls draws the background very often as mentioned before. Avoid this call
would however require copying part of the logic in PaintBackgroundWithSc if we
don't want to break things so I've skipped this for now.
The rest of the time is consumed by other stuff which is consuming only little
time.
Reporter | ||
Comment 34•22 years ago
|
||
Comment on attachment 112696 [details] [diff] [review]
patch v4
Requesting r= and sr=
The gain is 37%, not 39%. Sorry for smuggling 2 percent.
Attachment #112696 -
Flags: superreview?(dbaron)
Attachment #112696 -
Flags: review?(varga)
Updated•22 years ago
|
Keywords: mozilla1.3
Assignee | ||
Updated•22 years ago
|
Target Milestone: --- → mozilla1.4alpha
Comment 36•22 years ago
|
||
Comment on attachment 112696 [details] [diff] [review]
patch v4
Bah, this will conflict with my patch to bug 120734
Reporter | ||
Comment 37•22 years ago
|
||
Apart from that we're poking around in the same piece of code I don't see any
real problem here or am I missing something? If you deflate the textRect again
after the PaintBackgroundLayer everything should alright, right?
Comment 38•22 years ago
|
||
So this is ready for 1.4a :)
Assignee | ||
Comment 39•22 years ago
|
||
yeah, but I need to go through this patch and make sure everything is ok
give me some time
Reporter | ||
Comment 40•22 years ago
|
||
I hope recent checkins didn't break the patch.
http://bonsai.mozilla.org/cvslog.cgi?file=mozilla/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
Assignee | ||
Comment 41•22 years ago
|
||
Assignee | ||
Comment 42•22 years ago
|
||
Assignee | ||
Updated•22 years ago
|
Attachment #116649 -
Flags: superreview?(bryner)
Attachment #116649 -
Flags: review?(neil)
Assignee | ||
Updated•22 years ago
|
Attachment #112696 -
Attachment is obsolete: true
Attachment #112696 -
Flags: superreview?(dbaron)
Attachment #112696 -
Flags: review?(varga)
Assignee | ||
Comment 43•22 years ago
|
||
Rene, there were some changes in your patch4 which I didn't like or were not
quite right.
1. you added additional 6 member variables, each 4 bytes
I measured the the caching effect and it was around 1%.
I don't think it's a worth, especially when you increased mem footprint by 24
bytes. Keep in mind that XUL trees can be used as HTML selects in future (aka
XBL Forms). So memory footprint is also important here.
Btw, names of member variables should be preceded by 'm' character
2. I was told it's not a good idea to remove parenthesis for one line "if
statements"
3. IBMBIDI define removed ?
4. Can you explain that "<" -> "<=" change in EnsureRowIsVisible ?
5. Painting of drop feedback was not updated as dbaron pointed out
6. You forgot to call nsLeafBoxFrame::Paint for background layer as dbaron
pointed out
My patch only contains the removal of layers with all necessary changes (5, 6).
And I also refactored painting of drop feedback to match the style in this file.
Thanks for initial patch.
Status: NEW → ASSIGNED
Reporter | ||
Comment 44•22 years ago
|
||
Ad 1. I went for speed but if you think footprint is in this case more
important, no problem.
Ad 2. I didn't know that, sorry.
Ad 3. Yes, ifdef IBMBIDI was removed as part of bug 89203. See especially #46
and beyond.
Ad 4. Yes I can. If Mozilla is in Modern theme, go to the mail window and select
the first message in the threadpane. Now resize the threadpane and make it
smaller than 1 line. You'll see the line flicker. It'll jump back and forth
between the first and second item in the list. This fixes that problem.
Ad 5. I know but I couldn't get PaintDropFeedback get called (is it really still
in use?) and I couldn't really figure out what it was doing but since it seemed
not to draw over parts drawn by the foreground I figured it should be fine.
Ad 6. Oops, sorry.
Assignee | ||
Comment 45•22 years ago
|
||
>Ad 3. Yes, ifdef IBMBIDI was removed as part of bug 89203. See especially #46
>and beyond.
I see, but we shouldn't mix it with this bug, feel free to file a new one.
>Ad 4. Yes I can. If Mozilla is in Modern theme, go to the mail window and >select
>the first message in the threadpane. Now resize the threadpane and make it
>smaller than 1 line. You'll see the line flicker. It'll jump back and forth
>between the first and second item in the list. This fixes that problem.
Again, feel free to file a new bug for this issue.
>Ad 5. I know but I couldn't get PaintDropFeedback get called (is it really >still
>in use?) and I couldn't really figure out what it was doing but since it seemed
>not to draw over parts drawn by the foreground I figured it should be fine.
Yes, it's called, for example in bookmarks sidebar or bookmarks manager. It's
the line painted in between rows when you drag an item.
Comment 46•22 years ago
|
||
Comment on attachment 116649 [details] [diff] [review]
new patch
Doesn't break anything.
Attachment #116649 -
Flags: review?(neil) → review+
Updated•22 years ago
|
Attachment #116649 -
Flags: superreview?(bryner) → superreview+
Assignee | ||
Comment 47•22 years ago
|
||
checked in
Status: ASSIGNED → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Comment 48•21 years ago
|
||
Marking verified (rs). Checked in a long time ago and no comments since the checkin
Status: RESOLVED → VERIFIED
Component: XP Toolkit/Widgets: Trees → XUL
QA Contact: shrir → xptoolkit.widgets
You need to log in
before you can comment on or make changes to this bug.
Description
•