Modified: trunk/Source/WebKit2/ChangeLog (162945 => 162946)
--- trunk/Source/WebKit2/ChangeLog 2014-01-28 19:26:21 UTC (rev 162945)
+++ trunk/Source/WebKit2/ChangeLog 2014-01-28 19:33:49 UTC (rev 162946)
@@ -1,3 +1,52 @@
+2014-01-28 Tim Horton <timothy_hor...@apple.com>
+
+ WebKit2 View Gestures (Swipe): Snapshots should be purgeable
+ https://bugs.webkit.org/show_bug.cgi?id=127390
+ <rdar://problem/15876775>
+
+ Reviewed by Anders Carlsson.
+
+ Make snapshots be purgeable, and implement a straightforward
+ (if perhaps expensive, for now) cache eviction strategy to limit the
+ number of snapshots to 20.
+
+ * UIProcess/mac/ViewGestureController.mm:
+ (WebKit::ViewGestureController::beginSwipeGesture):
+ When beginning a gesture, attempt to make the retrieved snapshot
+ non-volatile. If it was purged while volatile, we won't use it, but if it
+ is still valid, we'll go ahead and use it as the swipe layer's contents.
+
+ (WebKit::ViewGestureController::removeSwipeSnapshot):
+ When removing the swipe snapshot, make it volatile once again.
+
+ * UIProcess/mac/ViewSnapshotStore.h:
+ Store a creation time along with the image.
+ Store and return IOSurfaces instead of CGImages.
+ Store snapshots and render tree sizes separately, so that we can
+ throw away snapshots but keep the render tree sizes indefinitely.
+
+ * UIProcess/mac/ViewSnapshotStore.mm:
+ (WebKit::ViewSnapshotStore::pruneSnapshots):
+ Cap the number of snapshots we'll ever have live at 20.
+ Enforce this cap by first trying to remove snapshots farthest
+ from the current back-forward list's current item, falling back
+ to removing the least recently created snapshot if there are no
+ snapshots owned by the current back-forward list.
+
+ (WebKit::createIOSurfaceFromImage):
+ Build an IOSurface from the CGImage snapshot we took, for ease of
+ use of its purgeability API.
+
+ (WebKit::ViewSnapshotStore::recordSnapshot):
+ Bail from taking the snapshot if the image is empty; this can happen
+ if the view is out of the window when the snapshot is taken.
+
+ Mark snapshots as purgeable as soon as they go into the cache.
+
+ (WebKit::ViewSnapshotStore::snapshotAndRenderTreeSize):
+ Return the target render tree size even if there is no snapshot image.
+ Take care not to look up an empty UUID.
+
2014-01-27 Alexey Proskuryakov <a...@apple.com>
Expose SQL database creation and modification times
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.mm (162945 => 162946)
--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.mm 2014-01-28 19:26:21 UTC (rev 162945)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.mm 2014-01-28 19:33:49 UTC (rev 162946)
@@ -41,6 +41,18 @@
#import <QuartzCore/QuartzCore.h>
#import <WebCore/WebCoreCALayerExtras.h>
+#if defined(__has_include) && __has_include(<IOSurface/IOSurfacePrivate.h>)
+#import <IOSurface/IOSurfacePrivate.h>
+#else
+enum {
+ kIOSurfacePurgeableNonVolatile = 0,
+ kIOSurfacePurgeableVolatile = 1,
+ kIOSurfacePurgeableEmpty = 2,
+};
+#endif
+
+extern "C" IOReturn IOSurfaceSetPurgeable(IOSurfaceRef buffer, uint32_t newState, uint32_t *oldState);
+
#if defined(__has_include) && __has_include(<QuartzCore/QuartzCorePrivate.h>)
#import <QuartzCore/QuartzCorePrivate.h>
#else
@@ -279,8 +291,16 @@
m_swipeSnapshotLayer = adoptNS([[CALayer alloc] init]);
[m_swipeSnapshotLayer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
- RetainPtr<CGImageRef> snapshot = ViewSnapshotStore::shared().snapshotAndRenderTreeSize(targetItem).first;
- [m_swipeSnapshotLayer setContents:(id)snapshot.get()];
+ RetainPtr<IOSurfaceRef> snapshot = ViewSnapshotStore::shared().snapshotAndRenderTreeSize(targetItem).first;
+
+ if (snapshot) {
+ uint32_t purgeabilityState;
+ IOSurfaceSetPurgeable(snapshot.get(), kIOSurfacePurgeableNonVolatile, &purgeabilityState);
+
+ if (purgeabilityState != kIOSurfacePurgeableEmpty)
+ [m_swipeSnapshotLayer setContents:(id)snapshot.get()];
+ }
+
[m_swipeSnapshotLayer setContentsGravity:kCAGravityTopLeft];
[m_swipeSnapshotLayer setContentsScale:m_webPageProxy.deviceScaleFactor()];
[m_swipeSnapshotLayer setFrame:rootLayer.frame];
@@ -383,6 +403,10 @@
if (m_activeGestureType != ViewGestureType::Swipe)
return;
+ IOSurfaceRef snapshotSurface = (IOSurfaceRef)[m_swipeSnapshotLayer contents];
+ if (snapshotSurface)
+ IOSurfaceSetPurgeable(snapshotSurface, kIOSurfacePurgeableVolatile, nullptr);
+
[m_webPageProxy.acceleratedCompositingRootLayer() setPosition:CGPointZero];
[m_swipeSnapshotLayer removeFromSuperlayer];
m_swipeSnapshotLayer = nullptr;
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h (162945 => 162946)
--- trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h 2014-01-28 19:26:21 UTC (rev 162945)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h 2014-01-28 19:33:49 UTC (rev 162946)
@@ -26,6 +26,7 @@
#ifndef ViewSnapshotStore_h
#define ViewSnapshotStore_h
+#include <chrono>
#include <wtf/HashMap.h>
#include <wtf/Noncopyable.h>
#include <wtf/RetainPtr.h>
@@ -45,14 +46,24 @@
static ViewSnapshotStore& shared();
void recordSnapshot(WebPageProxy&);
- std::pair<RetainPtr<CGImageRef>, uint64_t> snapshotAndRenderTreeSize(WebBackForwardListItem*);
+ std::pair<RetainPtr<IOSurfaceRef>, uint64_t> snapshotAndRenderTreeSize(WebBackForwardListItem*);
void disableSnapshotting() { m_enabled = false; }
void enableSnapshotting() { m_enabled = true; }
private:
- HashMap<String, std::pair<RetainPtr<CGImageRef>, uint64_t>> m_snapshotMap;
+ void pruneSnapshots(WebPageProxy&);
+ struct Snapshot {
+ RetainPtr<IOSurfaceRef> surface;
+ RetainPtr<CGContextRef> surfaceContext;
+
+ std::chrono::steady_clock::time_point creationTime;
+ };
+
+ HashMap<String, Snapshot> m_snapshotMap;
+ HashMap<String, uint64_t> m_renderTreeSizeMap;
+
bool m_enabled;
};
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm (162945 => 162946)
--- trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm 2014-01-28 19:26:21 UTC (rev 162945)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm 2014-01-28 19:33:49 UTC (rev 162946)
@@ -29,10 +29,29 @@
#import "WebBackForwardList.h"
#import "WebPageProxy.h"
#import <CoreGraphics/CoreGraphics.h>
+#import <IOSurface/IOSurface.h>
#import <WebCore/UUID.h>
+#if defined(__has_include) && __has_include(<CoreGraphics/CoreGraphicsPrivate.h>)
+#import <CoreGraphics/CoreGraphicsPrivate.h>
+#endif
+
+extern "C" CGContextRef CGIOSurfaceContextCreate(IOSurfaceRef surface, size_t width, size_t height, size_t bitsPerComponent, size_t bitsPerPixel, CGColorSpaceRef space, CGBitmapInfo bitmapInfo);
+
+#if defined(__has_include) && __has_include(<IOSurface/IOSurfacePrivate.h>)
+#import <IOSurface/IOSurfacePrivate.h>
+#else
+enum {
+ kIOSurfacePurgeableVolatile = 1,
+};
+#endif
+
+extern "C" IOReturn IOSurfaceSetPurgeable(IOSurfaceRef buffer, uint32_t newState, uint32_t *oldState);
+
using namespace WebCore;
+static const int maximumSnapshotCount = 20;
+
namespace WebKit {
ViewSnapshotStore::ViewSnapshotStore()
@@ -50,9 +69,86 @@
return store;
}
+void ViewSnapshotStore::pruneSnapshots(WebPageProxy& webPageProxy)
+{
+ if (m_snapshotMap.size() <= maximumSnapshotCount)
+ return;
+
+ uint32_t currentIndex = webPageProxy.backForwardList().currentIndex();
+ uint32_t maxDistance = 0;
+ WebBackForwardListItem* mostDistantSnapshottedItem = nullptr;
+ auto backForwardEntries = webPageProxy.backForwardList().entries();
+
+ // First, try to evict the snapshot for the page farthest from the current back-forward item.
+ for (uint32_t i = 0, entryCount = webPageProxy.backForwardList().entries().size(); i < entryCount; i++) {
+ uint32_t distance = std::max(currentIndex, i) - std::min(currentIndex, i);
+
+ if (i == currentIndex || distance < maxDistance)
+ continue;
+
+ WebBackForwardListItem* item = backForwardEntries[i].get();
+ String snapshotUUID = item->snapshotUUID();
+ if (!snapshotUUID.isEmpty() && m_snapshotMap.contains(snapshotUUID)) {
+ mostDistantSnapshottedItem = item;
+ maxDistance = distance;
+ }
+ }
+
+ if (mostDistantSnapshottedItem) {
+ m_snapshotMap.remove(mostDistantSnapshottedItem->snapshotUUID());
+ return;
+ }
+
+ // If we can't find a most distant item (perhaps because all the snapshots are from
+ // a different WebPageProxy's back-forward list), we should evict the the oldest item.
+ std::chrono::steady_clock::time_point oldestSnapshotTime = std::chrono::steady_clock::time_point::max();
+ String oldestSnapshotUUID;
+
+ for (const auto& uuidAndSnapshot : m_snapshotMap) {
+ if (uuidAndSnapshot.value.creationTime < oldestSnapshotTime) {
+ oldestSnapshotTime = uuidAndSnapshot.value.creationTime;
+ oldestSnapshotUUID = uuidAndSnapshot.key;
+ }
+ }
+
+ m_snapshotMap.remove(oldestSnapshotUUID);
+}
+
+static std::pair<RetainPtr<IOSurfaceRef>, RetainPtr<CGContextRef>> createIOSurfaceFromImage(CGImageRef image)
+{
+ unsigned pixelFormat = 'BGRA';
+ size_t bitsPerComponent = 8;
+ size_t bitsPerPixel = 32;
+ size_t bytesPerElement = 4;
+ size_t width = CGImageGetWidth(image);
+ size_t height = CGImageGetHeight(image);
+ size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
+ ASSERT(bytesPerRow);
+
+ size_t allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
+ ASSERT(allocSize);
+
+ NSDictionary *properties = @{
+ (id)kIOSurfaceWidth: @(width),
+ (id)kIOSurfaceHeight: @(height),
+ (id)kIOSurfacePixelFormat: @(pixelFormat),
+ (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
+ (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
+ (id)kIOSurfaceAllocSize: @(allocSize)
+ };
+
+ RetainPtr<IOSurfaceRef> surface = adoptCF(IOSurfaceCreate((CFDictionaryRef)properties));
+ CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+ RetainPtr<CGContextRef> surfaceContext = adoptCF(CGIOSurfaceContextCreate(surface.get(), width, height, bitsPerComponent, bitsPerPixel, CGImageGetColorSpace(image), bitmapInfo));
+ CGContextDrawImage(surfaceContext.get(), CGRectMake(0, 0, width, height), image);
+ CGContextFlush(surfaceContext.get());
+
+ return std::make_pair(surface, surfaceContext);
+}
+
void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy)
{
- RefPtr<WebBackForwardListItem> item = webPageProxy.backForwardList().currentItem();
+ WebBackForwardListItem* item = webPageProxy.backForwardList().currentItem();
if (!m_enabled)
return;
@@ -60,24 +156,49 @@
if (!item)
return;
- RetainPtr<CGImageRef> snapshot = webPageProxy.takeViewSnapshot();
+ RetainPtr<CGImageRef> snapshotImage = webPageProxy.takeViewSnapshot();
+ if (!snapshotImage)
+ return;
+ pruneSnapshots(webPageProxy);
+
String oldSnapshotUUID = item->snapshotUUID();
- if (!oldSnapshotUUID.isEmpty())
+ if (!oldSnapshotUUID.isEmpty()) {
m_snapshotMap.remove(oldSnapshotUUID);
+ m_renderTreeSizeMap.remove(oldSnapshotUUID);
+ }
item->setSnapshotUUID(createCanonicalUUIDString());
- m_snapshotMap.add(item->snapshotUUID(), std::make_pair(snapshot, webPageProxy.renderTreeSize()));
+
+ auto surfaceAndContext = createIOSurfaceFromImage(snapshotImage.get());
+
+ Snapshot snapshot;
+ snapshot.surface = surfaceAndContext.first;
+ snapshot.surfaceContext = surfaceAndContext.second;
+ snapshot.creationTime = std::chrono::steady_clock::now();
+
+ IOSurfaceSetPurgeable(snapshot.surface.get(), kIOSurfacePurgeableVolatile, nullptr);
+
+ m_snapshotMap.add(item->snapshotUUID(), snapshot);
+ m_renderTreeSizeMap.add(item->snapshotUUID(), webPageProxy.renderTreeSize());
}
-std::pair<RetainPtr<CGImageRef>, uint64_t> ViewSnapshotStore::snapshotAndRenderTreeSize(WebBackForwardListItem* item)
+std::pair<RetainPtr<IOSurfaceRef>, uint64_t> ViewSnapshotStore::snapshotAndRenderTreeSize(WebBackForwardListItem* item)
{
- const auto& snapshotAndRenderTreeSize = m_snapshotMap.find(item->snapshotUUID());
+ if (item->snapshotUUID().isEmpty())
+ return std::make_pair(nullptr, 0);
- if (snapshotAndRenderTreeSize == m_snapshotMap.end())
+ const auto& renderTreeSize = m_renderTreeSizeMap.find(item->snapshotUUID());
+ if (renderTreeSize == m_renderTreeSizeMap.end())
return std::make_pair(nullptr, 0);
- return snapshotAndRenderTreeSize->value;
+ const auto& snapshot = m_snapshotMap.find(item->snapshotUUID());
+ RetainPtr<IOSurfaceRef> surface;
+
+ if (snapshot != m_snapshotMap.end())
+ surface = snapshot->value.surface;
+
+ return std::make_pair(surface, renderTreeSize->value);
}
} // namespace WebKit