Diff
Modified: trunk/LayoutTests/ChangeLog (160944 => 160945)
--- trunk/LayoutTests/ChangeLog 2013-12-21 00:35:44 UTC (rev 160944)
+++ trunk/LayoutTests/ChangeLog 2013-12-21 00:43:27 UTC (rev 160945)
@@ -1,3 +1,18 @@
+2013-12-09 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Allow ImageBuffer to re-use IOSurfaces
+ https://bugs.webkit.org/show_bug.cgi?id=125477
+
+ Reviewed by Geoff Garen.
+
+ Now that we're re-using the backing store of canvases, this
+ test makes sure that if we draw to a canvas, then destroy it,
+ then create a new canvas (which should share the same backing
+ store) that it doesn't have the stale data in it
+
+ * fast/canvas/canvas-backing-store-reuse-expected.txt: Added.
+ * fast/canvas/canvas-backing-store-reuse.html: Added.
+
2013-12-19 Antti Koivisto <an...@apple.com>
Create render tree lazily
Added: trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt (0 => 160945)
--- trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt 2013-12-21 00:43:27 UTC (rev 160945)
@@ -0,0 +1,5 @@
+PASS data.data is [0, 0, 0, 0]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse.html (0 => 160945)
--- trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse.html (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-backing-store-reuse.html 2013-12-21 00:43:27 UTC (rev 160945)
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+(function() {
+ var canvas = document.createElement("canvas");
+ canvas.width = 99;
+ canvas.height = 99;
+ var context = canvas.getContext("2d");
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, 100, 100);
+})();
+
+if (window.GCController)
+ GCController.collect();
+
+var canvas = document.createElement("canvas");
+canvas.width = 95;
+canvas.height = 95;
+var context = canvas.getContext("2d");
+var data = "" 50, 1, 1);
+shouldBe("data.data", "[0, 0, 0, 0]");
+document.body.appendChild(canvas);
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/PerformanceTests/Canvas/reuse.html (0 => 160945)
--- trunk/PerformanceTests/Canvas/reuse.html (rev 0)
+++ trunk/PerformanceTests/Canvas/reuse.html 2013-12-21 00:43:27 UTC (rev 160945)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+
+var numCreated = 1000;
+
+function testCreation() {
+ (function() {
+ var canvases = [];
+ for (var i = 0; i < numCreated; i += 16) {
+ var canvas = document.createElement("canvas");
+ canvas.width = i;
+ canvas.height = i;
+ var context = canvas.getContext("2d");
+ context.fillRect(0, 0, 1, 1);
+ canvases.push(canvas);
+ }
+ })();
+
+ if (window.GCController)
+ window.GCController.collect();
+}
+
+PerfTestRunner.measureRunsPerSecond({run: function() {
+ testCreation();
+}});
+</script>
+</body>
+</html>
Modified: trunk/PerformanceTests/ChangeLog (160944 => 160945)
--- trunk/PerformanceTests/ChangeLog 2013-12-21 00:35:44 UTC (rev 160944)
+++ trunk/PerformanceTests/ChangeLog 2013-12-21 00:43:27 UTC (rev 160945)
@@ -1,3 +1,16 @@
+2013-12-09 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Allow ImageBuffer to re-use IOSurfaces
+ https://bugs.webkit.org/show_bug.cgi?id=125477
+
+ Reviewed by Geoff Garen.
+
+ This test times creating a variety of different sizes of canvases
+ once some have already been created. The second creation of the
+ canvases should re-use the existing IOSurfaces.
+
+ * Canvas/reuse.html: Added.
+
2013-12-15 Ryosuke Niwa <rn...@webkit.org>
REGRESSION: 2x regression on Dromaeo DOM query tests
Modified: trunk/Source/WebCore/ChangeLog (160944 => 160945)
--- trunk/Source/WebCore/ChangeLog 2013-12-21 00:35:44 UTC (rev 160944)
+++ trunk/Source/WebCore/ChangeLog 2013-12-21 00:43:27 UTC (rev 160945)
@@ -1,3 +1,49 @@
+2013-12-09 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Allow ImageBuffer to re-use IOSurfaces
+ https://bugs.webkit.org/show_bug.cgi?id=125477
+
+ Reviewed by Geoff Garen.
+
+ This test adds a static class, ImageBufferBackingStoreCache, that vends
+ IOSurfaces. It remembers IOSurfaces that have been returned to it until
+ a configurable timeout.
+
+ The storage used by this class is in the form of a HashMap from a
+ bucketed size to the IOSurface. There are many other data structures
+ that could be used, but this implementation gives a 80% hit rate on
+ normal browsing of some example sites with Canvas and
+ text-decoration-skip: ink. Because the buckets are fairly
+ small (rounding the width and height up to multiples of 8), traversing the
+ bucket contents takes on average 2 steps.
+
+ Test: fast/canvas/canvas-backing-store-reuse.html
+
+ * WebCore.xcodeproj/project.pbxproj: Added new caching class
+ * platform/graphics/cg/ImageBufferBackingStoreCache.cpp: Added.
+ (WebCore::createIOSurface): Moved from ImageBufferCG.cpp
+ (WebCore::ImageBufferBackingStoreCache::timerFired): Forget the cache
+ contents
+ (WebCore::ImageBufferBackingStoreCache::schedulePurgeTimer):
+ (WebCore::ImageBufferBackingStoreCache::get): Static getter
+ (WebCore::ImageBufferBackingStoreCache::ImageBufferBackingStoreCache):
+ (WebCore::ImageBufferBackingStoreCache::insertIntoCache): Memory-management
+ creation function
+ (WebCore::ImageBufferBackingStoreCache::takeFromCache): Memory-management
+ deletion function
+ (WebCore::ImageBufferBackingStoreCache::isAcceptableSurface): Does this cached
+ IOSurface fit the bill?
+ (WebCore::ImageBufferBackingStoreCache::tryTakeFromCache): Lookup
+ a bucket and walk through its contents
+ (WebCore::ImageBufferBackingStoreCache::getOrAllocate): Public function
+ for clients who want a IOSurface from the cache
+ (WebCore::ImageBufferBackingStoreCache::deallocate): Public
+ function for clients to return an IOSurface to the pool
+ * platform/graphics/cg/ImageBufferBackingStoreCache.h: Added.
+ * platform/graphics/cg/ImageBufferCG.cpp: Update to use new cache
+ (WebCore::ImageBuffer::ImageBuffer):
+ (WebCore::ImageBuffer::~ImageBuffer):
+
2013-12-20 Simon Fraser <simon.fra...@apple.com>
Change "threaded scrolling" terminology to "asynchronous scrolling"
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (160944 => 160945)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2013-12-21 00:35:44 UTC (rev 160944)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2013-12-21 00:43:27 UTC (rev 160945)
@@ -845,6 +845,8 @@
1C11CCC80AA6093700DADB20 /* DOMHTMLElement.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 85DF2EEB0AA387CB00AD64C5 /* DOMHTMLElement.h */; };
1C18DA58181AF6A500C4EF22 /* TextPainter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C18DA56181AF6A500C4EF22 /* TextPainter.cpp */; };
1C18DA59181AF6A500C4EF22 /* TextPainter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C18DA57181AF6A500C4EF22 /* TextPainter.h */; };
+ 1C21E57C183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C21E57A183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp */; };
+ 1C21E57D183ED1FF001C289D /* ImageBufferBackingStoreCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C21E57B183ED1FF001C289D /* ImageBufferBackingStoreCache.h */; };
1C26497A0D7E248A00BD10F2 /* DocumentLoaderMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C2649790D7E248A00BD10F2 /* DocumentLoaderMac.cpp */; };
1C26497C0D7E24EC00BD10F2 /* PageMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C26497B0D7E24EC00BD10F2 /* PageMac.cpp */; };
1C4C8F020AD85D87009475CE /* DeleteButtonController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C4C8F000AD85D87009475CE /* DeleteButtonController.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -7495,6 +7497,8 @@
1AFE11980CBFFCC4003017FA /* JSSQLResultSetRowList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSQLResultSetRowList.h; sourceTree = "<group>"; };
1C18DA56181AF6A500C4EF22 /* TextPainter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextPainter.cpp; sourceTree = "<group>"; };
1C18DA57181AF6A500C4EF22 /* TextPainter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextPainter.h; sourceTree = "<group>"; };
+ 1C21E57A183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageBufferBackingStoreCache.cpp; sourceTree = "<group>"; };
+ 1C21E57B183ED1FF001C289D /* ImageBufferBackingStoreCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageBufferBackingStoreCache.h; sourceTree = "<group>"; };
1C2649790D7E248A00BD10F2 /* DocumentLoaderMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentLoaderMac.cpp; sourceTree = "<group>"; };
1C26497B0D7E24EC00BD10F2 /* PageMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageMac.cpp; sourceTree = "<group>"; };
1C4C8EFF0AD85D87009475CE /* DeleteButtonController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeleteButtonController.cpp; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
@@ -19260,6 +19264,8 @@
1FC40FB81655C5910040F29E /* SubimageCacheWithTimer.cpp */,
1FC40FB71655C5910040F29E /* SubimageCacheWithTimer.h */,
B275352A0B053814002CE64F /* TransformationMatrixCG.cpp */,
+ 1C21E57A183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp */,
+ 1C21E57B183ED1FF001C289D /* ImageBufferBackingStoreCache.h */,
);
path = cg;
sourceTree = "<group>";
@@ -23673,6 +23679,7 @@
E1AD14231295EA7F00ACA989 /* JSHTMLInputElementCustom.h in Headers */,
A6148A7912E41E3B0044A784 /* JSHTMLKeygenElement.h in Headers */,
1AE2AB220A1CE63B00B42B25 /* JSHTMLLabelElement.h in Headers */,
+ 1C21E57D183ED1FF001C289D /* ImageBufferBackingStoreCache.h in Headers */,
1AE2AB240A1CE63B00B42B25 /* JSHTMLLegendElement.h in Headers */,
1AE2AB260A1CE63B00B42B25 /* JSHTMLLIElement.h in Headers */,
A80E7B0D0A19D606007FB8C5 /* JSHTMLLinkElement.h in Headers */,
@@ -26848,6 +26855,7 @@
B58CEB6A11913607002A6790 /* JSDatabaseSync.cpp in Sources */,
4162A4571011464700DFF3ED /* JSDedicatedWorkerGlobalScope.cpp in Sources */,
4162A454101145E300DFF3ED /* JSDedicatedWorkerGlobalScopeCustom.cpp in Sources */,
+ 1C21E57C183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp in Sources */,
FDA15ED112B03F94003A583A /* JSDelayNode.cpp in Sources */,
31FB1A65120A5D3F00DC02A0 /* JSDeviceMotionEvent.cpp in Sources */,
07C59B6317F4D1BF000FBCBB /* MockMediaStreamCenter.cpp in Sources */,
Added: trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp (0 => 160945)
--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp (rev 0)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp 2013-12-21 00:43:27 UTC (rev 160945)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageBufferBackingStoreCache.h"
+
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+#include <IOSurface/IOSurface.h>
+
+namespace WebCore {
+
+static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
+{
+ unsigned pixelFormat = 'BGRA';
+ unsigned bytesPerElement = 4;
+ int width = size.width();
+ int height = size.height();
+
+ unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
+ if (!bytesPerRow)
+ return 0;
+
+ unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
+ if (!allocSize)
+ return 0;
+
+ const int kNumCreationParameters = 6;
+ const void* keys[kNumCreationParameters];
+ const void* values[kNumCreationParameters];
+ keys[0] = kIOSurfaceWidth;
+ values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
+ keys[1] = kIOSurfaceHeight;
+ values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
+ keys[2] = kIOSurfacePixelFormat;
+ values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
+ keys[3] = kIOSurfaceBytesPerElement;
+ values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
+ keys[4] = kIOSurfaceBytesPerRow;
+ values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
+ keys[5] = kIOSurfaceAllocSize;
+ values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
+
+ RetainPtr<CFDictionaryRef> dict = adoptCF(CFDictionaryCreate(0, keys, values, kNumCreationParameters, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ for (unsigned i = 0; i < kNumCreationParameters; i++)
+ CFRelease(values[i]);
+
+ return adoptCF(IOSurfaceCreate(dict.get()));
+}
+
+ImageBufferBackingStoreCache& ImageBufferBackingStoreCache::get()
+{
+ DEFINE_STATIC_LOCAL(ImageBufferBackingStoreCache, cache, ());
+ return cache;
+}
+
+bool ImageBufferBackingStoreCache::isAcceptableSurface(const IOSurfaceAndContextWithCreationParams& info, const IntSize& requestedSize, CGColorSpaceRef colorSpace, bool needExactSize) const
+{
+ IOSurfaceRef surface = info.surface.get();
+ IntSize actualSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
+ if (!CFEqual(info.colorSpace.get(), colorSpace))
+ return false;
+ if (needExactSize && actualSize != requestedSize)
+ return false;
+ if (actualSize.width() < requestedSize.width() || actualSize.height() < requestedSize.height())
+ return false;
+ return true;
+}
+
+void ImageBufferBackingStoreCache::insertIntoCache(IOSurfaceAndContextWithCreationParams&& info)
+{
+ IOSurfaceRef surface = info.surface.get();
+ IntSize surfaceSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
+
+ auto toAdd = new IOSurfaceAndContextWithCreationParams(info);
+ auto insertedTuple = m_cachedSurfaces.add(convertSizeToKey(surfaceSize), InfoLinkedList());
+ insertedTuple.iterator->value.append(toAdd);
+
+ m_pixelsCached += surfaceSize.area();
+}
+
+auto ImageBufferBackingStoreCache::takeFromCache(CachedSurfaceMap::iterator iter, IOSurfaceAndContextWithCreationParams* info) -> IOSurfaceAndContextWithCreationParams
+{
+ ASSERT(info);
+ ASSERT(iter != m_cachedSurfaces.end());
+
+ IOSurfaceRef surface = info->surface.get();
+ m_pixelsCached -= IOSurfaceGetWidth(surface) * IOSurfaceGetHeight(surface);
+
+ iter->value.remove(info);
+ if (iter->value.isEmpty())
+ m_cachedSurfaces.remove(iter);
+ IOSurfaceAndContextWithCreationParams result = std::move(*info);
+ delete info;
+ return result;
+}
+
+bool ImageBufferBackingStoreCache::tryTakeFromCache(const IntSize& size, CGColorSpaceRef colorSpace, bool needExactSize, IOSurfaceAndContextWithCreationParams& outInfo)
+{
+ CachedSurfaceMap::iterator i = m_cachedSurfaces.find(convertSizeToKey(size));
+ if (i == m_cachedSurfaces.end())
+ return nullptr;
+ InfoLinkedList& ll = i->value;
+ for (auto info = ll.head(); info; info = info->next()) {
+ if (isAcceptableSurface(*info, size, colorSpace, needExactSize)) {
+ outInfo = takeFromCache(i, info);
+ return true;
+ }
+ }
+ return false;
+}
+
+ImageBufferBackingStoreCache::IOSurfaceAndContext ImageBufferBackingStoreCache::getOrAllocate(IntSize size, CGColorSpaceRef colorSpace, bool needExactSize)
+{
+ IOSurfaceAndContextWithCreationParams foundInfo;
+ if (tryTakeFromCache(size, colorSpace, needExactSize, foundInfo)) {
+ IOSurfaceRef surface = foundInfo.surface.get();
+ CGContextRef context = foundInfo.context.get();
+ CGContextSaveGState(context);
+ auto activeInserted = m_activeSurfaces.add(surface, std::move(foundInfo));
+ ASSERT(activeInserted.isNewEntry);
+ return activeInserted.iterator->value;
+ }
+
+ RetainPtr<IOSurfaceRef> surface = createIOSurface(size);
+ if (!surface.get())
+ return IOSurfaceAndContext();
+
+ RetainPtr<CGContextRef> context = adoptCF(wkIOSurfaceContextCreate(surface.get(), size.width(), size.height(), colorSpace));
+ if (!context.get())
+ return IOSurfaceAndContext();
+ CGContextSaveGState(context.get());
+
+ auto insertedTuple = m_activeSurfaces.add(surface, IOSurfaceAndContextWithCreationParams(surface.get(), context.get(), colorSpace));
+ ASSERT(insertedTuple.isNewEntry);
+
+ return insertedTuple.iterator->value;
+}
+
+void ImageBufferBackingStoreCache::deallocate(IOSurfaceRef surface)
+{
+ ActiveSurfaceMap::iterator lookup = m_activeSurfaces.find(surface);
+ ASSERT(lookup != m_activeSurfaces.end());
+
+ auto info = std::move(lookup->value);
+ m_activeSurfaces.remove(lookup);
+
+ IOSurfaceRef ioSurface = info.surface.get();
+ CGContextRef context = info.context.get();
+ IntSize surfaceSize(IOSurfaceGetWidth(ioSurface), IOSurfaceGetHeight(ioSurface));
+ int surfaceArea = surfaceSize.area();
+
+ static const int kMaxPixelsCached = 1024 * 1024 * 64; // 256MB
+ if (surfaceArea > kMaxPixelsCached)
+ return;
+
+ // Evict
+ auto bucket = m_cachedSurfaces.find(convertSizeToKey(surfaceSize));
+ if (bucket != m_cachedSurfaces.end()) {
+ for (int itemsInBucket = bucket->value.size();
+ itemsInBucket > 0 && m_pixelsCached + surfaceArea > kMaxPixelsCached;
+ --itemsInBucket)
+ takeFromCache(bucket, bucket->value.head());
+ }
+ while (m_pixelsCached + surfaceArea > kMaxPixelsCached) {
+ CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
+ takeFromCache(iter, iter->value.head());
+ }
+
+ CGContextRestoreGState(context);
+ // Clear opportunistically so CG has more time to carry it out.
+ CGContextClearRect(context, CGRectMake(0, 0, surfaceSize.width(), surfaceSize.height()));
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
+ CGContextFlush(context);
+#endif
+
+ insertIntoCache(std::move(info));
+
+ schedulePurgeTimer();
+}
+
+void ImageBufferBackingStoreCache::timerFired(Timer<ImageBufferBackingStoreCache>*)
+{
+ while (!m_cachedSurfaces.isEmpty()) {
+ CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
+ takeFromCache(iter, iter->value.head());
+ }
+}
+
+void ImageBufferBackingStoreCache::schedulePurgeTimer()
+{
+ if (m_purgeTimer.isActive())
+ m_purgeTimer.stop();
+
+ static const double purgeInterval = 5;
+ m_purgeTimer.startOneShot(purgeInterval);
+}
+
+}
+#endif // IOSURFACE_CANVAS_BACKING_STORE
Added: trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h (0 => 160945)
--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h (rev 0)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h 2013-12-21 00:43:27 UTC (rev 160945)
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ImageBufferBackingStoreCache_h
+#define ImageBufferBackingStoreCache_h
+
+#include "ImageBuffer.h"
+
+#include "Timer.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <wtf/DoublyLinkedList.h>
+#include <wtf/HashMap.h>
+
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+
+namespace WebCore {
+
+class ImageBufferBackingStoreCache {
+ WTF_MAKE_NONCOPYABLE(ImageBufferBackingStoreCache); WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ static ImageBufferBackingStoreCache& get();
+
+ struct IOSurfaceAndContext {
+ IOSurfaceAndContext()
+ {
+ }
+
+ IOSurfaceAndContext(IOSurfaceRef surface, CGContextRef context)
+ : surface(surface)
+ , context(context)
+ {
+ }
+
+ RetainPtr<IOSurfaceRef> surface;
+ RetainPtr<CGContextRef> context;
+ };
+
+ IOSurfaceAndContext getOrAllocate(IntSize, CGColorSpaceRef, bool needExactSize);
+ void deallocate(IOSurfaceRef);
+
+private:
+ ImageBufferBackingStoreCache()
+ : m_purgeTimer(this, &ImageBufferBackingStoreCache::timerFired)
+ , m_pixelsCached(0)
+ {
+ }
+
+ struct IOSurfaceAndContextWithCreationParams : public IOSurfaceAndContext, public DoublyLinkedListNode<IOSurfaceAndContextWithCreationParams> {
+ IOSurfaceAndContextWithCreationParams()
+ {
+ }
+
+ IOSurfaceAndContextWithCreationParams(IOSurfaceRef surface, CGContextRef context, CGColorSpaceRef colorSpace)
+ : IOSurfaceAndContext(surface, context)
+ , colorSpace(colorSpace)
+ {
+ }
+
+ IOSurfaceAndContextWithCreationParams* m_prev;
+ IOSurfaceAndContextWithCreationParams* m_next;
+ RetainPtr<CGColorSpaceRef> colorSpace;
+ };
+ typedef HashMap<RetainPtr<IOSurfaceRef>, IOSurfaceAndContextWithCreationParams> ActiveSurfaceMap;
+ typedef std::pair<int, int> CachedSurfaceKey;
+ typedef DoublyLinkedList<IOSurfaceAndContextWithCreationParams> InfoLinkedList;
+ typedef HashMap<CachedSurfaceKey, InfoLinkedList> CachedSurfaceMap;
+
+ static CachedSurfaceKey convertSizeToKey(const IntSize& size)
+ {
+ return std::make_pair(WTF::roundUpToMultipleOf(8, size.width()), WTF::roundUpToMultipleOf(8, size.height()));
+ }
+
+ IOSurfaceAndContextWithCreationParams takeFromCache(CachedSurfaceMap::iterator, IOSurfaceAndContextWithCreationParams*);
+ void insertIntoCache(IOSurfaceAndContextWithCreationParams&&);
+
+ // If we find an acceptable surface, this function removes it from the cache as
+ // well as placing it in the out parameter.
+ bool tryTakeFromCache(const IntSize&, CGColorSpaceRef, bool needExactSize, IOSurfaceAndContextWithCreationParams& outInfo);
+ bool isAcceptableSurface(const IOSurfaceAndContextWithCreationParams&, const IntSize&, CGColorSpaceRef, bool needExactSize) const;
+
+ void timerFired(Timer<ImageBufferBackingStoreCache>*);
+ void schedulePurgeTimer();
+
+ Timer<ImageBufferBackingStoreCache> m_purgeTimer;
+ ActiveSurfaceMap m_activeSurfaces;
+ CachedSurfaceMap m_cachedSurfaces;
+ int m_pixelsCached;
+};
+
+}
+#endif // IOSURFACE_CANVAS_BACKING_STORE
+
+#endif // ImageBufferBackingStoreCache_h
Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp (160944 => 160945)
--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp 2013-12-21 00:35:44 UTC (rev 160944)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp 2013-12-21 00:43:27 UTC (rev 160945)
@@ -47,6 +47,7 @@
#endif
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+#include "ImageBufferBackingStoreCache.h"
#include <IOSurface/IOSurface.h>
#endif
@@ -54,43 +55,6 @@
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
static const int maxIOSurfaceDimension = 4096;
-
-static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
-{
- unsigned pixelFormat = 'BGRA';
- unsigned bytesPerElement = 4;
- int width = size.width();
- int height = size.height();
-
- unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
- if (!bytesPerRow)
- return 0;
-
- unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
- if (!allocSize)
- return 0;
-
- const void *keys[6];
- const void *values[6];
- keys[0] = kIOSurfaceWidth;
- values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
- keys[1] = kIOSurfaceHeight;
- values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
- keys[2] = kIOSurfacePixelFormat;
- values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
- keys[3] = kIOSurfaceBytesPerElement;
- values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
- keys[4] = kIOSurfaceBytesPerRow;
- values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
- keys[5] = kIOSurfaceAllocSize;
- values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
-
- RetainPtr<CFDictionaryRef> dict = adoptCF(CFDictionaryCreate(0, keys, values, 6, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
- for (unsigned i = 0; i < 6; i++)
- CFRelease(values[i]);
-
- return adoptCF(IOSurfaceCreate(dict.get()));
-}
#endif
static void releaseImageData(void*, const void* data, size_t)
@@ -156,9 +120,15 @@
RetainPtr<CGContextRef> cgContext;
if (accelerateRendering) {
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
- m_data.m_surface = createIOSurface(m_data.m_backingStoreSize);
- FloatSize userBounds = scaleSizeToUserSpace(FloatSize(width.unsafeGet(), height.unsafeGet()), m_data.m_backingStoreSize, m_size);
- cgContext = adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), userBounds.width(), userBounds.height(), m_data.m_colorSpace));
+ ImageBufferBackingStoreCache::IOSurfaceAndContext infoFromPool = ImageBufferBackingStoreCache::get().getOrAllocate(
+ internalSize(), m_data.m_colorSpace, false);
+ cgContext = infoFromPool.context;
+ if (cgContext) {
+ m_data.m_surface = infoFromPool.surface;
+ m_data.m_backingStoreSize.setWidth(IOSurfaceGetWidth(m_data.m_surface.get()));
+ m_data.m_backingStoreSize.setHeight(IOSurfaceGetHeight(m_data.m_surface.get()));
+ m_data.m_bytesPerRow = IOSurfaceGetBytesPerRow(m_data.m_surface.get());
+ }
#endif
if (!cgContext)
accelerateRendering = false; // If allocation fails, fall back to non-accelerated path.
@@ -188,6 +158,10 @@
ImageBuffer::~ImageBuffer()
{
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+ if (m_data.m_surface)
+ ImageBufferBackingStoreCache::get().deallocate(m_data.m_surface.get());
+#endif
}
GraphicsContext* ImageBuffer::context() const