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 ((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
72namespace WebCore {
73
74int 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
88TilesManager::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
121TilesManager::~TilesManager()
122{
123    delete[] m_textureGenerators;
124}
125
126
127void 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
166void 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
184void 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
192void 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
236void 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
253void 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
262void TilesManager::dirtyAllTiles()
263{
264    dirtyTexturesVector(m_textures);
265    dirtyTexturesVector(m_tilesTextures);
266}
267
268void 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
290void 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
298TileTexture* 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
385void TilesManager::setHighEndGfx(bool highEnd)
386{
387    m_highEndGfx = highEnd;
388}
389
390bool TilesManager::highEndGfx()
391{
392    return m_highEndGfx;
393}
394
395int TilesManager::currentTextureCount()
396{
397    android::Mutex::Autolock lock(m_texturesLock);
398    return m_currentTextureCount;
399}
400
401int TilesManager::currentLayerTextureCount()
402{
403    android::Mutex::Autolock lock(m_texturesLock);
404    return m_currentLayerTextureCount;
405}
406
407void 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
422void 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
449TransferQueue* 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
463void 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
472void 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
506void 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
513bool 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
522void 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
529int TilesManager::tileWidth()
530{
531    return TILE_WIDTH;
532}
533
534int TilesManager::tileHeight()
535{
536    return TILE_HEIGHT;
537}
538
539TilesManager* TilesManager::instance()
540{
541    if (!gInstance) {
542        gInstance = new TilesManager();
543        ALOGV("instance(), new gInstance is %x", gInstance);
544    }
545    return gInstance;
546}
547
548TilesManager* TilesManager::gInstance = 0;
549
550} // namespace WebCore
551
552#endif // USE(ACCELERATED_COMPOSITING)
553