Bug 32121

Summary: [Qt] implement ImageDecoderQt::clearFrameBufferCache()
Product: WebKit Reporter: Yongjun Zhang <yongjun.zhang>
Component: New BugsAssignee: Yongjun Zhang <yongjun.zhang>
Status: RESOLVED INVALID    
Severity: Normal CC: andersca, commit-queue, eric, hausmann, laszlo.gombos, pkasting, webkit.review.bot, zecke
Priority: P2 Keywords: Qt
Version: 528+ (Nightly build)   
Hardware: PC   
OS: Linux   
Attachments:
Description Flags
first attempt to implement willLoadFromCache.
none
fix style violations.
zecke: review-
include zecke's comments.
none
big animated GIF for manual test (1097x899 with 12 frames).
none
null-check m_buffer and m_reader to avoid crashing in debug mode. none

Description Yongjun Zhang 2009-12-03 08:49:28 PST
ImageDecoder::clearFrameBufferCache(int index) is called to purge decoded images for animated GIFs.  The current implementation in Qt is empty.
Comment 1 Yongjun Zhang 2009-12-03 08:56:00 PST
Created attachment 44245 [details]
first attempt to implement willLoadFromCache.
Comment 2 WebKit Review Bot 2009-12-03 08:57:51 PST
Attachment 44245 [details] did not pass style-queue:

Failed to run "WebKitTools/Scripts/check-webkit-style" exit_code: 1
WebCore/platform/graphics/qt/ImageDecoderQt.cpp:154:  Missing spaces around =  [whitespace/operators] [4]
WebCore/platform/graphics/qt/ImageDecoderQt.cpp:154:  Missing spaces around <  [whitespace/operators] [3]
WebCore/platform/graphics/qt/ImageDecoderQt.cpp:163:  One line control clauses should not use braces.  [whitespace/braces] [4]
Total errors found: 3
Comment 3 Yongjun Zhang 2009-12-03 09:47:04 PST
Created attachment 44250 [details]
fix style violations.
Comment 4 WebKit Review Bot 2009-12-03 09:50:16 PST
style-queue ran check-webkit-style on attachment 44250 [details] without any errors.
Comment 5 Eric Seidel (no email) 2009-12-03 12:54:09 PST
Looks sane to me.
Comment 6 Simon Hausmann 2009-12-04 15:15:44 PST
Holger, you're best qualified to review this patch :)
Comment 7 Holger Freyther 2009-12-04 22:03:58 PST
Comment on attachment 44250 [details]
fix style violations.


> Index: WebCore/platform/graphics/qt/ImageDecoderQt.cpp
> ===================================================================
> --- WebCore/platform/graphics/qt/ImageDecoderQt.cpp	(revision 51636)
> +++ WebCore/platform/graphics/qt/ImageDecoderQt.cpp	(working cop
> -void ImageDecoderQt::clearFrameBufferCache(size_t /*index*/)
> +void ImageDecoderQt::clearFrameBufferCache(size_t index)
>  {
> +    if (index >= m_frameBufferCache.size())
> +        return;

I think this should be >...as of the semantic of clearFrameBufferCache..



> +    // create the image reader object again
> +    if (!m_reader)
> +        m_reader = new QImageReader(m_buffer, m_format);

m_buffer is deleted here...compare with ::FailRead and internalReadImage...



You should move the deletion and creation of m_reader, m_buffer into a common method.
Comment 8 Yongjun Zhang 2009-12-14 11:48:01 PST
Created attachment 44809 [details]
include zecke's comments.
Comment 9 WebKit Review Bot 2009-12-14 11:50:46 PST
style-queue ran check-webkit-style on attachment 44809 [details] without any errors.
Comment 10 Kenneth Rohde Christiansen 2009-12-14 13:36:01 PST
Comment on attachment 44809 [details]
include zecke's comments.

LGTM
Comment 11 Holger Freyther 2009-12-14 19:37:20 PST
Cool! You could also call RGBA32Buffer::clear to get the same effect as setting the status and setting an empty image.
Comment 12 WebKit Commit Bot 2009-12-22 18:12:00 PST
Comment on attachment 44809 [details]
include zecke's comments.

Clearing flags on attachment: 44809

Committed r52516: <http://trac.webkit.org/changeset/52516>
Comment 13 WebKit Commit Bot 2009-12-22 18:12:05 PST
All reviewed patches have been landed.  Closing bug.
Comment 14 Holger Freyther 2009-12-29 03:34:20 PST
On second look the patch is not correct.

There is no gurantee that the clearFromFrameBufferCache is only called once. It might be called more than once and then we will run into the assert on debug, and leak memory in release. And this is interesting. We try to do a patch to save memory and then we create a memory leak. Something must be wrong with our process.


I think we should go back to start and do it properly this time:

1.) create a manual test with a big enough GIF that will actually call clearFromFrameBufferCache as nothing in the layout test suite is executing this behaviour, and we also need to verify that we load all frames again.

2.) Do the change, run the test, look at the memory usage, upload the images of the memory usage (memusagestat).

3.) Land the patch if we actually save memory and we pass the manual test.
Comment 15 Yongjun Zhang 2009-12-30 06:55:55 PST
(In reply to comment #14)
> On second look the patch is not correct.
> 
> There is no gurantee that the clearFromFrameBufferCache is only called once. It
> might be called more than once and then we will run into the assert on debug,
> and leak memory in release. And this is interesting. We try to do a patch to
> save memory and then we create a memory leak. Something must be wrong with our
> process.

Good catch! The current implementation decodes all the frames in GIF image in ImageDecoderQt::frameCount() because m_reader->imageCount() always returns 0, which I believe it is a Qt bug. The problem you mentioned will show up when clearFromFrameBufferCache is called for the second time AND when there are still frames waiting for being decoded.

I think a NULL check on m_buffer and m_reader should be enough to fix this. Comments?
 
> 
> I think we should go back to start and do it properly this time:
> 
> 1.) create a manual test with a big enough GIF that will actually call
> clearFromFrameBufferCache as nothing in the layout test suite is executing this
> behaviour, and we also need to verify that we load all frames again.
> 
> 2.) Do the change, run the test, look at the memory usage, upload the images of
> the memory usage (memusagestat).
> 
> 3.) Land the patch if we actually save memory and we pass the manual test.

Sure, I will update the fix and run the manual test.
Comment 16 Yongjun Zhang 2010-01-11 13:03:12 PST
Created attachment 46303 [details]
big animated GIF for manual test (1097x899 with 12 frames).
Comment 17 Yongjun Zhang 2010-01-11 13:21:17 PST
I did manual test with a big animated GIF (see attached).  The result didn't show any memory savings ;(

What happens is after each loop, all frames are cleared and cachedFrameBuffer is also emptied.  When ImageDecoderQt::frameBufferAtIndex() is called again for the next frame, the decoder has to figure out the frame count by ImageDecoderQt::frameCount().  Due to an error in Qt image decoder, imageReader->Count() always returns 0, which triggers ImageDecoderQt to decode all the frames in forceLoadEverything().

As a result, ImageDecoderQt always decodes all frames.  I believe clearFrameBufferCache() is designed with the assumption that platform image decoders should decode animated frames on-demand, not decoding them as a whole bunch. 

I think the patch would work (after null-checking the m_buffer & m_reader) if Qt image decoder can reliably fetch image counts WITHOUT decoding all frames.  Otherwise this patch won't help reduce memory consumption as expected.
Comment 18 Peter Kasting 2010-01-11 13:54:15 PST
You can look at the open-source GIF decoder in WebCore/platform to see how it handles this stuff.  Basically, it assumes that it might get different frame count values throughout the image decoding process, so every time setData() is called, it dirties the frame count.  But once the end of the image has been reached, it knows that it has the correct frame count and keeps it forever after (even if clearFrameBufferCache() is called).

Meanwhile, clearFrameBufferCache() is called in such a way as to free frames we don't need once we've decoded them, assuming that we have only decoded up through the desired current frame.  If at some point you force all frames to decode at once, then even if you subsequently support clearFrameBufferCache(), your high-water memory usage is going to be just as bad as if you didn't support it.

So you want to avoid anything that forces all frames to decode.  This may mean that you have to deal with uncertain or changing frame counts during your first loop through the image.
Comment 19 Yongjun Zhang 2010-01-12 09:00:44 PST
Created attachment 46377 [details]
null-check m_buffer and m_reader to avoid crashing in debug mode.

As per previous comment, without fixing qt Imagedecoder's frameCount() bug, this patch won't help reduce the peak memory consumption.

However I still would like to submit this patch for comments.
Comment 20 Simon Hausmann 2010-01-21 06:21:51 PST
The Qt issue is now tracked at http://bugreports.qt.nokia.com/browse/QTBUG-7514

Yongjun, can you elaborate in JIRA what exactly the Qt bug is?
Comment 21 Yongjun Zhang 2010-01-22 08:25:39 PST
(In reply to comment #20)
> The Qt issue is now tracked at http://bugreports.qt.nokia.com/browse/QTBUG-7514
> 
> Yongjun, can you elaborate in JIRA what exactly the Qt bug is?

Hi Simon, I just added the following commment to the JIRA report.  Hopefully it can help.

"Some details about the QImageReader issue:

For GIF animated images, QImageReader::imageCount() always returns 0, regardless of the image data are complete or not. This forces ImageDecoderQt to decode ALL the frames in order to figure out the frame counts which causes unnecessary memory consumption & performance penalty.

It seems like QImageReader::jumpToImage(int) doesn't work either - it returns false always.
"
Comment 22 Tor Arne Vestbø 2010-03-05 09:39:33 PST
Please follow the QtWebKit bug reporting guidelines when reporting bugs.

See http://trac.webkit.org/wiki/QtWebKitBugs

Specifically:

  - The 'QtWebKit' component should be used for bugs/features in the public QtWebKit API layer, not to signify that the bug is specific to the Qt port of WebKit

    http://trac.webkit.org/wiki/QtWebKitBugs#Component