• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010, The Android Open Source Project
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  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * 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 THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #define LOG_TAG "TilesManager"
27 #define LOG_NDEBUG 1
28 
29 #include "config.h"
30 #include "TilesManager.h"
31 
32 #if USE(ACCELERATED_COMPOSITING)
33 
34 #include "AndroidLog.h"
35 #include "GLWebViewState.h"
36 #include "SkCanvas.h"
37 #include "SkDevice.h"
38 #include "SkPaint.h"
39 #include "Tile.h"
40 #include "TileTexture.h"
41 #include "TransferQueue.h"
42 
43 #include <android/native_window.h>
44 #include <cutils/atomic.h>
45 #include <gui/SurfaceTexture.h>
46 #include <gui/SurfaceTextureClient.h>
47 #include <wtf/CurrentTime.h>
48 
49 // Important: We need at least twice as many textures as is needed to cover
50 // one viewport, otherwise the allocation may stall.
51 // We need n textures for one TiledPage, and another n textures for the
52 // second page used when scaling.
53 // In our case, we use 256*256 textures. Both base and layers can use up to
54 // MAX_TEXTURE_ALLOCATION textures, which is 224MB GPU memory in total.
55 // For low end graphics systems, we cut this upper limit to half.
56 // We've found the viewport dependent value m_currentTextureCount is a reasonable
57 // number to cap the layer tile texturs, it worked on both phones and tablets.
58 // TODO: after merge the pool of base tiles and layer tiles, we should revisit
59 // the logic of allocation management.
60 #define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*4)
61 #define TILE_WIDTH 256
62 #define TILE_HEIGHT 256
63 
64 #define BYTES_PER_PIXEL 4 // 8888 config
65 
66 #define LAYER_TEXTURES_DESTROY_TIMEOUT 60 // If we do not need layers for 60 seconds, free the textures
67 
68 namespace WebCore {
69 
getMaxTextureAllocation()70 int TilesManager::getMaxTextureAllocation()
71 {
72     if (m_maxTextureAllocation == -1) {
73         GLint glMaxTextureSize = 0;
74         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize);
75         GLUtils::checkGlError("TilesManager::getMaxTextureAllocation");
76         // Half of glMaxTextureSize can be used for base, the other half for layers.
77         m_maxTextureAllocation = std::min(MAX_TEXTURE_ALLOCATION, glMaxTextureSize / 2);
78         if (!m_highEndGfx)
79             m_maxTextureAllocation = m_maxTextureAllocation / 2;
80     }
81     return m_maxTextureAllocation;
82 }
83 
TilesManager()84 TilesManager::TilesManager()
85     : m_layerTexturesRemain(true)
86     , m_highEndGfx(false)
87     , m_currentTextureCount(0)
88     , m_currentLayerTextureCount(0)
89     , m_maxTextureAllocation(-1)
90     , m_generatorReady(false)
91     , m_showVisualIndicator(false)
92     , m_invertedScreen(false)
93     , m_useMinimalMemory(true)
94     , m_useDoubleBuffering(true)
95     , m_contentUpdates(0)
96     , m_webkitContentUpdates(0)
97     , m_queue(0)
98     , m_drawGLCount(1)
99     , m_lastTimeLayersUsed(0)
100     , m_hasLayerTextures(false)
101     , m_eglContext(EGL_NO_CONTEXT)
102 {
103     ALOGV("TilesManager ctor");
104     m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION);
105     m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION);
106     m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION);
107     m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION);
108     m_pixmapsGenerationThread = new TexturesGenerator(this);
109     m_pixmapsGenerationThread->run("TexturesGenerator");
110 }
111 
allocateTextures()112 void TilesManager::allocateTextures()
113 {
114     int nbTexturesToAllocate = m_currentTextureCount - m_textures.size();
115     ALOGV("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_currentTextureCount);
116     int nbTexturesAllocated = 0;
117     for (int i = 0; i < nbTexturesToAllocate; i++) {
118         TileTexture* texture = new TileTexture(
119             tileWidth(), tileHeight());
120         // the atomic load ensures that the texture has been fully initialized
121         // before we pass a pointer for other threads to operate on
122         TileTexture* loadedTexture =
123             reinterpret_cast<TileTexture*>(
124             android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)));
125         m_textures.append(loadedTexture);
126         nbTexturesAllocated++;
127     }
128 
129     int nbLayersTexturesToAllocate = m_currentLayerTextureCount - m_tilesTextures.size();
130     ALOGV("%d layers tiles to allocate (%d textures planned)",
131           nbLayersTexturesToAllocate, m_currentLayerTextureCount);
132     int nbLayersTexturesAllocated = 0;
133     for (int i = 0; i < nbLayersTexturesToAllocate; i++) {
134         TileTexture* texture = new TileTexture(
135             tileWidth(), tileHeight());
136         // the atomic load ensures that the texture has been fully initialized
137         // before we pass a pointer for other threads to operate on
138         TileTexture* loadedTexture =
139             reinterpret_cast<TileTexture*>(
140             android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)));
141         m_tilesTextures.append(loadedTexture);
142         nbLayersTexturesAllocated++;
143     }
144     ALOGV("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)",
145           nbTexturesAllocated, m_textures.size(),
146           m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024,
147           nbLayersTexturesAllocated, m_tilesTextures.size(),
148           m_tilesTextures.size() * tileWidth() * tileHeight() * 4 / 1024 / 1024);
149 }
150 
discardTextures(bool allTextures,bool glTextures)151 void TilesManager::discardTextures(bool allTextures, bool glTextures)
152 {
153     const unsigned int max = m_textures.size();
154 
155     unsigned long long sparedDrawCount = ~0; // by default, spare no textures
156     if (!allTextures) {
157         // if we're not deallocating all textures, spare those with max drawcount
158         sparedDrawCount = 0;
159         for (unsigned int i = 0; i < max; i++) {
160             TextureOwner* owner = m_textures[i]->owner();
161             if (owner)
162                 sparedDrawCount = std::max(sparedDrawCount, owner->drawCount());
163         }
164     }
165     discardTexturesVector(sparedDrawCount, m_textures, glTextures);
166     discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures);
167 }
168 
markAllGLTexturesZero()169 void TilesManager::markAllGLTexturesZero()
170 {
171     for (unsigned int i = 0; i < m_textures.size(); i++)
172         m_textures[i]->m_ownTextureId = 0;
173     for (unsigned int i = 0; i < m_tilesTextures.size(); i++)
174         m_tilesTextures[i]->m_ownTextureId = 0;
175 }
176 
discardTexturesVector(unsigned long long sparedDrawCount,WTF::Vector<TileTexture * > & textures,bool deallocateGLTextures)177 void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount,
178                                          WTF::Vector<TileTexture*>& textures,
179                                          bool deallocateGLTextures)
180 {
181     const unsigned int max = textures.size();
182     int dealloc = 0;
183     WTF::Vector<int> discardedIndex;
184     for (unsigned int i = 0; i < max; i++) {
185         TextureOwner* owner = textures[i]->owner();
186         if (!owner || owner->drawCount() < sparedDrawCount) {
187             if (deallocateGLTextures) {
188                 // deallocate textures' gl memory
189                 textures[i]->discardGLTexture();
190                 discardedIndex.append(i);
191             } else if (owner) {
192                 // simply detach textures from owner
193                 static_cast<Tile*>(owner)->discardTextures();
194             }
195             dealloc++;
196         }
197     }
198 
199     bool base = textures == m_textures;
200     // Clean up the vector of TileTextures and reset the max texture count.
201     if (discardedIndex.size()) {
202         android::Mutex::Autolock lock(m_texturesLock);
203         for (int i = discardedIndex.size() - 1; i >= 0; i--)
204             textures.remove(discardedIndex[i]);
205 
206         int remainedTextureNumber = textures.size();
207         int* countPtr = base ? &m_currentTextureCount : &m_currentLayerTextureCount;
208         if (remainedTextureNumber < *countPtr) {
209             ALOGV("reset currentTextureCount for %s tiles from %d to %d",
210                   base ? "base" : "layer", *countPtr, remainedTextureNumber);
211             *countPtr = remainedTextureNumber;
212         }
213 
214     }
215 
216     ALOGV("Discarded %d %s textures (out of %d %s tiles)",
217           dealloc, (deallocateGLTextures ? "gl" : ""),
218           max, base ? "base" : "layer");
219 }
220 
gatherTexturesNumbers(int * nbTextures,int * nbAllocatedTextures,int * nbLayerTextures,int * nbAllocatedLayerTextures)221 void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures,
222                                         int* nbLayerTextures, int* nbAllocatedLayerTextures)
223 {
224     *nbTextures = m_textures.size();
225     for (unsigned int i = 0; i < m_textures.size(); i++) {
226         TileTexture* texture = m_textures[i];
227         if (texture->m_ownTextureId)
228             *nbAllocatedTextures += 1;
229     }
230     *nbLayerTextures = m_tilesTextures.size();
231     for (unsigned int i = 0; i < m_tilesTextures.size(); i++) {
232         TileTexture* texture = m_tilesTextures[i];
233         if (texture->m_ownTextureId)
234             *nbAllocatedLayerTextures += 1;
235     }
236 }
237 
dirtyTexturesVector(WTF::Vector<TileTexture * > & textures)238 void TilesManager::dirtyTexturesVector(WTF::Vector<TileTexture*>& textures)
239 {
240     for (unsigned int i = 0; i < textures.size(); i++) {
241         Tile* currentOwner = static_cast<Tile*>(textures[i]->owner());
242         if (currentOwner)
243             currentOwner->markAsDirty();
244     }
245 }
246 
dirtyAllTiles()247 void TilesManager::dirtyAllTiles()
248 {
249     dirtyTexturesVector(m_textures);
250     dirtyTexturesVector(m_tilesTextures);
251 }
252 
printTextures()253 void TilesManager::printTextures()
254 {
255 #ifdef DEBUG
256     ALOGV("++++++");
257     for (unsigned int i = 0; i < m_textures.size(); i++) {
258         TileTexture* texture = m_textures[i];
259         Tile* o = 0;
260         if (texture->owner())
261             o = (Tile*) texture->owner();
262         int x = -1;
263         int y = -1;
264         if (o) {
265             x = o->x();
266             y = o->y();
267         }
268         ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f",
269               i, texture, o, x, y, o ? o->scale() : 0);
270     }
271     ALOGV("------");
272 #endif // DEBUG
273 }
274 
gatherTextures()275 void TilesManager::gatherTextures()
276 {
277     android::Mutex::Autolock lock(m_texturesLock);
278     m_availableTextures = m_textures;
279     m_availableTilesTextures = m_tilesTextures;
280     m_layerTexturesRemain = true;
281 }
282 
getAvailableTexture(Tile * owner)283 TileTexture* TilesManager::getAvailableTexture(Tile* owner)
284 {
285     android::Mutex::Autolock lock(m_texturesLock);
286 
287     WTF::Vector<TileTexture*>* availableTexturePool;
288     if (owner->isLayerTile())
289         availableTexturePool = &m_availableTilesTextures;
290     else
291         availableTexturePool = &m_availableTextures;
292 
293     // Sanity check that the tile does not already own a texture
294     if (owner->backTexture() && owner->backTexture()->owner() == owner) {
295         int removeIndex = availableTexturePool->find(owner->backTexture());
296 
297         // TODO: investigate why texture isn't found
298         if (removeIndex >= 0)
299             availableTexturePool->remove(removeIndex);
300         return owner->backTexture();
301     }
302 
303     // The heuristic for selecting a texture is as follows:
304     //  1. Skip textures currently being painted, they can't be painted while
305     //         busy anyway
306     //  2. If a tile isn't owned, break with that one
307     //  3. Don't let tiles acquire their front textures
308     //  4. Otherwise, use the least recently prepared tile, but ignoring tiles
309     //         drawn in the last frame to avoid flickering
310 
311     TileTexture* farthestTexture = 0;
312     unsigned long long oldestDrawCount = getDrawGLCount() - 1;
313     const unsigned int max = availableTexturePool->size();
314     for (unsigned int i = 0; i < max; i++) {
315         TileTexture* texture = (*availableTexturePool)[i];
316         Tile* currentOwner = static_cast<Tile*>(texture->owner());
317         if (!currentOwner) {
318             // unused texture! take it!
319             farthestTexture = texture;
320             break;
321         }
322 
323         if (currentOwner == owner) {
324             // Don't let a tile acquire its own front texture, as the
325             // acquisition logic doesn't handle that
326             continue;
327         }
328 
329         unsigned long long textureDrawCount = currentOwner->drawCount();
330         if (oldestDrawCount > textureDrawCount) {
331             farthestTexture = texture;
332             oldestDrawCount = textureDrawCount;
333         }
334     }
335 
336     if (farthestTexture) {
337         Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner());
338         if (farthestTexture->acquire(owner)) {
339             if (previousOwner) {
340                 previousOwner->removeTexture(farthestTexture);
341 
342                 ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)",
343                       owner->isLayerTile() ? "LAYER" : "BASE",
344                       farthestTexture, previousOwner->x(), previousOwner->y(),
345                       owner->x(), owner->y(),
346                       oldestDrawCount, getDrawGLCount());
347             }
348 
349             availableTexturePool->remove(availableTexturePool->find(farthestTexture));
350             return farthestTexture;
351         }
352     } else {
353         if (owner->isLayerTile()) {
354             // couldn't find a tile for a layer, layers shouldn't request redraw
355             // TODO: once we do layer prefetching, don't set this for those
356             // tiles
357             m_layerTexturesRemain = false;
358         }
359     }
360 
361     ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available",
362           owner->isLayerTile() ? "LAYER" : "BASE",
363           owner, owner->x(), owner->y(), max);
364 #ifdef DEBUG
365     printTextures();
366 #endif // DEBUG
367     return 0;
368 }
369 
setHighEndGfx(bool highEnd)370 void TilesManager::setHighEndGfx(bool highEnd)
371 {
372     m_highEndGfx = highEnd;
373 }
374 
highEndGfx()375 bool TilesManager::highEndGfx()
376 {
377     return m_highEndGfx;
378 }
379 
currentTextureCount()380 int TilesManager::currentTextureCount()
381 {
382     android::Mutex::Autolock lock(m_texturesLock);
383     return m_currentTextureCount;
384 }
385 
currentLayerTextureCount()386 int TilesManager::currentLayerTextureCount()
387 {
388     android::Mutex::Autolock lock(m_texturesLock);
389     return m_currentLayerTextureCount;
390 }
391 
setCurrentTextureCount(int newTextureCount)392 void TilesManager::setCurrentTextureCount(int newTextureCount)
393 {
394     int maxTextureAllocation = getMaxTextureAllocation();
395     ALOGV("setCurrentTextureCount: %d (current: %d, max:%d)",
396          newTextureCount, m_currentTextureCount, maxTextureAllocation);
397     if (m_currentTextureCount == maxTextureAllocation ||
398         newTextureCount <= m_currentTextureCount)
399         return;
400 
401     android::Mutex::Autolock lock(m_texturesLock);
402     m_currentTextureCount = std::min(newTextureCount, maxTextureAllocation);
403 
404     allocateTextures();
405 }
406 
setCurrentLayerTextureCount(int newTextureCount)407 void TilesManager::setCurrentLayerTextureCount(int newTextureCount)
408 {
409     int maxTextureAllocation = getMaxTextureAllocation();
410     ALOGV("setCurrentLayerTextureCount: %d (current: %d, max:%d)",
411          newTextureCount, m_currentLayerTextureCount, maxTextureAllocation);
412     if (!newTextureCount && m_hasLayerTextures) {
413         double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed;
414         if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) {
415             unsigned long long sparedDrawCount = ~0; // by default, spare no textures
416             bool deleteGLTextures = true;
417             discardTexturesVector(sparedDrawCount, m_tilesTextures, deleteGLTextures);
418             m_hasLayerTextures = false;
419         }
420         return;
421     }
422     m_lastTimeLayersUsed = WTF::currentTime();
423     if (m_currentLayerTextureCount == maxTextureAllocation ||
424         newTextureCount <= m_currentLayerTextureCount)
425         return;
426 
427     android::Mutex::Autolock lock(m_texturesLock);
428     m_currentLayerTextureCount = std::min(newTextureCount, maxTextureAllocation);
429 
430     allocateTextures();
431     m_hasLayerTextures = true;
432 }
433 
transferQueue()434 TransferQueue* TilesManager::transferQueue()
435 {
436     // m_queue will be created on the UI thread, although it may
437     // be accessed from the TexturesGenerator. However, that can only happen after
438     // a previous transferQueue() call due to a prepare.
439     if (!m_queue)
440         m_queue = new TransferQueue(m_useMinimalMemory);
441     return m_queue;
442 }
443 
444 // When GL context changed or we get a low memory signal, we want to cleanup all
445 // the GPU memory webview is using.
446 // The recreation will be on the next incoming draw call at the drawGL of
447 // GLWebViewState or the VideoLayerAndroid
cleanupGLResources()448 void TilesManager::cleanupGLResources()
449 {
450     transferQueue()->cleanupGLResourcesAndQueue();
451     shader()->cleanupGLResources();
452     videoLayerManager()->cleanupGLResources();
453     m_eglContext = EGL_NO_CONTEXT;
454     GLUtils::checkGlError("TilesManager::cleanupGLResources");
455 }
456 
updateTilesIfContextVerified()457 void TilesManager::updateTilesIfContextVerified()
458 {
459     EGLContext ctx = eglGetCurrentContext();
460     GLUtils::checkEglError("contextChanged");
461     if (ctx != m_eglContext) {
462         if (m_eglContext != EGL_NO_CONTEXT) {
463             // A change in EGL context is an unexpected error, but we don't want to
464             // crash or ANR. Therefore, abandon the Surface Texture and GL resources;
465             // they'll be recreated later in setupDrawing. (We can't delete them
466             // since the context is gone)
467             ALOGE("Unexpected : EGLContext changed! current %x , expected %x",
468                   ctx, m_eglContext);
469             transferQueue()->resetQueue();
470             shader()->forceNeedsInit();
471             videoLayerManager()->forceNeedsInit();
472             markAllGLTexturesZero();
473         } else {
474             // This is the first time we went into this new EGL context.
475             // We will have the GL resources to be re-inited and we can't update
476             // dirty tiles yet.
477             ALOGD("new EGLContext from framework: %x ", ctx);
478         }
479     } else {
480         // Here before we draw, update the Tile which has updated content.
481         // Inside this function, just do GPU blits from the transfer queue into
482         // the Tiles' texture.
483         transferQueue()->updateDirtyTiles();
484         // Clean up GL textures for video layer.
485         videoLayerManager()->deleteUnusedTextures();
486     }
487     m_eglContext = ctx;
488     return;
489 }
490 
tileWidth()491 int TilesManager::tileWidth()
492 {
493     return TILE_WIDTH;
494 }
495 
tileHeight()496 int TilesManager::tileHeight()
497 {
498     return TILE_HEIGHT;
499 }
500 
instance()501 TilesManager* TilesManager::instance()
502 {
503     if (!gInstance) {
504         gInstance = new TilesManager();
505         ALOGV("instance(), new gInstance is %x", gInstance);
506     }
507     return gInstance;
508 }
509 
510 TilesManager* TilesManager::gInstance = 0;
511 
512 } // namespace WebCore
513 
514 #endif // USE(ACCELERATED_COMPOSITING)
515