diff -r 000000000000 -r 6474c204b198 gfx/layers/composite/ContainerLayerComposite.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/composite/ContainerLayerComposite.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,524 @@ +/* -*- 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 "ContainerLayerComposite.h" +#include // for min +#include "FrameMetrics.h" // for FrameMetrics +#include "Units.h" // for LayerRect, LayerPixel, etc +#include "gfx2DGlue.h" // for ToMatrix4x4 +#include "gfx3DMatrix.h" // for gfx3DMatrix +#include "gfxPrefs.h" // for gfxPrefs +#include "gfxUtils.h" // for gfxUtils, etc +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/gfx/BaseRect.h" // for BaseRect +#include "mozilla/gfx/Matrix.h" // for Matrix4x4 +#include "mozilla/gfx/Point.h" // for Point, IntPoint +#include "mozilla/gfx/Rect.h" // for IntRect, Rect +#include "mozilla/layers/Compositor.h" // for Compositor, etc +#include "mozilla/layers/CompositorTypes.h" // for DIAGNOSTIC_CONTAINER +#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc +#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsAutoPtr.h" // for nsRefPtr +#include "nsDebug.h" // for NS_ASSERTION +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE +#include "nsPoint.h" // for nsIntPoint +#include "nsRect.h" // for nsIntRect +#include "nsRegion.h" // for nsIntRegion +#include "nsTArray.h" // for nsAutoTArray +#include "TextRenderer.h" // for TextRenderer +#include + +namespace mozilla { +namespace layers { + +/** + * Returns a rectangle of content painted opaquely by aLayer. Very consertative; + * bails by returning an empty rect in any tricky situations. + */ +static nsIntRect +GetOpaqueRect(Layer* aLayer) +{ + nsIntRect result; + gfx::Matrix matrix; + bool is2D = aLayer->GetBaseTransform().Is2D(&matrix); + + // Just bail if there's anything difficult to handle. + if (!is2D || aLayer->GetMaskLayer() || + aLayer->GetEffectiveOpacity() != 1.0f || + matrix.HasNonIntegerTranslation()) { + return result; + } + + if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { + result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle(); + } else { + // Drill down into RefLayers because that's what we particularly care about; + // layer construction for aLayer will not have known about the opaqueness + // of any RefLayer subtrees. + RefLayer* refLayer = aLayer->AsRefLayer(); + if (refLayer && refLayer->GetFirstChild()) { + result = GetOpaqueRect(refLayer->GetFirstChild()); + } + } + + // Translate our opaque region to cover the child + gfx::Point point = matrix.GetTranslation(); + result.MoveBy(static_cast(point.x), static_cast(point.y)); + + const nsIntRect* clipRect = aLayer->GetEffectiveClipRect(); + if (clipRect) { + result.IntersectRect(result, *clipRect); + } + + return result; +} + +struct LayerVelocityUserData : public LayerUserData { +public: + LayerVelocityUserData() { + MOZ_COUNT_CTOR(LayerVelocityUserData); + } + ~LayerVelocityUserData() { + MOZ_COUNT_DTOR(LayerVelocityUserData); + } + + struct VelocityData { + VelocityData(TimeStamp frameTime, int scrollX, int scrollY) + : mFrameTime(frameTime) + , mPoint(scrollX, scrollY) + {} + + TimeStamp mFrameTime; + gfx::Point mPoint; + }; + std::vector mData; +}; + +static gfx::Point GetScrollData(Layer* aLayer) { + gfx::Matrix matrix; + if (aLayer->GetLocalTransform().Is2D(&matrix)) { + return matrix.GetTranslation(); + } + + gfx::Point origin; + return origin; +} + +static void DrawLayerInfo(const nsIntRect& aClipRect, + LayerManagerComposite* aManager, + Layer* aLayer) +{ + + if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) { + // XXX - should figure out a way to render this, but for now this + // is hard to do, since it will often get superimposed over the first + // child of the layer, which is bad. + return; + } + + nsAutoCString layerInfo; + aLayer->PrintInfo(layerInfo, ""); + + nsIntRegion visibleRegion = aLayer->GetVisibleRegion(); + + uint32_t maxWidth = std::min(visibleRegion.GetBounds().width, 500); + + nsIntPoint topLeft = visibleRegion.GetBounds().TopLeft(); + aManager->GetTextRenderer()->RenderText(layerInfo.get(), gfx::IntPoint(topLeft.x, topLeft.y), + aLayer->GetEffectiveTransform(), 16, + maxWidth); + +} + +static LayerVelocityUserData* GetVelocityData(Layer* aLayer) { + static char sLayerVelocityUserDataKey; + void* key = reinterpret_cast(&sLayerVelocityUserDataKey); + if (!aLayer->HasUserData(key)) { + LayerVelocityUserData* newData = new LayerVelocityUserData(); + aLayer->SetUserData(key, newData); + } + + return static_cast(aLayer->GetUserData(key)); +} + +static void DrawVelGraph(const nsIntRect& aClipRect, + LayerManagerComposite* aManager, + Layer* aLayer) { + Compositor* compositor = aManager->GetCompositor(); + gfx::Rect clipRect(aClipRect.x, aClipRect.y, + aClipRect.width, aClipRect.height); + + TimeStamp now = TimeStamp::Now(); + LayerVelocityUserData* velocityData = GetVelocityData(aLayer); + + if (velocityData->mData.size() >= 1 && + now > velocityData->mData[velocityData->mData.size() - 1].mFrameTime + + TimeDuration::FromMilliseconds(200)) { + // clear stale data + velocityData->mData.clear(); + } + + const gfx::Point layerTransform = GetScrollData(aLayer); + velocityData->mData.push_back( + LayerVelocityUserData::VelocityData(now, + static_cast(layerTransform.x), static_cast(layerTransform.y))); + + // TODO: dump to file + // XXX: Uncomment these lines to enable ScrollGraph logging. This is + // useful for HVGA phones or to output the data to accurate + // graphing software. + // printf_stderr("ScrollGraph (%p): %f, %f\n", + // aLayer, layerTransform.x, layerTransform.y); + + // Keep a circular buffer of 100. + size_t circularBufferSize = 100; + if (velocityData->mData.size() > circularBufferSize) { + velocityData->mData.erase(velocityData->mData.begin()); + } + + if (velocityData->mData.size() == 1) { + return; + } + + // Clear and disable the graph when it's flat + for (size_t i = 1; i < velocityData->mData.size(); i++) { + if (velocityData->mData[i - 1].mPoint != velocityData->mData[i].mPoint) { + break; + } + if (i == velocityData->mData.size() - 1) { + velocityData->mData.clear(); + return; + } + } + + if (aLayer->GetEffectiveVisibleRegion().GetBounds().width < 300 || + aLayer->GetEffectiveVisibleRegion().GetBounds().height < 300) { + // Don't want a graph for smaller layers + return; + } + + aManager->SetDebugOverlayWantsNextFrame(true); + + const gfx::Matrix4x4& transform = aLayer->GetEffectiveTransform(); + nsIntRect bounds = aLayer->GetEffectiveVisibleRegion().GetBounds(); + gfx::Rect graphBounds = gfx::Rect(bounds.x, bounds.y, + bounds.width, bounds.height); + gfx::Rect graphRect = gfx::Rect(bounds.x, bounds.y, 200, 100); + + float opacity = 1.0; + EffectChain effects; + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0.2f,0,0,1)); + compositor->DrawQuad(graphRect, + clipRect, + effects, + opacity, + transform); + + std::vector graph; + int yScaleFactor = 3; + for (int32_t i = (int32_t)velocityData->mData.size() - 2; i >= 0; i--) { + const gfx::Point& p1 = velocityData->mData[i+1].mPoint; + const gfx::Point& p2 = velocityData->mData[i].mPoint; + int vel = sqrt((p1.x - p2.x) * (p1.x - p2.x) + + (p1.y - p2.y) * (p1.y - p2.y)); + graph.push_back( + gfx::Point(bounds.x + graphRect.width / circularBufferSize * i, + graphBounds.y + graphRect.height - vel/yScaleFactor)); + } + + compositor->DrawLines(graph, clipRect, gfx::Color(0,1,0,1), + opacity, transform); +} + +// ContainerRender is shared between RefLayer and ContainerLayer +template void +ContainerRender(ContainerT* aContainer, + LayerManagerComposite* aManager, + const nsIntRect& aClipRect) +{ + /** + * Setup our temporary surface for rendering the contents of this container. + */ + RefPtr surface; + + Compositor* compositor = aManager->GetCompositor(); + + RefPtr previousTarget = compositor->GetCurrentRenderTarget(); + + nsIntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds(); + + aContainer->mSupportsComponentAlphaChildren = false; + + float opacity = aContainer->GetEffectiveOpacity(); + + bool needsSurface = aContainer->UseIntermediateSurface(); + if (needsSurface) { + SurfaceInitMode mode = INIT_MODE_CLEAR; + bool surfaceCopyNeeded = false; + gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y, + visibleRect.width, visibleRect.height); + gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y); + // we're about to create a framebuffer backed by textures to use as an intermediate + // surface. What to do if its size (as given by framebufferRect) would exceed the + // maximum texture size supported by the GL? The present code chooses the compromise + // of just clamping the framebuffer's size to the max supported size. + // This gives us a lower resolution rendering of the intermediate surface (children layers). + // See bug 827170 for a discussion. + int32_t maxTextureSize = compositor->GetMaxTextureSize(); + surfaceRect.width = std::min(maxTextureSize, surfaceRect.width); + surfaceRect.height = std::min(maxTextureSize, surfaceRect.height); + if (aContainer->GetEffectiveVisibleRegion().GetNumRects() == 1 && + (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE)) + { + // don't need a background, we're going to paint all opaque stuff + aContainer->mSupportsComponentAlphaChildren = true; + mode = INIT_MODE_NONE; + } else { + const gfx::Matrix4x4& transform3D = aContainer->GetEffectiveTransform(); + gfx::Matrix transform; + // If we have an opaque ancestor layer, then we can be sure that + // all the pixels we draw into are either opaque already or will be + // covered by something opaque. Otherwise copying up the background is + // not safe. + if (ContainerLayer::HasOpaqueAncestorLayer(aContainer) && + transform3D.Is2D(&transform) && !ThebesMatrix(transform).HasNonIntegerTranslation()) { + surfaceCopyNeeded = gfxPrefs::ComponentAlphaEnabled(); + sourcePoint.x += transform._31; + sourcePoint.y += transform._32; + aContainer->mSupportsComponentAlphaChildren + = gfxPrefs::ComponentAlphaEnabled(); + } + } + + sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin(); + if (surfaceCopyNeeded) { + surface = compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint); + } else { + surface = compositor->CreateRenderTarget(surfaceRect, mode); + } + + if (!surface) { + return; + } + + compositor->SetRenderTarget(surface); + } else { + surface = previousTarget; + aContainer->mSupportsComponentAlphaChildren = (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE) || + (aContainer->GetParent() && aContainer->GetParent()->SupportsComponentAlphaChildren()); + } + + nsAutoTArray children; + aContainer->SortChildrenBy3DZOrder(children); + + /** + * Render this container's contents. + */ + for (uint32_t i = 0; i < children.Length(); i++) { + LayerComposite* layerToRender = static_cast(children.ElementAt(i)->ImplData()); + + if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty() && + !layerToRender->GetLayer()->AsContainerLayer()) { + continue; + } + + nsIntRect clipRect = layerToRender->GetLayer()-> + CalculateScissorRect(aClipRect, &aManager->GetWorldTransform()); + if (clipRect.IsEmpty()) { + continue; + } + + nsIntRegion savedVisibleRegion; + bool restoreVisibleRegion = false; + if (i + 1 < children.Length() && + layerToRender->GetLayer()->GetEffectiveTransform().IsIdentity()) { + LayerComposite* nextLayer = static_cast(children.ElementAt(i + 1)->ImplData()); + nsIntRect nextLayerOpaqueRect; + if (nextLayer && nextLayer->GetLayer()) { + nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer()); + } + if (!nextLayerOpaqueRect.IsEmpty()) { + savedVisibleRegion = layerToRender->GetShadowVisibleRegion(); + nsIntRegion visibleRegion; + visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect); + if (visibleRegion.IsEmpty()) { + continue; + } + layerToRender->SetShadowVisibleRegion(visibleRegion); + restoreVisibleRegion = true; + } + } + + if (layerToRender->HasLayerBeenComposited()) { + // Composer2D will compose this layer so skip GPU composition + // this time & reset composition flag for next composition phase + layerToRender->SetLayerComposited(false); + nsIntRect clearRect = layerToRender->GetClearRect(); + if (!clearRect.IsEmpty()) { + // Clear layer's visible rect on FrameBuffer with transparent pixels + gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height); + compositor->ClearRect(fbRect); + layerToRender->SetClearRect(nsIntRect(0, 0, 0, 0)); + } + } else { + layerToRender->RenderLayer(clipRect); + } + + if (restoreVisibleRegion) { + // Restore the region in case it's not covered by opaque content next time + layerToRender->SetShadowVisibleRegion(savedVisibleRegion); + } + + if (gfxPrefs::LayersScrollGraph()) { + DrawVelGraph(clipRect, aManager, layerToRender->GetLayer()); + } + + if (gfxPrefs::DrawLayerInfo()) { + DrawLayerInfo(clipRect, aManager, layerToRender->GetLayer()); + } + // invariant: our GL context should be current here, I don't think we can + // assert it though + } + + if (needsSurface) { + // Unbind the current surface and rebind the previous one. +#ifdef MOZ_DUMP_PAINTING + if (gfxUtils::sDumpPainting) { + RefPtr surf = surface->Dump(aManager->GetCompositor()); + WriteSnapshotToDumpFile(aContainer, surf); + } +#endif + + compositor->SetRenderTarget(previousTarget); + EffectChain effectChain(aContainer); + LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(aContainer->GetMaskLayer(), + effectChain, + !aContainer->GetTransform().CanDraw2D()); + + effectChain.mPrimaryEffect = new EffectRenderTarget(surface); + + gfx::Rect rect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height); + gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); + aManager->GetCompositor()->DrawQuad(rect, clipRect, effectChain, opacity, + aContainer->GetEffectiveTransform()); + } + + if (aContainer->GetFrameMetrics().IsScrollable()) { + const FrameMetrics& frame = aContainer->GetFrameMetrics(); + LayerRect layerBounds = ParentLayerRect(frame.mCompositionBounds) * ParentLayerToLayerScale(1.0); + gfx::Rect rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height); + gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); + aManager->GetCompositor()->DrawDiagnostics(DIAGNOSTIC_CONTAINER, + rect, clipRect, + aContainer->GetEffectiveTransform()); + } +} + +ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager) + : ContainerLayer(aManager, nullptr) + , LayerComposite(aManager) +{ + MOZ_COUNT_CTOR(ContainerLayerComposite); + mImplData = static_cast(this); +} + +ContainerLayerComposite::~ContainerLayerComposite() +{ + MOZ_COUNT_DTOR(ContainerLayerComposite); + + // We don't Destroy() on destruction here because this destructor + // can be called after remote content has crashed, and it may not be + // safe to free the IPC resources of our children. Those resources + // are automatically cleaned up by IPDL-generated code. + // + // In the common case of normal shutdown, either + // LayerManagerComposite::Destroy(), a parent + // *ContainerLayerComposite::Destroy(), or Disconnect() will trigger + // cleanup of our resources. + while (mFirstChild) { + RemoveChild(mFirstChild); + } +} + +void +ContainerLayerComposite::Destroy() +{ + if (!mDestroyed) { + while (mFirstChild) { + static_cast(GetFirstChild()->ImplData())->Destroy(); + RemoveChild(mFirstChild); + } + mDestroyed = true; + } +} + +LayerComposite* +ContainerLayerComposite::GetFirstChildComposite() +{ + if (!mFirstChild) { + return nullptr; + } + return static_cast(mFirstChild->ImplData()); +} + +void +ContainerLayerComposite::RenderLayer(const nsIntRect& aClipRect) +{ + ContainerRender(this, mCompositeManager, aClipRect); +} + +void +ContainerLayerComposite::CleanupResources() +{ + for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) { + LayerComposite* layerToCleanup = static_cast(l->ImplData()); + layerToCleanup->CleanupResources(); + } +} + +RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager) + : RefLayer(aManager, nullptr) + , LayerComposite(aManager) +{ + mImplData = static_cast(this); +} + +RefLayerComposite::~RefLayerComposite() +{ + Destroy(); +} + +void +RefLayerComposite::Destroy() +{ + MOZ_ASSERT(!mFirstChild); + mDestroyed = true; +} + +LayerComposite* +RefLayerComposite::GetFirstChildComposite() +{ + if (!mFirstChild) { + return nullptr; + } + return static_cast(mFirstChild->ImplData()); +} + +void +RefLayerComposite::RenderLayer(const nsIntRect& aClipRect) +{ + ContainerRender(this, mCompositeManager, aClipRect); +} + +void +RefLayerComposite::CleanupResources() +{ +} + +} /* layers */ +} /* mozilla */