WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
Bug 168814
Asynchronous image decoding should consider the drawing size if it is smaller than the size of the image
https://bugs.webkit.org/show_bug.cgi?id=168814
Summary
Asynchronous image decoding should consider the drawing size if it is smaller...
Said Abou-Hallawa
Reported
2017-02-23 18:19:45 PST
If the size of the destinationRect is smaller than the image source size (e.g. 3000x3000 pixels), CGImageSourceCreateThumbnailAtIndex() is slower than CGImageSourceCreateImageAtIndex() in decoding this image. To overcome this problem, the entry (kCGImageSourceThumbnailMaxPixelSize, max(destinationRect.width, destinationRect.height)) is added to the options dictionary when calling CGImageSourceCreateThumbnailAtIndex(). This will avoid copying a large block of memory for the unscaled bitmap image.
Attachments
Patch
(41.41 KB, patch)
2017-02-23 18:31 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(48.77 KB, patch)
2017-02-24 09:56 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(50.51 KB, patch)
2017-02-24 16:47 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews113 for mac-elcapitan
(1.81 MB, application/zip)
2017-02-24 19:21 PST
,
Build Bot
no flags
Details
Patch
(53.35 KB, patch)
2017-02-27 18:25 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(52.16 KB, patch)
2017-02-28 10:30 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(54.45 KB, patch)
2017-02-28 22:20 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(54.74 KB, patch)
2017-03-01 09:44 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(54.62 KB, patch)
2017-03-01 11:41 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(61.46 KB, patch)
2017-03-05 17:08 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews100 for mac-elcapitan
(901.56 KB, application/zip)
2017-03-05 17:49 PST
,
Build Bot
no flags
Details
Patch
(65.02 KB, patch)
2017-03-06 17:51 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(65.13 KB, patch)
2017-03-07 11:32 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(65.34 KB, patch)
2017-03-07 18:19 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews114 for mac-elcapitan
(1.67 MB, application/zip)
2017-03-07 19:33 PST
,
Build Bot
no flags
Details
Patch
(65.34 KB, patch)
2017-03-07 19:38 PST
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Show Obsolete
(14)
View All
Add attachment
proposed patch, testcase, etc.
Said Abou-Hallawa
Comment 1
2017-02-23 18:31:57 PST
Created
attachment 302625
[details]
Patch
Said Abou-Hallawa
Comment 2
2017-02-24 09:56:48 PST
Created
attachment 302672
[details]
Patch
Said Abou-Hallawa
Comment 3
2017-02-24 10:43:03 PST
Comment on
attachment 302672
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=302672&action=review
> Source/WebCore/loader/cache/CachedImage.h:129 > + URL sourceUrl() const override { return m_cachedImages[0]->url(); }
Is m_cachedImages guaranteed to have at least one entry? Yes. Before <
http://trac.webkit.org/changeset/209914
>, CachedImage was inheriting from ImageObserver and all the virtual functions of ImageObserver were implemented inside CachedImage. After this change, CachedImage holds a RefPtr<CachedImageObserver> named m_imageObserver. This pointer is initialized in two places. (1) CachedImage::createImage(): this function creates a new m_image and the m_imageObserver is also created. And this (the CachedImage) is added to m_imageObserver. (2) CachedImage::setBodyDataFrom(): in this function m_image is cloned from the other CachedImage to this CachedImage. The m_imageObserver also references the m_imageObserver of the other CachedImage. Then this (the CachedImage) is added to m_imageObserver.
Simon Fraser (smfr)
Comment 4
2017-02-24 11:54:22 PST
Comment on
attachment 302672
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=302672&action=review
> Source/WebCore/ChangeLog:26 > + -- if (sizeForDrawing && sizeForDrawing.value().isZero()): async image decoding > + is needed but do not set kCGImageSourceThumbnailMaxPixelSize. This is the > + case where destinationRect.size() is larger than the image sourceSize. > + -- if (sizeForDrawing && !sizeForDrawing.value().isZero()): async image decoding > + is needed and kCGImageSourceThumbnailMaxPixelSize has to be set. The is the > + case where destinationRect.size() is smaller than the image sourceSize.
Do you really need to differentiate between these two states? Why not just always pass down sizeForDrawing, and make the determination based on that vs. the native size of the image? That would avoid this confusing 3-state system.
Said Abou-Hallawa
Comment 5
2017-02-24 16:47:12 PST
Created
attachment 302709
[details]
Patch
Said Abou-Hallawa
Comment 6
2017-02-24 16:57:47 PST
Comment on
attachment 302709
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=302709&action=review
> Source/WebCore/platform/graphics/BitmapImage.cpp:240 > +bool BitmapImage::isAsyncDecodingRequired()
Can this be const? It can but it will be very involving because BitmapImage::canAnimate() is not const. It is not const because it calls BitmapImage::frameCount() which is not const. BitmapImage::frameCount() is not const because ImageFrameCache::frameCount() has to cache the ImageFrameCache::m_frameCount if it is not set. This a general problem with most of the query functions in BitmapImage, ImageSource and ImageFrameCache. Most of the properties are lazily calculated in these query functions. So either, we keep these query functions non-const. Or we make all the image properties mutable.
Build Bot
Comment 7
2017-02-24 19:20:42 PST
Comment on
attachment 302709
[details]
Patch
Attachment 302709
[details]
did not pass mac-debug-ews (mac): Output:
http://webkit-queues.webkit.org/results/3188614
New failing tests: media/modern-media-controls/macos-fullscreen-media-controls/macos-fullscreen-media-controls-drag.html media/modern-media-controls/macos-fullscreen-media-controls/macos-fullscreen-media-controls-drag-is-prevented-over-button.html
Build Bot
Comment 8
2017-02-24 19:21:03 PST
Created
attachment 302727
[details]
Archive of layout-test-results from ews113 for mac-elcapitan The attached test failures were seen while running run-webkit-tests on the mac-debug-ews. Bot: ews113 Port: mac-elcapitan Platform: Mac OS X 10.11.6
Radar WebKit Bug Importer
Comment 9
2017-02-27 14:40:29 PST
<
rdar://problem/30742918
>
Said Abou-Hallawa
Comment 10
2017-02-27 18:25:06 PST
Created
attachment 302908
[details]
Patch
Said Abou-Hallawa
Comment 11
2017-02-28 10:30:04 PST
Created
attachment 302947
[details]
Patch
Tim Horton
Comment 12
2017-02-28 16:32:47 PST
Comment on
attachment 302947
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=302947&action=review
> Source/WebCore/platform/graphics/ImageFrameCache.h:144 > + IntSize sizeForDraw;
I would vote for "sizeForDrawing" or... something else, this sounds weird.
> Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp:88 > - static NeverDestroyed<RetainPtr<CFDictionaryRef>> optionsOnDemand = createImageSourceOptions(SubsamplingLevel::First, DecodingMode::OnDemand); > - static NeverDestroyed<RetainPtr<CFDictionaryRef>> optionsImmediate = createImageSourceOptions(SubsamplingLevel::First, DecodingMode::Immediate); > +static RetainPtr<CFMutableDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsamplingLevel)
So now we're building CFDictionaries all over the place, where they used to be static? That seems not great.
> Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp:183 > -NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, DecodingMode) const > +NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, , const std::optional<IntSize>&) const
This doesn't look right (, ,)
Said Abou-Hallawa
Comment 13
2017-02-28 22:20:09 PST
Created
attachment 303040
[details]
Patch
Said Abou-Hallawa
Comment 14
2017-02-28 22:21:52 PST
Comment on
attachment 302947
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=302947&action=review
>> Source/WebCore/platform/graphics/ImageFrameCache.h:144 >> + IntSize sizeForDraw; > > I would vote for "sizeForDrawing" or... something else, this sounds weird.
Done.
>> Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp:88 >> +static RetainPtr<CFMutableDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsamplingLevel) > > So now we're building CFDictionaries all over the place, where they used to be static? That seems not great.
I think I fixed this issue in the new patch.
>> Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp:183 >> +NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, , const std::optional<IntSize>&) const > > This doesn't look right (, ,)
Fixed. There is not a port to build Windows D2D so EWS did not catch it.
Said Abou-Hallawa
Comment 15
2017-03-01 09:44:56 PST
Created
attachment 303073
[details]
Patch
Said Abou-Hallawa
Comment 16
2017-03-01 11:12:23 PST
Comment on
attachment 303073
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=303073&action=review
> Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp:389 > + RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
This is wrong. I deleted the call to CGImageSourceCreateThumbnailAtIndex by mistake. This will disable the async image decoding completely. There should be a call to CGImageSourceCreateImageAtIndex() inside the if (!sizeForDrawing) above. And another call to CGImageSourceCreateThumbnailAtIndex() should be inside the else block.
Simon Fraser (smfr)
Comment 17
2017-03-01 11:25:10 PST
(In reply to
comment #16
) This will disable the async image decoding completely. Why didn't tests fail then?
Said Abou-Hallawa
Comment 18
2017-03-01 11:41:06 PST
Created
attachment 303090
[details]
Patch
Said Abou-Hallawa
Comment 19
2017-03-01 11:43:04 PST
Comment on
attachment 303090
[details]
Patch I made sure the animated images are decoded asynchronously and the frame rate is high as without this patch.
Said Abou-Hallawa
Comment 20
2017-03-01 13:02:59 PST
(In reply to
comment #17
)
> (In reply to
comment #16
) > This will disable the async image decoding completely. > > Why didn't tests fail then?
Because the current tests for animated image can only verify the correctness of the async image decoding path. These tests force async image decoding since the images they use are small and simple ones. They also set the m_desiredFrameDecodeTimeForTesting to fake a lengthy decoding time. The tests expect to get the frame drawn correctly but they do not test the frame is drawn because it went through a specific code path. They do not test if we used CGImageSourceCreateThumbnailAtIndex() instead of CGImageSourceCreateImageAtIndex() in the async image decoding path. What makes it difficult to do such test is the difference between decoding a simple small image using one of these two functions and then drawing it can hardly be noticeable. Using CGImageSourceCreateImageAtIndex() to decode an image asynchronously will still generate a valid CGImageRef. The only problem here is the real deducing will happen later at the drawing time.
Said Abou-Hallawa
Comment 21
2017-03-05 17:08:36 PST
Created
attachment 303487
[details]
Patch
Build Bot
Comment 22
2017-03-05 17:49:30 PST
Comment on
attachment 303487
[details]
Patch
Attachment 303487
[details]
did not pass mac-ews (mac): Output:
http://webkit-queues.webkit.org/results/3249414
New failing tests: media/modern-media-controls/tracks-support/tracks-support-show-panel-after-dragging-controls.html
Build Bot
Comment 23
2017-03-05 17:49:34 PST
Created
attachment 303490
[details]
Archive of layout-test-results from ews100 for mac-elcapitan The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews100 Port: mac-elcapitan Platform: Mac OS X 10.11.6
Simon Fraser (smfr)
Comment 24
2017-03-06 14:41:49 PST
Comment on
attachment 303487
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=303487&action=review
> Source/WebCore/platform/graphics/BitmapImage.cpp:245 > + if (!((canAnimate() && allowAnimatedImageAsyncDecoding()) || (!canAnimate() && allowLargeImageAsyncDecoding())))
I feel this could be simplified.
> Source/WebCore/platform/graphics/ImageFrame.h:151 > + std::optional<IntSize> m_sizeForDrawing;
Thinking about it some more, I think this is a fundamentally wrong change. I think we need to preserve the existing behavior that BitmapImage is agnostic to the size at which it's going to get pained. Tim points out that CachedImage has something relevant: CachedImage::setContainerSizeForRenderer().
Said Abou-Hallawa
Comment 25
2017-03-06 17:51:17 PST
Created
attachment 303593
[details]
Patch
Said Abou-Hallawa
Comment 26
2017-03-07 11:32:31 PST
Created
attachment 303686
[details]
Patch
Said Abou-Hallawa
Comment 27
2017-03-07 12:11:55 PST
Comment on
attachment 303487
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=303487&action=review
>> Source/WebCore/platform/graphics/BitmapImage.cpp:245 >> + if (!((canAnimate() && allowAnimatedImageAsyncDecoding()) || (!canAnimate() && allowLargeImageAsyncDecoding()))) > > I feel this could be simplified.
I replaced this function by the two functions to removed the complication. bool BitmapImage::isLargeImageAsyncDecodingRequired() { return !canAnimate() && allowLargeImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired()); } bool BitmapImage::isAnimatedImageAsyncDecodingRequired() { return canAnimate() && allowAnimatedImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired()); }
>> Source/WebCore/platform/graphics/ImageFrame.h:151 >> + std::optional<IntSize> m_sizeForDrawing; > > Thinking about it some more, I think this is a fundamentally wrong change. I think we need to preserve the existing behavior that BitmapImage is agnostic to the size at which it's going to get pained. > > Tim points out that CachedImage has something relevant: CachedImage::setContainerSizeForRenderer().
setContainerSizeForRenderer() is mainly used by the SVG to resize the FrameView. For the BitmapImage, I think the sizeForDrawing is hidden by the ImageFrame. The BitmapImage still returns the native size of the image when it is requested. The ImageFrame is just transient thing. It can be deleted at any point if we go under memory stress for example. It knows when and how to rebuild itself. When drawing for a specific size, it tries to do some optimization but it gives the highest quality of the image. If BitmapImage tries to draw for a size whose MaxPixelSize is larger the MaxPixelSize of the ImageFrame sizeForDrawing, ImageFrame rebuilds a new NativeImage with the larger MaxPixelSize.
Simon Fraser (smfr)
Comment 28
2017-03-07 17:00:07 PST
Comment on
attachment 303686
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=303686&action=review
> Source/WebCore/ChangeLog:9 > + (e.g. 3000x3000 pixels), CGImageSourceCreateThumbnailAtIndex() is slower
Because the thumbnail is decoding at the native size of the image? Doesn't that also imply higher memory use?
> Source/WebCore/ChangeLog:17 > + all the way from BitmapImage till ImageDecoder. If bool(sizeForDrawing) equals
till -> to
> Source/WebCore/ChangeLog:19 > + asynchronously.
synchronously?
> Source/WebCore/platform/graphics/BitmapImage.cpp:319 > + if (m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, *m_sizeForDrawing)) > + LOG(Images, "BitmapImage::%s - %p - url: %s [requesting async decoding for nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), nextFrame); > + else > + LOG(Images, "BitmapImage::%s - %p - url: %s [cachedFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_cachedFrameCount, nextFrame);
It's a bit odd to have the main decode happen in an if () statement whose result is only used for logging. This would be better as: bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, *m_sizeForDrawing); LOG(.... , isAsyncDecode, ...) with the appropriate UNUSED_PARAM if LOG is not defined.
> Source/WebCore/platform/graphics/ImageFrame.cpp:128 > +static int maxPixelSize(const IntSize& size)
I would call this maxDimension or largerOfWidthAndHeight
> Source/WebCore/platform/graphics/ImageFrame.cpp:141 > + return maxPixelSize(m_sizeForDecoding.last()) >= maxPixelSize(*sizeForDrawing);
Seems odd to compare a tall skinny image with a short, wide one. We may have to enhance this in future.
Said Abou-Hallawa
Comment 29
2017-03-07 18:19:26 PST
Created
attachment 303754
[details]
Patch
Build Bot
Comment 30
2017-03-07 19:33:04 PST
Comment on
attachment 303754
[details]
Patch
Attachment 303754
[details]
did not pass mac-debug-ews (mac): Output:
http://webkit-queues.webkit.org/results/3262854
New failing tests: media/track/track-in-band-style.html
Build Bot
Comment 31
2017-03-07 19:33:10 PST
Created
attachment 303761
[details]
Archive of layout-test-results from ews114 for mac-elcapitan The attached test failures were seen while running run-webkit-tests on the mac-debug-ews. Bot: ews114 Port: mac-elcapitan Platform: Mac OS X 10.11.6
Said Abou-Hallawa
Comment 32
2017-03-07 19:38:15 PST
Created
attachment 303762
[details]
Patch
WebKit Commit Bot
Comment 33
2017-03-07 19:54:32 PST
Comment on
attachment 303762
[details]
Patch Clearing flags on attachment: 303762 Committed
r213563
: <
http://trac.webkit.org/changeset/213563
>
WebKit Commit Bot
Comment 34
2017-03-07 19:54:41 PST
All reviewed patches have been landed. Closing bug.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug