From 94843e674258b40d8fd6af137a45c093ac8a08e2 Mon Sep 17 00:00:00 2001 From: "mmaxfield@apple.com" Date: Thu, 2 Jan 2014 23:32:47 +0000 Subject: [PATCH] Allow ImageBuffer to re-use IOSurfaces https://bugs.webkit.org/show_bug.cgi?id=125477 Source/WebCore: Reviewed by Geoff Garen. Modifications reviewed by Tim Horton. This patch is taken from r160945, but the modifications to ImageBufferCG.cpp have been reverted. 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): Copied 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. (WebCore::ImageBuffer::ImageBuffer): (WebCore::ImageBuffer::~ImageBuffer): PerformanceTests: 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. LayoutTests: 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. git-svn-id: https://svn.webkit.org/repository/webkit/trunk@161235 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- LayoutTests/ChangeLog | 15 ++ .../canvas-backing-store-reuse-expected.txt | 5 + .../canvas/canvas-backing-store-reuse.html | 33 +++ PerformanceTests/Canvas/reuse.html | 31 +++ PerformanceTests/ChangeLog | 13 ++ Source/WebCore/ChangeLog | 48 ++++ .../WebCore/WebCore.xcodeproj/project.pbxproj | 8 + .../cg/ImageBufferBackingStoreCache.cpp | 221 ++++++++++++++++++ .../cg/ImageBufferBackingStoreCache.h | 117 ++++++++++ 9 files changed, 491 insertions(+) create mode 100644 LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt create mode 100644 LayoutTests/fast/canvas/canvas-backing-store-reuse.html create mode 100644 PerformanceTests/Canvas/reuse.html create mode 100644 Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp create mode 100644 Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog index ce94d7a3fe30..54badf610e4d 100644 --- a/LayoutTests/ChangeLog +++ b/LayoutTests/ChangeLog @@ -1,3 +1,18 @@ +2014-01-02 Myles C. Maxfield + + 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. + 2014-01-02 Dirk Schulze Support values computed style for 'clip-path' property diff --git a/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt b/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt new file mode 100644 index 000000000000..8be3719c0525 --- /dev/null +++ b/LayoutTests/fast/canvas/canvas-backing-store-reuse-expected.txt @@ -0,0 +1,5 @@ +PASS data.data is [0, 0, 0, 0] +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/fast/canvas/canvas-backing-store-reuse.html b/LayoutTests/fast/canvas/canvas-backing-store-reuse.html new file mode 100644 index 000000000000..a3b7ce7a6148 --- /dev/null +++ b/LayoutTests/fast/canvas/canvas-backing-store-reuse.html @@ -0,0 +1,33 @@ + + + + + + + + + + diff --git a/PerformanceTests/Canvas/reuse.html b/PerformanceTests/Canvas/reuse.html new file mode 100644 index 000000000000..a5de2d10e2d9 --- /dev/null +++ b/PerformanceTests/Canvas/reuse.html @@ -0,0 +1,31 @@ + + + + + + + diff --git a/PerformanceTests/ChangeLog b/PerformanceTests/ChangeLog index 51b456657a0f..011b83c5c0c9 100644 --- a/PerformanceTests/ChangeLog +++ b/PerformanceTests/ChangeLog @@ -1,3 +1,16 @@ +2014-01-02 Myles C. Maxfield + + 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-23 Commit Queue Unreviewed, rolling out r160945. diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 9a05c2feb9d3..a0329bd96060 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,51 @@ +2014-01-02 Myles C. Maxfield + + Allow ImageBuffer to re-use IOSurfaces + https://bugs.webkit.org/show_bug.cgi?id=125477 + + Reviewed by Geoff Garen. Modifications reviewed by Tim Horton. + + This patch is taken from r160945, but the modifications to ImageBufferCG.cpp + have been reverted. + + 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): Copied 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. + (WebCore::ImageBuffer::ImageBuffer): + (WebCore::ImageBuffer::~ImageBuffer): + 2014-01-02 Piotr Grad Video-seek-with-negative-playback was flaky. diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 4f27255a969c..7891aab6b1dd 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -850,6 +850,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, ); }; }; @@ -7513,6 +7515,8 @@ 1AFE11980CBFFCC4003017FA /* JSSQLResultSetRowList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSQLResultSetRowList.h; sourceTree = ""; }; 1C18DA56181AF6A500C4EF22 /* TextPainter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextPainter.cpp; sourceTree = ""; }; 1C18DA57181AF6A500C4EF22 /* TextPainter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextPainter.h; sourceTree = ""; }; + 1C21E57A183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageBufferBackingStoreCache.cpp; sourceTree = ""; }; + 1C21E57B183ED1FF001C289D /* ImageBufferBackingStoreCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageBufferBackingStoreCache.h; sourceTree = ""; }; 1C2649790D7E248A00BD10F2 /* DocumentLoaderMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentLoaderMac.cpp; sourceTree = ""; }; 1C26497B0D7E24EC00BD10F2 /* PageMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageMac.cpp; sourceTree = ""; }; 1C4C8EFF0AD85D87009475CE /* DeleteButtonController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeleteButtonController.cpp; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; @@ -19325,6 +19329,8 @@ 1FC40FB81655C5910040F29E /* SubimageCacheWithTimer.cpp */, 1FC40FB71655C5910040F29E /* SubimageCacheWithTimer.h */, B275352A0B053814002CE64F /* TransformationMatrixCG.cpp */, + 1C21E57A183ED1FF001C289D /* ImageBufferBackingStoreCache.cpp */, + 1C21E57B183ED1FF001C289D /* ImageBufferBackingStoreCache.h */, ); path = cg; sourceTree = ""; @@ -23741,6 +23747,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 */, @@ -26927,6 +26934,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 */, diff --git a/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp b/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp new file mode 100644 index 000000000000..d5fd8851d06e --- /dev/null +++ b/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.cpp @@ -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 + +namespace WebCore { + +static RetainPtr 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 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 surface = createIOSurface(size); + if (!surface.get()) + return IOSurfaceAndContext(); + + RetainPtr 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*) +{ + 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 diff --git a/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h b/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h new file mode 100644 index 000000000000..245e2321310a --- /dev/null +++ b/Source/WebCore/platform/graphics/cg/ImageBufferBackingStoreCache.h @@ -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 +#include +#include + +#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 surface; + RetainPtr 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(IOSurfaceRef surface, CGContextRef context, CGColorSpaceRef colorSpace) + : IOSurfaceAndContext(surface, context) + , colorSpace(colorSpace) + { + } + + IOSurfaceAndContextWithCreationParams* m_prev; + IOSurfaceAndContextWithCreationParams* m_next; + RetainPtr colorSpace; + }; + typedef HashMap, IOSurfaceAndContextWithCreationParams> ActiveSurfaceMap; + typedef std::pair CachedSurfaceKey; + typedef DoublyLinkedList InfoLinkedList; + typedef HashMap 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*); + void schedulePurgeTimer(); + + Timer m_purgeTimer; + ActiveSurfaceMap m_activeSurfaces; + CachedSurfaceMap m_cachedSurfaces; + int m_pixelsCached; +}; + +} +#endif // IOSURFACE_CANVAS_BACKING_STORE + +#endif // ImageBufferBackingStoreCache_h -- 2.36.0