// Note: this need to be called within th lock. // Only called by updateDirtyBaseTiles() for now void TransferQueue::cleanupTransportQueue() { int index = getNextTransferQueueIndex(); for (int i = 0 ; i < ST_BUFFER_NUMBER; 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) XLOGC("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 BaseTile* tile = m_transferQueue[index].savedBaseTilePtr; BaseTileTexture* texture = m_transferQueue[index].savedBaseTileTexturePtr; 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(); XLOG("transfer queue discarded tile %p, removed texture", tile); } m_transferQueue[index].savedBaseTilePtr = 0; m_transferQueue[index].savedBaseTileTexturePtr = 0; m_transferQueue[index].status = emptyItem; } index = (index + 1) % ST_BUFFER_NUMBER; } }
bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale) { if (!m_glWebViewState) return false; if (!m_invalRegion.isEmpty() && !m_prepare) return false; if (m_scale != scale) return false; int swaps = 0; bool fullSwap = true; for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) { for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) { BaseTile* t = getBaseTile(x, y); if (!t || !t->isTileReady()) fullSwap = false; } } // swap every tile on page (even if off screen) for (int j = 0; j < m_baseTileSize; j++) { BaseTile& tile = m_baseTiles[j]; if (tile.swapTexturesIfNeeded()) swaps++; } XLOG("%p greedy swapped %d textures, returning true", this, swaps); return fullSwap; }
std::shared_ptr<Terrain> TerrainGenerator::generate() const { if (!levelForElement) levelForElement = initElementTerrainLevels(); std::shared_ptr<Terrain> terrain = std::make_shared<Terrain>(m_settings); assert(PxGetPhysics().getNbScenes() == 1); PxScene * pxScene; PxGetPhysics().getScenes(&pxScene, 1); // The tileID determines the position of the current tile in the grid of tiles. // Tiles get shifted by -(numTilesPerAxis + 1)/2 so that we have the Tile(0,0,0) in the origin. int maxxID = m_settings.tilesX - int((m_settings.tilesX + 1) * 0.5); int minxID = maxxID - m_settings.tilesX + 1; int maxzID = m_settings.tilesZ - int((m_settings.tilesZ + 1) * 0.5); int minzID = maxzID - m_settings.tilesZ + 1; terrain->minTileXID = minxID; terrain->minTileZID = minzID; for (int xID = minxID; xID <= maxxID; ++xID) for (int zID = minzID; zID <= maxzID; ++zID) { TileID tileIDBase(TerrainLevel::BaseLevel, xID, zID); std::initializer_list<std::string> baseElements = { "bedrock", "sand", "grassland" }; /** create terrain object and pass terrain data */ BaseTile * baseTile = new BaseTile(*terrain, tileIDBase, baseElements); // create the terrain using diamond square algorithm diamondSquare(*baseTile); // and apply the elements to the landscape applyElementsByHeight(*baseTile); /** same thing for the liquid level, just that we do not add a terrain type texture */ TileID tileIDLiquid(TerrainLevel::WaterLevel, xID, zID); LiquidTile * liquidTile = new LiquidTile(*terrain, tileIDLiquid); /** Create physx objects: an actor with its transformed shapes * move tile according to its id, and by one half tile size, so the center of Tile(0,0,0) is in the origin */ PxTransform pxTerrainTransform = PxTransform(PxVec3(m_settings.tileBorderLength() * (xID - 0.5f), 0.0f, m_settings.tileBorderLength() * (zID - 0.5f))); PxRigidStatic * actor = PxGetPhysics().createRigidStatic(pxTerrainTransform); terrain->m_pxActors.emplace(tileIDBase, actor); baseTile->createPxObjects(*actor); liquidTile->createPxObjects(*actor); pxScene->addActor(*actor); TileID temperatureID(TerrainLevel::TemperatureLevel, xID, zID); // the tile registers itself in the terrain new TemperatureTile(*terrain, temperatureID, *baseTile, *liquidTile); } return terrain; }
void TerrainGenerator::applyElementsByHeight(BaseTile & tile) const { uint8_t sand = tile.elementIndex("sand"); uint8_t grassland = tile.elementIndex("grassland"); uint8_t bedrock = tile.elementIndex("bedrock"); float sandMaxHeight = 2.5f; // under water + shore float grasslandMaxHeight = m_settings.maxHeight * 0.2f; for (unsigned int row = 0; row < tile.samplesPerAxis - 1; ++row) { const unsigned int rowOffset = row * tile.samplesPerAxis; for (unsigned int column = 0; column < tile.samplesPerAxis - 1; ++column) { const unsigned int index = rowOffset + column; float height = 0.25f * ( tile.valueAt(index) + tile.valueAt(row + 1, column) + tile.valueAt(row, column + 1) + tile.valueAt(row + 1, column + 1)); if (height < sandMaxHeight) { tile.setElement(index, sand); continue; } if (height < grasslandMaxHeight) { tile.setElement(index, grassland); continue; } tile.setElement(index, bedrock); } } }
void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo, int x, int y, const SkBitmap& bitmap) { if (!tryUpdateQueueWithBitmap(renderInfo, x, y, bitmap)) { // failed placing bitmap in queue, discard tile's texture so it will be // re-enqueued (and repainted) BaseTile* tile = renderInfo->baseTile; if (tile) tile->backTextureTransferFail(); } }
static shared_ptr<PixelCache> create(BaseTile &tile, uint radius) { auto center = tile.getCenter(); Area boundingBox(center - vec2(radius, radius), center + vec2(radius, radius)); shared_ptr<PixelCache> output = make_shared<PixelCache>(); for (float x = boundingBox.getX1(); x < boundingBox.getX2(); x++) { for (float y = boundingBox.getY1(); y < boundingBox.getY2(); y++) { if (tile.contains(vec2(x, y))) { output->mInsidePositions.push_back(ivec2(x, y)); } } } output->mBoundingBox = boundingBox; return output; }
// When bliting, if the item from the transfer queue is mismatching b/t the // BaseTile and the content, then the item is considered as obsolete, and // the content is discarded. bool TransferQueue::checkObsolete(int index) { BaseTile* baseTilePtr = m_transferQueue[index].savedBaseTilePtr; if (!baseTilePtr) { XLOG("Invalid savedBaseTilePtr , such that the tile is obsolete"); return true; } BaseTileTexture* baseTileTexture = baseTilePtr->backTexture(); if (!baseTileTexture) { XLOG("Invalid baseTileTexture , such that the tile is obsolete"); return true; } const TextureTileInfo* tileInfo = &m_transferQueue[index].tileInfo; if (tileInfo->m_x != baseTilePtr->x() || tileInfo->m_y != baseTilePtr->y() || tileInfo->m_scale != baseTilePtr->scale() || tileInfo->m_painter != baseTilePtr->painter()) { XLOG("Mismatching x, y, scale or painter , such that the tile is obsolete"); return true; } return false; }
void TilesManager::printTextures() { #ifdef DEBUG XLOG("++++++"); for (unsigned int i = 0; i < m_textures.size(); i++) { BaseTileTexture* texture = m_textures[i]; BaseTile* o = 0; if (texture->owner()) o = (BaseTile*) texture->owner(); int x = -1; int y = -1; if (o) { x = o->x(); y = o->y(); } XLOG("[%d] texture %x busy: %d owner: %x (%d, %d) page: %x scale: %.2f", i, texture, texture->busy(), o, x, y, o ? o->page() : 0, o ? o->scale() : 0); } XLOG("------"); #endif // DEBUG }
void TilesProfiler::nextTile(BaseTile& tile, float scale, bool inView) { if (!m_enabled || (m_records.size() > MAX_PROF_FRAMES) || (m_records.size() == 0)) return; bool isReady = tile.isTileReady(); int left = tile.x() * TilesManager::tileWidth(); int top = tile.y() * TilesManager::tileWidth(); int right = left + TilesManager::tileWidth(); int bottom = top + TilesManager::tileWidth(); if (inView) { if (isReady) m_goodTiles++; else m_badTiles++; } m_records.last().append(TileProfileRecord( left, top, right, bottom, scale, isReady, (int)tile.drawCount())); XLOG("adding tile %d %d %d %d, scale %f", left, top, right, bottom, scale); }
void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds) { for (int i = 0; i < tilesInRow; i++) { int x = firstTileX; // If we are goingLeft, we want to schedule the tiles starting from the // right (and to the left if not). This is because tiles are appended to // the list and the texture uploader goes through the set front to back. if (goingLeft) x += (tilesInRow - 1) - i; else x += i; BaseTile* currentTile = 0; BaseTile* availableTile = 0; for (int j = 0; j < m_baseTileSize; j++) { BaseTile& tile = m_baseTiles[j]; if (tile.x() == x && tile.y() == y) { currentTile = &tile; break; } if (!availableTile || (tile.drawCount() < availableTile->drawCount())) availableTile = &tile; } if (!currentTile && availableTile) { XLOG("STEALING tile %d, %d (draw count %llu) for tile %d, %d", availableTile->x(), availableTile->y(), availableTile->drawCount(), x, y); availableTile->discardTextures(); // don't wait for textures to be stolen currentTile = availableTile; } if (!currentTile) { XLOG("ERROR: No tile available for tile %d %d", x, y); } if (currentTile) { currentTile->setGLWebViewState(m_glWebViewState); currentTile->setPage(this); currentTile->setContents(this, x, y, m_scale); // TODO: move below (which is largely the same for layers / tiled // page) into prepare() function // ensure there is a texture associated with the tile and then check to // see if the texture is dirty and in need of repainting if (currentTile->isDirty() || !currentTile->frontTexture()) currentTile->reserveTexture(); if (currentTile->backTexture() && currentTile->isDirty() && !currentTile->isRepaintPending()) { PaintTileOperation *operation = new PaintTileOperation(currentTile); TilesManager::instance()->scheduleOperation(operation); } } } }
BaseTileTexture* TilesManager::getAvailableTexture(BaseTile* owner) { android::Mutex::Autolock lock(m_texturesLock); // Sanity check that the tile does not already own a texture if (owner->backTexture() && owner->backTexture()->owner() == owner) { XLOG("same owner (%d, %d), getAvailableBackTexture(%x) => texture %x", owner->x(), owner->y(), owner, owner->backTexture()); if (owner->isLayerTile()) m_availableTilesTextures.remove(m_availableTilesTextures.find(owner->backTexture())); else m_availableTextures.remove(m_availableTextures.find(owner->backTexture())); return owner->backTexture(); } WTF::Vector<BaseTileTexture*>* availableTexturePool; if (owner->isLayerTile()) { availableTexturePool = &m_availableTilesTextures; } else { availableTexturePool = &m_availableTextures; } // 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. If we find a tile in the same page with a different scale, // it's old and not visible. Break with that one // 5. Otherwise, use the least recently prepared tile, but ignoring tiles // drawn in the last frame to avoid flickering BaseTileTexture* farthestTexture = 0; unsigned long long oldestDrawCount = getDrawGLCount() - 1; const unsigned int max = availableTexturePool->size(); for (unsigned int i = 0; i < max; i++) { BaseTileTexture* texture = (*availableTexturePool)[i]; BaseTile* currentOwner = static_cast<BaseTile*>(texture->owner()); if (texture->busy()) { // don't bother, since the acquire() will likely fail continue; } 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; } if (currentOwner->painter() == owner->painter() && texture->scale() != owner->scale()) { // if we render the back page with one scale, then another while // still zooming, we recycle the tiles with the old scale instead of // taking ones from the front page farthestTexture = texture; break; } unsigned long long textureDrawCount = currentOwner->drawCount(); if (oldestDrawCount > textureDrawCount) { farthestTexture = texture; oldestDrawCount = textureDrawCount; } } if (farthestTexture) { BaseTile* previousOwner = static_cast<BaseTile*>(farthestTexture->owner()); if (farthestTexture->acquire(owner)) { if (previousOwner) { previousOwner->removeTexture(farthestTexture); XLOG("%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; } } XLOG("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; }