CTTE Timer and DeferrableOneShotTimer
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferBackingStoreCache.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "ImageBufferBackingStoreCache.h"
28
29 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
30 #include <CoreGraphics/CoreGraphics.h>
31 #include <IOSurface/IOSurface.h>
32
33 static const double purgeInterval = 5;
34
35 namespace WebCore {
36
37 static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
38 {
39     unsigned pixelFormat = 'BGRA';
40     unsigned bytesPerElement = 4;
41     int width = size.width();
42     int height = size.height();
43
44     unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
45     if (!bytesPerRow)
46         return 0;
47
48     unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
49     if (!allocSize)
50         return 0;
51
52     const int kNumCreationParameters = 6;
53     const void* keys[kNumCreationParameters];
54     const void* values[kNumCreationParameters];
55     keys[0] = kIOSurfaceWidth;
56     values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
57     keys[1] = kIOSurfaceHeight;
58     values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
59     keys[2] = kIOSurfacePixelFormat;
60     values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
61     keys[3] = kIOSurfaceBytesPerElement;
62     values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
63     keys[4] = kIOSurfaceBytesPerRow;
64     values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
65     keys[5] = kIOSurfaceAllocSize;
66     values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
67
68     RetainPtr<CFDictionaryRef> dict = adoptCF(CFDictionaryCreate(0, keys, values, kNumCreationParameters, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
69     for (unsigned i = 0; i < kNumCreationParameters; i++)
70         CFRelease(values[i]);
71
72     return adoptCF(IOSurfaceCreate(dict.get()));
73 }
74
75 ImageBufferBackingStoreCache::ImageBufferBackingStoreCache()
76     : m_purgeTimer(this, &ImageBufferBackingStoreCache::timerFired, purgeInterval)
77     , m_pixelsCached(0)
78     {
79     }
80
81 ImageBufferBackingStoreCache& ImageBufferBackingStoreCache::get()
82 {
83     DEFINE_STATIC_LOCAL(ImageBufferBackingStoreCache, cache, ());
84     return cache;
85 }
86
87 bool ImageBufferBackingStoreCache::isAcceptableSurface(const IOSurfaceAndContextWithCreationParams& info, const IntSize& requestedSize, CGColorSpaceRef colorSpace, bool needExactSize) const
88 {
89     IOSurfaceRef surface = info.surface.get();
90     IntSize actualSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
91     if (!CFEqual(info.colorSpace.get(), colorSpace))
92         return false;
93     if (needExactSize && actualSize != requestedSize)
94         return false;
95     if (actualSize.width() < requestedSize.width() || actualSize.height() < requestedSize.height())
96         return false;
97     return true;
98 }
99
100 void ImageBufferBackingStoreCache::insertIntoCache(IOSurfaceAndContextWithCreationParams&& info)
101 {
102     IOSurfaceRef surface = info.surface.get();
103     IntSize surfaceSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
104
105     auto toAdd = new IOSurfaceAndContextWithCreationParams(info);
106     auto insertedTuple = m_cachedSurfaces.add(convertSizeToKey(surfaceSize), InfoLinkedList());
107     insertedTuple.iterator->value.append(toAdd);
108     
109     m_pixelsCached += surfaceSize.area();
110 }
111
112 auto ImageBufferBackingStoreCache::takeFromCache(CachedSurfaceMap::iterator iter, IOSurfaceAndContextWithCreationParams* info) -> IOSurfaceAndContextWithCreationParams
113 {
114     ASSERT(info);
115     ASSERT(iter != m_cachedSurfaces.end());
116
117     IOSurfaceRef surface = info->surface.get();
118     m_pixelsCached -= IOSurfaceGetWidth(surface) * IOSurfaceGetHeight(surface);
119
120     iter->value.remove(info);
121     if (iter->value.isEmpty())
122         m_cachedSurfaces.remove(iter);
123     IOSurfaceAndContextWithCreationParams result = std::move(*info);
124     delete info;
125     return result;
126 }
127
128 bool ImageBufferBackingStoreCache::tryTakeFromCache(const IntSize& size, CGColorSpaceRef colorSpace, bool needExactSize, IOSurfaceAndContextWithCreationParams& outInfo)
129 {
130     CachedSurfaceMap::iterator i = m_cachedSurfaces.find(convertSizeToKey(size));
131     if (i == m_cachedSurfaces.end())
132         return nullptr;
133     InfoLinkedList& ll = i->value;
134     for (auto info = ll.head(); info; info = info->next()) {
135         if (isAcceptableSurface(*info, size, colorSpace, needExactSize)) {
136             outInfo = takeFromCache(i, info);
137             return true;
138         }
139     }
140     return false;
141 }
142
143 ImageBufferBackingStoreCache::IOSurfaceAndContext ImageBufferBackingStoreCache::getOrAllocate(IntSize size, CGColorSpaceRef colorSpace, bool needExactSize)
144 {
145     IOSurfaceAndContextWithCreationParams foundInfo;
146     if (tryTakeFromCache(size, colorSpace, needExactSize, foundInfo)) {
147         IOSurfaceRef surface = foundInfo.surface.get();
148         CGContextRef context = foundInfo.context.get();
149         CGContextSaveGState(context);
150         auto activeInserted = m_activeSurfaces.add(surface, std::move(foundInfo));
151         ASSERT(activeInserted.isNewEntry);
152         return activeInserted.iterator->value;
153     }
154
155     RetainPtr<IOSurfaceRef> surface = createIOSurface(size);
156     if (!surface.get())
157         return IOSurfaceAndContext();
158
159     RetainPtr<CGContextRef> context = adoptCF(wkIOSurfaceContextCreate(surface.get(), size.width(), size.height(), colorSpace));
160     if (!context.get())
161         return IOSurfaceAndContext();
162     CGContextSaveGState(context.get());
163
164     auto insertedTuple = m_activeSurfaces.add(surface, IOSurfaceAndContextWithCreationParams(surface.get(), context.get(), colorSpace));
165     ASSERT(insertedTuple.isNewEntry);
166
167     return insertedTuple.iterator->value;
168 }
169
170 void ImageBufferBackingStoreCache::deallocate(IOSurfaceRef surface)
171 {
172     ActiveSurfaceMap::iterator lookup = m_activeSurfaces.find(surface);
173     ASSERT(lookup != m_activeSurfaces.end());
174     
175     auto info = std::move(lookup->value);
176     m_activeSurfaces.remove(lookup);
177     
178     IOSurfaceRef ioSurface = info.surface.get();
179     CGContextRef context = info.context.get();
180     IntSize surfaceSize(IOSurfaceGetWidth(ioSurface), IOSurfaceGetHeight(ioSurface));
181     int surfaceArea = surfaceSize.area();
182
183     static const int kMaxPixelsCached = 1024 * 1024 * 64; // 256MB
184     if (surfaceArea > kMaxPixelsCached)
185         return;
186
187     // Evict
188     auto bucket = m_cachedSurfaces.find(convertSizeToKey(surfaceSize));
189     if (bucket != m_cachedSurfaces.end()) {
190         for (int itemsInBucket = bucket->value.size();
191             itemsInBucket > 0 && m_pixelsCached + surfaceArea > kMaxPixelsCached;
192             --itemsInBucket)
193             takeFromCache(bucket, bucket->value.head());
194     }
195     while (m_pixelsCached + surfaceArea > kMaxPixelsCached) {
196         CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
197         takeFromCache(iter, iter->value.head());
198     }
199
200     CGContextRestoreGState(context);
201     // Clear opportunistically so CG has more time to carry it out.
202     CGContextClearRect(context, CGRectMake(0, 0, surfaceSize.width(), surfaceSize.height()));
203 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
204     CGContextFlush(context);
205 #endif
206
207     insertIntoCache(std::move(info));
208
209     schedulePurgeTimer();
210 }
211
212 void ImageBufferBackingStoreCache::timerFired(DeferrableOneShotTimer<ImageBufferBackingStoreCache>&)
213 {
214     while (!m_cachedSurfaces.isEmpty()) {
215         CachedSurfaceMap::iterator iter = m_cachedSurfaces.begin();
216         takeFromCache(iter, iter->value.head());
217     }
218 }
219
220 void ImageBufferBackingStoreCache::schedulePurgeTimer()
221 {
222     m_purgeTimer.restart();
223 }
224
225 }
226 #endif // IOSURFACE_CANVAS_BACKING_STORE