• 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/GLConsumer.h>
46 #include <gui/Surface.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 ((10+TILE_PREFETCH_DISTANCE*2)*(7+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 // Eventually this should be dynamically be determined, and smart scheduling
69 // between the generators should be implemented
70 #define NUM_TEXTURES_GENERATORS 1
71 
72 namespace WebCore {
73 
getMaxTextureAllocation()74 int TilesManager::getMaxTextureAllocation()
75 {
76     if (m_maxTextureAllocation == -1) {
77         GLint glMaxTextureSize = 0;
78         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize);
79         GLUtils::checkGlError("TilesManager::getMaxTextureAllocation");
80         // Half of glMaxTextureSize can be used for base, the other half for layers.
81         m_maxTextureAllocation = std::min(MAX_TEXTURE_ALLOCATION, glMaxTextureSize / 2);
82         if (!m_highEndGfx)
83             m_maxTextureAllocation = m_maxTextureAllocation / 2;
84     }
85     return m_maxTextureAllocation;
86 }
87 
TilesManager()88 TilesManager::TilesManager()
89     : m_layerTexturesRemain(true)
90     , m_highEndGfx(false)
91     , m_currentTextureCount(0)
92     , m_currentLayerTextureCount(0)
93     , m_maxTextureAllocation(-1)
94     , m_generatorReady(false)
95     , m_showVisualIndicator(false)
96     , m_invertedScreen(false)
97     , m_useMinimalMemory(true)
98     , m_useDoubleBuffering(true)
99     , m_contentUpdates(0)
100     , m_webkitContentUpdates(0)
101     , m_queue(0)
102     , m_drawGLCount(1)
103     , m_lastTimeLayersUsed(0)
104     , m_hasLayerTextures(false)
105     , m_eglContext(EGL_NO_CONTEXT)
106 {
107     ALOGV("TilesManager ctor");
108     m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2);
109     m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2);
110     m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2);
111     m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2);
112 
113     m_textureGenerators = new sp<TexturesGenerator>[NUM_TEXTURES_GENERATORS];
114     for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++) {
115         m_textureGenerators[i] = new TexturesGenerator(this);
116         ALOGD("Starting TG #%d, %p", i, m_textureGenerators[i].get());
117         m_textureGenerators[i]->run("TexturesGenerator");
118     }
119 }
120 
~TilesManager()121 TilesManager::~TilesManager()
122 {
123     delete[] m_textureGenerators;
124 }
125 
126 
allocateTextures()127 void TilesManager::allocateTextures()
128 {
129     int nbTexturesToAllocate = m_currentTextureCount - m_textures.size();
130     ALOGV("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_currentTextureCount);
131     int nbTexturesAllocated = 0;
132     for (int i = 0; i < nbTexturesToAllocate; i++) {
133         TileTexture* texture = new TileTexture(
134             tileWidth(), tileHeight());
135         // the atomic load ensures that the texture has been fully initialized
136         // before we pass a pointer for other threads to operate on
137         TileTexture* loadedTexture =
138             reinterpret_cast<TileTexture*>(
139             android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)));
140         m_textures.append(loadedTexture);
141         nbTexturesAllocated++;
142     }
143 
144     int nbLayersTexturesToAllocate = m_currentLayerTextureCount - m_tilesTextures.size();
145     ALOGV("%d layers tiles to allocate (%d textures planned)",
146           nbLayersTexturesToAllocate, m_currentLayerTextureCount);
147     int nbLayersTexturesAllocated = 0;
148     for (int i = 0; i < nbLayersTexturesToAllocate; i++) {
149         TileTexture* texture = new TileTexture(
150             tileWidth(), tileHeight());
151         // the atomic load ensures that the texture has been fully initialized
152         // before we pass a pointer for other threads to operate on
153         TileTexture* loadedTexture =
154             reinterpret_cast<TileTexture*>(
155             android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)));
156         m_tilesTextures.append(loadedTexture);
157         nbLayersTexturesAllocated++;
158     }
159     ALOGV("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)",
160           nbTexturesAllocated, m_textures.size(),
161           m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024,
162           nbLayersTexturesAllocated, m_tilesTextures.size(),
163           m_tilesTextures.size() * tileWidth() * tileHeight() * 4 / 1024 / 1024);
164 }
165 
discardTextures(bool allTextures,bool glTextures)166 void TilesManager::discardTextures(bool allTextures, bool glTextures)
167 {
168     const unsigned int max = m_textures.size();
169 
170     unsigned long long sparedDrawCount = ~0; // by default, spare no textures
171     if (!allTextures) {
172         // if we're not deallocating all textures, spare those with max drawcount
173         sparedDrawCount = 0;
174         for (unsigned int i = 0; i < max; i++) {
175             TextureOwner* owner = m_textures[i]->owner();
176             if (owner)
177                 sparedDrawCount = std::max(sparedDrawCount, owner->drawCount());
178         }
179     }
180     discardTexturesVector(sparedDrawCount, m_textures, glTextures);
181     discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures);
182 }
183 
markAllGLTexturesZero()184 void TilesManager::markAllGLTexturesZero()
185 {
186     for (unsigned int i = 0; i < m_textures.size(); i++)
187         m_textures[i]->m_ownTextureId = 0;
188     for (unsigned int i = 0; i < m_tilesTextures.size(); i++)
189         m_tilesTextures[i]->m_ownTextureId = 0;
190 }
191 
discardTexturesVector(unsigned long long sparedDrawCount,WTF::Vector<TileTexture * > & textures,bool deallocateGLTextures)192 void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount,
193                                          WTF::Vector<TileTexture*>& textures,
194                                          bool deallocateGLTextures)
195 {
196     const unsigned int max = textures.size();
197     int dealloc = 0;
198     WTF::Vector<int> discardedIndex;
199     for (unsigned int i = 0; i < max; i++) {
200         TextureOwner* owner = textures[i]->owner();
201         if (!owner || owner->drawCount() < sparedDrawCount) {
202             if (deallocateGLTextures) {
203                 // deallocate textures' gl memory
204                 textures[i]->discardGLTexture();
205                 discardedIndex.append(i);
206             } else if (owner) {
207                 // simply detach textures from owner
208                 static_cast<Tile*>(owner)->discardTextures();
209             }
210             dealloc++;
211         }
212     }
213 
214     bool base = textures == m_textures;
215     // Clean up the vector of TileTextures and reset the max texture count.
216     if (discardedIndex.size()) {
217         android::Mutex::Autolock lock(m_texturesLock);
218         for (int i = discardedIndex.size() - 1; i >= 0; i--)
219             textures.remove(discardedIndex[i]);
220 
221         int remainedTextureNumber = textures.size();
222         int* countPtr = base ? &m_currentTextureCount : &m_currentLayerTextureCount;
223         if (remainedTextureNumber < *countPtr) {
224             ALOGV("reset currentTextureCount for %s tiles from %d to %d",
225                   base ? "base" : "layer", *countPtr, remainedTextureNumber);
226             *countPtr = remainedTextureNumber;
227         }
228 
229     }
230 
231     ALOGV("Discarded %d %s textures (out of %d %s tiles)",
232           dealloc, (deallocateGLTextures ? "gl" : ""),
233           max, base ? "base" : "layer");
234 }
235 
gatherTexturesNumbers(int * nbTextures,int * nbAllocatedTextures,int * nbLayerTextures,int * nbAllocatedLayerTextures)236 void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures,
237                                         int* nbLayerTextures, int* nbAllocatedLayerTextures)
238 {
239     *nbTextures = m_textures.size();
240     for (unsigned int i = 0; i < m_textures.size(); i++) {
241         TileTexture* texture = m_textures[i];
242         if (texture->m_ownTextureId)
243             *nbAllocatedTextures += 1;
244     }
245     *nbLayerTextures = m_tilesTextures.size();
246     for (unsigned int i = 0; i < m_tilesTextures.size(); i++) {
247         TileTexture* texture = m_tilesTextures[i];
248         if (texture->m_ownTextureId)
249             *nbAllocatedLayerTextures += 1;
250     }
251 }
252 
dirtyTexturesVector(WTF::Vector<TileTexture * > & textures)253 void TilesManager::dirtyTexturesVector(WTF::Vector<TileTexture*>& textures)
254 {
255     for (unsigned int i = 0; i < textures.size(); i++) {
256         Tile* currentOwner = static_cast<Tile*>(textures[i]->owner());
257         if (currentOwner)
258             currentOwner->markAsDirty();
259     }
260 }
261 
dirtyAllTiles()262 void TilesManager::dirtyAllTiles()
263 {
264     dirtyTexturesVector(m_textures);
265     dirtyTexturesVector(m_tilesTextures);
266 }
267 
printTextures()268 void TilesManager::printTextures()
269 {
270 #ifdef DEBUG
271     ALOGV("++++++");
272     for (unsigned int i = 0; i < m_textures.size(); i++) {
273         TileTexture* texture = m_textures[i];
274         Tile* o = 0;
275         if (texture->owner())
276             o = (Tile*) texture->owner();
277         int x = -1;
278         int y = -1;
279         if (o) {
280             x = o->x();
281             y = o->y();
282         }
283         ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f",
284               i, texture, o, x, y, o ? o->scale() : 0);
285     }
286     ALOGV("------");
287 #endif // DEBUG
288 }
289 
gatherTextures()290 void TilesManager::gatherTextures()
291 {
292     android::Mutex::Autolock lock(m_texturesLock);
293     m_availableTextures = m_textures;
294     m_availableTilesTextures = m_tilesTextures;
295     m_layerTexturesRemain = true;
296 }
297 
getAvailableTexture(Tile * owner)298 TileTexture* TilesManager::getAvailableTexture(Tile* owner)
299 {
300     android::Mutex::Autolock lock(m_texturesLock);
301 
302     WTF::Vector<TileTexture*>* availableTexturePool;
303     if (owner->isLayerTile())
304         availableTexturePool = &m_availableTilesTextures;
305     else
306         availableTexturePool = &m_availableTextures;
307 
308     // Sanity check that the tile does not already own a texture
309     if (owner->backTexture() && owner->backTexture()->owner() == owner) {
310         int removeIndex = availableTexturePool->find(owner->backTexture());
311 
312         // TODO: investigate why texture isn't found
313         if (removeIndex >= 0)
314             availableTexturePool->remove(removeIndex);
315         return owner->backTexture();
316     }
317 
318     // The heuristic for selecting a texture is as follows:
319     //  1. Skip textures currently being painted, they can't be painted while
320     //         busy anyway
321     //  2. If a tile isn't owned, break with that one
322     //  3. Don't let tiles acquire their front textures
323     //  4. Otherwise, use the least recently prepared tile, but ignoring tiles
324     //         drawn in the last frame to avoid flickering
325 
326     TileTexture* farthestTexture = 0;
327     unsigned long long oldestDrawCount = getDrawGLCount() - 1;
328     const unsigned int max = availableTexturePool->size();
329     for (unsigned int i = 0; i < max; i++) {
330         TileTexture* texture = (*availableTexturePool)[i];
331         Tile* currentOwner = static_cast<Tile*>(texture->owner());
332         if (!currentOwner) {
333             // unused texture! take it!
334             farthestTexture = texture;
335             break;
336         }
337 
338         if (currentOwner == owner) {
339             // Don't let a tile acquire its own front texture, as the
340             // acquisition logic doesn't handle that
341             continue;
342         }
343 
344         unsigned long long textureDrawCount = currentOwner->drawCount();
345         if (oldestDrawCount > textureDrawCount) {
346             farthestTexture = texture;
347             oldestDrawCount = textureDrawCount;
348         }
349     }
350 
351     if (farthestTexture) {
352         Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner());
353         if (farthestTexture->acquire(owner)) {
354             if (previousOwner) {
355                 previousOwner->removeTexture(farthestTexture);
356 
357                 ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)",
358                       owner->isLayerTile() ? "LAYER" : "BASE",
359                       farthestTexture, previousOwner->x(), previousOwner->y(),
360                       owner->x(), owner->y(),
361                       oldestDrawCount, getDrawGLCount());
362             }
363 
364             availableTexturePool->remove(availableTexturePool->find(farthestTexture));
365             return farthestTexture;
366         }
367     } else {
368         if (owner->isLayerTile()) {
369             // couldn't find a tile for a layer, layers shouldn't request redraw
370             // TODO: once we do layer prefetching, don't set this for those
371             // tiles
372             m_layerTexturesRemain = false;
373         }
374     }
375 
376     ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available",
377           owner->isLayerTile() ? "LAYER" : "BASE",
378           owner, owner->x(), owner->y(), max);
379 #ifdef DEBUG
380     printTextures();
381 #endif // DEBUG
382     return 0;
383 }
384 
setHighEndGfx(bool highEnd)385 void TilesManager::setHighEndGfx(bool highEnd)
386 {
387     m_highEndGfx = highEnd;
388 }
389 
highEndGfx()390 bool TilesManager::highEndGfx()
391 {
392     return m_highEndGfx;
393 }
394 
currentTextureCount()395 int TilesManager::currentTextureCount()
396 {
397     android::Mutex::Autolock lock(m_texturesLock);
398     return m_currentTextureCount;
399 }
400 
currentLayerTextureCount()401 int TilesManager::currentLayerTextureCount()
402 {
403     android::Mutex::Autolock lock(m_texturesLock);
404     return m_currentLayerTextureCount;
405 }
406 
setCurrentTextureCount(int newTextureCount)407 void TilesManager::setCurrentTextureCount(int newTextureCount)
408 {
409     int maxTextureAllocation = getMaxTextureAllocation();
410     ALOGV("setCurrentTextureCount: %d (current: %d, max:%d)",
411          newTextureCount, m_currentTextureCount, maxTextureAllocation);
412     if (m_currentTextureCount == maxTextureAllocation ||
413         newTextureCount <= m_currentTextureCount)
414         return;
415 
416     android::Mutex::Autolock lock(m_texturesLock);
417     m_currentTextureCount = std::min(newTextureCount, maxTextureAllocation);
418 
419     allocateTextures();
420 }
421 
setCurrentLayerTextureCount(int newTextureCount)422 void TilesManager::setCurrentLayerTextureCount(int newTextureCount)
423 {
424     int maxTextureAllocation = getMaxTextureAllocation();
425     ALOGV("setCurrentLayerTextureCount: %d (current: %d, max:%d)",
426          newTextureCount, m_currentLayerTextureCount, maxTextureAllocation);
427     if (!newTextureCount && m_hasLayerTextures) {
428         double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed;
429         if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) {
430             unsigned long long sparedDrawCount = ~0; // by default, spare no textures
431             bool deleteGLTextures = true;
432             discardTexturesVector(sparedDrawCount, m_tilesTextures, deleteGLTextures);
433             m_hasLayerTextures = false;
434         }
435         return;
436     }
437     m_lastTimeLayersUsed = WTF::currentTime();
438     if (m_currentLayerTextureCount == maxTextureAllocation ||
439         newTextureCount <= m_currentLayerTextureCount)
440         return;
441 
442     android::Mutex::Autolock lock(m_texturesLock);
443     m_currentLayerTextureCount = std::min(newTextureCount, maxTextureAllocation);
444 
445     allocateTextures();
446     m_hasLayerTextures = true;
447 }
448 
transferQueue()449 TransferQueue* TilesManager::transferQueue()
450 {
451     // m_queue will be created on the UI thread, although it may
452     // be accessed from the TexturesGenerator. However, that can only happen after
453     // a previous transferQueue() call due to a prepare.
454     if (!m_queue)
455         m_queue = new TransferQueue(m_useMinimalMemory && !m_highEndGfx);
456     return m_queue;
457 }
458 
459 // When GL context changed or we get a low memory signal, we want to cleanup all
460 // the GPU memory webview is using.
461 // The recreation will be on the next incoming draw call at the drawGL of
462 // GLWebViewState or the VideoLayerAndroid
cleanupGLResources()463 void TilesManager::cleanupGLResources()
464 {
465     transferQueue()->cleanupGLResourcesAndQueue();
466     shader()->cleanupGLResources();
467     videoLayerManager()->cleanupGLResources();
468     m_eglContext = EGL_NO_CONTEXT;
469     GLUtils::checkGlError("TilesManager::cleanupGLResources");
470 }
471 
updateTilesIfContextVerified()472 void TilesManager::updateTilesIfContextVerified()
473 {
474     EGLContext ctx = eglGetCurrentContext();
475     GLUtils::checkEglError("contextChanged");
476     if (ctx != m_eglContext) {
477         if (m_eglContext != EGL_NO_CONTEXT) {
478             // A change in EGL context is an unexpected error, but we don't want to
479             // crash or ANR. Therefore, abandon the Surface Texture and GL resources;
480             // they'll be recreated later in setupDrawing. (We can't delete them
481             // since the context is gone)
482             ALOGE("Unexpected : EGLContext changed! current %x , expected %x",
483                   ctx, m_eglContext);
484             transferQueue()->resetQueue();
485             shader()->forceNeedsInit();
486             videoLayerManager()->forceNeedsInit();
487             markAllGLTexturesZero();
488         } else {
489             // This is the first time we went into this new EGL context.
490             // We will have the GL resources to be re-inited and we can't update
491             // dirty tiles yet.
492             ALOGD("new EGLContext from framework: %x ", ctx);
493         }
494     } else {
495         // Here before we draw, update the Tile which has updated content.
496         // Inside this function, just do GPU blits from the transfer queue into
497         // the Tiles' texture.
498         transferQueue()->updateDirtyTiles();
499         // Clean up GL textures for video layer.
500         videoLayerManager()->deleteUnusedTextures();
501     }
502     m_eglContext = ctx;
503     return;
504 }
505 
removeOperationsForFilter(OperationFilter * filter)506 void TilesManager::removeOperationsForFilter(OperationFilter* filter)
507 {
508     for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++)
509         m_textureGenerators[i]->removeOperationsForFilter(filter);
510     delete filter;
511 }
512 
tryUpdateOperationWithPainter(Tile * tile,TilePainter * painter)513 bool TilesManager::tryUpdateOperationWithPainter(Tile* tile, TilePainter* painter)
514 {
515     for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++) {
516         if (m_textureGenerators[i]->tryUpdateOperationWithPainter(tile, painter))
517             return true;
518     }
519     return false;
520 }
521 
scheduleOperation(QueuedOperation * operation)522 void TilesManager::scheduleOperation(QueuedOperation* operation)
523 {
524     // TODO: painter awareness, store prefer awareness, store preferred thread into painter
525     m_scheduleThread = (m_scheduleThread + 1) % NUM_TEXTURES_GENERATORS;
526     m_textureGenerators[m_scheduleThread]->scheduleOperation(operation);
527 }
528 
tileWidth()529 int TilesManager::tileWidth()
530 {
531     return TILE_WIDTH;
532 }
533 
tileHeight()534 int TilesManager::tileHeight()
535 {
536     return TILE_HEIGHT;
537 }
538 
instance()539 TilesManager* TilesManager::instance()
540 {
541     if (!gInstance) {
542         gInstance = new TilesManager();
543         ALOGV("instance(), new gInstance is %x", gInstance);
544     }
545     return gInstance;
546 }
547 
548 TilesManager* TilesManager::gInstance = 0;
549 
550 } // namespace WebCore
551 
552 #endif // USE(ACCELERATED_COMPOSITING)
553