Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright 2011, 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 "TransferQueue"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "TransferQueue.h"
     31 
     32 #if USE(ACCELERATED_COMPOSITING)
     33 
     34 #include "AndroidLog.h"
     35 #include "BaseRenderer.h"
     36 #include "DrawQuadData.h"
     37 #include "GLUtils.h"
     38 #include "Tile.h"
     39 #include "TileTexture.h"
     40 #include "TilesManager.h"
     41 #include <android/native_window.h>
     42 #include <gui/SurfaceTexture.h>
     43 #include <gui/SurfaceTextureClient.h>
     44 
     45 // For simple webView usage, MINIMAL_SIZE is recommended for memory saving.
     46 // In browser case, EFFICIENT_SIZE is preferred.
     47 #define MINIMAL_SIZE 1
     48 #define EFFICIENT_SIZE 6
     49 
     50 // Set this to 1 if we would like to take the new GpuUpload approach which
     51 // relied on the glCopyTexSubImage2D instead of a glDraw call
     52 #define GPU_UPLOAD_WITHOUT_DRAW 1
     53 
     54 namespace WebCore {
     55 
     56 TransferQueue::TransferQueue(bool useMinimalMem)
     57     : m_eglSurface(EGL_NO_SURFACE)
     58     , m_transferQueueIndex(0)
     59     , m_fboID(0)
     60     , m_sharedSurfaceTextureId(0)
     61     , m_hasGLContext(true)
     62     , m_currentDisplay(EGL_NO_DISPLAY)
     63     , m_currentUploadType(DEFAULT_UPLOAD_TYPE)
     64 {
     65     memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit));
     66     m_transferQueueSize = useMinimalMem ? MINIMAL_SIZE : EFFICIENT_SIZE;
     67     m_emptyItemCount = m_transferQueueSize;
     68     m_transferQueue = new TileTransferData[m_transferQueueSize];
     69 }
     70 
     71 TransferQueue::~TransferQueue()
     72 {
     73     android::Mutex::Autolock lock(m_transferQueueItemLocks);
     74     cleanupGLResources();
     75     delete[] m_transferQueue;
     76 }
     77 
     78 // Set the queue to be totally empty, abandon the Surface Texture. This should
     79 // be called only when we hit a wrong EGL Context in an error situation.
     80 void TransferQueue::resetQueue()
     81 {
     82     android::Mutex::Autolock lock(m_transferQueueItemLocks);
     83     emptyAndAbandonQueue();
     84     m_sharedSurfaceTextureId = 0;
     85 }
     86 
     87 // This should be called within the m_transferQueueItemLocks.
     88 // Now only called by emptyQueue() and destructor.
     89 void TransferQueue::cleanupGLResources()
     90 {
     91     if (m_fboID) {
     92         glDeleteFramebuffers(1, &m_fboID);
     93         m_fboID = 0;
     94     }
     95     if (m_sharedSurfaceTextureId) {
     96         glDeleteTextures(1, &m_sharedSurfaceTextureId);
     97         m_sharedSurfaceTextureId = 0;
     98     }
     99 }
    100 
    101 void TransferQueue::initGLResources(int width, int height)
    102 {
    103     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    104     if (!m_sharedSurfaceTextureId) {
    105         glGenTextures(1, &m_sharedSurfaceTextureId);
    106         sp<BufferQueue> bufferQueue(new BufferQueue(true));
    107         m_sharedSurfaceTexture =
    108 #if GPU_UPLOAD_WITHOUT_DRAW
    109             new android::SurfaceTexture(m_sharedSurfaceTextureId, true,
    110                                         GL_TEXTURE_2D, true, bufferQueue);
    111 #else
    112             new android::SurfaceTexture(m_sharedSurfaceTextureId, true,
    113                                         GL_TEXTURE_EXTERNAL_OES, true,
    114                                         bufferQueue);
    115 #endif
    116         m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture);
    117         m_sharedSurfaceTexture->setSynchronousMode(true);
    118 
    119         int extraBuffersNeeded = 0;
    120         int extraHackyBuffersNeeded = 0;
    121         if (m_transferQueueSize == EFFICIENT_SIZE)
    122             extraHackyBuffersNeeded = 13;
    123         m_ANW->query(m_ANW.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
    124                      &extraBuffersNeeded);
    125         bufferQueue->setBufferCount(m_transferQueueSize + extraBuffersNeeded +
    126                 extraHackyBuffersNeeded);
    127 
    128         int result = native_window_set_buffers_geometry(m_ANW.get(),
    129                 width, height, HAL_PIXEL_FORMAT_RGBA_8888);
    130         GLUtils::checkSurfaceTextureError("native_window_set_buffers_geometry", result);
    131         result = native_window_set_usage(m_ANW.get(),
    132                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
    133         GLUtils::checkSurfaceTextureError("native_window_set_usage", result);
    134     }
    135 
    136     if (!m_fboID)
    137         glGenFramebuffers(1, &m_fboID);
    138 }
    139 
    140 // When bliting, if the item from the transfer queue is mismatching b/t the
    141 // Tile and the content, then the item is considered as obsolete, and
    142 // the content is discarded.
    143 bool TransferQueue::checkObsolete(const TileTransferData* data)
    144 {
    145     Tile* baseTilePtr = data->savedTilePtr;
    146     if (!baseTilePtr) {
    147         ALOGV("Invalid savedTilePtr , such that the tile is obsolete");
    148         return true;
    149     }
    150 
    151     TileTexture* baseTileTexture = baseTilePtr->backTexture();
    152     if (!baseTileTexture || baseTileTexture != data->savedTileTexturePtr) {
    153         ALOGV("Invalid baseTileTexture %p (vs expected %p), such that the tile is obsolete",
    154               baseTileTexture, data->savedTileTexturePtr);
    155         return true;
    156     }
    157 
    158     return false;
    159 }
    160 
    161 void TransferQueue::blitTileFromQueue(GLuint fboID, TileTexture* destTex,
    162                                       GLuint srcTexId, GLenum srcTexTarget,
    163                                       int index)
    164 {
    165 #if GPU_UPLOAD_WITHOUT_DRAW
    166     glBindFramebuffer(GL_FRAMEBUFFER, fboID);
    167     glBindTexture(GL_TEXTURE_2D, destTex->m_ownTextureId);
    168 
    169     int textureWidth = destTex->getSize().width();
    170     int textureHeight = destTex->getSize().height();
    171 
    172     glFramebufferTexture2D(GL_FRAMEBUFFER,
    173                            GL_COLOR_ATTACHMENT0,
    174                            GL_TEXTURE_2D,
    175                            srcTexId,
    176                            0);
    177 
    178     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
    179                         textureWidth, textureHeight);
    180     if (GLUtils::checkGlError("At the end of blitTileFromQueue()")) {
    181 #ifndef DEBUG
    182         if (GLUtils::allowGLLog())
    183 #endif
    184         ALOGE("blitTileFromQueue ERROR: fboId %d, destTexId %d, srcTexId %d,"
    185               " textureWidth %d, textureHeight %d", fboID, destTex->m_ownTextureId,
    186               srcTexId, textureWidth, textureHeight);
    187     }
    188 #else
    189     // Then set up the FBO and copy the SurfTex content in.
    190     glBindFramebuffer(GL_FRAMEBUFFER, fboID);
    191     glFramebufferTexture2D(GL_FRAMEBUFFER,
    192                            GL_COLOR_ATTACHMENT0,
    193                            GL_TEXTURE_2D,
    194                            destTex->m_ownTextureId,
    195                            0);
    196     setGLStateForCopy(destTex->getSize().width(),
    197                       destTex->getSize().height());
    198     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    199     if (status != GL_FRAMEBUFFER_COMPLETE) {
    200         ALOGV("Error: glCheckFramebufferStatus failed");
    201         return;
    202     }
    203 
    204     // Use empty rect to set up the special matrix to draw.
    205     SkRect rect  = SkRect::MakeEmpty();
    206 
    207     TextureQuadData data(srcTexId, GL_NEAREST, srcTexTarget, Blit, 0, 0, 1.0, false);
    208     TilesManager::instance()->shader()->drawQuad(&data);
    209 #endif
    210 }
    211 
    212 // This function must be called inside the m_transferQueueItemLocks, for the
    213 // wait and getHasGLContext().
    214 // Only called by updateQueueWithBitmap() for now.
    215 bool TransferQueue::readyForUpdate()
    216 {
    217     if (!getHasGLContext())
    218         return false;
    219     // Don't use a while loop since when the WebView tear down, the emptyCount
    220     // will still be 0, and we bailed out b/c of GL context lost.
    221     if (!m_emptyItemCount)
    222         m_transferQueueItemCond.wait(m_transferQueueItemLocks);
    223 
    224     if (!getHasGLContext())
    225         return false;
    226 
    227     return true;
    228 }
    229 
    230 // Both getHasGLContext and setHasGLContext should be called within the lock.
    231 bool TransferQueue::getHasGLContext()
    232 {
    233     return m_hasGLContext;
    234 }
    235 
    236 void TransferQueue::setHasGLContext(bool hasContext)
    237 {
    238     m_hasGLContext = hasContext;
    239 }
    240 
    241 // Call within a m_transferQueueItemLocks, now called by resetQueue() and
    242 // cleanupGLResoucesAndQueue()
    243 void TransferQueue::emptyAndAbandonQueue()
    244 {
    245     for (int i = 0 ; i < m_transferQueueSize; i++)
    246         clearItemInTranferQueue(i);
    247     m_emptyItemCount = m_transferQueueSize;
    248     clearPureColorQueue();
    249 
    250     if (m_sharedSurfaceTexture.get()) {
    251         m_sharedSurfaceTexture->abandon();
    252         m_sharedSurfaceTexture.clear();
    253     }
    254     // This can prevent the tex gen thread to produce, until next incoming draw.
    255     setHasGLContext(false);
    256 }
    257 
    258 void TransferQueue::cleanupGLResourcesAndQueue()
    259 {
    260     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    261     emptyAndAbandonQueue();
    262     cleanupGLResources();
    263 }
    264 
    265 // Set all the content in the queue to pendingDiscard, after this, there will
    266 // be nothing added to the queue, and this can be called in any thread.
    267 // However, in order to discard the content in the Surface Texture using
    268 // updateTexImage, cleanupPendingDiscard need to be called on the UI thread.
    269 // Must be called within a m_transferQueueItemLocks.
    270 void TransferQueue::setPendingDiscard()
    271 {
    272     for (int i = 0 ; i < m_transferQueueSize; i++)
    273         if (m_transferQueue[i].status == pendingBlit)
    274             m_transferQueue[i].status = pendingDiscard;
    275 
    276     clearPureColorQueue();
    277 
    278     bool GLContextExisted = getHasGLContext();
    279     // Unblock the Tex Gen thread first before Tile Page deletion.
    280     // Otherwise, there will be a deadlock while removing operations.
    281     setHasGLContext(false);
    282 
    283     // Only signal once when GL context lost.
    284     if (GLContextExisted)
    285         m_transferQueueItemCond.signal();
    286 }
    287 
    288 void TransferQueue::clearPureColorQueue()
    289 {
    290     for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) {
    291         SkSafeUnref(m_pureColorTileQueue[i].savedTilePainter);
    292         m_pureColorTileQueue[i].savedTilePainter = 0;
    293     }
    294     m_pureColorTileQueue.clear();
    295 }
    296 
    297 void TransferQueue::updatePureColorTiles()
    298 {
    299     for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) {
    300         TileTransferData* data = &m_pureColorTileQueue[i];
    301         if (data->status == pendingBlit) {
    302             TileTexture* destTexture = 0;
    303             bool obsoleteTile = checkObsolete(data);
    304             if (!obsoleteTile) {
    305                 destTexture = data->savedTilePtr->backTexture();
    306                 destTexture->setPureColor(data->pureColor);
    307                 destTexture->transferComplete();
    308             }
    309         } else if (data->status == emptyItem || data->status == pendingDiscard) {
    310             // The queue should be clear instead of setting to different status.
    311             ALOGV("Warning: Don't expect an emptyItem here.");
    312         }
    313     }
    314     clearPureColorQueue();
    315 }
    316 
    317 // Call on UI thread to copy from the shared Surface Texture to the Tile's texture.
    318 void TransferQueue::updateDirtyTiles()
    319 {
    320     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    321 
    322     cleanupPendingDiscard();
    323     if (!getHasGLContext())
    324         setHasGLContext(true);
    325 
    326     // Check the pure color tile first, since it is simpler.
    327     updatePureColorTiles();
    328 
    329     // Start from the oldest item, we call the updateTexImage to retrive
    330     // the texture and blit that into each Tile's texture.
    331     const int nextItemIndex = getNextTransferQueueIndex();
    332     int index = nextItemIndex;
    333     bool usedFboForUpload = false;
    334     for (int k = 0; k < m_transferQueueSize ; k++) {
    335         if (m_transferQueue[index].status == pendingBlit) {
    336             bool obsoleteTile = checkObsolete(&m_transferQueue[index]);
    337             // Save the needed info, update the Surf Tex, clean up the item in
    338             // the queue. Then either move on to next item or copy the content.
    339             TileTexture* destTexture = 0;
    340             if (!obsoleteTile)
    341                 destTexture = m_transferQueue[index].savedTilePtr->backTexture();
    342 
    343             if (m_transferQueue[index].uploadType == GpuUpload) {
    344                 status_t result = m_sharedSurfaceTexture->updateTexImage();
    345                 if (result != OK)
    346                     ALOGE("unexpected error: updateTexImage return %d", result);
    347             }
    348 
    349             if (obsoleteTile) {
    350                 ALOGV("Warning: the texture is obsolete for this baseTile");
    351                 clearItemInTranferQueue(index);
    352                 index = (index + 1) % m_transferQueueSize;
    353                 continue;
    354             }
    355 
    356             // guarantee that we have a texture to blit into
    357             destTexture->requireGLTexture();
    358             GLUtils::checkGlError("before blitTileFromQueue");
    359             if (m_transferQueue[index].uploadType == CpuUpload) {
    360                 // Here we just need to upload the bitmap content to the GL Texture
    361                 GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId,
    362                                                  *m_transferQueue[index].bitmap);
    363             } else {
    364                 if (!usedFboForUpload) {
    365                     saveGLState();
    366                     usedFboForUpload = true;
    367                 }
    368                 blitTileFromQueue(m_fboID, destTexture, m_sharedSurfaceTextureId,
    369                                   m_sharedSurfaceTexture->getCurrentTextureTarget(),
    370                                   index);
    371             }
    372 
    373             destTexture->setPure(false);
    374             destTexture->transferComplete();
    375             clearItemInTranferQueue(index);
    376             ALOGV("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d",
    377                   m_transferQueue[index].savedTilePtr,
    378                   destTexture,
    379                   destTexture->m_ownTextureId);
    380         }
    381         index = (index + 1) % m_transferQueueSize;
    382     }
    383 
    384     // Clean up FBO setup. Doing this for both CPU/GPU upload can make the
    385     // dynamic switch possible. Moving this out from the loop can save some
    386     // milli-seconds.
    387     if (usedFboForUpload) {
    388         restoreGLState();
    389         GLUtils::checkGlError("updateDirtyTiles");
    390     }
    391 
    392     m_emptyItemCount = m_transferQueueSize;
    393     m_transferQueueItemCond.signal();
    394 }
    395 
    396 void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo,
    397                                           SkBitmap& bitmap)
    398 {
    399     TRACE_METHOD();
    400     if (!tryUpdateQueueWithBitmap(renderInfo, bitmap)) {
    401         // failed placing bitmap in queue, discard tile's texture so it will be
    402         // re-enqueued (and repainted)
    403         Tile* tile = renderInfo->baseTile;
    404         if (tile)
    405             tile->backTextureTransferFail();
    406     }
    407 }
    408 
    409 bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo,
    410                                              SkBitmap& bitmap)
    411 {
    412     // This lock need to cover the full update since it is possible that queue
    413     // will be cleaned up in the middle of this update without the lock.
    414     // The Surface Texture will not block us since the readyForUpdate will check
    415     // availability of the slots in the queue first.
    416     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    417     bool ready = readyForUpdate();
    418     TextureUploadType currentUploadType = m_currentUploadType;
    419     if (!ready) {
    420         ALOGV("Quit bitmap update: not ready! for tile x y %d %d",
    421               renderInfo->x, renderInfo->y);
    422         return false;
    423     }
    424     if (currentUploadType == GpuUpload) {
    425         // a) Dequeue the Surface Texture and write into the buffer
    426         if (!m_ANW.get()) {
    427             ALOGV("ERROR: ANW is null");
    428             return false;
    429         }
    430 
    431         if (!GLUtils::updateSharedSurfaceTextureWithBitmap(m_ANW.get(), bitmap))
    432             return false;
    433     }
    434 
    435     // b) After update the Surface Texture, now udpate the transfer queue info.
    436     addItemInTransferQueue(renderInfo, currentUploadType, bitmap);
    437 
    438     ALOGV("Bitmap updated x, y %d %d, baseTile %p",
    439           renderInfo->x, renderInfo->y, renderInfo->baseTile);
    440     return true;
    441 }
    442 
    443 void TransferQueue::addItemInPureColorQueue(const TileRenderInfo* renderInfo)
    444 {
    445     // The pure color tiles' queue will be read from UI thread and written in
    446     // Tex Gen thread, thus we need to have a lock here.
    447     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    448     TileTransferData data;
    449     addItemCommon(renderInfo, GpuUpload, &data);
    450     data.pureColor = renderInfo->pureColor;
    451     m_pureColorTileQueue.append(data);
    452 }
    453 
    454 void TransferQueue::clearItemInTranferQueue(int index)
    455 {
    456     m_transferQueue[index].savedTilePtr = 0;
    457     SkSafeUnref(m_transferQueue[index].savedTilePainter);
    458     m_transferQueue[index].savedTilePainter = 0;
    459     m_transferQueue[index].status = emptyItem;
    460 }
    461 
    462 // Translates the info from TileRenderInfo and others to TileTransferData.
    463 // This is used by pure color tiles and normal tiles.
    464 void TransferQueue::addItemCommon(const TileRenderInfo* renderInfo,
    465                                   TextureUploadType type,
    466                                   TileTransferData* data)
    467 {
    468     data->savedTileTexturePtr = renderInfo->baseTile->backTexture();
    469     data->savedTilePainter = renderInfo->tilePainter;
    470     SkSafeRef(data->savedTilePainter);
    471     data->savedTilePtr = renderInfo->baseTile;
    472     data->status = pendingBlit;
    473     data->uploadType = type;
    474 
    475     IntRect inval(0, 0, 0, 0);
    476 }
    477 
    478 // Note that there should be lock/unlock around this function call.
    479 // Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap.
    480 void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo,
    481                                            TextureUploadType type,
    482                                            SkBitmap& bitmap)
    483 {
    484     m_transferQueueIndex = (m_transferQueueIndex + 1) % m_transferQueueSize;
    485 
    486     int index = m_transferQueueIndex;
    487     if (m_transferQueue[index].savedTilePtr
    488         || m_transferQueue[index].status != emptyItem) {
    489         ALOGV("ERROR update a tile which is dirty already @ index %d", index);
    490     }
    491 
    492     TileTransferData* data = &m_transferQueue[index];
    493     addItemCommon(renderInfo, type, data);
    494     if (type == CpuUpload) {
    495         // Lazily create the bitmap
    496         if (!m_transferQueue[index].bitmap) {
    497             m_transferQueue[index].bitmap = new SkBitmap();
    498             int w = bitmap.width();
    499             int h = bitmap.height();
    500             m_transferQueue[index].bitmap->setConfig(bitmap.config(), w, h);
    501             m_transferQueue[index].bitmap->allocPixels();
    502         }
    503         SkBitmap temp = (*m_transferQueue[index].bitmap);
    504         (*m_transferQueue[index].bitmap) = bitmap;
    505         bitmap = temp;
    506     }
    507 
    508     m_emptyItemCount--;
    509 }
    510 
    511 void TransferQueue::setTextureUploadType(TextureUploadType type)
    512 {
    513     android::Mutex::Autolock lock(m_transferQueueItemLocks);
    514     if (m_currentUploadType == type)
    515         return;
    516 
    517     setPendingDiscard();
    518 
    519     m_currentUploadType = type;
    520     ALOGD("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload");
    521 }
    522 
    523 // Note: this need to be called within the lock and on the UI thread.
    524 // Only called by updateDirtyTiles() and emptyQueue() for now
    525 void TransferQueue::cleanupPendingDiscard()
    526 {
    527     int index = getNextTransferQueueIndex();
    528 
    529     for (int i = 0 ; i < m_transferQueueSize; i++) {
    530         if (m_transferQueue[index].status == pendingDiscard) {
    531             // No matter what the current upload type is, as long as there has
    532             // been a Surf Tex enqueue operation, this updateTexImage need to
    533             // be called to keep things in sync.
    534             if (m_transferQueue[index].uploadType == GpuUpload) {
    535                 status_t result = m_sharedSurfaceTexture->updateTexImage();
    536                 if (result != OK)
    537                     ALOGE("unexpected error: updateTexImage return %d", result);
    538             }
    539 
    540             // since tiles in the queue may be from another webview, remove
    541             // their textures so that they will be repainted / retransferred
    542             Tile* tile = m_transferQueue[index].savedTilePtr;
    543             TileTexture* texture = m_transferQueue[index].savedTileTexturePtr;
    544             if (tile && texture && texture->owner() == tile) {
    545                 // since tile destruction removes textures on the UI thread, the
    546                 // texture->owner ptr guarantees the tile is valid
    547                 tile->discardBackTexture();
    548                 ALOGV("transfer queue discarded tile %p, removed texture", tile);
    549             }
    550             clearItemInTranferQueue(index);
    551         }
    552         index = (index + 1) % m_transferQueueSize;
    553     }
    554 }
    555 
    556 void TransferQueue::saveGLState()
    557 {
    558     glGetIntegerv(GL_FRAMEBUFFER_BINDING, m_GLStateBeforeBlit.bufferId);
    559     glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport);
    560     glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor);
    561     glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth);
    562 #ifdef DEBUG
    563     glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor);
    564 #endif
    565 }
    566 
    567 void TransferQueue::setGLStateForCopy(int width, int height)
    568 {
    569     // Need to match the texture size.
    570     glViewport(0, 0, width, height);
    571     glDisable(GL_SCISSOR_TEST);
    572     glDisable(GL_DEPTH_TEST);
    573     // Clear the content is only for debug purpose.
    574 #ifdef DEBUG
    575     glClearColor(0, 0, 0, 0);
    576     glClear(GL_COLOR_BUFFER_BIT);
    577 #endif
    578 }
    579 
    580 void TransferQueue::restoreGLState()
    581 {
    582     glBindFramebuffer(GL_FRAMEBUFFER, m_GLStateBeforeBlit.bufferId[0]);
    583     glViewport(m_GLStateBeforeBlit.viewport[0],
    584                m_GLStateBeforeBlit.viewport[1],
    585                m_GLStateBeforeBlit.viewport[2],
    586                m_GLStateBeforeBlit.viewport[3]);
    587 
    588     if (m_GLStateBeforeBlit.scissor[0])
    589         glEnable(GL_SCISSOR_TEST);
    590 
    591     if (m_GLStateBeforeBlit.depth[0])
    592         glEnable(GL_DEPTH_TEST);
    593 #ifdef DEBUG
    594     glClearColor(m_GLStateBeforeBlit.clearColor[0],
    595                  m_GLStateBeforeBlit.clearColor[1],
    596                  m_GLStateBeforeBlit.clearColor[2],
    597                  m_GLStateBeforeBlit.clearColor[3]);
    598 #endif
    599 }
    600 
    601 int TransferQueue::getNextTransferQueueIndex()
    602 {
    603     return (m_transferQueueIndex + 1) % m_transferQueueSize;
    604 }
    605 
    606 } // namespace WebCore
    607 
    608 #endif // USE(ACCELERATED_COMPOSITING
    609