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