Diff
Modified: trunk/Source/WebKit2/ChangeLog (168688 => 168689)
--- trunk/Source/WebKit2/ChangeLog 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/ChangeLog 2014-05-13 18:11:46 UTC (rev 168689)
@@ -1,3 +1,54 @@
+2014-05-13 Tim Horton <[email protected]>
+
+ WebKit2 View Gestures (Swipe): Encode snapshots as JPEG on some platforms
+ https://bugs.webkit.org/show_bug.cgi?id=127788
+ <rdar://problem/15928241>
+
+ Reviewed by Anders Carlsson.
+
+ Allow JPEG snapshots. Compression is done asynchronously.
+ Enable JPEG snapshots (for now) on all PLATFORM(MAC).
+ We'll switch back to IOSurface snapshots on 10.9+ after <rdar://problem/16734031> is resolved.
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ * UIProcess/API/mac/WKView.mm:
+ (-[WKView _takeViewSnapshot]):
+ Store the image size on the ViewSnapshot.
+ Move IOSurface-related code to ViewSnapshotStore.
+
+ * UIProcess/ios/ViewGestureControllerIOS.mm:
+ (WebKit::ViewGestureController::beginSwipeGesture):
+ Adopt asLayerContents().
+
+ * UIProcess/mac/ViewGestureController.h:
+ * UIProcess/mac/ViewGestureControllerMac.mm:
+ (WebKit::ViewGestureController::retrieveSnapshotForItem):
+ (WebKit::ViewGestureController::beginSwipeGesture):
+ (WebKit::ViewGestureController::removeSwipeSnapshot):
+ Adopt asLayerContents().
+ Put the snapshot on the right layer (the parent of the snapshot image layer);
+ if it's on the snapshot image layer itself, if there is no image, the shadow
+ is displayed on top of the white placeholder.
+ Only reset snapshot purgeability if we're using IOSurface snapshots.
+
+ * UIProcess/mac/ViewSnapshotStore.h:
+ Add a bunch of macros to allow us to switch the snapshot backing store implementation.
+
+ * UIProcess/mac/ViewSnapshotStore.mm:
+ (WebKit::ViewSnapshotStore::ViewSnapshotStore):
+ (WebKit::ViewSnapshotStore::~ViewSnapshotStore):
+ (WebKit::ViewSnapshotStore::recordSnapshot):
+ If takeViewSnapshot() fails, don't remove the existing snapshot.
+
+ (WebKit::createIOSurfaceFromImage):
+ (WebKit::compressImageAsJPEG):
+ (WebKit::ViewSnapshotStore::reduceSnapshotMemoryCost):
+ (WebKit::ViewSnapshotStore::didCompressSnapshot):
+ (WebKit::ViewSnapshot::clearImage):
+ (WebKit::ViewSnapshot::asLayerContents):
+ Asynchronously compress snapshots - if we're using JPEG snapshots - for a
+ very large memory win (~20-30x).
+
2014-05-12 Darin Adler <[email protected]>
Make a few icon database improvements
Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm 2014-05-13 18:11:46 UTC (rev 168689)
@@ -511,6 +511,7 @@
CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, snapshot.slotID, 0, 0, &transform);
+ snapshot.size = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
snapshot.imageSizeInBytes = snapshotSize.width * snapshotSize.height * 4;
return snapshot;
Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm 2014-05-13 18:11:46 UTC (rev 168689)
@@ -3040,19 +3040,6 @@
return _data->_rootLayer.get();
}
-static RefPtr<IOSurface> createIOSurfaceFromImage(CGImageRef image)
-{
- size_t width = CGImageGetWidth(image);
- size_t height = CGImageGetHeight(image);
-
- RefPtr<IOSurface> surface = IOSurface::create(IntSize(width, height), ColorSpaceDeviceRGB);
- RetainPtr<CGContextRef> surfaceContext = surface->ensurePlatformContext();
- CGContextDrawImage(surfaceContext.get(), CGRectMake(0, 0, width, height), image);
- CGContextFlush(surfaceContext.get());
-
- return surface;
-}
-
- (ViewSnapshot)_takeViewSnapshot
{
NSWindow *window = self.window;
@@ -3091,8 +3078,12 @@
auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
- snapshot.surface = createIOSurfaceFromImage(croppedSnapshotImage.get());
- snapshot.imageSizeInBytes = snapshot.surface->totalBytes();
+ snapshot.image = croppedSnapshotImage.get();
+
+ IntSize imageSize(CGImageGetWidth(croppedSnapshotImage.get()), CGImageGetHeight(croppedSnapshotImage.get()));
+ snapshot.size = imageSize;
+ snapshot.imageSizeInBytes = imageSize.width() * imageSize.height() * 4;
+
return snapshot;
}
Modified: trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm 2014-05-13 18:11:46 UTC (rev 168689)
@@ -158,7 +158,7 @@
ViewSnapshot snapshot;
if (ViewSnapshotStore::shared().getSnapshot(targetItem, snapshot) && snapshot.hasImage())
- [m_snapshotView layer].contents = [CAContext objectForSlot:snapshot.slotID];
+ [m_snapshotView layer].contents = snapshot.asLayerContents();
[m_snapshotView setBackgroundColor:[UIColor whiteColor]];
[m_snapshotView layer].contentsGravity = kCAGravityTopLeft;
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h 2014-05-13 18:11:46 UTC (rev 168689)
@@ -50,6 +50,7 @@
namespace WebKit {
+struct ViewSnapshot;
class WebBackForwardListItem;
class WebPageProxy;
@@ -131,7 +132,7 @@
void endSwipeGesture(WebBackForwardListItem* targetItem, bool cancelled);
bool deltaIsSufficientToBeginSwipe(NSEvent *);
bool scrollEventCanBecomeSwipe(NSEvent *, SwipeDirection&);
- WebCore::IOSurface* retrieveSnapshotForItem(WebBackForwardListItem*, WebCore::FloatSize swipeLayerSize, float topContentInset);
+ bool retrieveSnapshotForItem(WebBackForwardListItem* targetItem, WebCore::FloatSize swipeLayerSize, float topContentInset, ViewSnapshot&);
CALayer *determineSnapshotLayerParent() const;
CALayer *determineLayerAdjacentToSnapshotForParent(SwipeDirection, CALayer *snapshotLayerParent) const;
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm 2014-05-13 18:11:46 UTC (rev 168689)
@@ -451,28 +451,21 @@
return layerAdjacentToSnapshot;
}
-IOSurface* ViewGestureController::retrieveSnapshotForItem(WebBackForwardListItem* targetItem, FloatSize swipeLayerSize, float topContentInset)
+bool ViewGestureController::retrieveSnapshotForItem(WebBackForwardListItem* targetItem, FloatSize swipeLayerSize, float topContentInset, ViewSnapshot& snapshot)
{
- ViewSnapshot snapshot;
if (!ViewSnapshotStore::shared().getSnapshot(targetItem, snapshot))
- return nullptr;
+ return false;
- if (!snapshot.surface)
- return nullptr;
-
float deviceScaleFactor = m_webPageProxy.deviceScaleFactor();
if (snapshot.deviceScaleFactor != deviceScaleFactor)
- return nullptr;
+ return false;
FloatSize unobscuredSwipeLayerSizeInDeviceCoordinates = swipeLayerSize - FloatSize(0, topContentInset);
unobscuredSwipeLayerSizeInDeviceCoordinates.scale(deviceScaleFactor);
- if (snapshot.surface->size() != unobscuredSwipeLayerSizeInDeviceCoordinates)
- return nullptr;
+ if (snapshot.size != unobscuredSwipeLayerSizeInDeviceCoordinates)
+ return false;
- if (snapshot.surface->setIsVolatile(false) != IOSurface::SurfaceState::Valid)
- return nullptr;
-
- return snapshot.surface.get();
+ return true;
}
static bool layerGeometryFlippedToRoot(CALayer *layer)
@@ -535,10 +528,12 @@
CALayer *snapshotLayerParent = determineSnapshotLayerParent();
bool geometryIsFlippedToRoot = layerGeometryFlippedToRoot(snapshotLayerParent);
- IOSurface* snapshot = retrieveSnapshotForItem(targetItem, swipeArea.size(), topContentInset);
- if (snapshot) {
- m_currentSwipeSnapshotSurface = snapshot;
- [m_swipeSnapshotLayer setContents:(id)snapshot->surface()];
+ ViewSnapshot snapshot;
+ if (retrieveSnapshotForItem(targetItem, swipeArea.size(), topContentInset, snapshot)) {
+ [m_swipeSnapshotLayer setContents:snapshot.asLayerContents()];
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
+ m_currentSwipeSnapshotSurface = snapshot.surface;
+#endif
}
[m_swipeLayer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
@@ -571,11 +566,11 @@
[rootContentLayer setShadowRadius:swipeOverlayShadowRadius];
[rootContentLayer setShadowPath:shadowPath.get()];
} else {
- RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect([m_swipeSnapshotLayer bounds], 0));
- [m_swipeSnapshotLayer setShadowColor:CGColorGetConstantColor(kCGColorBlack)];
- [m_swipeSnapshotLayer setShadowOpacity:swipeOverlayShadowOpacity];
- [m_swipeSnapshotLayer setShadowRadius:swipeOverlayShadowRadius];
- [m_swipeSnapshotLayer setShadowPath:shadowPath.get()];
+ RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect([m_swipeLayer bounds], 0));
+ [m_swipeLayer setShadowColor:CGColorGetConstantColor(kCGColorBlack)];
+ [m_swipeLayer setShadowOpacity:swipeOverlayShadowOpacity];
+ [m_swipeLayer setShadowRadius:swipeOverlayShadowRadius];
+ [m_swipeLayer setShadowPath:shadowPath.get()];
}
}
@@ -671,9 +666,11 @@
if (m_activeGestureType != ViewGestureType::Swipe)
return;
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
if (m_currentSwipeSnapshotSurface)
m_currentSwipeSnapshotSurface->setIsVolatile(true);
m_currentSwipeSnapshotSurface = nullptr;
+#endif
for (const auto& layer : m_currentSwipeLiveLayers)
[layer setTransform:CATransform3DIdentity];
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h 2014-05-13 18:11:46 UTC (rev 168689)
@@ -35,26 +35,40 @@
OBJC_CLASS CAContext;
+// FIXME: Enable IOSurface snapshots for 10.9+ when <rdar://problem/16734031> is resolved.
+#if PLATFORM(MAC)
+#define USE_JPEG_VIEW_SNAPSHOTS true
+#define USE_IOSURFACE_VIEW_SNAPSHOTS false
+#define USE_RENDER_SERVER_VIEW_SNAPSHOTS false
+#else
+#define USE_JPEG_VIEW_SNAPSHOTS false
+#define USE_IOSURFACE_VIEW_SNAPSHOTS false
+#define USE_RENDER_SERVER_VIEW_SNAPSHOTS true
+#endif
+
namespace WebKit {
class WebBackForwardListItem;
class WebPageProxy;
struct ViewSnapshot {
-#if PLATFORM(MAC)
+#if USE_JPEG_VIEW_SNAPSHOTS || USE_IOSURFACE_VIEW_SNAPSHOTS
RefPtr<WebCore::IOSurface> surface;
+ RetainPtr<CGImageRef> image;
#endif
-#if PLATFORM(IOS)
+#if USE_RENDER_SERVER_VIEW_SNAPSHOTS
uint32_t slotID = 0;
#endif
std::chrono::steady_clock::time_point creationTime;
uint64_t renderTreeSize;
float deviceScaleFactor;
+ WebCore::IntSize size;
size_t imageSizeInBytes = 0;
void clearImage();
bool hasImage() const;
+ id asLayerContents();
};
class ViewSnapshotStore {
@@ -80,7 +94,13 @@
private:
void pruneSnapshots(WebPageProxy&);
void removeSnapshotImage(ViewSnapshot&);
+ void reduceSnapshotMemoryCost(const String& uuid);
+#if USE_JPEG_VIEW_SNAPSHOTS
+ void didCompressSnapshot(const String& uuid, RetainPtr<CGImageRef> newImage, size_t newImageSize);
+ dispatch_queue_t m_compressionQueue;
+#endif
+
HashMap<String, ViewSnapshot> m_snapshotMap;
bool m_enabled;
Modified: trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm (168688 => 168689)
--- trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm 2014-05-13 18:01:29 UTC (rev 168688)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm 2014-05-13 18:11:46 UTC (rev 168689)
@@ -38,10 +38,10 @@
using namespace WebCore;
-#if PLATFORM(MAC)
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
static const size_t maximumSnapshotCacheSize = 400 * (1024 * 1024);
-#elif PLATFORM(IOS)
-// Because snapshots are not purgeable, we should keep fewer around.
+#elif USE_JPEG_VIEW_SNAPSHOTS || USE_RENDER_SERVER_VIEW_SNAPSHOTS
+// Because non-IOSurface snapshots are not purgeable, we should keep fewer around.
static const size_t maximumSnapshotCacheSize = 50 * (1024 * 1024);
#endif
@@ -51,10 +51,16 @@
: m_enabled(true)
, m_snapshotCacheSize(0)
{
+#if USE_JPEG_VIEW_SNAPSHOTS
+ m_compressionQueue = dispatch_queue_create("com.apple.WebKit.ViewSnapshotStore.CompressionQueue", nullptr);
+#endif
}
ViewSnapshotStore::~ViewSnapshotStore()
{
+#if USE_JPEG_VIEW_SNAPSHOTS
+ dispatch_release(m_compressionQueue);
+#endif
discardSnapshots();
}
@@ -158,6 +164,10 @@
pruneSnapshots(webPageProxy);
+ ViewSnapshot snapshot = webPageProxy.takeViewSnapshot();
+ if (!snapshot.hasImage())
+ return;
+
String oldSnapshotUUID = item->snapshotUUID();
if (!oldSnapshotUUID.isEmpty()) {
const auto& oldSnapshotIter = m_snapshotMap.find(oldSnapshotUUID);
@@ -167,15 +177,16 @@
}
}
- item->setSnapshotUUID(createCanonicalUUIDString());
-
- ViewSnapshot snapshot = webPageProxy.takeViewSnapshot();
snapshot.creationTime = std::chrono::steady_clock::now();
snapshot.renderTreeSize = webPageProxy.renderTreeSize();
snapshot.deviceScaleFactor = webPageProxy.deviceScaleFactor();
+ item->setSnapshotUUID(createCanonicalUUIDString());
+
m_snapshotMap.add(item->snapshotUUID(), snapshot);
m_snapshotCacheSize += snapshot.imageSizeInBytes;
+
+ reduceSnapshotMemoryCost(item->snapshotUUID());
}
bool ViewSnapshotStore::getSnapshot(WebBackForwardListItem* item, ViewSnapshot& snapshot)
@@ -196,6 +207,84 @@
removeSnapshotImage(snapshot);
}
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
+static RefPtr<IOSurface> createIOSurfaceFromImage(CGImageRef image)
+{
+ size_t width = CGImageGetWidth(image);
+ size_t height = CGImageGetHeight(image);
+
+ RefPtr<IOSurface> surface = IOSurface::create(IntSize(width, height), ColorSpaceDeviceRGB);
+ RetainPtr<CGContextRef> surfaceContext = surface->ensurePlatformContext();
+ CGContextDrawImage(surfaceContext.get(), CGRectMake(0, 0, width, height), image);
+ CGContextFlush(surfaceContext.get());
+
+ surface->setIsVolatile(true);
+
+ return surface;
+}
+#endif
+
+#if USE_JPEG_VIEW_SNAPSHOTS
+static std::pair<RetainPtr<CGImageRef>, size_t> compressImageAsJPEG(CGImageRef image)
+{
+ RetainPtr<NSData> compressedData = adoptNS([[NSMutableData alloc] init]);
+ RetainPtr<CGImageDestinationRef> destination = adoptCF(CGImageDestinationCreateWithData((CFMutableDataRef)compressedData.get(), kUTTypeJPEG, 1, 0));
+
+ RetainPtr<NSDictionary> options = @{
+ (NSString*)kCGImageDestinationLossyCompressionQuality: @0.9
+ };
+
+ CGImageDestinationAddImage(destination.get(), image, (CFDictionaryRef)options.get());
+ CGImageDestinationFinalize(destination.get());
+
+ RetainPtr<CGImageSourceRef> imageSourceRef = adoptCF(CGImageSourceCreateWithData((CFDataRef)compressedData.get(), 0));
+ RetainPtr<CGImageRef> compressedSnapshot = adoptCF(CGImageSourceCreateImageAtIndex(imageSourceRef.get(), 0, 0));
+
+ return std::make_pair(compressedSnapshot, [compressedData length]);
+}
+#endif
+
+void ViewSnapshotStore::reduceSnapshotMemoryCost(const String& uuid)
+{
+ const auto& snapshotIterator = m_snapshotMap.find(uuid);
+ if (snapshotIterator == m_snapshotMap.end())
+ return;
+ ViewSnapshot& snapshot = snapshotIterator->value;
+ if (!snapshot.hasImage())
+ return;
+
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
+ snapshot.surface = createIOSurfaceFromImage(snapshot.image.get());
+ snapshot.image = nullptr;
+#elif USE_JPEG_VIEW_SNAPSHOTS
+ RetainPtr<CGImageRef> originalImage = snapshot.image.get();
+ dispatch_async(m_compressionQueue, [uuid, originalImage] {
+ auto imageAndBytes = compressImageAsJPEG(originalImage.get());
+ dispatch_async(dispatch_get_main_queue(), [uuid, imageAndBytes] {
+ ViewSnapshotStore::shared().didCompressSnapshot(uuid, imageAndBytes.first, imageAndBytes.second);
+ });
+ });
+#endif
+}
+
+#if USE_JPEG_VIEW_SNAPSHOTS
+void ViewSnapshotStore::didCompressSnapshot(const String& uuid, RetainPtr<CGImageRef> newImage, size_t newImageSize)
+{
+ const auto& snapshotIterator = m_snapshotMap.find(uuid);
+ if (snapshotIterator == m_snapshotMap.end())
+ return;
+ ViewSnapshot& snapshot = snapshotIterator->value;
+
+ size_t originalImageSize = snapshot.imageSizeInBytes;
+ if (!newImage || newImageSize > originalImageSize)
+ return;
+ snapshot.image = newImage;
+ snapshot.imageSizeInBytes = newImageSize;
+ m_snapshotCacheSize += newImageSize;
+ m_snapshotCacheSize -= originalImageSize;
+}
+#endif
+
bool ViewSnapshot::hasImage() const
{
return imageSizeInBytes;
@@ -203,9 +292,13 @@
void ViewSnapshot::clearImage()
{
-#if PLATFORM(MAC)
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
surface = nullptr;
-#elif PLATFORM(IOS)
+#endif
+#if USE_IOSURFACE_VIEW_SNAPSHOTS || USE_JPEG_VIEW_SNAPSHOTS
+ image = nullptr;
+#endif
+#if USE_RENDER_SERVER_VIEW_SNAPSHOTS
if (slotID)
[ViewSnapshotStore::snapshottingContext() deleteSlot:slotID];
slotID = 0;
@@ -213,4 +306,22 @@
imageSizeInBytes = 0;
}
+id ViewSnapshot::asLayerContents()
+{
+#if USE_IOSURFACE_VIEW_SNAPSHOTS
+ if (surface) {
+ if (surface->setIsVolatile(false) != IOSurface::SurfaceState::Valid)
+ return nullptr;
+
+ return (id)surface->surface();
+ }
+#endif
+#if USE_IOSURFACE_VIEW_SNAPSHOTS || USE_JPEG_VIEW_SNAPSHOTS
+ return (id)image.get();
+#endif
+#if USE_RENDER_SERVER_VIEW_SNAPSHOTS
+ return [CAContext objectForSlot:slotID];
+#endif
+}
+
} // namespace WebKit