bool
WebRenderPaintedLayer::UpdateImageClient()
{
  MOZ_ASSERT(WrManager()->GetPaintedLayerCallback());
  LayerIntRegion visibleRegion = GetVisibleRegion();
  LayerIntRect bounds = visibleRegion.GetBounds();
  LayerIntSize size = bounds.Size();
  IntSize imageSize(size.width, size.height);

  UpdateImageHelper helper(mImageContainer, mImageClient, imageSize);

  {
    RefPtr<DrawTarget> target = helper.GetDrawTarget();
    if (!target) {
      return false;
    }

    target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
    target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
    RefPtr<gfxContext> ctx =
        gfxContext::CreatePreservingTransformOrNull(target);
    MOZ_ASSERT(ctx); // already checked the target above

    WrManager()->GetPaintedLayerCallback()(this,
                                           ctx,
                                           visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
                                           DrawRegionClip::DRAW, nsIntRegion(), WrManager()->GetPaintedLayerCallbackData());

    if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
      target->SetTransform(Matrix());
      target->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5)));
    }
  }

  if (!helper.UpdateImage()) {
    return false;
  }

  return true;
}
void RenderViewMLGPU::PrepareClears() {
  // We don't do any clearing if we're copying from a source backdrop.
  if (mContainer && mContainer->NeedsSurfaceCopy()) {
    return;
  }

  // Get the list of rects to clear. If using the depth buffer, we don't
  // care if it's accurate since the GPU will do occlusion testing for us.
  // If not using the depth buffer, we subtract out the occluded region.
  LayerIntRegion region = LayerIntRect::FromUnknownRect(mInvalidBounds);
  if (!mUseDepthBuffer) {
    // Don't let the clear region become too complicated.
    region.SubOut(mOccludedRegion);
    region.SimplifyOutward(kMaxClearViewRects);
  }

  Maybe<int32_t> sortIndex;
  if (mUseDepthBuffer) {
    // Note that we use the lowest available sorting index, to ensure that when
    // using the z-buffer, we don't draw over already-drawn content.
    sortIndex = Some(mNextSortIndex++);
  }

  nsTArray<IntRect> rects = ToRectArray(region);
  mDevice->PrepareClearRegion(&mPreClear, std::move(rects), sortIndex);

  if (!mPostClearRegion.IsEmpty()) {
    // Prepare the final clear as well. Note that we always do this clear at the
    // very end, even when the depth buffer is enabled, so we don't bother
    // setting a useful sorting index. If and when we try to ship the depth
    // buffer, we would execute this clear earlier in the pipeline and give it
    // the closest possible z-ordering to the screen.
    nsTArray<IntRect> rects = ToRectArray(mPostClearRegion);
    mDevice->PrepareClearRegion(&mPostClear, std::move(rects), Nothing());
  }
}
void
LayerManagerComposite::PostProcessLayers(Layer* aLayer,
                                         nsIntRegion& aOpaqueRegion,
                                         LayerIntRegion& aVisibleRegion,
                                         const Maybe<ParentLayerIntRect>& aClipFromAncestors)
{
  if (aLayer->Extend3DContext()) {
    // For layers participating 3D rendering context, their visible
    // region should be empty (invisible), so we pass through them
    // without doing anything.

    // Direct children of the establisher may have a clip, becaue the
    // item containing it; ex. of nsHTMLScrollFrame, may give it one.
    Maybe<ParentLayerIntRect> layerClip =
      aLayer->AsHostLayer()->GetShadowClipRect();
    Maybe<ParentLayerIntRect> ancestorClipForChildren =
      IntersectMaybeRects(layerClip, aClipFromAncestors);
    MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
               "Only direct children of the establisher could have a clip");

    for (Layer* child = aLayer->GetLastChild();
         child;
         child = child->GetPrevSibling()) {
      PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
                        ancestorClipForChildren);
    }
    return;
  }

  nsIntRegion localOpaque;
  // Treat layers on the path to the root of the 3D rendering context as
  // a giant layer if it is a leaf.
  Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
  Matrix transform2d;
  Maybe<IntPoint> integerTranslation;
  // If aLayer has a simple transform (only an integer translation) then we
  // can easily convert aOpaqueRegion into pre-transform coordinates and include
  // that region.
  if (transform.Is2D(&transform2d)) {
    if (transform2d.IsIntegerTranslation()) {
      integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
      localOpaque = aOpaqueRegion;
      localOpaque.MoveBy(-*integerTranslation);
    }
  }

  // Compute a clip that's the combination of our layer clip with the clip
  // from our ancestors.
  LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer());
  Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
  MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
             "The layer with a clip should not participate "
             "a 3D rendering context");
  Maybe<ParentLayerIntRect> outsideClip =
    IntersectMaybeRects(layerClip, aClipFromAncestors);

  // Convert the combined clip into our pre-transform coordinate space, so
  // that it can later be intersected with our visible region.
  // If our transform is a perspective, there's no meaningful insideClip rect
  // we can compute (it would need to be a cone).
  Maybe<LayerIntRect> insideClip;
  if (outsideClip && !transform.HasPerspectiveComponent()) {
    Matrix4x4 inverse = transform;
    if (inverse.Invert()) {
      Maybe<LayerRect> insideClipFloat =
        UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse),
                      ParentLayerRect(*outsideClip),
                      LayerRect::MaxIntRect());
      if (insideClipFloat) {
        insideClipFloat->RoundOut();
        LayerIntRect insideClipInt;
        if (insideClipFloat->ToIntRect(&insideClipInt)) {
          insideClip = Some(insideClipInt);
        }
      }
    }
  }

  Maybe<ParentLayerIntRect> ancestorClipForChildren;
  if (insideClip) {
    ancestorClipForChildren =
      Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
  }

  // Save the value of localOpaque, which currently stores the region obscured
  // by siblings (and uncles and such), before our descendants contribute to it.
  nsIntRegion obscured = localOpaque;

  // Recurse on our descendants, in front-to-back order. In this process:
  //  - Occlusions are computed for them, and they contribute to localOpaque.
  //  - They recalculate their visible regions, taking ancestorClipForChildren
  //    into account, and accumulate them into descendantsVisibleRegion.
  LayerIntRegion descendantsVisibleRegion;
  bool hasPreserve3DChild = false;
  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
    PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
    if (child->Extend3DContext()) {
      hasPreserve3DChild = true;
    }
  }

  // Recalculate our visible region.
  LayerIntRegion visible = composite->GetShadowVisibleRegion();

  // If we have descendants, throw away the visible region stored on this
  // layer, and use the region accumulated by our descendants instead.
  if (aLayer->GetFirstChild() && !hasPreserve3DChild) {
    visible = descendantsVisibleRegion;
  }

  // Subtract any areas that we know to be opaque.
  if (!obscured.IsEmpty()) {
    visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
  }

  // Clip the visible region using the combined clip.
  if (insideClip) {
    visible.AndWith(*insideClip);
  }
  composite->SetShadowVisibleRegion(visible);

  // Transform the newly calculated visible region into our parent's space,
  // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
  // for the caller to use.
  ParentLayerIntRegion visibleParentSpace = TransformBy(
      ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
  if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
    visibleParentSpace.AndWith(*clipRect);
  }
  aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
      PixelCastJustification::MovingDownToChildren));

  // If we have a simple transform, then we can add our opaque area into
  // aOpaqueRegion.
  if (integerTranslation &&
      !aLayer->HasMaskLayers() &&
      aLayer->IsOpaqueForVisibility()) {
    if (aLayer->IsOpaque()) {
      localOpaque.OrWith(composite->GetFullyRenderedRegion());
    }
    localOpaque.MoveBy(*integerTranslation);
    if (layerClip) {
      localOpaque.AndWith(layerClip->ToUnknownRect());
    }
    aOpaqueRegion.OrWith(localOpaque);
  }
}
Beispiel #4
0
void
WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
{
  // XXX We won't keep using ContentClient for WebRenderPaintedLayer in the future and
  // there is a crash problem for ContentClient on MacOS. So replace ContentClient with
  // ImageClient. See bug 1341001.
  //RenderLayerWithReadback(nullptr);

  if (!mImageContainer) {
    mImageContainer = LayerManager::CreateImageContainer();
  }

  if (!mImageClient) {
    mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
                                                  WrBridge(),
                                                  TextureFlags::DEFAULT);
    if (!mImageClient) {
      return;
    }
    mImageClient->Connect();
  }

  if (!mExternalImageId) {
    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
    MOZ_ASSERT(mExternalImageId);
  }

  LayerIntRegion visibleRegion = GetVisibleRegion();
  LayerIntRect bounds = visibleRegion.GetBounds();
  LayerIntSize size = bounds.Size();
  if (size.IsEmpty()) {
      if (gfxPrefs::LayersDump()) {
        printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
      }
      return;
  }

  IntSize imageSize(size.width, size.height);
  RefPtr<TextureClient> texture = mImageClient->GetTextureClientRecycler()->CreateOrRecycle(SurfaceFormat::B8G8R8A8,
                                                                                            imageSize,
                                                                                            BackendSelector::Content,
                                                                                            TextureFlags::DEFAULT);
  if (!texture) {
    return;
  }

  {
    TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
    if (!autoLock.Succeeded()) {
      return;
    }
    RefPtr<DrawTarget> target = texture->BorrowDrawTarget();
    if (!target) {
      return;
    }
    target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
    target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
    MOZ_ASSERT(ctx); // already checked the target above

    Manager()->GetPaintedLayerCallback()(this,
                                         ctx,
                                         visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
                                         DrawRegionClip::DRAW, nsIntRegion(), Manager()->GetPaintedLayerCallbackData());
  }
  RefPtr<TextureWrapperImage> image = new TextureWrapperImage(texture, IntRect(IntPoint(0, 0), imageSize));
  mImageContainer->SetCurrentImageInTransaction(image);
  if (!mImageClient->UpdateImage(mImageContainer, /* unused */0)) {
    return;
  }

  gfx::Matrix4x4 transform = GetTransform();
  gfx::Rect relBounds = GetWrRelBounds();
  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);

  gfx::Rect rect(0, 0, size.width, size.height);
  gfx::Rect clipRect = GetWrClipRect(rect);

  Maybe<WrImageMask> mask = BuildWrMaskLayer();
  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect));

  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());

  DumpLayerInfo("PaintedLayer", rect);

  WrImageKey key;
  key.mNamespace = WrBridge()->GetNamespace();
  key.mHandle = WrBridge()->GetNextResourceId();
  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));

  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
                              wr::ToWrRect(overflow),
                              mask.ptrOr(nullptr),
                              1.0f,
                              //GetAnimations(),
                              transform,
                              mixBlendMode);
  aBuilder.PushImage(wr::ToWrRect(rect), clip, wr::ImageRendering::Auto, key);
  aBuilder.PopStackingContext();
}
bool RenderViewMLGPU::UpdateVisibleRegion(ItemInfo& aItem) {
  // If the item has some kind of complex transform, we perform a very
  // simple occlusion test and move on. We using a depth buffer we skip
  // CPU-based occlusion culling as well, since the GPU will do most of our
  // culling work for us.
  if (mUseDepthBuffer || !aItem.translation ||
      !gfxPrefs::AdvancedLayersEnableCPUOcclusion()) {
    // Update the render region even if we won't compute visibility, since some
    // layer types (like Canvas and Image) need to have the visible region
    // clamped.
    LayerIntRegion region = aItem.layer->GetShadowVisibleRegion();
    aItem.layer->SetRenderRegion(std::move(region));

    AL_LOG("RenderView %p simple occlusion test, bounds=%s, translation?=%d\n",
           this, Stringify(aItem.bounds).c_str(), aItem.translation ? 1 : 0);
    return mInvalidBounds.Intersects(aItem.bounds);
  }

  MOZ_ASSERT(aItem.rectilinear);

  AL_LOG("RenderView %p starting visibility tests:\n", this);
  AL_LOG("  occluded=%s\n", Stringify(mOccludedRegion).c_str());

  // Compute the translation into render target space.
  LayerIntPoint translation = LayerIntPoint::FromUnknownPoint(
      aItem.translation.value() - mTargetOffset);
  AL_LOG("  translation=%s\n", Stringify(translation).c_str());

  IntRect clip = aItem.layer->GetComputedClipRect().ToUnknownRect();
  AL_LOG("  clip=%s\n", Stringify(translation).c_str());

  LayerIntRegion region = aItem.layer->GetShadowVisibleRegion();
  region.MoveBy(translation);
  AL_LOG("  effective-visible=%s\n", Stringify(region).c_str());

  region.SubOut(mOccludedRegion);
  region.AndWith(LayerIntRect::FromUnknownRect(mInvalidBounds));
  region.AndWith(LayerIntRect::FromUnknownRect(clip));
  if (region.IsEmpty()) {
    return false;
  }

  // Move the visible region back into layer space.
  region.MoveBy(-translation);
  AL_LOG("  new-local-visible=%s\n", Stringify(region).c_str());

  aItem.layer->SetRenderRegion(std::move(region));

  // Apply the new occluded area. We do another dance with the translation to
  // avoid copying the region. We do this after the SetRegionToRender call to
  // accomodate the possiblity of a layer changing its visible region.
  if (aItem.opaque) {
    mOccludedRegion.MoveBy(-translation);
    mOccludedRegion.OrWith(aItem.layer->GetRenderRegion());
    mOccludedRegion.MoveBy(translation);
    AL_LOG("  new-occluded=%s\n", Stringify(mOccludedRegion).c_str());

    // If the occluded region gets too complicated, we reset it.
    if (mOccludedRegion.GetNumRects() >= 32) {
      mOccludedRegion.SetEmpty();
      AL_LOG("  clear-occluded, too many rects\n");
    }
  }
  return true;
}
void
LayerManagerComposite::PostProcessLayers(Layer* aLayer,
                                         nsIntRegion& aOpaqueRegion,
                                         LayerIntRegion& aVisibleRegion)
{
  nsIntRegion localOpaque;
  Matrix transform2d;
  Maybe<nsIntPoint> integerTranslation;
  // If aLayer has a simple transform (only an integer translation) then we
  // can easily convert aOpaqueRegion into pre-transform coordinates and include
  // that region.
  if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
    if (transform2d.IsIntegerTranslation()) {
      integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation()));
      localOpaque = aOpaqueRegion;
      localOpaque.MoveBy(-*integerTranslation);
    }
  }

  // Save the value of localOpaque, which currently stores the region obscured
  // by siblings (and uncles and such), before our descendants contribute to it.
  nsIntRegion obscured = localOpaque;

  // Recurse on our descendants, in front-to-back order. In this process:
  //  - Occlusions are computed for them, and they contribute to localOpaque.
  //  - They recalculate their visible regions, and accumulate them into
  //    descendantsVisibleRegion.
  LayerIntRegion descendantsVisibleRegion;
  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
    PostProcessLayers(child, localOpaque, descendantsVisibleRegion);
  }

  // Recalculate our visible region.
  LayerComposite* composite = aLayer->AsLayerComposite();
  LayerIntRegion visible = composite->GetShadowVisibleRegion();

  // If we have descendants, throw away the visible region stored on this
  // layer, and use the region accumulated by our descendants instead.
  if (aLayer->GetFirstChild()) {
    visible = descendantsVisibleRegion;
  }

  // Subtract any areas that we know to be opaque.
  if (!obscured.IsEmpty()) {
    visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
  }

  composite->SetShadowVisibleRegion(visible);

  // Transform the newly calculated visible region into our parent's space,
  // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
  // for the caller to use.
  ParentLayerIntRegion visibleParentSpace = TransformTo<ParentLayerPixel>(
      aLayer->GetLocalTransform(), visible);
  if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
    visibleParentSpace.AndWith(*clipRect);
  }
  aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
      PixelCastJustification::MovingDownToChildren));

  // If we have a simple transform, then we can add our opaque area into
  // aOpaqueRegion.
  if (integerTranslation &&
      !aLayer->HasMaskLayers() &&
      aLayer->IsOpaqueForVisibility()) {
    if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
      localOpaque.OrWith(composite->GetFullyRenderedRegion());
    }
    localOpaque.MoveBy(*integerTranslation);
    const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect();
    if (clip) {
      localOpaque.AndWith(clip->ToUnknownRect());
    }
    aOpaqueRegion.OrWith(localOpaque);
  }
}
void
WebRenderPaintedLayerBlob::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                       const StackingContextHelper& aSc)
{
  LayerIntRegion visibleRegion = GetVisibleRegion();
  LayerIntRect bounds = visibleRegion.GetBounds();
  LayerIntSize size = bounds.Size();

  if (visibleRegion.IsEmpty()) {
    if (gfxPrefs::LayersDump()) {
      printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
    }
    return;
  }

  nsIntRegion regionToPaint;
  regionToPaint.Sub(mVisibleRegion.ToUnknownRegion(), mValidRegion);

  // We have something to paint but can't. This usually happens only in
  // empty transactions
  if (!regionToPaint.IsEmpty() && !WrManager()->GetPaintedLayerCallback()) {
    WrManager()->SetTransactionIncomplete();
    return;
  }

  IntSize imageSize(size.ToUnknownSize());
  if (!regionToPaint.IsEmpty() && WrManager()->GetPaintedLayerCallback()) {
    RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>();
    RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, imageSize, gfx::SurfaceFormat::B8G8R8X8);
    RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt);

    dt->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
    dt->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dt);
    MOZ_ASSERT(ctx); // already checked the target above

    WrManager()->GetPaintedLayerCallback()(this,
                                           ctx,
                                           visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
                                           DrawRegionClip::DRAW, nsIntRegion(), WrManager()->GetPaintedLayerCallbackData());

    if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
      dt->SetTransform(Matrix());
      dt->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5)));
    }

    wr::ByteBuffer bytes;
    bytes.Allocate(recorder->RecordingSize());
    DebugOnly<bool> ok = recorder->CopyRecording((char*)bytes.AsSlice().begin().get(), bytes.AsSlice().length());
    MOZ_ASSERT(ok);

    //XXX: We should switch to updating the blob image instead of adding a new one
    //     That will get rid of this discard bit
    if (mImageKey.isSome()) {
      WrManager()->AddImageKeyForDiscard(mImageKey.value());
    }
    mImageKey = Some(GetImageKey());
    WrBridge()->SendAddBlobImage(mImageKey.value(), imageSize, size.width * 4, dt->GetFormat(), bytes);
  } else {
    MOZ_ASSERT(GetInvalidRegion().IsEmpty());
  }

  ScrollingLayersHelper scroller(this, aBuilder, aSc);
  StackingContextHelper sc(aSc, aBuilder, this);
  LayerRect rect = Bounds();
  DumpLayerInfo("PaintedLayer", rect);

  LayerRect clipRect = ClipRect().valueOr(rect);
  Maybe<WrImageMask> mask = BuildWrMaskLayer(&sc);
  WrClipRegionToken clip = aBuilder.PushClipRegion(
      sc.ToRelativeWrRect(clipRect),
      mask.ptrOr(nullptr));

  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, wr::ImageRendering::Auto, mImageKey.value());
}