Closed Bug 846028 Opened 12 years ago Closed 12 years ago

SVG images generate too many OnStopFrame calls

Categories

(Core :: Graphics: ImageLib, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla22
Tracking Status
firefox23 --- disabled

People

(Reporter: seth, Assigned: seth)

References

Details

Attachments

(1 file, 3 obsolete files)

I noticed as part of investigating bug 835658 that test_animSVGImage.html (introduced in bug 649440) generates way more VectorImage::Draw calls than I expected. These calls are happening even when nothing has changed about the underyling SVG document (ie, the current time of the SVG document is unchanged). The reason this is happening is that SVGRootRenderingObserver adds itself immediately back to the list of observers of its target in DoUpdate. That means that it receives duplicate invalidation notifications (since a single event that invalidates the underlying SVG document can generate many of them) and it calls VectorImage::InvalidateObserver for each one, generating OnStopFrame events each time. test_animSVGImage takes a screenshot each time, causing an additional call to VectorImage::Draw. The real problem is that there are too many OnStopFrame events being generated. My recommended fix is to leave SVGRootRenderingObserver out of the rendering observer list until VectorImage::Draw gets called. This seems logical to me - an SVG frame is "generated" whenever VectorImage::Draw gets called, so there should be a 1-to-1 correspondence between OnStopFrame and VectorImage::Draw.
paraphrasing IRC: { <dholbert> seth, just to be sure I understand: Are you simply proposing that we delay adding ourselves back to the list of observers until Draw gets called? <seth> correct. <dholbert> so we'd effectively be coalescing invalidates <seth> yup, exactly <dholbert> I sort of assumed that would already be happening at a higher level, I think. I'm a bit surprised that it's not. But even if this *is* supposed to happen elsewhere, this seems reasonable No sense in sending invalidations that we already know are redundant. <seth> yeah, that's my view too. }
Attachment #719202 - Flags: review?(dholbert)
Comment on attachment 719202 [details] [diff] [review] Coalesce invalidations in VectorImage. >+ void ResumeListening() >+ { >+ if (!mInObserverList) { >+ Element* elem = GetTarget(); >+ if (elem) { >+ nsSVGEffects::AddRenderingObserver(elem, this); >+ mInObserverList = true; >+ } >+ } >+ } It looks like nsSVGRenderingObserver::GetReferencedElement() basically does all of this already (by design). I wonder if we should just call that (perhaps directly from Draw), to share code? > mVectorImage->InvalidateObserver(); > } [...] >+ // We may have been removed from the observer list by our caller. Rather >+ // than add ourselves back here, we wait until Draw gets called, ensuring >+ // that we generate FrameChanged and OnStopFrame at most once per Draw call. > } I don't think that last line is quite what you want to say. Technically, we already *do* generate one FrameChanged/OnStopFrame per draw call, since they're what trigger the Draw calls. :) The problem is we're generating lots of them (and, consequently, lots of draw calls). Maybe replace that last line to say "...that we coalesce invalidations between Draw calls" >@@ -749,16 +757,20 @@ VectorImage::Draw(gfxContext* aContext, > >+ // Allow ourselves to generate FrameChanged and OnStopFrame events again. >+ if (mRenderingObserver) >+ mRenderingObserver->ResumeListening(); No need to null-check mRenderingObserver. It's initialized in OnSVGDocumentLoaded(), which is the same place we set mIsFullyLoaded to true -- and if we didn't visit that code, we'd have already bailed out of Draw right at its initial mIsFullyLoaded check. If you like, you could add MOZ_ASSERT(mRenderingObserver, "should have initialized rendering observer when document loaded"); or something to that effect, though. Also: As above, I wonder if this should just call mRenderingObserver->GetReferencedElement()? (and ignore the result)
Thanks for the review! (In reply to Daniel Holbert [:dholbert] from comment #3) > It looks like nsSVGRenderingObserver::GetReferencedElement() basically does > all of this already (by design). > > I wonder if we should just call that (perhaps directly from Draw), to share > code? Ugh, I'm not a big fan of getters that have implicit side effects like that. I guess we could use it with a comment explaining what's happening. > Maybe replace that last line to say "...that we coalesce invalidations > between Draw calls" Sounds good. > If you like, you could add MOZ_ASSERT(mRenderingObserver, "should have > initialized rendering observer when document loaded"); or something to that > effect, though. Cool. I'll switch it to an assert.
(In reply to Seth Fowler [:seth] from comment #5) > Ugh, I'm not a big fan of getters that have implicit side effects like that. Yeah... It's a bit gross, but it's nice for the code-sharing (and assertion-sharing) benefits. (It'd be nicer if GetReferencedElement() were split into a helper registration-method and a getter or something.) Maybe we could keep ResumeListening(), and just make it call GetReferencedElement() (with a one-line comment explaining why)?
(In reply to Daniel Holbert [:dholbert] from comment #6) > Maybe we could keep ResumeListening(), and just make it call > GetReferencedElement() (with a one-line comment explaining why)? Sounds like a plan.
Comment on attachment 719202 [details] [diff] [review] Coalesce invalidations in VectorImage. cool. r=me w/ that & my other notes from comment 3 addressed. Thanks!
Attachment #719202 - Flags: review?(dholbert) → review+
Updated patch. No meaningful changes to behavior, so I don't think this needs another try run. If try looks good this is ready to go.
Attachment #719202 - Attachment is obsolete: true
Comment on attachment 719293 [details] [diff] [review] Coalesce invalidations in VectorImage. Two minor nits: >+ // Allow ourselves to another FrameChanged and OnStopFrame event. I think there's a word missing there -- s/another/fire another/? >+ NS_ABORT_IF_FALSE(mRenderingObserver, "Should have initialized rendering " >+ "observer in OnSVGDocumentLoaded"); Just use MOZ_ASSERT. It's fewer characters and the New Hotness for fatal assertions. (We should probably just switch over all the NS_ABORT_IF_FALSE's in this file; feel free to file a bug on that, or maybe I will at some point.)
Thanks Daniel; I'll take care of those things. (In reply to Daniel Holbert [:dholbert] from comment #10) > Just use MOZ_ASSERT. It's fewer characters and the New Hotness for fatal > assertions. > > (We should probably just switch over all the NS_ABORT_IF_FALSE's in this > file; feel free to file a bug on that, or maybe I will at some point.) Sweet. I didn't realize MOZ_ASSERT was fatal; I thought it was one of those (IMO poorly named) "nonfatal assertions". I'll switch things over in this patch, and start using MOZ_ASSERT in the future. Proposition: you could do the switchover of the other instances as part of bug 846078. Consider that r=me in advance if you do.
Updated patch.
Attachment #719293 - Attachment is obsolete: true
Backed out for causing reftest crashes (with an assertion that I'm betting you'll find interesting). https://hg.mozilla.org/integration/mozilla-inbound/rev/7c2b8a955b46 https://tbpl.mozilla.org/php/getParsedLog.php?id=20197743&tree=Mozilla-Inbound 14:56:54 INFO - REFTEST TEST-START | file:///Users/cltbld/talos-slave/test/build/tests/reftest/tests/layout/reftests/svg/as-image/background-image-rect-1svg.html | 6721 / 9177 (73%) 14:56:54 INFO - ++DOMWINDOW == 445 (0x14790ea48) [serial = 18339] [outer = 0x1108048c8] 14:56:54 INFO - Assertion failure: mRenderingObserver (Should have initialized rendering observer in OnSVGDocumentLoaded), at ../../../image/src/VectorImage.cpp:769 14:56:56 WARNING - TEST-UNEXPECTED-FAIL | file:///Users/cltbld/talos-slave/test/build/tests/reftest/tests/layout/reftests/svg/as-image/background-image-rect-1svg.html | Exited with code 1 during test run 14:56:56 WARNING - This is a harness error. 14:56:56 INFO - INFO | automation.py | Application ran for: 0:35:54.094942 14:56:56 INFO - INFO | automation.py | Reading PID log: /var/folders/qd/srwd5f710sj0fcl9z464lkj00000gn/T/tmp2UFHtEpidlog 14:57:12 INFO - PROCESS-CRASH | file:///Users/cltbld/talos-slave/test/build/tests/reftest/tests/layout/reftests/svg/as-image/background-image-rect-1svg.html | application crashed [@ mozilla::image::VectorImage::Draw(gfxContext*, gfxPattern::GraphicsFilter, gfxMatrix const&, gfxRect const&, nsIntRect const&, nsIntSize const&, mozilla::SVGImageContext const*, unsigned int)] 14:57:12 INFO - Crash dump filename: /var/folders/qd/srwd5f710sj0fcl9z464lkj00000gn/T/tmp4tsrTz/minidumps/68ADE2E9-C2DB-433B-BA20-4F2071C5C77E.dmp 14:57:12 INFO - Operating system: Mac OS X 14:57:12 INFO - 10.7.2 11C74 14:57:12 INFO - CPU: amd64 14:57:12 INFO - family 6 model 23 stepping 10 14:57:12 INFO - 2 CPUs 14:57:12 INFO - Crash reason: EXC_BAD_ACCESS / KERN_INVALID_ADDRESS 14:57:12 INFO - Crash address: 0x0 14:57:12 INFO - Thread 0 (crashed) 14:57:12 INFO - 0 XUL!mozilla::image::VectorImage::Draw(gfxContext*, gfxPattern::GraphicsFilter, gfxMatrix const&, gfxRect const&, nsIntRect const&, nsIntSize const&, mozilla::SVGImageContext const*, unsigned int) [VectorImage.cpp:57fc2a37629f : 768 + 0x0] 14:57:12 INFO - rbx = 0x00007fff747ec630 r12 = 0x0000003200000032 14:57:12 INFO - r13 = 0x000000011e00b0d0 r14 = 0x000000011a973e10 14:57:12 INFO - r15 = 0x000000011a61aaa0 rip = 0x00000001012b770f 14:57:12 INFO - rsp = 0x00007fff5fbfb330 rbp = 0x00007fff5fbfb430 14:57:12 INFO - Found by: given as instruction pointer in context 14:57:12 INFO - 1 XUL!_ZZN7mozilla5imageL14get_header_strEPcS1_mE3hex + 0x2c081 14:57:12 INFO - rip = 0x0000000103783a92 rsp = 0x00007fff5fbfb428 14:57:12 INFO - rbp = 0x00007fff5fbfb430 14:57:12 INFO - Found by: stack scanning 14:57:12 INFO - 2 XUL!DrawImageInternal [nsLayoutUtils.cpp:57fc2a37629f : 4001 + 0x38] 14:57:12 INFO - rip = 0x0000000101392871 rsp = 0x00007fff5fbfb440 14:57:12 INFO - rbp = 0x00007fff5fbfb580 14:57:12 INFO - Found by: stack scanning
Consider the problem identified. When a VectorImage is created via ExtractFrame, it doesn't have a rendering observer. That means we should assert (mRenderingObserver || mRestrictedRegion), and we need an if after the assert instead of blinding dereferencing mRenderingObserver. New patch coming up.
Updated patch. Note that by switching back to an if statement we are back to the situation in the version of the patch that had green reftests on try, so I don't think we need another try run here.
Attachment #719623 - Attachment is obsolete: true
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla22
Depends on: 851416
Depends on: 879139
Blocks: 883416
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: