Bug 158715

Summary: [iOS] PDFDocumentImage should cache only a sub image of the PDF when caching the whole image is expensive
Product: WebKit Reporter: Said Abou-Hallawa <sabouhallawa>
Component: ImagesAssignee: Said Abou-Hallawa <sabouhallawa>
Status: RESOLVED FIXED    
Severity: Normal CC: andersca, commit-queue, darin, dino, esprehn+autocc, ggaren, glenn, jonlee, kondapallykalyan, simon.fraser, thorton, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=157857
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch none

Said Abou-Hallawa
Reported 2016-06-13 18:08:14 PDT
This is a follow up for the bug https://bugs.webkit.org/show_bug.cgi?id=157857. This bug was partially fixed in http://trac.webkit.org/changeset/201629 for low end devices whose ram sizes are less than 1GB ram. The bug was not addressed for higher end devices which larger ram sizes. We need to use some heuristic than can work for all ram sizes. We can say, for every 1GB of ram we allow 16MB memory increase in the PDF cachedImage size. Or more formally, the PDF cachedImage will be created only: if (ramSize() / PDF_cachedImage_size > 1GB / 16MB)
Attachments
Patch (2.46 KB, patch)
2016-06-13 18:09 PDT, Said Abou-Hallawa
no flags
Patch (9.64 KB, patch)
2016-07-07 23:01 PDT, Said Abou-Hallawa
no flags
Patch (9.55 KB, patch)
2016-07-08 15:03 PDT, Said Abou-Hallawa
no flags
Patch (20.08 KB, patch)
2016-07-14 10:04 PDT, Said Abou-Hallawa
no flags
Patch (19.91 KB, patch)
2016-07-14 10:34 PDT, Said Abou-Hallawa
no flags
Patch (20.90 KB, patch)
2016-07-15 17:05 PDT, Said Abou-Hallawa
no flags
Patch (19.81 KB, patch)
2016-07-15 17:18 PDT, Said Abou-Hallawa
no flags
Said Abou-Hallawa
Comment 1 2016-06-13 18:09:47 PDT
Alexey Proskuryakov
Comment 2 2016-06-20 16:15:15 PDT
Comment on attachment 281219 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=281219&action=review > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:152 > + if (ramSize() / ImageBuffer::compatibleBufferSize(dstRect.size(), context).area() < memoryThreshold / maxArea) { Can compatibleBufferSize return 0?
Said Abou-Hallawa
Comment 3 2016-07-07 23:01:50 PDT
Said Abou-Hallawa
Comment 4 2016-07-08 15:03:37 PDT
Said Abou-Hallawa
Comment 5 2016-07-11 12:10:07 PDT
(In reply to comment #2) > Comment on attachment 281219 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=281219&action=review > > > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:152 > > + if (ramSize() / ImageBuffer::compatibleBufferSize(dstRect.size(), context).area() < memoryThreshold / maxArea) { > > Can compatibleBufferSize return 0? I changed the patch to allocate memory for a rectangle of the image only.
Said Abou-Hallawa
Comment 6 2016-07-13 12:23:21 PDT
Can I get this patch reviewed? Any feedback would be also very helpful.
Simon Fraser (smfr)
Comment 7 2016-07-13 13:16:16 PDT
Comment on attachment 283201 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=283201&action=review > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:146 > + // Moving coordinates to -srcRect.location() => srcContext(srcRect.x(), srcRect.y()) -> dstContext(0, 0) > + // Moving coordinates to dstRect.location() => srcContext(0, 0) -> dstContext(dstRect.x(), dstRect.y()) > + // Combining both => srcContext(srcRect.x(), srcRect.y()) -> dstContext(dstRect.x(), dstRect.y()) > + context.translate(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y()); This seems like a bug fix, so can you make a layout test for it? > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:170 > + FloatRect rect = context.clipBounds(); > + rect.setX(std::max(rect.center().x() - maxSize.width() / 2, dstRect.x())); > + rect.setY(std::max(rect.center().y() - maxSize.height() / 2, dstRect.y())); > + > + // Cover as much as we could from the dstRect. > + rect.setWidth(std::min(maxSize.width(), dstRect.width())); > + rect.setHeight(std::min(maxSize.height(), dstRect.height())); It's hard to follow this logic, and I'm not sure if rect will always cover destRect. Can you rewrite this using shiftXEdgeTo() etc? > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:214 > + if (s_allDecodedDataSize + m_cachedImageRect.size().area() * 4 - m_cachedBytes > s_maxDecodedDataSize) { > + destroyDecodedData(); Should m_cachedImageRect be left as a valid rectangle if we didn't actually cache anything?
Said Abou-Hallawa
Comment 8 2016-07-14 10:04:24 PDT
Said Abou-Hallawa
Comment 9 2016-07-14 10:07:46 PDT
Said Abou-Hallawa
Comment 10 2016-07-14 10:27:07 PDT
Comment on attachment 283201 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=283201&action=review >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:146 >> + context.translate(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y()); > > This seems like a bug fix, so can you make a layout test for it? Done. But I had to add an internal setting to disable caching the PDF image to expose the bug. The new test fails without this fix. >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:170 >> + rect.setHeight(std::min(maxSize.height(), dstRect.height())); > > It's hard to follow this logic, and I'm not sure if rect will always cover destRect. Can you rewrite this using shiftXEdgeTo() etc? The size of dstRect is always the whole image size. Drawing the image relies on clipping the graphics context to the dirty rectangle. Because we scale the cached image rectangle by the scaling factor, this means we have to allocate memory for imageSize * scaleFator which might exceed the memory limit of the device. Instead we try to cover the dirty rectangle and get as much as we could around it. So I start from the center of the dirty rectangle and get the maximum rectangle that is smaller than the memory limit. This means rect may not cover dstRect. >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:214 >> + destroyDecodedData(); > > Should m_cachedImageRect be left as a valid rectangle if we didn't actually cache anything? destroyDecodedData() now sets m_cachedImageRect to an empty rectangle.
Said Abou-Hallawa
Comment 11 2016-07-14 10:34:45 PDT
Dean Jackson
Comment 12 2016-07-15 15:23:49 PDT
Comment on attachment 283658 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=283658&action=review > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:154 > + // Moving coordinates to -srcRect.location() => srcContext(srcRect.x(), srcRect.y()) -> dstContext(0, 0) > + // Moving coordinates to dstRect.location() => srcContext(0, 0) -> dstContext(dstRect.x(), dstRect.y()) > + // Combining both => srcContext(srcRect.x(), srcRect.y()) -> dstContext(dstRect.x(), dstRect.y()) I don't understand what these comments mean. > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:163 > +// To avoid the jetsam on iOS, we are going to limit the size of all the PDF cachedImages to be 64MB. > +static const size_t s_maxCachedImageSide = 4 * KB; > +static const size_t s_maxCachedImageSize = s_maxCachedImageSide * s_maxCachedImageSide * 4; Isn't that 64KB, not 64MB? Also, maybe we should use Edge rather than Side, because I thought it was a typo :) > Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:224 > + // Cache the PDF image only if the size of the new image won't exceed the limit Nit: missing .
Said Abou-Hallawa
Comment 13 2016-07-15 17:05:15 PDT
Said Abou-Hallawa
Comment 14 2016-07-15 17:18:58 PDT
Said Abou-Hallawa
Comment 15 2016-07-15 17:25:42 PDT
Comment on attachment 283658 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=283658&action=review >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:154 >> + // Combining both => srcContext(srcRect.x(), srcRect.y()) -> dstContext(dstRect.x(), dstRect.y()) > > I don't understand what these comments mean. I fixed the comment but here is more explanation I used because the original code confused me: The following translation context.translate(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y()); means a src point will be drawn at a dst point according to the following transformation: | dstX | | 1 0 dstRect.x() - srcRect.x() | | srcX | | srcX + dstRect.x() - srcRect.x() | | dstY | = | 0 1 dstRect.y() - srcRect.y() | | srcY | = | srcY + dstRect.y() - srcRect.y() | | 1 | | 0 0 1 | | 1 | | 1 | If we want to get which src point will be drawn at the top-left corner of dstRect we can use the following equality | dstRect.x() | = | srcX + dstRect.x() - srcRect.x() | | srcX | | srcRect.x() | | dstRect.y() | = | srcY + dstRect.y() - srcRect.y() |, but this can only happen when | srcY | = | srcRect.y() | | 1 | = | 1 | | 1 | | 1 | This means the top-left corner of the srcRect will be drawn at the top-left corner of dstRect, or: | srcRect.x() | | dstRect.x() | | srcRect.y() | will be drawn at | dstRect.y() | | 1 | | 1 | The rest of the points will be mapped the same way such that the srcRect will be drawn in dstRect. >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:163 >> +static const size_t s_maxCachedImageSize = s_maxCachedImageSide * s_maxCachedImageSide * 4; > > Isn't that 64KB, not 64MB? > > Also, maybe we should use Edge rather than Side, because I thought it was a typo :) It was wrong to use the macro KB in this definition. What I meant was: 1. The maximum memory size for the cachedPDFImage = 64 MB 2. This means the maximum area of the cachedPDFImage = 64M / 4 = 16M pixels 3. If we choose cachedPDFImage to be square, then the maximum length of the cachedPDFImage side = sqrt(16M) = 4K pixels. I changed the naming and the definition to remove the confusion. >> Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp:224 >> + // Cache the PDF image only if the size of the new image won't exceed the limit > > Nit: missing . Fixed.
WebKit Commit Bot
Comment 16 2016-07-18 15:47:32 PDT
Comment on attachment 283826 [details] Patch Clearing flags on attachment: 283826 Committed r203378: <http://trac.webkit.org/changeset/203378>
WebKit Commit Bot
Comment 17 2016-07-18 15:47:39 PDT
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.