summaryrefslogtreecommitdiffstats
path: root/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/mlgpu/ContainerLayerMLGPU.cpp')
-rw-r--r--gfx/layers/mlgpu/ContainerLayerMLGPU.cpp242
1 files changed, 242 insertions, 0 deletions
diff --git a/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp b/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp
new file mode 100644
index 0000000000..e4fc8f737e
--- /dev/null
+++ b/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContainerLayerMLGPU.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "LayerManagerMLGPU.h"
+#include "MLGDevice.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Types.h"
+#include "UnitTransforms.h"
+#include "UtilityMLGPU.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+ContainerLayerMLGPU::ContainerLayerMLGPU(LayerManagerMLGPU* aManager)
+ : ContainerLayer(aManager, nullptr),
+ LayerMLGPU(aManager),
+ mInvalidateEntireSurface(false),
+ mSurfaceCopyNeeded(false),
+ mView(nullptr) {}
+
+ContainerLayerMLGPU::~ContainerLayerMLGPU() {
+ while (mFirstChild) {
+ RemoveChild(mFirstChild);
+ }
+}
+
+bool ContainerLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder) {
+ mView = nullptr;
+
+ if (!UseIntermediateSurface()) {
+ // Set this so we invalidate the entire cached render target (if any)
+ // if our container uses an intermediate surface again later.
+ mInvalidateEntireSurface = true;
+ return true;
+ }
+
+ mChildrenChanged = false;
+
+ mTargetOffset = GetIntermediateSurfaceRect().TopLeft().ToUnknownPoint();
+ mTargetSize = GetIntermediateSurfaceRect().Size().ToUnknownSize();
+
+ if (mRenderTarget && mRenderTarget->GetSize() != mTargetSize) {
+ mRenderTarget = nullptr;
+ }
+
+ // Note that if a surface copy is needed, we always redraw the
+ // whole surface (on-demand). This is a rare case - the old
+ // Compositor already does this - and it saves us having to
+ // do much more complicated invalidation.
+ bool surfaceCopyNeeded = false;
+ DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
+ if (surfaceCopyNeeded != mSurfaceCopyNeeded || surfaceCopyNeeded) {
+ mInvalidateEntireSurface = true;
+ }
+ mSurfaceCopyNeeded = surfaceCopyNeeded;
+
+ gfx::IntRect viewport(gfx::IntPoint(0, 0), mTargetSize);
+ if (!mRenderTarget || !StaticPrefs::layers_mlgpu_enable_invalidation() ||
+ mInvalidateEntireSurface) {
+ // Fine-grained invalidation is disabled, invalidate everything.
+ mInvalidRect = viewport;
+ } else {
+ // Clamp the invalid rect to the viewport.
+ mInvalidRect -= mTargetOffset;
+ mInvalidRect = mInvalidRect.Intersect(viewport);
+ }
+
+ mInvalidateEntireSurface = false;
+ return true;
+}
+
+static IntRect GetTransformedBounds(Layer* aLayer) {
+ IntRect bounds = aLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect();
+ if (bounds.IsEmpty()) {
+ return bounds;
+ }
+
+ const Matrix4x4& transform = aLayer->GetEffectiveTransform();
+ Rect rect =
+ transform.TransformAndClipBounds(Rect(bounds), Rect::MaxIntRect());
+ rect.RoundOut();
+ rect.ToIntRect(&bounds);
+ return bounds;
+}
+
+/* static */
+Maybe<IntRect> ContainerLayerMLGPU::FindVisibleBounds(
+ Layer* aLayer, const Maybe<RenderTargetIntRect>& aClip) {
+ AL_LOG(" visiting child %p\n", aLayer);
+ AL_LOG_IF(aClip, " parent clip: %s\n", Stringify(aClip.value()).c_str());
+
+ ContainerLayer* container = aLayer->AsContainerLayer();
+ if (container) {
+ if (container->UseIntermediateSurface()) {
+ ContainerLayerMLGPU* c =
+ container->AsHostLayer()->AsLayerMLGPU()->AsContainerLayerMLGPU();
+ if (!c) {
+ gfxCriticalError()
+ << "not container: "
+ << container->AsHostLayer()->AsLayerMLGPU()->GetType();
+ }
+ MOZ_RELEASE_ASSERT(c);
+ c->ComputeIntermediateSurfaceBounds();
+ } else {
+ Maybe<IntRect> accumulated = Some(IntRect());
+
+ // Traverse children.
+ for (Layer* child = container->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ Maybe<RenderTargetIntRect> clip = aClip;
+ if (const Maybe<ParentLayerIntRect>& childClip =
+ child->AsHostLayer()->GetShadowClipRect()) {
+ RenderTargetIntRect rtChildClip = TransformBy(
+ ViewAs<ParentLayerToRenderTargetMatrix4x4>(
+ aLayer->GetEffectiveTransform(),
+ PixelCastJustification::RenderTargetIsParentLayerForRoot),
+ childClip.value());
+ clip = IntersectMaybeRects(clip, Some(rtChildClip));
+ AL_LOG(" target clip: %s\n", Stringify(rtChildClip).c_str());
+ AL_LOG_IF(clip, " full clip: %s\n",
+ Stringify(clip.value()).c_str());
+ }
+
+ Maybe<IntRect> childBounds = FindVisibleBounds(child, clip);
+ if (!childBounds) {
+ return Nothing();
+ }
+
+ accumulated = accumulated->SafeUnion(childBounds.value());
+ if (!accumulated) {
+ return Nothing();
+ }
+ }
+ return accumulated;
+ }
+ }
+
+ IntRect bounds = GetTransformedBounds(aLayer);
+ AL_LOG(" layer bounds: %s\n", Stringify(bounds).c_str());
+
+ if (aClip) {
+ bounds = bounds.Intersect(aClip.value().ToUnknownRect());
+ AL_LOG(" clipped bounds: %s\n", Stringify(bounds).c_str());
+ }
+ return Some(bounds);
+}
+
+void ContainerLayerMLGPU::ComputeIntermediateSurfaceBounds() {
+ Maybe<IntRect> bounds = Some(IntRect());
+ for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+ Maybe<RenderTargetIntRect> clip = ViewAs<RenderTargetPixel>(
+ child->AsHostLayer()->GetShadowClipRect(),
+ PixelCastJustification::RenderTargetIsParentLayerForRoot);
+ Maybe<IntRect> childBounds = FindVisibleBounds(child, clip);
+ if (!childBounds) {
+ return;
+ }
+
+ bounds = bounds->SafeUnion(childBounds.value());
+ if (!bounds) {
+ return;
+ }
+ }
+
+ SetShadowVisibleRegion(LayerIntRect::FromUnknownRect(bounds.value()));
+}
+
+void ContainerLayerMLGPU::OnLayerManagerChange(LayerManagerMLGPU* aManager) {
+ ClearCachedResources();
+}
+
+RefPtr<MLGRenderTarget> ContainerLayerMLGPU::UpdateRenderTarget(
+ MLGDevice* aDevice, MLGRenderTargetFlags aFlags) {
+ if (mRenderTarget) {
+ return mRenderTarget;
+ }
+
+ mRenderTarget = aDevice->CreateRenderTarget(mTargetSize, aFlags);
+ if (!mRenderTarget) {
+ gfxWarning()
+ << "Failed to create an intermediate render target for ContainerLayer";
+ return nullptr;
+ }
+
+ return mRenderTarget;
+}
+
+void ContainerLayerMLGPU::SetInvalidCompositeRect(const gfx::IntRect* aRect) {
+ // For simplicity we only track the bounds of the invalid area, since regions
+ // are expensive.
+ //
+ // Note we add the bounds to the invalid rect from the last frame, since we
+ // only clear the area that we actually paint. If this overflows we use the
+ // last render target size, since if that changes we'll invalidate everything
+ // anyway.
+ if (aRect) {
+ if (Maybe<gfx::IntRect> result = mInvalidRect.SafeUnion(*aRect)) {
+ mInvalidRect = result.value();
+ } else {
+ mInvalidateEntireSurface = true;
+ }
+ } else {
+ mInvalidateEntireSurface = true;
+ }
+}
+
+void ContainerLayerMLGPU::ClearCachedResources() { mRenderTarget = nullptr; }
+
+bool ContainerLayerMLGPU::IsContentOpaque() {
+ if (GetMixBlendMode() != gfx::CompositionOp::OP_OVER) {
+ // We need to read from what's underneath us, so we consider our content to
+ // be not opaque.
+ return false;
+ }
+ return LayerMLGPU::IsContentOpaque();
+}
+
+const LayerIntRegion& ContainerLayerMLGPU::GetShadowVisibleRegion() {
+ if (!UseIntermediateSurface()) {
+ RecomputeShadowVisibleRegionFromChildren();
+ }
+
+ return mShadowVisibleRegion;
+}
+
+const LayerIntRegion& RefLayerMLGPU::GetShadowVisibleRegion() {
+ if (!UseIntermediateSurface()) {
+ RecomputeShadowVisibleRegionFromChildren();
+ }
+
+ return mShadowVisibleRegion;
+}
+
+} // namespace layers
+} // namespace mozilla