Bug 71368 - Switch SVGImage cache to store ImageBuffers instead of whole SVGImages, including a DOM/Render tree
Summary: Switch SVGImage cache to store ImageBuffers instead of whole SVGImages, inclu...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: SVG (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nikolas Zimmermann
URL:
Keywords:
Depends on:
Blocks: 71226 71253 71793
  Show dependency treegraph
 
Reported: 2011-11-02 08:58 PDT by Nikolas Zimmermann
Modified: 2012-01-10 15:08 PST (History)
8 users (show)

See Also:


Attachments
Patch v1 (116.76 KB, patch)
2011-11-05 16:06 PDT, Nikolas Zimmermann
gyuyoung.kim: commit-queue-
Details | Formatted Diff | Diff
Patch v2 (120.70 KB, patch)
2011-11-06 09:22 PST, Nikolas Zimmermann
koivisto: review+
webkit.review.bot: commit-queue-
Details | Formatted Diff | Diff
Fix an obvious error leading to canvas regression (2.55 KB, patch)
2011-11-08 03:43 PST, Nikolas Zimmermann
zherczeg: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Nikolas Zimmermann 2011-11-02 08:58:51 PDT
The current approach of having N-SVGImages, including N views/dom/render trees is tricky, as we have multiple SVGImageChromeClients whose invalidateContentsAndWindow() calls all inform the same ImageObserver, the CachedImage. Any change of any of the documents in the SVGImage cache cause imageChanged() calls in RenderImage, leading to races.

Another side-effect of using multiple SVGImages, is that animations restart each time the document is zoomed.
This is very likely to cause of the regressions, see bug 71226 & 71253, that bug 47156 caused.

I've now switched this again to use a single SVGImage, but cache ImageBuffers at different sizes and create Images from them, if needed.
If a resource is now shared by eg. two consumers (say <html:img> and background-image of a <div>), and if it contains animations following happens now: the FrameView::layout() thats triggered from the embedded documents layout timer, marks the ImageBuffer in the CachedImage dirty and RenderImage::imageChanged() / RenderBox::imageChanged() both trigger repaints of themselves in the host document). Once the host document repaints,  the ImageBuffers are cleared and repainted using SVGImage::drawSVGToImageBuffer(), which takes a snapshot of the current frame.  This doesn't cause any recreation of ImageBuffers and/or SVGImages anymore.

This will also cleanup the code a lot. Patch will come soon.
Comment 1 Nikolas Zimmermann 2011-11-05 16:06:13 PDT
Created attachment 113764 [details]
Patch v1

This should fix the SVGImage repainting regressions that chromium/Qt see and also speedup SVGImage painting a lot.
Uploading early to get EWS feedback...
Comment 2 Gyuyoung Kim 2011-11-05 16:19:20 PDT
Comment on attachment 113764 [details]
Patch v1

Attachment 113764 [details] did not pass efl-ews (efl):
Output: http://queues.webkit.org/results/10334386
Comment 3 WebKit Review Bot 2011-11-05 17:44:38 PDT
Comment on attachment 113764 [details]
Patch v1

Attachment 113764 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://queues.webkit.org/results/10330327

New failing tests:
svg/as-background-image/svg-as-background-4.html
svg/as-image/img-preserveAspectRatio-support-1.html
css2.1/20110323/background-intrinsic-004.htm
svg/as-image/img-preserveAspectRatio-support-2.html
svg/as-background-image/svg-as-background-2.html
css2.1/20110323/background-intrinsic-005.htm
svg/as-background-image/svg-as-background-with-relative-size.html
svg/as-image/animated-svg-as-image.html
svg/as-border-image/svg-as-border-image.html
svg/as-background-image/svg-background-partial-redraw.html
svg/as-background-image/svg-as-background-1.html
svg/as-background-image/svg-as-background-3.html
svg/as-image/animated-svg-as-image-same-image.html
svg/as-image/animated-svg-as-image-no-fixed-intrinsic-size.html
svg/as-background-image/background-image-preserveaspectRatio-support.html
Comment 4 Nikolas Zimmermann 2011-11-06 09:22:38 PST
Created attachment 113785 [details]
Patch v2

Attempt to fix win/efl builds. Fix for win: copy forwarding headers from svg/graphics. Fix for efl: add svg/graphics to WebKit include dirs, as CachedImage.h includes SVGImageCache.h. Add some more information to the ChangeLog, to aid in reviewing.

I'm not sure about the failures cr-linux EWS reports. It passes pixel tests w/o problems on Mac 10.6.
Can someone get these results for me? I'd be happy to check them first.
Comment 5 Adam Barth 2011-11-06 09:26:10 PST
Have you tried building and running the Chromium port yourself (on Mac)?  Just pass --chromium to update-webkit, build-webkit, and new-run-webkit-tests.
Comment 6 Nikolas Zimmermann 2011-11-06 10:07:23 PST
(In reply to comment #5)
> Have you tried building and running the Chromium port yourself (on Mac)?  Just pass --chromium to update-webkit, build-webkit, and new-run-webkit-tests.
I rather hoped someone could grab it from the bots.
It's been a while since I built chromium on linux, I never tried it on mac. Can I use the same source tree that I use for building cg/mac, or will this mess up my regular builds?
Comment 7 Adam Barth 2011-11-06 11:50:55 PST
It shouldn't disturb any other builds.

The problem is that having me ssh into the bots, zip up the results, and host them on my personal web site doesn't really scale.
Comment 8 WebKit Review Bot 2011-11-06 21:25:00 PST
Comment on attachment 113785 [details]
Patch v2

Attachment 113785 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://queues.webkit.org/results/10331579

New failing tests:
svg/as-background-image/svg-as-background-4.html
svg/as-image/img-preserveAspectRatio-support-1.html
css2.1/20110323/background-intrinsic-004.htm
svg/as-image/img-preserveAspectRatio-support-2.html
svg/as-background-image/svg-as-background-2.html
css2.1/20110323/background-intrinsic-005.htm
svg/as-background-image/svg-as-background-with-relative-size.html
svg/as-image/animated-svg-as-image.html
svg/as-border-image/svg-as-border-image.html
svg/as-background-image/svg-background-partial-redraw.html
svg/as-background-image/svg-as-background-1.html
svg/as-background-image/svg-as-background-3.html
svg/as-image/animated-svg-as-image-same-image.html
svg/as-image/animated-svg-as-image-no-fixed-intrinsic-size.html
svg/as-background-image/background-image-preserveaspectRatio-support.html
Comment 9 Nikolas Zimmermann 2011-11-07 05:02:34 PST
(In reply to comment #7)
> It shouldn't disturb any other builds.
> 
> The problem is that having me ssh into the bots, zip up the results, and host them on my personal web site doesn't really scale.

Indeed build-webkit --chromium && nrwt --chromium --tolerance 0 -p works great on Mac. I could reproduce the exact list of failures that the both returned, and phew these are all marginal changes, almost not visible, reported as 0% by DRT. This is a side-effect that we're now drawing ImageBuffers on to context instead of SVG documents through RenderView & friends. Green light from chromium, doesn't break anything there.
Comment 10 Antti Koivisto 2011-11-07 07:32:37 PST
Comment on attachment 113785 [details]
Patch v2

View in context: https://bugs.webkit.org/attachment.cgi?id=113785&action=review

r=me as this seems to be an improvement. 

I like introduction of the bitmap cache for svg images though I'm rather unhappy that this is done as regression fix instead of as no-behavior-changes performace patch.

Please think carefully if this is really the architecture you need. The FIXMEs here indicate that this still leaves major issues unsolved.

> Source/WebCore/ChangeLog:11
> +        Fix regressions/races introduced by r98852. SVGImage repainting didn't work under certain circumstences.
> +        Avoid creating multiple SVGImages and caching them for different sizes/zoom levels. Introduce SVGImageCache
> +        which holds rendered versions of the SVGImage at certain sizes/zoom levels. It holds (ImageBuffer, Image) pairs
> +        for each renderer, associated with a size and zoom level.

You should explain why and when the repainting didn't work and how this patch fixes the problem.

This patch introduces bitmap caching for SVG images, a major change. You should talk more about performance and memory implications.

> Source/WebCore/page/DragController.cpp:659
> +    // Don't use cachedImage->imageForRenderer() here as that may return BitmapImages for cached SVG Images.
> +    // Users of getImage() want access to the SVGImage, in order to figure out the filename extensions,
> +    // which would be empty when asking the cached BitmapImages.
>      return (cachedImage && !cachedImage->errorOccurred()) ?

It sounds like this code should be refactored or at least renamed (getImageForFileNameExtension()?) as it does surpising things. 

Does some test cover this?

> Source/WebCore/rendering/ImageBySizeCache.cpp:68
> -Image* ImageBySizeCache::getImage(const RenderObject* renderer, const IntSize& size, float zoom)
> +Image* ImageBySizeCache::getImage(const RenderObject* renderer, const IntSize& size)

Since ImageBySizeCache now has only one client, I think it should be merged back to CSSImageGeneratorValue. It is bit too confusing as a standalone class without clear code sharing purpose.

> Source/WebCore/svg/graphics/SVGImage.cpp:157
> +    // FIXME: This doesn't work correctly with animations. If an image contains animations, that say run for 2 seconds,
> +    // and we currently have one <img> that displays us. If we open another document referencing the same SVGImage it
> +    // will display the document at a time where animations already ran - even though it has its own ImageBuffer.
> +    // We currently don't implement SVGSVGElement::setCurrentTime, and can NOT go back in time, once animations started.
> +    // There's no way to fix this besides avoiding style/attribute mutations from SVGAnimationElement.

How do you know that fixing this rather major issue won't require rearchitecting everything yet again?

> Source/WebCore/svg/graphics/SVGImageCache.cpp:95
> +void SVGImageCache::redrawTimerFired(Timer<SVGImageCache>*)
> +{

I find it strange that a cache class has a redraw timer. Seems like someone else should take care of making the calls async.

> Source/WebCore/svg/graphics/SVGImageCache.h:50
> +    void setRequestedSizeAndZoom(const RenderObject*, const IntSize&, float zoom);
> +    void getRequestedSizeAndZoom(const RenderObject*, IntSize*, float* zoom);

Since you define a type of size/zoom pair, you should use it everywhere.

> Source/WebCore/svg/graphics/SVGImageCache.h:59
> +    struct CachedSizeAndZoom {

Word "Cached" adds no information as this type is already scoped to "SVGImageCache"

> Source/WebCore/svg/graphics/SVGImageCache.h:75
> +    struct CachedImageData {

Same here.

> Source/WebCore/svg/graphics/SVGImageCache.h:83
> +        CachedImageData(ImageBuffer* newBuffer, PassRefPtr<Image> newImage, const IntSize& newSize, float newZoom)

Could use SizeAndZoom type.

> Source/WebCore/svg/graphics/SVGImageCache.h:94
> +        float zoom;
> +        IntSize size;

again.

> Source/WebCore/svg/graphics/SVGImageCache.h:105
> +    typedef HashMap<const RenderObject*, CachedSizeAndZoom> CachedSizeAndZoomMap;
> +    typedef HashMap<const RenderObject*, CachedImageData> CachedImageDataMap;
> +
> +    SVGImage* m_svgImage;
> +    CachedSizeAndZoomMap m_cachedSizeAndZoomMap;
> +    CachedImageDataMap m_cachedImageDataMap;

Word "Cached" adds no information as this is already scoped to "SVGImageCache"
Comment 11 Nikolas Zimmermann 2011-11-07 09:45:25 PST
(In reply to comment #10)
> r=me as this seems to be an improvement. 
Thanks. 

> I like introduction of the bitmap cache for svg images though I'm rather unhappy that this is done as regression fix instead of as no-behavior-changes performace patch.
I tried to fix the regression w/o doing the bitmap cache switch, and it turned out it would need a major redesign of ImageBySizeCache, which I wanted to avoid, as when introducing the bitmap cache we wouldn't need it any longer.

> 
> Please think carefully if this is really the architecture you need. The FIXMEs here indicate that this still leaves major issues unsolved.
As discussed on IRC, fixing the FIXME requires more work on SMIL to stop SVG animations from mutating attributes/properties, which makes it impossible to reset animations and/or seek to a different time. The SVGImageCache will stay as-is though.
 
> > Source/WebCore/ChangeLog:11
> > +        Fix regressions/races introduced by r98852. SVGImage repainting didn't work under certain circumstences.
> > +        Avoid creating multiple SVGImages and caching them for different sizes/zoom levels. Introduce SVGImageCache
> > +        which holds rendered versions of the SVGImage at certain sizes/zoom levels. It holds (ImageBuffer, Image) pairs
> > +        for each renderer, associated with a size and zoom level.
> 
> You should explain why and when the repainting didn't work and how this patch fixes the problem.
> 
> This patch introduces bitmap caching for SVG images, a major change. You should talk more about performance and memory implications.
Ok. I will update it.

> 
> > Source/WebCore/page/DragController.cpp:659
> > +    // Don't use cachedImage->imageForRenderer() here as that may return BitmapImages for cached SVG Images.
> > +    // Users of getImage() want access to the SVGImage, in order to figure out the filename extensions,
> > +    // which would be empty when asking the cached BitmapImages.
> >      return (cachedImage && !cachedImage->errorOccurred()) ?
> 
> It sounds like this code should be refactored or at least renamed (getImageForFileNameExtension()?) as it does surpising things. 
> 
> Does some test cover this?
This code used cachedImage->image() before, and I made it use imageForRenderer(), this reverts it again. Other call sites don't use imageForRenderer() if they want to query the file name extension.
The testcase svg/as-image/drag-svg-as-image.html hits an assertion w/o that change.

> > Source/WebCore/rendering/ImageBySizeCache.cpp:68
> > -Image* ImageBySizeCache::getImage(const RenderObject* renderer, const IntSize& size, float zoom)
> > +Image* ImageBySizeCache::getImage(const RenderObject* renderer, const IntSize& size)
> 
> Since ImageBySizeCache now has only one client, I think it should be merged back to CSSImageGeneratorValue. It is bit too confusing as a standalone class without clear code sharing purpose.
I will do that in a follow-up patch.

> > Source/WebCore/svg/graphics/SVGImage.cpp:157
> > +    // FIXME: This doesn't work correctly with animations. If an image contains animations, that say run for 2 seconds,
> > +    // and we currently have one <img> that displays us. If we open another document referencing the same SVGImage it
> > +    // will display the document at a time where animations already ran - even though it has its own ImageBuffer.
> > +    // We currently don't implement SVGSVGElement::setCurrentTime, and can NOT go back in time, once animations started.
> > +    // There's no way to fix this besides avoiding style/attribute mutations from SVGAnimationElement.
> 
> How do you know that fixing this rather major issue won't require rearchitecting everything yet again?
I can not guarantee it unless I prototype a fix for that. Note that problem is present since the beginning of the SVGImage introduction, I just named it now :-)

> 
> > Source/WebCore/svg/graphics/SVGImageCache.cpp:95
> > +void SVGImageCache::redrawTimerFired(Timer<SVGImageCache>*)
> > +{
> 
> I find it strange that a cache class has a redraw timer. Seems like someone else should take care of making the calls async.
I could only move the timer to CachedImage, not sure if thats an improvement.
Maybe it should be renamed to m_updateCacheTimer? or m_updateTimer?

> > Source/WebCore/svg/graphics/SVGImageCache.h:50
> > +    void setRequestedSizeAndZoom(const RenderObject*, const IntSize&, float zoom);
> > +    void getRequestedSizeAndZoom(const RenderObject*, IntSize*, float* zoom);
> 
> Since you define a type of size/zoom pair, you should use it everywhere.
Fixed.

> > Source/WebCore/svg/graphics/SVGImageCache.h:59
> > +    struct CachedSizeAndZoom {
> 
> Word "Cached" adds no information as this type is already scoped to "SVGImageCache"
Fixed.

> > Source/WebCore/svg/graphics/SVGImageCache.h:75
> > +    struct CachedImageData {
> 
> Same here.
Fixed.

> > Source/WebCore/svg/graphics/SVGImageCache.h:83
> > +        CachedImageData(ImageBuffer* newBuffer, PassRefPtr<Image> newImage, const IntSize& newSize, float newZoom)
> 
> Could use SizeAndZoom type.
Fixed.

> > Source/WebCore/svg/graphics/SVGImageCache.h:94
> > +        float zoom;
> > +        IntSize size;
> 
> again.
Fixed.

> > Source/WebCore/svg/graphics/SVGImageCache.h:105
> > +    typedef HashMap<const RenderObject*, CachedSizeAndZoom> CachedSizeAndZoomMap;
> > +    typedef HashMap<const RenderObject*, CachedImageData> CachedImageDataMap;
> > +
> > +    SVGImage* m_svgImage;
> > +    CachedSizeAndZoomMap m_cachedSizeAndZoomMap;
> > +    CachedImageDataMap m_cachedImageDataMap;
> 
> Word "Cached" adds no information as this is already scoped to "SVGImageCache"
Fixed.
Comment 12 Nikolas Zimmermann 2011-11-08 01:41:10 PST
Landed in r99539, now watching bots to rebaseline tests, once results appear.
Comment 13 Nikolas Zimmermann 2011-11-08 03:30:22 PST
A regression appeared: fast/canvas/svg-taint.html and other canvas related taint checks.
The root of this is using imageForRenderer(), instead of CachedImage. I'm fixing it.
Comment 14 Nikolas Zimmermann 2011-11-08 03:43:15 PST
Created attachment 114031 [details]
Fix an obvious error leading to canvas regression

This fixes a regression with the canvas-taint tests on several bots.
Comment 15 Zoltan Herczeg 2011-11-08 03:45:02 PST
Comment on attachment 114031 [details]
Fix an obvious error leading to canvas regression

r=me
Comment 16 Nikolas Zimmermann 2011-11-08 03:47:06 PST
Thanks Zoltan! Landed fix in r99543. Should make gtk/qt happy again.
Comment 17 Nikolas Zimmermann 2011-11-08 03:55:12 PST
(In reply to comment #16)
> Thanks Zoltan! Landed fix in r99543. Should make gtk/qt happy again.
Oops, an unused variable remained, fixed in r99544.
Comment 18 Nikolas Zimmermann 2011-11-08 06:28:51 PST
Remaining errors are probably flakey testcases, further investigation continues in 71793.
Mac/Gtk and friends work fine, so I'm closing this bug.