2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "ImageBufferBackingStoreCache.h"
29 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
30 #include <IOSurface/IOSurface.h>
34 static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
36 unsigned pixelFormat = 'BGRA';
37 unsigned bytesPerElement = 4;
38 int width = size.width();
39 int height = size.height();
41 unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
45 unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
49 const int kNumCreationParameters = 6;
50 const void* keys[kNumCreationParameters];
51 const void* values[kNumCreationParameters];
52 keys[0] = kIOSurfaceWidth;
53 values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
54 keys[1] = kIOSurfaceHeight;
55 values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
56 keys[2] = kIOSurfacePixelFormat;
57 values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
58 keys[3] = kIOSurfaceBytesPerElement;
59 values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
60 keys[4] = kIOSurfaceBytesPerRow;
61 values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
62 keys[5] = kIOSurfaceAllocSize;
63 values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
65 RetainPtr<CFDictionaryRef> dict = adoptCF(CFDictionaryCreate(0, keys, values, kNumCreationParameters, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
66 for (unsigned i = 0; i < kNumCreationParameters; i++)
69 return adoptCF(IOSurfaceCreate(dict.get()));
72 ImageBufferBackingStoreCache& ImageBufferBackingStoreCache::get()
74 DEFINE_STATIC_LOCAL(ImageBufferBackingStoreCache, cache, ());
78 bool ImageBufferBackingStoreCache::isAcceptableSurface(const IOSurfaceAndContextWithCreationParams& info, const IntSize& requestedSize, CGColorSpaceRef colorSpace, bool needExactSize) const
80 IOSurfaceRef surface = info.surface.get();
81 IntSize actualSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
82 if (!CFEqual(info.colorSpace.get(), colorSpace))
84 if (needExactSize && actualSize != requestedSize)
86 if (actualSize.width() < requestedSize.width() || actualSize.height() < requestedSize.height())
91 void ImageBufferBackingStoreCache::insertIntoCache(IOSurfaceAndContextWithCreationParams&& info)
93 IOSurfaceRef surface = info.surface.get();
94 IntSize surfaceSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
96 auto toAdd = new IOSurfaceAndContextWithCreationParams(info);
97 auto insertedTuple = m_cachedSurfaces.add(convertSizeToKey(surfaceSize), InfoLinkedList());
98 insertedTuple.iterator->value.append(toAdd);
100 m_pixelsCached += surfaceSize.area();
103 auto ImageBufferBackingStoreCache::takeFromCache(CachedSurfaceMap::iterator iter, IOSurfaceAndContextWithCreationParams* info) -> IOSurfaceAndContextWithCreationParams
106 ASSERT(iter != m_cachedSurfaces.end());
108 IOSurfaceRef surface = info->surface.get();
109 m_pixelsCached -= IOSurfaceGetWidth(surface) * IOSurfaceGetHeight(surface);
111 iter->value.remove(info);
112 if (iter->value.isEmpty())
113 m_cachedSurfaces.remove(iter);
114 IOSurfaceAndContextWithCreationParams result = std::move(*info);
119 bool ImageBufferBackingStoreCache::tryTakeFromCache(const IntSize& size, CGColorSpaceRef colorSpace, bool needExactSize, IOSurfaceAndContextWithCreationParams& outInfo)
121 CachedSurfaceMap::iterator i = m_cachedSurfaces.find(convertSizeToKey(size));
122 if (i == m_cachedSurfaces.end())
124 InfoLinkedList& ll = i->value;
125 for (auto info = ll.head(); info; info = info->next()) {
126 if (isAcceptableSurface(*info, size, colorSpace, needExactSize)) {
127 outInfo = takeFromCache(i, info);
134 ImageBufferBackingStoreCache::IOSurfaceAndContext ImageBufferBackingStoreCache::getOrAllocate(IntSize size, CGColorSpaceRef colorSpace, bool needExactSize)
136 IOSurfaceAndContextWithCreationParams foundInfo;
137 if (tryTakeFromCache(size, colorSpace, needExactSize, foundInfo)) {
138 IOSurfaceRef surface = foundInfo.surface.get();
139 CGContextRef context = foundInfo.context.get();
140 CGContextSaveGState(context);
141 auto activeInserted = m_activeSurfaces.add(surface, std::move(foundInfo));
142 ASSERT(activeInserted.isNewEntry);
143 return activeInserted.iterator->value;
146 RetainPtr<IOSurfaceRef> surface = createIOSurface(size);
148 return IOSurfaceAndContext();
150 RetainPtr<CGContextRef> context = adoptCF(wkIOSurfaceContextCreate(surface.get(), size.width(), size.height(), colorSpace));
152 return IOSurfaceAndContext();
153 CGContextSaveGState(context.get());
155 auto insertedTuple = m_activeSurfaces.add(surface, IOSurfaceAndContextWithCreationParams(surface.get(), context.get(), colorSpace));
156 ASSERT(insertedTuple.isNewEntry);
158 return insertedTuple.iterator->value;
161 void ImageBufferBackingStoreCache::deallocate(IOSurfaceRef surface)
163 ActiveSurfaceMap::iterator lookup = m_activeSurfaces.find(surface);
164 ASSERT(lookup != m_activeSurfaces.end());
166 auto info = std::move(lookup->value);
167 m_activeSurfaces.remove(lookup);
169 IOSurfaceRef ioSurface = info.surface.get();
170 CGContextRef context = info.context.get();
171 IntSize surfaceSize(IOSurfaceGetWidth(ioSurface), IOSurfaceGetHeight(ioSurface));
172 int surfaceArea = surfaceSize.area();
174 static const int kMaxPixelsCached = 1024 * 1024 * 64; // 256MB
175 if (surfaceArea > kMaxPixelsCached)
179 auto bucket = m_cachedSurfaces.find(convertSizeToKey(surfaceSize));
180 if (bucket != m_cachedSurfaces.end()) {
181 for (int itemsInBucket = bucket->value.size();
182 itemsInBucket > 0 && m_pixelsCached + surfaceArea > kMaxPixelsCached;
184 takeFromCache(bucket, bucket->value.head());
186 while (m_pixelsCached + surfaceArea > kMaxPixelsCached) {
187 CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
188 takeFromCache(iter, iter->value.head());
191 CGContextRestoreGState(context);
192 // Clear opportunistically so CG has more time to carry it out.
193 CGContextClearRect(context, CGRectMake(0, 0, surfaceSize.width(), surfaceSize.height()));
194 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
195 CGContextFlush(context);
198 insertIntoCache(std::move(info));
200 schedulePurgeTimer();
203 void ImageBufferBackingStoreCache::timerFired(Timer<ImageBufferBackingStoreCache>*)
205 while (!m_cachedSurfaces.isEmpty()) {
206 CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
207 takeFromCache(iter, iter->value.head());
211 void ImageBufferBackingStoreCache::schedulePurgeTimer()
213 if (m_purgeTimer.isActive())
216 static const double purgeInterval = 5;
217 m_purgeTimer.startOneShot(purgeInterval);
221 #endif // IOSURFACE_CANVAS_BACKING_STORE