// Note: this need to be called within the lock and on the UI thread. // Only called by updateDirtyTiles() and emptyQueue() for now void TransferQueue::cleanupPendingDiscard() { int index = getNextTransferQueueIndex(); for (int i = 0 ; i < m_transferQueueSize; i++) { if (m_transferQueue[index].status == pendingDiscard) { // No matter what the current upload type is, as long as there has // been a Surf Tex enqueue operation, this updateTexImage need to // be called to keep things in sync. if (m_transferQueue[index].uploadType == GpuUpload) { status_t result = m_sharedSurfaceTexture->updateTexImage(); if (result != OK) ALOGE("unexpected error: updateTexImage return %d", result); } // since tiles in the queue may be from another webview, remove // their textures so that they will be repainted / retransferred Tile* tile = m_transferQueue[index].savedTilePtr; TileTexture* texture = m_transferQueue[index].savedTileTexturePtr; if (tile && texture && texture->owner() == tile) { // since tile destruction removes textures on the UI thread, the // texture->owner ptr guarantees the tile is valid tile->discardBackTexture(); ALOGV("transfer queue discarded tile %p, removed texture", tile); } clearItemInTranferQueue(index); } index = (index + 1) % m_transferQueueSize; } }
void TransferQueue::updatePureColorTiles() { for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) { TileTransferData* data = &m_pureColorTileQueue[i]; if (data->status == pendingBlit) { TileTexture* destTexture = 0; bool obsoleteTile = checkObsolete(data); if (!obsoleteTile) { destTexture = data->savedTilePtr->backTexture(); destTexture->setPureColor(data->pureColor); destTexture->transferComplete(); } } else if (data->status == emptyItem || data->status == pendingDiscard) { // The queue should be clear instead of setting to different status. ALOGV("Warning: Don't expect an emptyItem here."); } } clearPureColorQueue(); }
bool Tile::isTileReady() { // Return true if the tile's most recently drawn texture is up to date android::AutoMutex lock(m_atomicSync); TileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture; if (!texture) return false; if (texture->owner() != this) return false; if (m_dirty) return false; if (m_state != ReadyToSwap && m_state != UpToDate) return false; return true; }
void TilesManager::printTextures() { #ifdef DEBUG ALOGV("++++++"); for (unsigned int i = 0; i < m_textures.size(); i++) { TileTexture* texture = m_textures[i]; Tile* o = 0; if (texture->owner()) o = (Tile*) texture->owner(); int x = -1; int y = -1; if (o) { x = o->x(); y = o->y(); } ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f", i, texture, o, x, y, o ? o->scale() : 0); } ALOGV("------"); #endif // DEBUG }
// Call on UI thread to copy from the shared Surface Texture to the Tile's texture. void TransferQueue::updateDirtyTiles() { android::Mutex::Autolock lock(m_transferQueueItemLocks); cleanupPendingDiscard(); if (!getHasGLContext()) setHasGLContext(true); // Check the pure color tile first, since it is simpler. updatePureColorTiles(); // Start from the oldest item, we call the updateTexImage to retrive // the texture and blit that into each Tile's texture. const int nextItemIndex = getNextTransferQueueIndex(); int index = nextItemIndex; bool usedFboForUpload = false; for (int k = 0; k < m_transferQueueSize ; k++) { if (m_transferQueue[index].status == pendingBlit) { bool obsoleteTile = checkObsolete(&m_transferQueue[index]); // Save the needed info, update the Surf Tex, clean up the item in // the queue. Then either move on to next item or copy the content. TileTexture* destTexture = 0; if (!obsoleteTile) destTexture = m_transferQueue[index].savedTilePtr->backTexture(); if (m_transferQueue[index].uploadType == GpuUpload) { status_t result = m_sharedSurfaceTexture->updateTexImage(); if (result != OK) ALOGE("unexpected error: updateTexImage return %d", result); } if (obsoleteTile) { ALOGV("Warning: the texture is obsolete for this baseTile"); clearItemInTranferQueue(index); index = (index + 1) % m_transferQueueSize; continue; } // guarantee that we have a texture to blit into destTexture->requireGLTexture(); GLUtils::checkGlError("before blitTileFromQueue"); if (m_transferQueue[index].uploadType == CpuUpload) { // Here we just need to upload the bitmap content to the GL Texture GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, *m_transferQueue[index].bitmap); } else { if (!usedFboForUpload) { saveGLState(); usedFboForUpload = true; } blitTileFromQueue(m_fboID, destTexture, m_sharedSurfaceTextureId, m_sharedSurfaceTexture->getCurrentTextureTarget(), index); } destTexture->setPure(false); destTexture->transferComplete(); clearItemInTranferQueue(index); ALOGV("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d", m_transferQueue[index].savedTilePtr, destTexture, destTexture->m_ownTextureId); } index = (index + 1) % m_transferQueueSize; } // Clean up FBO setup. Doing this for both CPU/GPU upload can make the // dynamic switch possible. Moving this out from the loop can save some // milli-seconds. if (usedFboForUpload) { restoreGLState(); GLUtils::checkGlError("updateDirtyTiles"); } m_emptyItemCount = m_transferQueueSize; m_transferQueueItemCond.signal(); }
TileTexture* TilesManager::getAvailableTexture(Tile* owner) { android::Mutex::Autolock lock(m_texturesLock); WTF::Vector<TileTexture*>* availableTexturePool; if (owner->isLayerTile()) availableTexturePool = &m_availableTilesTextures; else availableTexturePool = &m_availableTextures; // Sanity check that the tile does not already own a texture if (owner->backTexture() && owner->backTexture()->owner() == owner) { int removeIndex = availableTexturePool->find(owner->backTexture()); // TODO: investigate why texture isn't found if (removeIndex >= 0) availableTexturePool->remove(removeIndex); return owner->backTexture(); } // The heuristic for selecting a texture is as follows: // 1. Skip textures currently being painted, they can't be painted while // busy anyway // 2. If a tile isn't owned, break with that one // 3. Don't let tiles acquire their front textures // 4. Otherwise, use the least recently prepared tile, but ignoring tiles // drawn in the last frame to avoid flickering TileTexture* farthestTexture = 0; unsigned long long oldestDrawCount = getDrawGLCount() - 1; const unsigned int max = availableTexturePool->size(); for (unsigned int i = 0; i < max; i++) { TileTexture* texture = (*availableTexturePool)[i]; Tile* currentOwner = static_cast<Tile*>(texture->owner()); if (!currentOwner) { // unused texture! take it! farthestTexture = texture; break; } if (currentOwner == owner) { // Don't let a tile acquire its own front texture, as the // acquisition logic doesn't handle that continue; } unsigned long long textureDrawCount = currentOwner->drawCount(); if (oldestDrawCount > textureDrawCount) { farthestTexture = texture; oldestDrawCount = textureDrawCount; } } if (farthestTexture) { Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner()); if (farthestTexture->acquire(owner)) { if (previousOwner) { previousOwner->removeTexture(farthestTexture); ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", owner->isLayerTile() ? "LAYER" : "BASE", farthestTexture, previousOwner->x(), previousOwner->y(), owner->x(), owner->y(), oldestDrawCount, getDrawGLCount()); } availableTexturePool->remove(availableTexturePool->find(farthestTexture)); return farthestTexture; } } else { if (owner->isLayerTile()) { // couldn't find a tile for a layer, layers shouldn't request redraw // TODO: once we do layer prefetching, don't set this for those // tiles m_layerTexturesRemain = false; } } ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available", owner->isLayerTile() ? "LAYER" : "BASE", owner, owner->x(), owner->y(), max); #ifdef DEBUG printTextures(); #endif // DEBUG return 0; }
// This is called from the texture generation thread void Tile::paintBitmap(TilePainter* painter, BaseRenderer* renderer) { // We acquire the values below atomically. This ensures that we are reading // values correctly across cores. Further, once we have these values they // can be updated by other threads without consequence. m_atomicSync.lock(); bool dirty = m_dirty; TileTexture* texture = m_backTexture; SkRegion dirtyArea = m_dirtyArea; float scale = m_scale; const int x = m_x; const int y = m_y; if (!dirty || !texture) { m_atomicSync.unlock(); return; } if (m_state != Unpainted) { ALOGV("Warning: started painting tile %p, but was at state %d, ft %p bt %p", this, m_state, m_frontTexture, m_backTexture); } m_state = PaintingStarted; TextureInfo* textureInfo = texture->getTextureInfo(); m_atomicSync.unlock(); // at this point we can safely check the ownership (if the texture got // transferred to another Tile under us) if (texture->owner() != this) { return; } // setup the common renderInfo fields; TileRenderInfo renderInfo; renderInfo.x = x; renderInfo.y = y; renderInfo.scale = scale; renderInfo.tileSize = texture->getSize(); renderInfo.tilePainter = painter; renderInfo.baseTile = this; renderInfo.textureInfo = textureInfo; const float tileWidth = renderInfo.tileSize.width(); const float tileHeight = renderInfo.tileSize.height(); renderer->renderTiledContent(renderInfo); m_atomicSync.lock(); if (texture == m_backTexture) { // set the fullrepaint flags m_fullRepaint = false; // The various checks to see if we are still dirty... m_dirty = false; if (m_scale != scale) m_dirty = true; m_dirtyArea.setEmpty(); ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); validatePaint(); } else { ALOGV("tile %p no longer owns texture %p, m_state %d. ft %p bt %p", this, texture, m_state, m_frontTexture, m_backTexture); } m_atomicSync.unlock(); }