/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "LayerManagerComposite.h" #include // for size_t #include // for uint16_t, uint32_t #include "CanvasLayerComposite.h" // for CanvasLayerComposite #include "ColorLayerComposite.h" // for ColorLayerComposite #include "Composer2D.h" // for Composer2D #include "CompositableHost.h" // for CompositableHost #include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc #include "FrameMetrics.h" // for FrameMetrics #include "GeckoProfiler.h" // for profiler_set_frame_number, etc #include "ImageLayerComposite.h" // for ImageLayerComposite #include "Layers.h" // for Layer, ContainerLayer, etc #include "ThebesLayerComposite.h" // for ThebesLayerComposite #include "TiledLayerBuffer.h" // for TiledLayerComposer #include "Units.h" // for ScreenIntRect #include "gfx2DGlue.h" // for ToMatrix4x4 #include "gfx3DMatrix.h" // for gfx3DMatrix #include "gfxMatrix.h" // for gfxMatrix #include "gfxPlatform.h" // for gfxPlatform #ifdef XP_MACOSX #include "gfxPlatformMac.h" #endif #include "gfxPoint.h" // for gfxIntSize #include "gfxRect.h" // for gfxRect #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/RefPtr.h" // for RefPtr, TemporaryRef #include "mozilla/gfx/2D.h" // for DrawTarget #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for IntSize, Point #include "mozilla/gfx/Rect.h" // for Rect #include "mozilla/gfx/Types.h" // for Color, SurfaceFormat #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc #include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_HAVE_LOG, etc #include "ipc/ShadowLayerUtils.h" #include "mozilla/mozalloc.h" // for operator new, etc #include "nsAutoPtr.h" // for nsRefPtr #include "nsCOMPtr.h" // for already_AddRefed #include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc #include "nsISupportsImpl.h" // for Layer::AddRef, etc #include "nsIWidget.h" // for nsIWidget #include "nsPoint.h" // for nsIntPoint #include "nsRect.h" // for nsIntRect #include "nsRegion.h" // for nsIntRegion, etc #ifdef MOZ_WIDGET_ANDROID #include #endif #include "GeckoProfiler.h" class gfxASurface; class gfxContext; struct nsIntSize; namespace mozilla { namespace layers { class ImageLayer; using namespace mozilla::gfx; using namespace mozilla::gl; static LayerComposite* ToLayerComposite(Layer* aLayer) { return static_cast(aLayer->ImplData()); } static void ClearSubtree(Layer* aLayer) { ToLayerComposite(aLayer)->CleanupResources(); for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ClearSubtree(child); } } void LayerManagerComposite::ClearCachedResources(Layer* aSubtree) { MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this); Layer* subtree = aSubtree ? aSubtree : mRoot.get(); if (!subtree) { return; } ClearSubtree(subtree); // FIXME [bjacob] // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here. // Do we need that? } /** * LayerManagerComposite */ LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor) : mCompositor(aCompositor) { MOZ_ASSERT(aCompositor); } LayerManagerComposite::~LayerManagerComposite() { Destroy(); } bool LayerManagerComposite::Initialize() { bool result = mCompositor->Initialize(); mComposer2D = mCompositor->GetWidget()->GetComposer2D(); return result; } void LayerManagerComposite::Destroy() { if (!mDestroyed) { mCompositor->GetWidget()->CleanupWindowEffects(); if (mRoot) { RootLayer()->Destroy(); } mRoot = nullptr; mCompositor->Destroy(); mDestroyed = true; } } void LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect) { mRenderBounds = aRect; } void LayerManagerComposite::BeginTransaction() { mInTransaction = true; } void LayerManagerComposite::BeginTransactionWithTarget(gfxContext *aTarget) { mInTransaction = true; #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } mCompositor->SetTargetContext(aTarget); } bool LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags) { mInTransaction = false; if (!mRoot) return false; EndTransaction(nullptr, nullptr); return true; } void LayerManagerComposite::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { mInTransaction = false; #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { if (aFlags & END_NO_COMPOSITE) { // Apply pending tree updates before recomputing effective // properties. mRoot->ApplyPendingUpdatesToSubtree(); } // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); mThebesLayerCallback = aCallback; mThebesLayerCallbackData = aCallbackData; Render(); mThebesLayerCallback = nullptr; mThebesLayerCallbackData = nullptr; } mCompositor->SetTargetContext(nullptr); #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif } already_AddRefed LayerManagerComposite::CreateOptimalMaskSurface(const gfxIntSize &aSize) { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } already_AddRefed LayerManagerComposite::CreateThebesLayer() { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } already_AddRefed LayerManagerComposite::CreateContainerLayer() { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } already_AddRefed LayerManagerComposite::CreateImageLayer() { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } already_AddRefed LayerManagerComposite::CreateColorLayer() { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } already_AddRefed LayerManagerComposite::CreateCanvasLayer() { NS_RUNTIMEABORT("Should only be called on the drawing side"); return nullptr; } LayerComposite* LayerManagerComposite::RootLayer() const { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return static_cast(mRoot->ImplData()); } static uint16_t sFrameCount = 0; void LayerManagerComposite::RenderDebugOverlay(const Rect& aBounds) { if (!gfxPlatform::DrawFrameCounter()) { return; } profiler_set_frame_number(sFrameCount); uint16_t frameNumber = sFrameCount; const uint16_t bitWidth = 3; float opacity = 1.0; gfx::Rect clip(0,0, bitWidth*16, bitWidth); for (size_t i = 0; i < 16; i++) { gfx::Color bitColor; if ((frameNumber >> i) & 0x1) { bitColor = gfx::Color(0, 0, 0, 1.0); } else { bitColor = gfx::Color(1.0, 1.0, 1.0, 1.0); } EffectChain effects; effects.mPrimaryEffect = new EffectSolidColor(bitColor); mCompositor->DrawQuad(gfx::Rect(bitWidth*i, 0, bitWidth, bitWidth), clip, effects, opacity, gfx::Matrix4x4(), gfx::Point()); } // We intentionally overflow at 2^16. sFrameCount++; } void LayerManagerComposite::Render() { PROFILER_LABEL("LayerManagerComposite", "Render"); if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } if (mComposer2D && mComposer2D->TryRender(mRoot, mWorldMatrix)) { mCompositor->EndFrameForExternalComposition(mWorldMatrix); return; } { PROFILER_LABEL("LayerManagerComposite", "PreRender"); mCompositor->GetWidget()->PreRender(this); } nsIntRect clipRect; Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); Rect actualBounds; if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); WorldTransformRect(clipRect); Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(&rect, mWorldMatrix, bounds, nullptr, &actualBounds); } else { gfx::Rect rect; mCompositor->BeginFrame(nullptr, mWorldMatrix, bounds, &rect, &actualBounds); clipRect = nsIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); // Render our layers. RootLayer()->RenderLayer(nsIntPoint(0, 0), clipRect); // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame"); mCompositor->EndFrame(); } } void LayerManagerComposite::SetWorldTransform(const gfxMatrix& aMatrix) { NS_ASSERTION(aMatrix.PreservesAxisAlignedRectangles(), "SetWorldTransform only accepts matrices that satisfy PreservesAxisAlignedRectangles"); NS_ASSERTION(!aMatrix.HasNonIntegerScale(), "SetWorldTransform only accepts matrices with integer scale"); mWorldMatrix = aMatrix; } gfxMatrix& LayerManagerComposite::GetWorldTransform(void) { return mWorldMatrix; } void LayerManagerComposite::WorldTransformRect(nsIntRect& aRect) { gfxRect grect(aRect.x, aRect.y, aRect.width, aRect.height); grect = mWorldMatrix.TransformBounds(grect); aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height()); } static void SubtractTransformedRegion(nsIntRegion& aRegion, const nsIntRegion& aRegionToSubtract, const gfx3DMatrix& aTransform) { if (aRegionToSubtract.IsEmpty()) { return; } // For each rect in the region, find out its bounds in screen space and // subtract it from the screen region. nsIntRegionRectIterator it(aRegionToSubtract); while (const nsIntRect* rect = it.Next()) { gfxRect incompleteRect = aTransform.TransformBounds(gfxRect(*rect)); aRegion.Sub(aRegion, nsIntRect(incompleteRect.x, incompleteRect.y, incompleteRect.width, incompleteRect.height)); } } /* static */ void LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer, nsIntRegion& aScreenRegion, nsIntRegion& aLowPrecisionScreenRegion, const gfx3DMatrix& aTransform) { if (aLayer->GetOpacity() <= 0.f || (aScreenRegion.IsEmpty() && aLowPrecisionScreenRegion.IsEmpty())) { return; } // If the layer's a container, recurse into all of its children ContainerLayer* container = aLayer->AsContainerLayer(); if (container) { // Accumulate the transform of intermediate surfaces gfx3DMatrix transform = aTransform; if (container->UseIntermediateSurface()) { transform = aLayer->GetEffectiveTransform(); transform.PreMultiply(aTransform); } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform); } return; } // Only thebes layers can be incomplete ThebesLayer* thebesLayer = aLayer->AsThebesLayer(); if (!thebesLayer) { return; } // See if there's any incomplete rendering nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion(); incompleteRegion.Sub(incompleteRegion, thebesLayer->GetValidRegion()); if (!incompleteRegion.IsEmpty()) { // Calculate the transform to get between screen and layer space gfx3DMatrix transformToScreen = aLayer->GetEffectiveTransform(); transformToScreen.PreMultiply(aTransform); SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen); // See if there's any incomplete low-precision rendering TiledLayerComposer* composer = nullptr; LayerComposite* shadow = aLayer->AsLayerComposite(); if (shadow) { composer = shadow->GetTiledLayerComposer(); if (composer) { incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion()); if (!incompleteRegion.IsEmpty()) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } } // If we can't get a valid low precision region, assume it's the same as // the high precision region. if (!composer) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } } #ifdef MOZ_ANDROID_OMTC static float GetDisplayportCoverage(const CSSRect& aDisplayPort, const gfx3DMatrix& aTransformToScreen, const nsIntRect& aScreenRect) { gfxRect transformedDisplayport = aTransformToScreen.TransformBounds(gfxRect(aDisplayPort.x, aDisplayPort.y, aDisplayPort.width, aDisplayPort.height)); transformedDisplayport.RoundOut(); nsIntRect displayport = nsIntRect(transformedDisplayport.x, transformedDisplayport.y, transformedDisplayport.width, transformedDisplayport.height); if (!displayport.Contains(aScreenRect)) { nsIntRegion coveredRegion; coveredRegion.And(aScreenRect, displayport); return coveredRegion.Area() / (float)(aScreenRect.width * aScreenRect.height); } return 1.0f; } #endif // MOZ_ANDROID_OMTC float LayerManagerComposite::ComputeRenderIntegrity() { // We only ever have incomplete rendering when progressive tiles are enabled. Layer* root = GetRoot(); if (!gfxPlatform::UseProgressiveTilePainting() || !root) { return 1.f; } const FrameMetrics& rootMetrics = root->AsContainerLayer()->GetFrameMetrics(); nsIntRect screenRect(rootMetrics.mCompositionBounds.x, rootMetrics.mCompositionBounds.y, rootMetrics.mCompositionBounds.width, rootMetrics.mCompositionBounds.height); float lowPrecisionMultiplier = 1.0f; float highPrecisionMultiplier = 1.0f; #ifdef MOZ_ANDROID_OMTC // Use the transform on the primary scrollable layer and its FrameMetrics // to find out how much of the viewport the current displayport covers Layer* primaryScrollable = GetPrimaryScrollableLayer(); if (primaryScrollable) { // This is derived from the code in // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree. const gfx3DMatrix& rootTransform = root->GetTransform(); float devPixelRatioX = 1 / rootTransform.GetXScale(); float devPixelRatioY = 1 / rootTransform.GetYScale(); gfx3DMatrix transform = primaryScrollable->GetEffectiveTransform(); transform.ScalePost(devPixelRatioX, devPixelRatioY, 1); const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); // Clip the screen rect to the document bounds gfxRect documentBounds = transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.mScrollOffset.x, metrics.mScrollableRect.y - metrics.mScrollOffset.y, metrics.mScrollableRect.width, metrics.mScrollableRect.height)); documentBounds.RoundOut(); screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y, documentBounds.width, documentBounds.height)); // If the screen rect is empty, the user has scrolled entirely into // over-scroll and so we can be considered to have full integrity. if (screenRect.IsEmpty()) { return 1.0f; } // Work out how much of the critical display-port covers the screen bool hasLowPrecision = false; if (!metrics.mCriticalDisplayPort.IsEmpty()) { hasLowPrecision = true; highPrecisionMultiplier = GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect); } // Work out how much of the display-port covers the screen if (!metrics.mDisplayPort.IsEmpty()) { if (hasLowPrecision) { lowPrecisionMultiplier = GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); } else { lowPrecisionMultiplier = highPrecisionMultiplier = GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); } } } // If none of the screen is covered, we have zero integrity. if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) { return 0.0f; } #endif // MOZ_ANDROID_OMTC nsIntRegion screenRegion(screenRect); nsIntRegion lowPrecisionScreenRegion(screenRect); gfx3DMatrix transform; ComputeRenderIntegrityInternal(root, screenRegion, lowPrecisionScreenRegion, transform); if (!screenRegion.IsEqual(screenRect)) { // Calculate the area of the region. All rects in an nsRegion are // non-overlapping. float screenArea = screenRect.width * screenRect.height; float highPrecisionIntegrity = screenRegion.Area() / screenArea; float lowPrecisionIntegrity = 1.f; if (!lowPrecisionScreenRegion.IsEqual(screenRect)) { lowPrecisionIntegrity = lowPrecisionScreenRegion.Area() / screenArea; } return ((highPrecisionIntegrity * highPrecisionMultiplier) + (lowPrecisionIntegrity * lowPrecisionMultiplier)) / 2; } return 1.f; } already_AddRefed LayerManagerComposite::CreateThebesLayerComposite() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new ThebesLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateContainerLayerComposite() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new ContainerLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateImageLayerComposite() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new ImageLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateColorLayerComposite() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new ColorLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateCanvasLayerComposite() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new CanvasLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateRefLayerComposite() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return nsRefPtr(new RefLayerComposite(this)).forget(); } LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer, EffectChain& aEffects, bool aIs3D) : mCompositable(nullptr) { if (!aMaskLayer) { return; } mCompositable = static_cast(aMaskLayer->ImplData())->GetCompositableHost(); if (!mCompositable) { NS_WARNING("Mask layer with no compositable host"); return; } gfx::Matrix4x4 transform; ToMatrix4x4(aMaskLayer->GetEffectiveTransform(), transform); mCompositable->AddMaskEffect(aEffects, transform, aIs3D); } LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect() { if (!mCompositable) { return; } mCompositable->RemoveMaskEffect(); } TemporaryRef LayerManagerComposite::CreateDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) { #ifdef XP_MACOSX // We don't want to accelerate if the surface is too small which indicates // that it's likely used for an icon/static image. We also don't want to // accelerate anything that is above the maximum texture size of weakest gpu. // Safari uses 5000 area as the minimum for acceleration, we decided 64^2 is more logical. bool useAcceleration = aSize.width <= 4096 && aSize.height <= 4096 && aSize.width > 64 && aSize.height > 64 && gfxPlatformMac::GetPlatform()->UseAcceleratedCanvas(); if (useAcceleration) { return Factory::CreateDrawTarget(BACKEND_COREGRAPHICS_ACCELERATED, aSize, aFormat); } #endif return LayerManager::CreateDrawTarget(aSize, aFormat); } LayerComposite::LayerComposite(LayerManagerComposite *aManager) : mCompositeManager(aManager) , mCompositor(aManager->GetCompositor()) , mShadowOpacity(1.0) , mUseShadowClipRect(false) , mShadowTransformSetByAnimation(false) , mDestroyed(false) { } LayerComposite::~LayerComposite() { } void LayerComposite::Destroy() { if (!mDestroyed) { mDestroyed = true; CleanupResources(); } } const nsIntSize& LayerManagerComposite::GetWidgetSize() { return mCompositor->GetWidgetSize(); } void LayerManagerComposite::SetCompositorID(uint32_t aID) { NS_ASSERTION(mCompositor, "No compositor"); mCompositor->SetCompositorID(aID); } void LayerManagerComposite::NotifyShadowTreeTransaction() { mCompositor->NotifyLayersTransaction(); } bool LayerManagerComposite::CanUseCanvasLayerForSize(const gfxIntSize &aSize) { return mCompositor->CanUseCanvasLayerForSize(aSize); } TextureFactoryIdentifier LayerManagerComposite::GetTextureFactoryIdentifier() { return mCompositor->GetTextureFactoryIdentifier(); } int32_t LayerManagerComposite::GetMaxTextureSize() const { return mCompositor->GetMaxTextureSize(); } #ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS /*static*/ already_AddRefed LayerManagerComposite::OpenDescriptorForDirectTexturing(GLContext*, const SurfaceDescriptor&, GLenum) { return nullptr; } /*static*/ bool LayerManagerComposite::SupportsDirectTexturing() { return false; } /*static*/ void LayerManagerComposite::PlatformSyncBeforeReplyUpdate() { } #endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS) } /* layers */ } /* mozilla */