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