[chromium] Respect memory needed for RenderSurfaces when reserving contents textures
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / cc / CCPrioritizedTextureManager.cpp
1 /*
2  * Copyright (C) 2012, Google 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'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26
27 #include "CCPrioritizedTextureManager.h"
28
29 #include "CCPrioritizedTexture.h"
30 #include "CCPriorityCalculator.h"
31 #include "LayerRendererChromium.h"
32 #include "TraceEvent.h"
33 #include <algorithm>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 CCPrioritizedTextureManager::CCPrioritizedTextureManager(size_t maxMemoryLimitBytes, int)
40     : m_maxMemoryLimitBytes(maxMemoryLimitBytes)
41     , m_memoryUseBytes(0)
42     , m_memoryAboveCutoffBytes(0)
43     , m_memoryAvailableBytes(0)
44 {
45 }
46
47 CCPrioritizedTextureManager::~CCPrioritizedTextureManager()
48 {
49     while (m_textures.size() > 0)
50         unregisterTexture(*m_textures.begin());
51
52     // Each remaining backing is a leaked opengl texture. We don't have the allocator
53     // to delete the textures at this time so clearMemory() needs to be called before this.
54     while (m_backings.size() > 0)
55         destroyBacking(*m_backings.begin(), 0);
56 }
57
58 void CCPrioritizedTextureManager::prioritizeTextures(size_t renderSurfacesMemoryBytes)
59 {
60     TRACE_EVENT0("cc", "CCPrioritizedTextureManager::prioritizeTextures");
61
62 #if !ASSERT_DISABLED
63     assertInvariants();
64 #endif
65
66     // Sorting textures in this function could be replaced by a slightly
67     // modified O(n) quick-select to partition textures rather than
68     // sort them (if performance of the sort becomes an issue).
69
70     TextureVector& sortedTextures = m_tempTextureVector;
71     BackingVector& sortedBackings = m_tempBackingVector;
72     sortedTextures.clear();
73     sortedBackings.clear();
74
75     // Copy all textures into a vector and sort them.
76     for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it)
77         sortedTextures.append(*it);
78     std::sort(sortedTextures.begin(), sortedTextures.end(), compareTextures);
79
80     m_memoryAvailableBytes = m_maxMemoryLimitBytes;
81     m_priorityCutoff = CCPriorityCalculator::lowestPriority();
82     bool reservedRenderSurfaces = false;
83     size_t memoryBytes = 0;
84     for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
85         if ((*it)->requestPriority() == CCPriorityCalculator::lowestPriority())
86             break;
87
88         // FIXME: We can make placeholder objects similar to textures to represent the render surface memory request.
89         if (!reservedRenderSurfaces && CCPriorityCalculator::priorityIsLower((*it)->requestPriority(), CCPriorityCalculator::renderSurfacePriority())) {
90             size_t newMemoryBytes = memoryBytes + renderSurfacesMemoryBytes;
91             if (newMemoryBytes > m_memoryAvailableBytes) {
92                 m_priorityCutoff = (*it)->requestPriority();
93                 m_memoryAvailableBytes = memoryBytes;
94                 break;
95             }
96             m_memoryAvailableBytes -= renderSurfacesMemoryBytes;
97             reservedRenderSurfaces = true;
98         }
99
100         size_t newMemoryBytes = memoryBytes + (*it)->memorySizeBytes();
101         if (newMemoryBytes > m_memoryAvailableBytes) {
102             m_priorityCutoff = (*it)->requestPriority();
103             break;
104         }
105
106         memoryBytes = newMemoryBytes;
107     }
108
109     // Only allow textures if they are higher than the cutoff. All textures
110     // of the same priority are accepted or rejected together, rather than
111     // being partially allowed randomly.
112     m_memoryAboveCutoffBytes = 0;
113     for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
114         bool isAbovePriorityCutoff = CCPriorityCalculator::priorityIsHigher((*it)->requestPriority(), m_priorityCutoff);
115         (*it)->setAbovePriorityCutoff(isAbovePriorityCutoff);
116         if (isAbovePriorityCutoff)
117             m_memoryAboveCutoffBytes += (*it)->memorySizeBytes();
118     }
119     ASSERT(m_memoryAboveCutoffBytes <= m_memoryAvailableBytes);
120
121     // Put backings in eviction/recycling order.
122     for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
123         sortedBackings.append(*it);
124     std::sort(sortedBackings.begin(), sortedBackings.end(), compareBackings);
125
126     for (BackingVector::iterator it = sortedBackings.begin(); it != sortedBackings.end(); ++it) {
127         m_backings.remove(*it);
128         m_backings.add(*it);
129     }
130
131     sortedTextures.clear();
132     sortedBackings.clear();
133
134 #if !ASSERT_DISABLED
135     assertInvariants();
136     ASSERT(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
137 #endif
138 }
139
140 void CCPrioritizedTextureManager::clearPriorities()
141 {
142     for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
143         // FIXME: We should remove this and just set all priorities to
144         //        CCPriorityCalculator::lowestPriority() once we have priorities
145         //        for all textures (we can't currently calculate distances for
146         //        off-screen textures).
147         (*it)->setRequestPriority(CCPriorityCalculator::lingeringPriority((*it)->requestPriority()));
148     }
149 }
150
151 bool CCPrioritizedTextureManager::requestLate(CCPrioritizedTexture* texture)
152 {
153     // This is already above cutoff, so don't double count it's memory below.
154     if (texture->isAbovePriorityCutoff())
155         return true;
156
157     if (CCPriorityCalculator::priorityIsLower(texture->requestPriority(), m_priorityCutoff))
158         return false;
159
160     size_t newMemoryBytes = m_memoryAboveCutoffBytes + texture->memorySizeBytes();
161     if (newMemoryBytes > m_memoryAvailableBytes)
162         return false;
163
164     m_memoryAboveCutoffBytes = newMemoryBytes;
165     texture->setAbovePriorityCutoff(true);
166     if (texture->currentBacking()) {
167         m_backings.remove(texture->currentBacking());
168         m_backings.add(texture->currentBacking());
169     }
170     return true;
171 }
172
173 void CCPrioritizedTextureManager::acquireBackingTextureIfNeeded(CCPrioritizedTexture* texture, TextureAllocator* allocator)
174 {
175     ASSERT(texture->isAbovePriorityCutoff());
176     if (texture->currentBacking() || !texture->isAbovePriorityCutoff())
177         return;
178
179     // Find a backing below, by either recycling or allocating.
180     CCPrioritizedTexture::Backing* backing = 0;
181
182     // First try to recycle
183     for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
184         if ((*it)->currentTexture() && (*it)->currentTexture()->isAbovePriorityCutoff())
185             break;
186         if ((*it)->size() == texture->size() && (*it)->format() == texture->format()) {
187             backing = (*it);
188             break;
189         }
190     }
191
192     // Otherwise reduce memory and just allocate a new backing texures.
193     if (!backing) {
194         reduceMemory(m_memoryAvailableBytes - texture->memorySizeBytes(), allocator);
195         backing = createBacking(texture->size(), texture->format(), allocator);
196     }
197
198     // Move the used backing texture to the end of the eviction list.
199     if (backing->currentTexture())
200         unlink(backing->currentTexture(), backing);
201     link(texture, backing);
202     m_backings.remove(backing);
203     m_backings.add(backing);
204 }
205
206 void CCPrioritizedTextureManager::reduceMemory(size_t limitBytes, TextureAllocator* allocator)
207 {
208     if (memoryUseBytes() <= limitBytes)
209         return;
210     // Destroy backings until we are below the limit,
211     // or until all backings remaining are above the cutoff.
212     while (memoryUseBytes() > limitBytes && m_backings.size() > 0) {
213         BackingSet::iterator it = m_backings.begin();
214         if ((*it)->currentTexture() && (*it)->currentTexture()->isAbovePriorityCutoff())
215             break;
216         destroyBacking((*it), allocator);
217     }
218 }
219
220 void CCPrioritizedTextureManager::reduceMemory(TextureAllocator* allocator)
221 {
222     reduceMemory(m_memoryAvailableBytes, allocator);
223     ASSERT(memoryUseBytes() <= maxMemoryLimitBytes());
224
225     // We currently collect backings from deleted textures for later recycling.
226     // However, if we do that forever we will always use the max limit even if
227     // we really need very little memory. This should probably be solved by reducing the
228     // limit externally, but until then this just does some "clean up" of unused
229     // backing textures (any more than 10%).
230     size_t wastedMemory = 0;
231     for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
232         if ((*it)->currentTexture())
233             break;
234         wastedMemory += (*it)->memorySizeBytes();
235     }
236     size_t tenPercentOfMemory = m_memoryAvailableBytes / 10;
237     if (wastedMemory <= tenPercentOfMemory)
238         return;
239     reduceMemory(memoryUseBytes() - (wastedMemory - tenPercentOfMemory), allocator);
240 }
241
242 void CCPrioritizedTextureManager::clearAllMemory(TextureAllocator* allocator)
243 {
244     // Unlink and destroy all backing textures.
245     while (m_backings.size() > 0) {
246         BackingSet::iterator it = m_backings.begin();
247         if ((*it)->currentTexture())
248             unlink((*it)->currentTexture(), (*it));
249         destroyBacking((*it), allocator);
250     }
251 }
252
253 void CCPrioritizedTextureManager::allBackingTexturesWereDeleted()
254 {
255     // Same as clearAllMemory, except all our textures were already
256     // deleted externally, so we don't delete them. Passing no
257     // allocator results in leaking the (now invalid) texture ids.
258     clearAllMemory(0);
259 }
260
261 void CCPrioritizedTextureManager::unlink(CCPrioritizedTexture* texture, CCPrioritizedTexture::Backing* backing)
262 {
263     ASSERT(texture && backing);
264     ASSERT(texture->currentBacking() == backing);
265     ASSERT(backing->currentTexture() == texture);
266
267     texture->setCurrentBacking(0);
268     backing->setCurrentTexture(0);
269 }
270
271 void CCPrioritizedTextureManager::link(CCPrioritizedTexture* texture, CCPrioritizedTexture::Backing* backing)
272 {
273     ASSERT(texture && backing);
274     ASSERT(!texture->currentBacking());
275     ASSERT(!backing->currentTexture());
276
277     texture->setCurrentBacking(backing);
278     backing->setCurrentTexture(texture);
279 }
280
281
282 void CCPrioritizedTextureManager::registerTexture(CCPrioritizedTexture* texture)
283 {
284     ASSERT(texture);
285     ASSERT(!texture->textureManager());
286     ASSERT(!texture->currentBacking());
287     ASSERT(m_textures.find(texture) == m_textures.end());
288
289     texture->setManagerInternal(this);
290     m_textures.add(texture);
291
292 }
293
294 void CCPrioritizedTextureManager::unregisterTexture(CCPrioritizedTexture* texture)
295 {
296     ASSERT(texture);
297     ASSERT(m_textures.find(texture) != m_textures.end());
298
299     returnBackingTexture(texture);
300     texture->setManagerInternal(0);
301     m_textures.remove(texture);
302     texture->setAbovePriorityCutoff(false);
303 }
304
305
306 void CCPrioritizedTextureManager::returnBackingTexture(CCPrioritizedTexture* texture)
307 {
308     if (texture->currentBacking()) {
309         // Move the backing texture to the front for eviction/recycling and unlink it.
310         m_backings.remove(texture->currentBacking());
311         m_backings.insertBefore(m_backings.begin(), texture->currentBacking());
312         unlink(texture, texture->currentBacking());
313     }
314 }
315
316 CCPrioritizedTexture::Backing* CCPrioritizedTextureManager::createBacking(IntSize size, GC3Denum format, TextureAllocator* allocator)
317 {
318     ASSERT(allocator);
319
320     unsigned textureId = allocator->createTexture(size, format);
321     CCPrioritizedTexture::Backing* backing = new CCPrioritizedTexture::Backing(size, format, textureId);
322     m_memoryUseBytes += backing->memorySizeBytes();
323     // Put backing texture at the front for eviction, since it isn't in use yet.
324     m_backings.insertBefore(m_backings.begin(), backing);
325     return backing;
326 }
327
328 void CCPrioritizedTextureManager::destroyBacking(CCPrioritizedTexture::Backing* backing, TextureAllocator* allocator)
329 {
330     ASSERT(backing);
331     ASSERT(!backing->currentTexture() || !backing->currentTexture()->isAbovePriorityCutoff());
332     ASSERT(m_backings.find(backing) != m_backings.end());
333
334     if (allocator)
335         allocator->deleteTexture(backing->textureId(), backing->size(), backing->format());
336     if (backing->currentTexture())
337         unlink(backing->currentTexture(), backing);
338     m_memoryUseBytes -= backing->memorySizeBytes();
339     m_backings.remove(backing);
340
341     delete backing;
342 }
343
344
345 #if !ASSERT_DISABLED
346 void CCPrioritizedTextureManager::assertInvariants()
347 {
348     // If we hit any of these asserts, there is a bug in this class. To see
349     // where the bug is, call this function at the beginning and end of
350     // every public function.
351
352     // Backings/textures must be doubly-linked and only to other backings/textures in this manager.
353     for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
354         if ((*it)->currentTexture()) {
355             ASSERT(m_textures.find((*it)->currentTexture()) != m_textures.end());
356             ASSERT((*it)->currentTexture()->currentBacking() == (*it));
357         }
358     }
359     for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
360         if ((*it)->currentBacking()) {
361             ASSERT(m_backings.find((*it)->currentBacking()) != m_backings.end());
362             ASSERT((*it)->currentBacking()->currentTexture() == (*it));
363         }
364     }
365
366     // At all times, backings that can be evicted must always come before
367     // backings that can't be evicted in the backing texture list (otherwise
368     // reduceMemory will not find all textures available for eviction/recycling).
369     bool reachedProtected = false;
370     for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
371         if ((*it)->currentTexture() && (*it)->currentTexture()->isAbovePriorityCutoff())
372             reachedProtected = true;
373         if (reachedProtected)
374             ASSERT((*it)->currentTexture() && (*it)->currentTexture()->isAbovePriorityCutoff());
375     }
376 }
377 #endif
378
379
380 } // namespace WebCore