<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[176115] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/176115">176115</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2014-11-13 20:26:45 -0800 (Thu, 13 Nov 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[mac] Keep around more decoded image data, since it's purgeable
https://bugs.webkit.org/show_bug.cgi?id=125273
&lt;rdar://problem/13205438&gt;

Reviewed by Simon Fraser.

No new tests, just an optimization.

Instead of throwing away decoded image data eagerly, allow the operating
system to manage the memory via the standard purgeability mechanism,
where it can.

This improves the performance on some pathological cases (extremely large
animated GIFs) by up to 8x.

* loader/cache/MemoryCache.cpp:
(WebCore::MemoryCache::pruneLiveResourcesToSize):
Don't prune live resources' decoded data if it is purgeable.

* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::destroyDecodedDataIfNecessary):
Don't eagerly throw away decoded image data if it's purgeable.

* loader/cache/CachedImage.h:
* loader/cache/CachedResource.h:
(WebCore::CachedResource::decodedDataIsPurgeable):
* platform/graphics/BitmapImage.h:
* platform/graphics/Image.h:
(WebCore::Image::decodedDataIsPurgeable):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedImageh">trunk/Source/WebCore/loader/cache/CachedImage.h</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedResourceh">trunk/Source/WebCore/loader/cache/CachedResource.h</a></li>
<li><a href="#trunkSourceWebCoreloadercacheMemoryCachecpp">trunk/Source/WebCore/loader/cache/MemoryCache.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsBitmapImagecpp">trunk/Source/WebCore/platform/graphics/BitmapImage.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsBitmapImageh">trunk/Source/WebCore/platform/graphics/BitmapImage.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsImageh">trunk/Source/WebCore/platform/graphics/Image.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicscgImageSourceCGcpp">trunk/Source/WebCore/platform/graphics/cg/ImageSourceCG.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/ChangeLog        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2014-11-13  Tim Horton  &lt;timothy_horton@apple.com&gt;
+
+        [mac] Keep around more decoded image data, since it's purgeable
+        https://bugs.webkit.org/show_bug.cgi?id=125273
+        &lt;rdar://problem/13205438&gt;
+
+        Reviewed by Simon Fraser.
+
+        No new tests, just an optimization.
+
+        Instead of throwing away decoded image data eagerly, allow the operating
+        system to manage the memory via the standard purgeability mechanism,
+        where it can.
+
+        This improves the performance on some pathological cases (extremely large
+        animated GIFs) by up to 8x.
+
+        * loader/cache/MemoryCache.cpp:
+        (WebCore::MemoryCache::pruneLiveResourcesToSize):
+        Don't prune live resources' decoded data if it is purgeable.
+
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::destroyDecodedDataIfNecessary):
+        Don't eagerly throw away decoded image data if it's purgeable.
+
+        * loader/cache/CachedImage.h:
+        * loader/cache/CachedResource.h:
+        (WebCore::CachedResource::decodedDataIsPurgeable):
+        * platform/graphics/BitmapImage.h:
+        * platform/graphics/Image.h:
+        (WebCore::Image::decodedDataIsPurgeable):
+
</ins><span class="cx"> 2014-11-13  Myles C. Maxfield  &lt;litherum@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Addressing post-review comment
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImage.h        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -24,6 +24,7 @@
</span><span class="cx"> #define CachedImage_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CachedResource.h&quot;
</span><ins>+#include &quot;Image.h&quot;
</ins><span class="cx"> #include &quot;ImageObserver.h&quot;
</span><span class="cx"> #include &quot;IntRect.h&quot;
</span><span class="cx"> #include &quot;IntSizeHash.h&quot;
</span><span class="lines">@@ -115,6 +116,8 @@
</span><span class="cx"> 
</span><span class="cx">     virtual bool stillNeedsLoad() const override { return !errorOccurred() &amp;&amp; status() == Unknown &amp;&amp; !isLoading(); }
</span><span class="cx"> 
</span><ins>+    virtual bool decodedDataIsPurgeable() const override { return m_image &amp;&amp; m_image-&gt;decodedDataIsPurgeable(); }
+
</ins><span class="cx">     // ImageObserver
</span><span class="cx">     virtual void decodedSizeChanged(const Image*, int delta) override;
</span><span class="cx">     virtual void didDraw(const Image*) override;
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedResourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedResource.h (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedResource.h        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/loader/cache/CachedResource.h        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -144,6 +144,8 @@
</span><span class="cx">     unsigned encodedSize() const { return m_encodedSize; }
</span><span class="cx">     unsigned decodedSize() const { return m_decodedSize; }
</span><span class="cx">     unsigned overheadSize() const;
</span><ins>+
+    virtual bool decodedDataIsPurgeable() const { return false; }
</ins><span class="cx">     
</span><span class="cx">     bool isLoaded() const { return !m_loading; } // FIXME. Method name is inaccurate. Loading might not have started yet.
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheMemoryCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/MemoryCache.cpp (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/MemoryCache.cpp        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/loader/cache/MemoryCache.cpp        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -342,6 +342,11 @@
</span><span class="cx">             if (!shouldDestroyDecodedDataForAllLiveResources &amp;&amp; elapsedTime &lt; cMinDelayBeforeLiveDecodedPrune)
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><ins>+            if (current-&gt;decodedDataIsPurgeable()) {
+                current = prev;
+                continue;
+            }
+
</ins><span class="cx">             // Destroy our decoded data. This will remove us from 
</span><span class="cx">             // m_liveDecodedResources, and possibly move us to a different LRU 
</span><span class="cx">             // list in m_allResources.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -132,6 +132,11 @@
</span><span class="cx">     const unsigned largeAnimationCutoff = 5242880;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    // If decoded data is purgeable, the operating system will
+    // take care of throwing it away when the system is under pressure.
+    if (decodedDataIsPurgeable())
+        return;
+
</ins><span class="cx">     // If we have decoded frames but there is no encoded data, we shouldn't destroy
</span><span class="cx">     // the decoded image since we won't be able to reconstruct it later.
</span><span class="cx">     if (!data() &amp;&amp; m_frames.size())
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.h        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -283,6 +283,15 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> private:
</span><ins>+    virtual bool decodedDataIsPurgeable() const override
+    {
+#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101000
+        return true;
+#else
+        return false;
+#endif
+    }
+
</ins><span class="cx">     ImageSource m_source;
</span><span class="cx">     mutable IntSize m_size; // The size to use for the overall image (will just be the size of the first image).
</span><span class="cx">     mutable IntSize m_sizeRespectingOrientation;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/Image.h (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/Image.h        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/platform/graphics/Image.h        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -114,6 +114,7 @@
</span><span class="cx">     virtual String filenameExtension() const { return String(); } // null string if unknown
</span><span class="cx"> 
</span><span class="cx">     virtual void destroyDecodedData(bool destroyAll = true) = 0;
</span><ins>+    virtual bool decodedDataIsPurgeable() const { return false; }
</ins><span class="cx"> 
</span><span class="cx">     SharedBuffer* data() { return m_encodedImageData.get(); }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicscgImageSourceCGcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/cg/ImageSourceCG.cpp (176114 => 176115)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/cg/ImageSourceCG.cpp        2014-11-14 02:53:22 UTC (rev 176114)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageSourceCG.cpp        2014-11-14 04:26:45 UTC (rev 176115)
</span><span class="lines">@@ -50,6 +50,17 @@
</span><span class="cx"> const CFStringRef kCGImageSourceSubsampleFactor = CFSTR(&quot;kCGImageSourceSubsampleFactor&quot;);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if __has_include(&lt;CoreGraphics/CGImagePrivate.h&gt;)
+#import &lt;CoreGraphics/CGImagePrivate.h&gt;
+#else
+enum {
+    kCGImageCachingTransient = 1,
+    kCGImageCachingTemporary = 3
+};
+typedef uint32_t CGImageCachingFlags;
+extern &quot;C&quot; void CGImageSetCachingFlags(CGImageRef image, CGImageCachingFlags flags);
+#endif
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> const CFStringRef WebCoreCGImagePropertyAPNGUnclampedDelayTime = CFSTR(&quot;UnclampedDelayTime&quot;);
</span><span class="lines">@@ -343,7 +354,7 @@
</span><span class="cx"> CGImageRef ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
</span><span class="cx"> {
</span><span class="cx">     if (!initialized())
</span><del>-        return 0;
</del><ins>+        return nullptr;
</ins><span class="cx"> 
</span><span class="cx">     RetainPtr&lt;CGImageRef&gt; image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(subsamplingLevel).get()));
</span><span class="cx"> 
</span><span class="lines">@@ -364,8 +375,19 @@
</span><span class="cx"> 
</span><span class="cx">     CFStringRef imageUTI = CGImageSourceGetType(m_decoder);
</span><span class="cx">     static const CFStringRef xbmUTI = CFSTR(&quot;public.xbitmap-image&quot;);
</span><del>-    if (!imageUTI || !CFEqual(imageUTI, xbmUTI))
</del><ins>+
+    if (!imageUTI)
</ins><span class="cx">         return image.leakRef();
</span><ins>+
+#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101000
+    if (CFEqual(imageUTI, kUTTypeGIF)) {
+        CGImageSetCachingFlags(image.get(), kCGImageCachingTransient);
+        return image.leakRef();
+    }
+#endif
+
+    if (!CFEqual(imageUTI, xbmUTI))
+        return image.leakRef();
</ins><span class="cx">     
</span><span class="cx">     // If it is an xbm image, mask out all the white areas to render them transparent.
</span><span class="cx">     const CGFloat maskingColors[6] = {255, 255,  255, 255, 255, 255};
</span></span></pre>
</div>
</div>

</body>
</html>