void GraphicalBoardFrame::appendHandler(const QString &text, bool shiftPressed) { if (!isOnBoard(m_arrowRoot)) return; if (!hasCandidate()) return; Quackle::LetterString appendedLetterString(QuackleIO::Util::encode(text)); if (appendedLetterString.length() == 0 || Quackle::String::front(appendedLetterString) < QUACKLE_FIRST_LETTER) return; if (shiftPressed) appendedLetterString = Quackle::String::setBlankness(appendedLetterString); else appendedLetterString = Quackle::String::clearBlankness(appendedLetterString); Quackle::Move newCandidate(m_candidate); Quackle::LetterString newTiles(m_candidate.tiles() + appendedLetterString); if (!m_ignoreRack && !m_rack.contains(Quackle::String::usedTiles(newTiles))) { Quackle::LetterString blankedNewTiles(m_candidate.tiles() + Quackle::String::setBlankness(appendedLetterString)); if (m_rack.contains(Quackle::String::usedTiles(blankedNewTiles))) { newTiles = blankedNewTiles; } } newCandidate.setTiles(newTiles); Quackle::LetterString hoppedTiles; QSize currentTile(m_arrowRoot); while (true) { const QSize nextTile = currentTile + arrowVector(); bool stopHere = false; bool resetArrowAfter = false; if (!isOnBoard(nextTile)) { stopHere = true; } else { Quackle::Board::TileInformation nextTileInformation(m_board.tileInformation(nextTile.height(), nextTile.width())); if (nextTileInformation.tileType != Quackle::Board::LetterTile) stopHere = true; } if (stopHere) { newCandidate.setTiles(newCandidate.tiles() + hoppedTiles); if (resetArrowAfter) resetArrow(); else m_arrowRoot = nextTile; break; } hoppedTiles += QUACKLE_PLAYED_THRU_MARK; currentTile = nextTile; } prettifyAndSetLocalCandidate(newCandidate); }
bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles, Compositor* aCompositor, ISurfaceAllocator* aAllocator) { if (mResolution != aTiles.resolution() || aTiles.tileSize() != mTileSize) { 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 newTiles(aTiles.firstTileX(), aTiles.firstTileY(), aTiles.retainedWidth(), aTiles.retainedHeight()); const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles(); // Step 1, unlock all the old tiles that haven't been unlocked yet. Any tiles that // exist in both the old and new sets will have been locked again by content, so this // doesn't result in the surface being writeable again. MarkTilesForUnlock(); TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles)); mRetainedTiles.SetLength(tileDescriptors.Length()); // Step 2, deserialize the incoming set of tiles into mRetainedTiles, and attempt // to recycle the TextureSource for any repeated tiles. // // Since we don't have any retained 'tile' object, we have to search for instances // of the same TextureHost in the old tile set. The cost of binding a TextureHost // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really // high, so we avoid this whenever possible. for (size_t i = 0; i < tileDescriptors.Length(); i++) { const TileDescriptor& tileDesc = tileDescriptors[i]; TileHost& tile = mRetainedTiles[i]; if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) { NS_WARN_IF_FALSE(tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor, "Unrecognised tile descriptor type"); continue; } const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); const TileLock& ipcLock = texturedDesc.sharedLock(); if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { return false; } tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent()); tile.mTextureHost->SetCompositor(aCompositor); if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { tile.mTextureHostOnWhite = TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent()); } tile.mTilePosition = newTiles.TilePosition(i); // If this same tile texture existed in the old tile set then this will move the texture // source into our new tile. oldRetainedTiles.RecycleTextureSourceForTile(tile); } // Step 3, attempt to recycle unused texture sources from the old tile set into new tiles. // // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way // to ensure that any implicit locking on the old gralloc image is released. for (TileHost& tile : mRetainedTiles) { if (!tile.mTextureHost || tile.mTextureSource) { continue; } oldRetainedTiles.RecycleTextureSource(tile); } // Step 4, handle the texture uploads, texture source binding and release the // copy-on-write locks for textures with an internal buffer. for (size_t i = 0; i < mRetainedTiles.Length(); i++) { TileHost& tile = mRetainedTiles[i]; if (!tile.mTextureHost) { continue; } const TileDescriptor& tileDesc = tileDescriptors[i]; const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); UseTileTexture(tile.mTextureHost, tile.mTextureSource, texturedDesc.updateRect(), aCompositor); if (tile.mTextureHostOnWhite) { UseTileTexture(tile.mTextureHostOnWhite, tile.mTextureSourceOnWhite, texturedDesc.updateRect(), aCompositor); } if (tile.mTextureHost->HasInternalBuffer()) { // Now that we did the texture upload (in UseTileTexture), we can release // the lock. tile.ReadUnlock(); } } mTiles = newTiles; mTileSize = aTiles.tileSize(); mTileOrigin = aTiles.tileOrigin(); mValidRegion = aTiles.validRegion(); mResolution = aTiles.resolution(); mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(), aTiles.frameYResolution()); return true; }
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; }