TEST(TiledLayerBuffer, TilesPlacement) { for (int firstY = -10; firstY < 10; ++firstY) { for (int firstX = -10; firstX < 10; ++firstX) { for (int height = 1; height < 10; ++height) { for (int width = 1; width < 10; ++width) { const TilesPlacement p1 = TilesPlacement(firstX, firstY, width, height); // Check that HasTile returns false with some positions that we know // not to be in the rectangle of the TilesPlacement. ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX - 1, 0))); ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY - 1))); ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX + width + 1, 0))); ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY + height + 1))); // Verify that all positions within the rect that defines the // TilesPlacement map to indices between 0 and width*height. for (int y = firstY; y < (firstY+height); ++y) { for (int x = firstX; x < (firstX+width); ++x) { ASSERT_TRUE(p1.HasTile(TileIntPoint(x,y))); ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) >= 0); ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) < width * height); } } // XXX - This causes some versions of gcc to warn that it optimizes // away the test, which gets caught in -WError in PGO builds. // The lazy thing to do is to just comment this out since this specific // test isn't critically important, but we should remove the warning instead. // cf. bug 1179287 // // Verify that indices map to positions that are within the rect that // defines the TilesPlacement. // for (int i = 0; i < width * height; ++i) { // ASSERT_TRUE(p1.TilePosition(i).x >= firstX); // ASSERT_TRUE(p1.TilePosition(i).x < firstX + width); // ASSERT_TRUE(p1.TilePosition(i).y >= firstY); // ASSERT_TRUE(p1.TilePosition(i).y < firstY + height); // } } } } } }
bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles, Compositor* aCompositor, ISurfaceAllocator* aAllocator) { if (mResolution != aTiles.resolution()) { Clear(); } MOZ_ASSERT(aAllocator); MOZ_ASSERT(aCompositor); if (!aAllocator || !aCompositor) { return false; } if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) { // There are divisions by mResolution so this protects the compositor process // against malicious content processes and fuzzing. return false; } TilesPlacement oldTiles = mTiles; TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(), aTiles.retainedWidth(), aTiles.retainedHeight()); const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles(); nsTArray<TileHost> oldRetainedTiles; mRetainedTiles.SwapElements(oldRetainedTiles); mRetainedTiles.SetLength(tileDescriptors.Length()); // Step 1, we need to unlock tiles that don't have an internal buffer after the // next frame where they are replaced. // Since we are about to replace the tiles' textures, we need to keep their locks // somewhere (in mPreviousSharedLock) until we composite the layer. for (size_t i = 0; i < oldRetainedTiles.Length(); ++i) { TileHost& tile = oldRetainedTiles[i]; // It can happen that we still have a previous lock at this point, // if we changed a tile's front buffer (causing mSharedLock to // go into mPreviousSharedLock, and then did not composite that tile until // the next transaction, either because the tile is offscreen or because the // two transactions happened with no composition in between (over-production). tile.ReadUnlockPrevious(); if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) { MOZ_ASSERT(tile.mSharedLock); const TileIntPoint tilePosition = oldTiles.TilePosition(i); if (newTiles.HasTile(tilePosition)) { // This tile still exist in the new buffer tile.mPreviousSharedLock = tile.mSharedLock; tile.mSharedLock = nullptr; } else { // This tile does not exist anymore in the new buffer because the size // changed. tile.ReadUnlock(); } } // By now we should not have anything in mSharedLock. MOZ_ASSERT(!tile.mSharedLock); } // Step 2, move the tiles in mRetainedTiles at places that correspond to where // they should be with the new retained with and height rather than the // old one. for (size_t i = 0; i < tileDescriptors.Length(); i++) { const TileIntPoint tilePosition = newTiles.TilePosition(i); // First, get the already existing tiles to the right place in the array, // and use placeholders where there was no tiles. if (!oldTiles.HasTile(tilePosition)) { mRetainedTiles[i] = GetPlaceholderTile(); } else { mRetainedTiles[i] = oldRetainedTiles[oldTiles.TileIndex(tilePosition)]; // If we hit this assertion it means we probably mixed something up in the // logic that tries to reuse tiles on the compositor side. It is most likely // benign, but we are missing some fast paths so let's try to make it not happen. MOZ_ASSERT(tilePosition.x == mRetainedTiles[i].x && tilePosition.y == mRetainedTiles[i].y); } } // It is important to remove the duplicated reference to tiles before calling // TextureHost::PrepareTextureSource, etc. because depending on the textures // ref counts we may or may not get some of the fast paths. oldRetainedTiles.Clear(); // Step 3, handle the texture updates and release the copy-on-write locks. for (size_t i = 0; i < mRetainedTiles.Length(); i++) { const TileDescriptor& tileDesc = tileDescriptors[i]; TileHost& tile = mRetainedTiles[i]; switch (tileDesc.type()) { case TileDescriptor::TTexturedTileDescriptor: { const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); const TileLock& ipcLock = texturedDesc.sharedLock(); if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { return false; } RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost( texturedDesc.textureParent() ); RefPtr<TextureHost> textureOnWhite = nullptr; if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { textureOnWhite = TextureHost::AsTextureHost( texturedDesc.textureOnWhite().get_PTextureParent() ); } UseTileTexture(tile.mTextureHost, tile.mTextureSource, texturedDesc.updateRect(), textureHost, aCompositor); if (textureOnWhite) { UseTileTexture(tile.mTextureHostOnWhite, tile.mTextureSourceOnWhite, texturedDesc.updateRect(), textureOnWhite, aCompositor); } else { // We could still have component alpha textures from a previous frame. tile.mTextureSourceOnWhite = nullptr; tile.mTextureHostOnWhite = nullptr; } if (textureHost->HasInternalBuffer()) { // Now that we did the texture upload (in UseTileTexture), we can release // the lock. tile.ReadUnlock(); } break; } default: NS_WARNING("Unrecognised tile descriptor type"); case TileDescriptor::TPlaceholderTileDescriptor: { if (tile.mTextureHost) { tile.mTextureHost->UnbindTextureSource(); tile.mTextureSource = nullptr; } if (tile.mTextureHostOnWhite) { tile.mTextureHostOnWhite->UnbindTextureSource(); tile.mTextureSourceOnWhite = nullptr; } // we may have a previous lock, and are about to loose our reference to it. // It is okay to unlock it because we just destroyed the texture source. tile.ReadUnlockPrevious(); tile = GetPlaceholderTile(); break; } } TileIntPoint tilePosition = newTiles.TilePosition(i); tile.x = tilePosition.x; tile.y = tilePosition.y; } mTiles = newTiles; mValidRegion = aTiles.validRegion(); mResolution = aTiles.resolution(); mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(), aTiles.frameYResolution()); return true; }