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