bool
ClientTiledThebesLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
                                           const nsIntRegion& aVisibleRegion,
                                           LayerManager::DrawThebesLayerCallback aCallback,
                                           void* aCallbackData)
{
  // Render the low precision buffer, if the visible region is larger than the
  // critical display port.
  if (!nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(aVisibleRegion)) {
    nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion();
    oldValidRegion.And(oldValidRegion, aVisibleRegion);

    bool updatedBuffer = false;

    // If the frame resolution or format have changed, invalidate the buffer
    if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution ||
        mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) {
      if (!mLowPrecisionValidRegion.IsEmpty()) {
        updatedBuffer = true;
      }
      oldValidRegion.SetEmpty();
      mLowPrecisionValidRegion.SetEmpty();
      mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution);
      aInvalidRegion = aVisibleRegion;
    }

    // Invalidate previously valid content that is no longer visible
    if (mPaintData.mLowPrecisionPaintCount == 1) {
      mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion);
    }
    mPaintData.mLowPrecisionPaintCount++;

    // Remove the valid high-precision region from the invalid low-precision
    // region. We don't want to spend time drawing things twice.
    aInvalidRegion.Sub(aInvalidRegion, mValidRegion);

    TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str());
    TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str());

    if (!aInvalidRegion.IsEmpty()) {
      updatedBuffer = mContentClient->mLowPrecisionTiledBuffer.ProgressiveUpdate(
                            mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
                            &mPaintData, aCallback, aCallbackData);
    }

    TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
    return updatedBuffer;
  }
  if (!mLowPrecisionValidRegion.IsEmpty()) {
    TILING_LOG("TILING %p: Clearing low-precision buffer\n", this);
    // Clear the low precision tiled buffer.
    mLowPrecisionValidRegion.SetEmpty();
    mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState();
    // Return true here so we send a Painted callback after clearing the valid
    // region of the low precision buffer. This allows the shadow buffer's valid
    // region to be updated and the associated resources to be freed.
    return true;
  }
  return false;
}
void
ClientTiledPaintedLayer::EndPaint()
{
  mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
  mPaintData.mPaintFinished = true;
  mPaintData.mFirstPaint = false;
  TILING_LOG("TILING %p: Paint finished\n", this);
}
bool
ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
                                            const nsIntRegion& aVisibleRegion,
                                            LayerManager::DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData)
{
  // If we have no high-precision stuff to draw, or we have started drawing low-precision
  // already, then we shouldn't do anything there.
  if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) {
    return false;
  }

  // Only draw progressively when the resolution is unchanged, and we're not
  // in a reftest scenario (that's what the HasShadowManager() check is for).
  if (gfxPlatform::GetPlatform()->UseProgressivePaint() &&
      !ClientManager()->HasShadowTarget() &&
      mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) {
    // Store the old valid region, then clear it before painting.
    // We clip the old valid region to the visible region, as it only gets
    // used to decide stale content (currently valid and previously visible)
    nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion();
    oldValidRegion.And(oldValidRegion, aVisibleRegion);
    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
      oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
    }

    TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());

    return mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, aInvalidRegion,
                      oldValidRegion, &mPaintData, aCallback, aCallbackData);
  }

  // Otherwise do a non-progressive paint

  mValidRegion = aVisibleRegion;
  if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
    mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
  }

  TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
  TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());

  mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
  mContentClient->mTiledBuffer.PaintThebes(mValidRegion, aInvalidRegion, aCallback, aCallbackData);
  return true;
}
bool
ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
                                            const nsIntRegion& aVisibleRegion,
                                            LayerManager::DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData)
{
  // If we have no high-precision stuff to draw, or we have started drawing low-precision
  // already, then we shouldn't do anything there.
  if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) {
    return false;
  }

  // Only draw progressively when the resolution is unchanged
  if (UseProgressiveDraw() &&
      mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) {
    // Store the old valid region, then clear it before painting.
    // We clip the old valid region to the visible region, as it only gets
    // used to decide stale content (currently valid and previously visible)
    nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion();
    oldValidRegion.And(oldValidRegion, aVisibleRegion);
    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
      oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect());
    }

    TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());

    return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion,
                      oldValidRegion, &mPaintData, aCallback, aCallbackData);
  }

  // Otherwise do a non-progressive paint

  mValidRegion = aVisibleRegion;
  if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
    mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect());
  }

  TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
  TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());

  mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
  mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion,
                                                aCallback, aCallbackData);
  mPaintData.mPaintFinished = true;
  return true;
}
void
ClientTiledPaintedLayer::RenderLayer()
{
  LayerManager::DrawPaintedLayerCallback callback =
    ClientManager()->GetPaintedLayerCallback();
  void *data = ClientManager()->GetPaintedLayerCallbackData();
  if (!callback) {
    ClientManager()->SetTransactionIncomplete();
    return;
  }

  if (!mContentClient) {
    mContentClient = new TiledContentClient(this, ClientManager());

    mContentClient->Connect();
    ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
    MOZ_ASSERT(mContentClient->GetForwarder());
  }

  if (mContentClient->mTiledBuffer.HasFormatChanged()) {
    mValidRegion = nsIntRegion();
    mContentClient->mTiledBuffer.ResetPaintedAndValidState();
  }

  TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
  TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
  TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());

  nsIntRegion neededRegion = mVisibleRegion;
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
  // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
  if (MayResample()) {
    // If we're resampling then bilinear filtering can read up to 1 pixel
    // outside of our texture coords. Make the visible region a single rect,
    // and pad it out by 1 pixel (restricted to tile boundaries) so that
    // we always have valid content or transparent pixels to sample from.
    nsIntRect bounds = neededRegion.GetBounds();
    nsIntRect wholeTiles = bounds;
    wholeTiles.Inflate(nsIntSize(
      gfxPlatform::GetPlatform()->GetTileWidth(),
      gfxPlatform::GetPlatform()->GetTileHeight()));
    nsIntRect padded = bounds;
    padded.Inflate(1);
    padded.IntersectRect(padded, wholeTiles);
    neededRegion = padded;
  }
#endif

  nsIntRegion invalidRegion;
  invalidRegion.Sub(neededRegion, mValidRegion);
  if (invalidRegion.IsEmpty()) {
    EndPaint();
    return;
  }

  if (!ClientManager()->IsRepeatTransaction()) {
    // Only paint the mask layer on the first transaction.
    if (GetMaskLayer()) {
      ToClientLayer(GetMaskLayer())->RenderLayer();
    }

    // In some cases we can take a fast path and just be done with it.
    if (UseFastPath()) {
      TILING_LOG("TILING %p: Taking fast-path\n", this);
      mValidRegion = neededRegion;
      mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data);
      ClientManager()->Hold(this);
      mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
      return;
    }

    // For more complex cases we need to calculate a bunch of metrics before we
    // can do the paint.
    BeginPaint();
    if (mPaintData.mPaintFinished) {
      return;
    }

    // Make sure that tiles that fall outside of the visible region or outside of the
    // critical displayport are discarded on the first update. Also make sure that we
    // only draw stuff inside the critical displayport on the first update.
    mValidRegion.And(mValidRegion, neededRegion);
    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
      mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
      invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
    }

    TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str());
    TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
  } else {
    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
      invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
    }
    TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
  }

  nsIntRegion lowPrecisionInvalidRegion;
  if (gfxPrefs::UseLowPrecisionBuffer()) {
    // Calculate the invalid region for the low precision buffer. Make sure
    // to remove the valid high-precision area so we don't double-paint it.
    lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion);
    lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
  }
  TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str());

  bool updatedHighPrecision = RenderHighPrecision(invalidRegion,
                                                  neededRegion,
                                                  callback, data);
  if (updatedHighPrecision) {
    ClientManager()->Hold(this);
    mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);

    if (!mPaintData.mPaintFinished) {
      // There is still more high-res stuff to paint, so we're not
      // done yet. A subsequent transaction will take care of this.
      ClientManager()->SetRepeatTransaction();
      return;
    }
  }

  // If there is nothing to draw in low-precision, then we're done.
  if (lowPrecisionInvalidRegion.IsEmpty()) {
    EndPaint();
    return;
  }

  if (updatedHighPrecision) {
    // If there are low precision updates, but we just did some high-precision
    // updates, then mark the paint as unfinished and request a repeat transaction.
    // This is so that we don't perform low-precision updates in the same transaction
    // as high-precision updates.
    TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this);
    ClientManager()->SetRepeatTransaction();
    mPaintData.mLowPrecisionPaintCount = 1;
    mPaintData.mPaintFinished = false;
    return;
  }

  bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion,
                                                neededRegion,
                                                callback, data);
  if (updatedLowPrecision) {
    ClientManager()->Hold(this);
    mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);

    if (!mPaintData.mPaintFinished) {
      // There is still more low-res stuff to paint, so we're not
      // done yet. A subsequent transaction will take care of this.
      ClientManager()->SetRepeatTransaction();
      return;
    }
  }

  // If we get here, we've done all the high- and low-precision
  // paints we wanted to do, so we can finish the paint and chill.
  EndPaint();
}
void
ClientTiledPaintedLayer::BeginPaint()
{
  mPaintData.mLowPrecisionPaintCount = 0;
  mPaintData.mPaintFinished = false;
  mPaintData.mCompositionBounds.SetEmpty();
  mPaintData.mCriticalDisplayPort.SetEmpty();

  if (!GetBaseTransform().Is2D()) {
    // Give up if there is a complex CSS transform on the layer. We might
    // eventually support these but for now it's too complicated to handle
    // given that it's a pretty rare scenario.
    return;
  }

  // Get the metrics of the nearest scrollable layer and the nearest layer
  // with a displayport.
  LayerMetricsWrapper scrollAncestor;
  LayerMetricsWrapper displayPortAncestor;
  GetAncestorLayers(&scrollAncestor, &displayPortAncestor);

  if (!displayPortAncestor || !scrollAncestor) {
    // No displayport or scroll ancestor, so we can't do progressive rendering.
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G)
    // Both Android and b2g are guaranteed to have a displayport set, so this
    // should never happen.
    NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor");
#endif
    return;
  }

  TILING_LOG("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this,
    scrollAncestor.GetLayer(), displayPortAncestor.GetLayer());

  const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
  const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();

  // Calculate the transform required to convert ParentLayer space of our
  // display port ancestor to the Layer space of this layer.
  gfx::Matrix4x4 transformDisplayPortToLayer =
    GetTransformToAncestorsParentLayer(this, displayPortAncestor);
  transformDisplayPortToLayer.Invert();

  // Compute the critical display port that applies to this layer in the
  // LayoutDevice space of this layer.
  ParentLayerRect criticalDisplayPort =
    (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
    + displayportMetrics.mCompositionBounds.TopLeft();
  mPaintData.mCriticalDisplayPort = RoundedOut(
    ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort));
  TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str());

  // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
  // before any async transforms have occurred, we can use the zoom for this.
  mPaintData.mResolution = displayportMetrics.GetZoom();
  TILING_LOG("TILING %p: Resolution %f\n", this, mPaintData.mPresShellResolution.scale);

  // Store the applicable composition bounds in this layer's Layer units.
  mPaintData.mTransformToCompBounds =
    GetTransformToAncestorsParentLayer(this, scrollAncestor);
  gfx::Matrix4x4 transformToBounds = mPaintData.mTransformToCompBounds;
  transformToBounds.Invert();
  mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform(
    transformToBounds, scrollMetrics.mCompositionBounds);
  TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());

  // Calculate the scroll offset since the last transaction
  mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
  TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
}
void
ClientTiledPaintedLayer::BeginPaint()
{
  mPaintData.ResetPaintData();

  if (!GetBaseTransform().Is2D()) {
    // Give up if there is a complex CSS transform on the layer. We might
    // eventually support these but for now it's too complicated to handle
    // given that it's a pretty rare scenario.
    return;
  }

  // Get the metrics of the nearest scrollable layer and the nearest layer
  // with a displayport.
  LayerMetricsWrapper scrollAncestor;
  LayerMetricsWrapper displayPortAncestor;
  bool hasTransformAnimation;
  GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation);

  if (!displayPortAncestor || !scrollAncestor) {
    // No displayport or scroll ancestor, so we can't do progressive rendering.
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
    // Both Android and b2g on phones are guaranteed to have a displayport set, so this
    // should never happen.
    NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor");
#endif
    return;
  }

  TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this,
    scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation);

  const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
  const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();

  // Calculate the transform required to convert ParentLayer space of our
  // display port ancestor to the Layer space of this layer.
  gfx::Matrix4x4 transformDisplayPortToLayer =
    GetTransformToAncestorsParentLayer(this, displayPortAncestor);
  transformDisplayPortToLayer.Invert();

  LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));

  // Compute the critical display port that applies to this layer in the
  // LayoutDevice space of this layer, but only if there is no OMT animation
  // on this layer. If there is an OMT animation then we need to draw the whole
  // visible region of this layer as determined by layout, because we don't know
  // what parts of it might move into view in the compositor.
  if (!hasTransformAnimation &&
      mContentClient->GetLowPrecisionTiledBuffer()) {
    ParentLayerRect criticalDisplayPort =
      (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
      + displayportMetrics.GetCompositionBounds().TopLeft();
    Maybe<LayerRect> criticalDisplayPortTransformed =
      ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds);
    if (!criticalDisplayPortTransformed) {
      mPaintData.ResetPaintData();
      return;
    }
    mPaintData.mCriticalDisplayPort = RoundedToInt(*criticalDisplayPortTransformed);
  }
  TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str());

  // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
  // before any async transforms have occurred, we can use the zoom for this.
  mPaintData.mResolution = displayportMetrics.GetZoom();
  TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str());

  // Store the applicable composition bounds in this layer's Layer units.
  mPaintData.mTransformToCompBounds =
    GetTransformToAncestorsParentLayer(this, scrollAncestor);
  gfx::Matrix4x4 transformToBounds = mPaintData.mTransformToCompBounds;
  transformToBounds.Invert();
  Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform(
    transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds);
  if (!compositionBoundsTransformed) {
    mPaintData.ResetPaintData();
    return;
  }
  mPaintData.mCompositionBounds = *compositionBoundsTransformed;
  TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());

  // Calculate the scroll offset since the last transaction
  mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
  TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
}
void ClientSingleTiledLayerBuffer::PaintThebes(
    const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion,
    const nsIntRegion& aDirtyRegion,
    LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
    TilePaintFlags aFlags) {
  mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
  bool asyncPaint = !!(aFlags & TilePaintFlags::Async);

  // Compare layer valid region size to current backbuffer size, discard if not
  // matching.
  gfx::IntSize size = aNewValidRegion.GetBounds().Size();
  gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
  nsIntRegion paintRegion = aPaintRegion;

  RefPtr<TextureClient> discardedFrontBuffer = nullptr;
  RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
  nsIntRegion discardedValidRegion;

  if (mSize != size || mTilingOrigin != origin) {
    discardedFrontBuffer = mTile.mFrontBuffer;
    discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
    discardedValidRegion = mValidRegion;

    TILING_LOG(
        "TILING %p: Single-tile valid region changed. Discarding buffers.\n",
        &mPaintedLayer);
    ResetPaintedAndValidState();
    mSize = size;
    mTilingOrigin = origin;
    paintRegion = aNewValidRegion;
  }

  SurfaceMode mode;
  gfxContentType content = GetContentType(&mode);
  mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);

  if (mTile.IsPlaceholderTile()) {
    mTile.SetTextureAllocator(this);
  }

  if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
    AutoTArray<uint64_t, 2> syncTextureSerials;
    mTile.GetSyncTextureSerials(mode, syncTextureSerials);
    if (syncTextureSerials.Length() > 0) {
      mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
    }
  }

  // The dirty region relative to the top-left of the tile.
  nsIntRegion tileVisibleRegion = aNewValidRegion.MovedBy(-mTilingOrigin);
  nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);

  Maybe<AcquiredBackBuffer> backBuffer =
      mTile.AcquireBackBuffer(mCompositableClient, tileDirtyRegion,
                              tileVisibleRegion, content, mode, aFlags);

  if (!backBuffer) {
    return;
  }

  // Mark the area we need to paint in the back buffer as invalid in the
  // front buffer as they will become out of sync.
  mTile.mInvalidFront.OrWith(tileDirtyRegion);

  // Add backbuffer's invalid region to the dirty region to be painted.
  // This will be empty if we were able to copy from the front in to the back.
  nsIntRegion tileInvalidRegion = mTile.mInvalidBack;
  tileInvalidRegion.AndWith(tileVisibleRegion);

  paintRegion.OrWith(tileInvalidRegion.MovedBy(mTilingOrigin));
  tileDirtyRegion.OrWith(tileInvalidRegion);

  // Mark the region we will be painting and the region we copied from the front
  // buffer as needing to be uploaded to the compositor
  mTile.mUpdateRect =
      tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect);

  // If the old frontbuffer was discarded then attempt to copy what we
  // can from it to the new backbuffer.
  if (discardedFrontBuffer) {
    nsIntRegion copyableRegion;
    copyableRegion.And(aNewValidRegion, discardedValidRegion);
    copyableRegion.SubOut(aDirtyRegion);

    OpenMode readMode =
        asyncPaint ? OpenMode::OPEN_READ_ASYNC : OpenMode::OPEN_READ;

    DualTextureClientAutoLock discardedBuffer(
        discardedFrontBuffer, discardedFrontBufferOnWhite, readMode);

    if (discardedBuffer.Succeeded()) {
      RefPtr<gfx::SourceSurface> discardedSurface = discardedBuffer->Snapshot();

      for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
        const gfx::IntRect src =
            iter.Get() - discardedValidRegion.GetBounds().TopLeft();
        const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;

        backBuffer->mTarget->CopySurface(discardedSurface, src, dest);
      }

      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n",
                 &mPaintedLayer, Stringify(copyableRegion).c_str());

      // We don't need to repaint valid content that was just copied.
      paintRegion.SubOut(copyableRegion);
      copyableRegion.MoveBy(-mTilingOrigin);
      tileDirtyRegion.SubOut(copyableRegion);
    } else {
      gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front "
                      "buffer's draw target";
    }
  }

  if (mode != SurfaceMode::SURFACE_OPAQUE) {
    for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
      const gfx::Rect drawRect(iter.Get().X(), iter.Get().Y(),
                               iter.Get().Width(), iter.Get().Height());
      backBuffer->mTarget->ClearRect(drawRect);
    }
  }

  // Paint into the target
  {
    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(backBuffer->mTarget);
    if (!ctx) {
      gfxDevCrash(gfx::LogReason::InvalidContext)
          << "SingleTiledContextClient context problem "
          << gfx::hexa(backBuffer->mTarget);
      return;
    }
    ctx->SetMatrix(
        ctx->CurrentMatrix().PreTranslate(-mTilingOrigin.x, -mTilingOrigin.y));

    aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion,
              DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
  }

  if (asyncPaint) {
    if (!backBuffer->mCapture->IsEmpty()) {
      UniquePtr<PaintTask> task(new PaintTask());
      task->mCapture = backBuffer->mCapture;
      task->mTarget = backBuffer->mBackBuffer;
      task->mClients = std::move(backBuffer->mTextureClients);
      if (discardedFrontBuffer) {
        task->mClients.AppendElement(discardedFrontBuffer);
      }
      if (discardedFrontBufferOnWhite) {
        task->mClients.AppendElement(discardedFrontBufferOnWhite);
      }

      // The target is an alias for the capture, and the paint thread expects
      // to be the only one with a reference to the capture
      backBuffer->mTarget = nullptr;
      backBuffer->mCapture = nullptr;

      PaintThread::Get()->QueuePaintTask(std::move(task));
      mManager->SetQueuedAsyncPaints();
    }
  } else {
    MOZ_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer);
    MOZ_ASSERT(!backBuffer->mCapture);
  }

  // The new buffer is now validated, remove the dirty region from it.
  mTile.mInvalidBack.SubOut(tileDirtyRegion);

  backBuffer = Nothing();

  mTile.Flip();
  UnlockTile(mTile);

  mValidRegion = aNewValidRegion;
  mLastPaintSurfaceMode = mode;
  mLastPaintContentType = content;
}
void
ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                          const nsIntRegion& aPaintRegion,
                                          const nsIntRegion& aDirtyRegion,
                                          LayerManager::DrawPaintedLayerCallback aCallback,
                                          void* aCallbackData,
                                          bool aIsProgressive)
{
  mWasLastPaintProgressive = aIsProgressive;

  // Compare layer valid region size to current backbuffer size, discard if not matching.
  gfx::IntSize size = aNewValidRegion.GetBounds().Size();
  gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
  nsIntRegion paintRegion = aPaintRegion;

  RefPtr<TextureClient> discardedFrontBuffer = nullptr;
  RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
  nsIntRegion discardedValidRegion;

  if (mSize != size ||
      mTilingOrigin != origin) {
    discardedFrontBuffer = mTile.mFrontBuffer;
    discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
    discardedValidRegion = mValidRegion;

    TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
;
    ResetPaintedAndValidState();
    mSize = size;
    mTilingOrigin = origin;
    paintRegion = aNewValidRegion;
  }

  SurfaceMode mode;
  gfxContentType content = GetContentType(&mode);
  mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);

  if (mTile.IsPlaceholderTile()) {
    mTile.SetTextureAllocator(this);
  }

  // The dirty region relative to the top-left of the tile.
  nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);

  nsIntRegion extraPainted;
  RefPtr<TextureClient> backBufferOnWhite;
  RefPtr<TextureClient> backBuffer =
    mTile.GetBackBuffer(mCompositableClient,
                        tileDirtyRegion,
                        content, mode,
                        extraPainted,
                        &backBufferOnWhite);

  mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());

  extraPainted.MoveBy(mTilingOrigin);
  extraPainted.And(extraPainted, aNewValidRegion);
  mPaintedRegion.OrWith(paintRegion);
  mPaintedRegion.OrWith(extraPainted);

  if (!backBuffer) {
    return;
  }

  RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
  RefPtr<gfx::DrawTarget> dtOnWhite;
  if (backBufferOnWhite) {
    dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
  }

  if (mode != SurfaceMode::SURFACE_OPAQUE) {
    for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
      const gfx::IntRect& rect = iter.Get();
      if (dtOnWhite) {
        dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
                     gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
        dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
                            gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
      } else {
        dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
      }
    }
  }

  // If the old frontbuffer was discarded then attempt to copy what we
  // can from it to the new backbuffer.
  if (discardedFrontBuffer) {
    nsIntRegion copyableRegion;
    copyableRegion.And(aNewValidRegion, discardedValidRegion);
    copyableRegion.SubOut(aDirtyRegion);

    if (!copyableRegion.IsEmpty()) {
      TextureClientAutoLock frontLock(discardedFrontBuffer,
                                      OpenMode::OPEN_READ);
      if (frontLock.Succeeded()) {
        for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
          const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
          const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
          discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
        }
      }

      if (discardedFrontBufferOnWhite && backBufferOnWhite) {
        TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
                                               OpenMode::OPEN_READ);
        if (frontOnWhiteLock.Succeeded()) {
          for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
            const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
            const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;

            discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
                                                             &rect, &dest);
          }
        }
      }

      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());

      // We don't need to repaint valid content that was just copied.
      paintRegion.SubOut(copyableRegion);
    }
  }

  if (dtOnWhite) {
    dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
    dtOnWhite = nullptr;
  }

  {
    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
    if (!ctx) {
      gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
      return;
    }
    ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));

    aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
  }

  // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
  // now out of sync.
  mTile.mInvalidFront.OrWith(tileDirtyRegion);

  // The new buffer is now validated, remove the dirty region from it.
  mTile.mInvalidBack.SubOut(tileDirtyRegion);

  dt = nullptr;

  mTile.Flip();
  UnlockTile(mTile);

  if (backBuffer->HasIntermediateBuffer()) {
    // If our new buffer has an internal buffer, we don't want to keep another
    // TextureClient around unnecessarily, so discard the back-buffer.
    mTile.DiscardBackBuffer();
  }

  mValidRegion = aNewValidRegion;
  mLastPaintSurfaceMode = mode;
  mLastPaintContentType = content;
}