michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ 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 "Layers.h" michael@0: #include // for max, min michael@0: #include "AnimationCommon.h" // for ComputedTimingFunction michael@0: #include "CompositableHost.h" // for CompositableHost michael@0: #include "ImageContainer.h" // for ImageContainer, etc michael@0: #include "ImageLayers.h" // for ImageLayer michael@0: #include "LayerSorter.h" // for SortLayersBy3DZOrder michael@0: #include "LayersLogging.h" // for AppendToString michael@0: #include "ReadbackLayer.h" // for ReadbackLayer michael@0: #include "gfxPlatform.h" // for gfxPlatform michael@0: #include "gfxUtils.h" // for gfxUtils, etc michael@0: #include "gfx2DGlue.h" michael@0: #include "mozilla/DebugOnly.h" // for DebugOnly michael@0: #include "mozilla/Telemetry.h" // for Accumulate michael@0: #include "mozilla/gfx/2D.h" // for DrawTarget michael@0: #include "mozilla/gfx/BaseSize.h" // for BaseSize michael@0: #include "mozilla/gfx/Matrix.h" // for Matrix4x4 michael@0: #include "mozilla/layers/AsyncPanZoomController.h" michael@0: #include "mozilla/layers/Compositor.h" // for Compositor michael@0: #include "mozilla/layers/CompositorTypes.h" michael@0: #include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite michael@0: #include "mozilla/layers/LayersMessages.h" // for TransformFunction, etc michael@0: #include "nsAString.h" michael@0: #include "nsCSSValue.h" // for nsCSSValue::Array, etc michael@0: #include "nsPrintfCString.h" // for nsPrintfCString michael@0: #include "nsStyleStruct.h" // for nsTimingFunction, etc michael@0: michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::gfx; michael@0: michael@0: typedef FrameMetrics::ViewID ViewID; michael@0: const ViewID FrameMetrics::NULL_SCROLL_ID = 0; michael@0: michael@0: uint8_t gLayerManagerLayerBuilder; michael@0: michael@0: FILE* michael@0: FILEOrDefault(FILE* aFile) michael@0: { michael@0: return aFile ? aFile : stderr; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: //-------------------------------------------------- michael@0: // LayerManager michael@0: Layer* michael@0: LayerManager::GetPrimaryScrollableLayer() michael@0: { michael@0: if (!mRoot) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsTArray queue; michael@0: queue.AppendElement(mRoot); michael@0: while (queue.Length()) { michael@0: ContainerLayer* containerLayer = queue[0]->AsContainerLayer(); michael@0: queue.RemoveElementAt(0); michael@0: if (!containerLayer) { michael@0: continue; michael@0: } michael@0: michael@0: const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics(); michael@0: if (frameMetrics.IsScrollable()) { michael@0: return containerLayer; michael@0: } michael@0: michael@0: Layer* child = containerLayer->GetFirstChild(); michael@0: while (child) { michael@0: queue.AppendElement(child); michael@0: child = child->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: return mRoot; michael@0: } michael@0: michael@0: void michael@0: LayerManager::GetScrollableLayers(nsTArray& aArray) michael@0: { michael@0: if (!mRoot) { michael@0: return; michael@0: } michael@0: michael@0: nsTArray queue; michael@0: queue.AppendElement(mRoot); michael@0: while (!queue.IsEmpty()) { michael@0: ContainerLayer* containerLayer = queue.LastElement()->AsContainerLayer(); michael@0: queue.RemoveElementAt(queue.Length() - 1); michael@0: if (!containerLayer) { michael@0: continue; michael@0: } michael@0: michael@0: const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics(); michael@0: if (frameMetrics.IsScrollable()) { michael@0: aArray.AppendElement(containerLayer); michael@0: continue; michael@0: } michael@0: michael@0: Layer* child = containerLayer->GetFirstChild(); michael@0: while (child) { michael@0: queue.AppendElement(child); michael@0: child = child->GetNextSibling(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: LayerManager::CreateOptimalDrawTarget(const gfx::IntSize &aSize, michael@0: SurfaceFormat aFormat) michael@0: { michael@0: return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSize, michael@0: aFormat); michael@0: } michael@0: michael@0: TemporaryRef michael@0: LayerManager::CreateOptimalMaskDrawTarget(const gfx::IntSize &aSize) michael@0: { michael@0: return CreateOptimalDrawTarget(aSize, SurfaceFormat::A8); michael@0: } michael@0: michael@0: TemporaryRef michael@0: LayerManager::CreateDrawTarget(const IntSize &aSize, michael@0: SurfaceFormat aFormat) michael@0: { michael@0: return gfxPlatform::GetPlatform()-> michael@0: CreateOffscreenCanvasDrawTarget(aSize, aFormat); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: LayerManager::Mutated(Layer* aLayer) michael@0: { michael@0: } michael@0: #endif // DEBUG michael@0: michael@0: already_AddRefed michael@0: LayerManager::CreateImageContainer() michael@0: { michael@0: nsRefPtr container = new ImageContainer(ImageContainer::DISABLE_ASYNC); michael@0: return container.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManager::CreateAsynchronousImageContainer() michael@0: { michael@0: nsRefPtr container = new ImageContainer(ImageContainer::ENABLE_ASYNC); michael@0: return container.forget(); michael@0: } michael@0: michael@0: //-------------------------------------------------- michael@0: // Layer michael@0: michael@0: Layer::Layer(LayerManager* aManager, void* aImplData) : michael@0: mManager(aManager), michael@0: mParent(nullptr), michael@0: mNextSibling(nullptr), michael@0: mPrevSibling(nullptr), michael@0: mImplData(aImplData), michael@0: mMaskLayer(nullptr), michael@0: mPostXScale(1.0f), michael@0: mPostYScale(1.0f), michael@0: mOpacity(1.0), michael@0: mMixBlendMode(CompositionOp::OP_OVER), michael@0: mForceIsolatedGroup(false), michael@0: mContentFlags(0), michael@0: mUseClipRect(false), michael@0: mUseTileSourceRect(false), michael@0: mIsFixedPosition(false), michael@0: mMargins(0, 0, 0, 0), michael@0: mStickyPositionData(nullptr), michael@0: mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID), michael@0: mScrollbarDirection(ScrollDirection::NONE), michael@0: mDebugColorIndex(0), michael@0: mAnimationGeneration(0) michael@0: {} michael@0: michael@0: Layer::~Layer() michael@0: {} michael@0: michael@0: Animation* michael@0: Layer::AddAnimation() michael@0: { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AddAnimation", this)); michael@0: michael@0: MOZ_ASSERT(!mPendingAnimations, "should have called ClearAnimations first"); michael@0: michael@0: Animation* anim = mAnimations.AppendElement(); michael@0: michael@0: Mutated(); michael@0: return anim; michael@0: } michael@0: michael@0: void michael@0: Layer::ClearAnimations() michael@0: { michael@0: mPendingAnimations = nullptr; michael@0: michael@0: if (mAnimations.IsEmpty() && mAnimationData.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClearAnimations", this)); michael@0: mAnimations.Clear(); michael@0: mAnimationData.Clear(); michael@0: Mutated(); michael@0: } michael@0: michael@0: Animation* michael@0: Layer::AddAnimationForNextTransaction() michael@0: { michael@0: MOZ_ASSERT(mPendingAnimations, michael@0: "should have called ClearAnimationsForNextTransaction first"); michael@0: michael@0: Animation* anim = mPendingAnimations->AppendElement(); michael@0: michael@0: return anim; michael@0: } michael@0: michael@0: void michael@0: Layer::ClearAnimationsForNextTransaction() michael@0: { michael@0: // Ensure we have a non-null mPendingAnimations to mark a future clear. michael@0: if (!mPendingAnimations) { michael@0: mPendingAnimations = new AnimationArray; michael@0: } michael@0: michael@0: mPendingAnimations->Clear(); michael@0: } michael@0: michael@0: static nsCSSValueSharedList* michael@0: CreateCSSValueList(const InfallibleTArray& aFunctions) michael@0: { michael@0: nsAutoPtr result; michael@0: nsCSSValueList** resultTail = getter_Transfers(result); michael@0: for (uint32_t i = 0; i < aFunctions.Length(); i++) { michael@0: nsRefPtr arr; michael@0: switch (aFunctions[i].type()) { michael@0: case TransformFunction::TRotationX: michael@0: { michael@0: float theta = aFunctions[i].get_RotationX().radians(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatex, resultTail); michael@0: arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TRotationY: michael@0: { michael@0: float theta = aFunctions[i].get_RotationY().radians(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatey, resultTail); michael@0: arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TRotationZ: michael@0: { michael@0: float theta = aFunctions[i].get_RotationZ().radians(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatez, resultTail); michael@0: arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TRotation: michael@0: { michael@0: float theta = aFunctions[i].get_Rotation().radians(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate, resultTail); michael@0: arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TRotation3D: michael@0: { michael@0: float x = aFunctions[i].get_Rotation3D().x(); michael@0: float y = aFunctions[i].get_Rotation3D().y(); michael@0: float z = aFunctions[i].get_Rotation3D().z(); michael@0: float theta = aFunctions[i].get_Rotation3D().radians(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate3d, resultTail); michael@0: arr->Item(1).SetFloatValue(x, eCSSUnit_Number); michael@0: arr->Item(2).SetFloatValue(y, eCSSUnit_Number); michael@0: arr->Item(3).SetFloatValue(z, eCSSUnit_Number); michael@0: arr->Item(4).SetFloatValue(theta, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TScale: michael@0: { michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_scale3d, resultTail); michael@0: arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number); michael@0: arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number); michael@0: arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number); michael@0: break; michael@0: } michael@0: case TransformFunction::TTranslation: michael@0: { michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_translate3d, resultTail); michael@0: arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel); michael@0: arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel); michael@0: arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel); michael@0: break; michael@0: } michael@0: case TransformFunction::TSkewX: michael@0: { michael@0: float x = aFunctions[i].get_SkewX().x(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewx, resultTail); michael@0: arr->Item(1).SetFloatValue(x, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TSkewY: michael@0: { michael@0: float y = aFunctions[i].get_SkewY().y(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewy, resultTail); michael@0: arr->Item(1).SetFloatValue(y, eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TSkew: michael@0: { michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skew, resultTail); michael@0: arr->Item(1).SetFloatValue(aFunctions[i].get_Skew().x(), eCSSUnit_Radian); michael@0: arr->Item(2).SetFloatValue(aFunctions[i].get_Skew().y(), eCSSUnit_Radian); michael@0: break; michael@0: } michael@0: case TransformFunction::TTransformMatrix: michael@0: { michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_matrix3d, resultTail); michael@0: const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value(); michael@0: arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number); michael@0: arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number); michael@0: arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number); michael@0: arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number); michael@0: arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number); michael@0: arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number); michael@0: arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number); michael@0: arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number); michael@0: arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number); michael@0: arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number); michael@0: arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number); michael@0: arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number); michael@0: arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number); michael@0: arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number); michael@0: arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number); michael@0: arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number); michael@0: break; michael@0: } michael@0: case TransformFunction::TPerspective: michael@0: { michael@0: float perspective = aFunctions[i].get_Perspective().value(); michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_perspective, resultTail); michael@0: arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel); michael@0: break; michael@0: } michael@0: default: michael@0: NS_ASSERTION(false, "All functions should be implemented?"); michael@0: } michael@0: } michael@0: if (aFunctions.Length() == 0) { michael@0: result = new nsCSSValueList(); michael@0: result->mValue.SetNoneValue(); michael@0: } michael@0: return new nsCSSValueSharedList(result.forget()); michael@0: } michael@0: michael@0: void michael@0: Layer::SetAnimations(const AnimationArray& aAnimations) michael@0: { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this)); michael@0: michael@0: mAnimations = aAnimations; michael@0: mAnimationData.Clear(); michael@0: for (uint32_t i = 0; i < mAnimations.Length(); i++) { michael@0: AnimData* data = mAnimationData.AppendElement(); michael@0: InfallibleTArray >& functions = data->mFunctions; michael@0: const InfallibleTArray& segments = michael@0: mAnimations.ElementAt(i).segments(); michael@0: for (uint32_t j = 0; j < segments.Length(); j++) { michael@0: TimingFunction tf = segments.ElementAt(j).sampleFn(); michael@0: css::ComputedTimingFunction* ctf = new css::ComputedTimingFunction(); michael@0: switch (tf.type()) { michael@0: case TimingFunction::TCubicBezierFunction: { michael@0: CubicBezierFunction cbf = tf.get_CubicBezierFunction(); michael@0: ctf->Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2())); michael@0: break; michael@0: } michael@0: default: { michael@0: NS_ASSERTION(tf.type() == TimingFunction::TStepFunction, michael@0: "Function must be bezier or step"); michael@0: StepFunction sf = tf.get_StepFunction(); michael@0: nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart michael@0: : nsTimingFunction::StepEnd; michael@0: ctf->Init(nsTimingFunction(type, sf.steps())); michael@0: break; michael@0: } michael@0: } michael@0: functions.AppendElement(ctf); michael@0: } michael@0: michael@0: // Precompute the nsStyleAnimation::Values that we need if this is a transform michael@0: // animation. michael@0: InfallibleTArray& startValues = data->mStartValues; michael@0: InfallibleTArray& endValues = data->mEndValues; michael@0: for (uint32_t j = 0; j < mAnimations[i].segments().Length(); j++) { michael@0: const AnimationSegment& segment = mAnimations[i].segments()[j]; michael@0: nsStyleAnimation::Value* startValue = startValues.AppendElement(); michael@0: nsStyleAnimation::Value* endValue = endValues.AppendElement(); michael@0: if (segment.endState().type() == Animatable::TArrayOfTransformFunction) { michael@0: const InfallibleTArray& startFunctions = michael@0: segment.startState().get_ArrayOfTransformFunction(); michael@0: startValue->SetTransformValue(CreateCSSValueList(startFunctions)); michael@0: michael@0: const InfallibleTArray& endFunctions = michael@0: segment.endState().get_ArrayOfTransformFunction(); michael@0: endValue->SetTransformValue(CreateCSSValueList(endFunctions)); michael@0: } else { michael@0: NS_ASSERTION(segment.endState().type() == Animatable::Tfloat, michael@0: "Unknown Animatable type"); michael@0: startValue->SetFloatValue(segment.startState().get_float()); michael@0: endValue->SetFloatValue(segment.endState().get_float()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: Mutated(); michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::SetAsyncPanZoomController(AsyncPanZoomController *controller) michael@0: { michael@0: mAPZC = controller; michael@0: } michael@0: michael@0: AsyncPanZoomController* michael@0: ContainerLayer::GetAsyncPanZoomController() const michael@0: { michael@0: #ifdef DEBUG michael@0: if (mAPZC) { michael@0: MOZ_ASSERT(GetFrameMetrics().IsScrollable()); michael@0: } michael@0: #endif michael@0: return mAPZC; michael@0: } michael@0: michael@0: void michael@0: Layer::ApplyPendingUpdatesToSubtree() michael@0: { michael@0: ApplyPendingUpdatesForThisTransaction(); michael@0: for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) { michael@0: child->ApplyPendingUpdatesToSubtree(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: Layer::CanUseOpaqueSurface() michael@0: { michael@0: // If the visible content in the layer is opaque, there is no need michael@0: // for an alpha channel. michael@0: if (GetContentFlags() & CONTENT_OPAQUE) michael@0: return true; michael@0: // Also, if this layer is the bottommost layer in a container which michael@0: // doesn't need an alpha channel, we can use an opaque surface for this michael@0: // layer too. Any transparent areas must be covered by something else michael@0: // in the container. michael@0: ContainerLayer* parent = GetParent(); michael@0: return parent && parent->GetFirstChild() == this && michael@0: parent->CanUseOpaqueSurface(); michael@0: } michael@0: michael@0: // NB: eventually these methods will be defined unconditionally, and michael@0: // can be moved into Layers.h michael@0: const nsIntRect* michael@0: Layer::GetEffectiveClipRect() michael@0: { michael@0: if (LayerComposite* shadow = AsLayerComposite()) { michael@0: return shadow->GetShadowClipRect(); michael@0: } michael@0: return GetClipRect(); michael@0: } michael@0: michael@0: const nsIntRegion& michael@0: Layer::GetEffectiveVisibleRegion() michael@0: { michael@0: if (LayerComposite* shadow = AsLayerComposite()) { michael@0: return shadow->GetShadowVisibleRegion(); michael@0: } michael@0: return GetVisibleRegion(); michael@0: } michael@0: michael@0: Matrix4x4 michael@0: Layer::SnapTransformTranslation(const Matrix4x4& aTransform, michael@0: Matrix* aResidualTransform) michael@0: { michael@0: if (aResidualTransform) { michael@0: *aResidualTransform = Matrix(); michael@0: } michael@0: michael@0: Matrix matrix2D; michael@0: Matrix4x4 result; michael@0: if (mManager->IsSnappingEffectiveTransforms() && michael@0: aTransform.Is2D(&matrix2D) && michael@0: !matrix2D.HasNonTranslation() && michael@0: matrix2D.HasNonIntegerTranslation()) { michael@0: IntPoint snappedTranslation = RoundedToInt(matrix2D.GetTranslation()); michael@0: Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x, michael@0: snappedTranslation.y); michael@0: result = Matrix4x4::From2D(snappedMatrix); michael@0: if (aResidualTransform) { michael@0: // set aResidualTransform so that aResidual * snappedMatrix == matrix2D. michael@0: // (I.e., appying snappedMatrix after aResidualTransform gives the michael@0: // ideal transform.) michael@0: *aResidualTransform = michael@0: Matrix::Translation(matrix2D._31 - snappedTranslation.x, michael@0: matrix2D._32 - snappedTranslation.y); michael@0: } michael@0: } else { michael@0: result = aTransform; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: Matrix4x4 michael@0: Layer::SnapTransform(const Matrix4x4& aTransform, michael@0: const gfxRect& aSnapRect, michael@0: Matrix* aResidualTransform) michael@0: { michael@0: if (aResidualTransform) { michael@0: *aResidualTransform = Matrix(); michael@0: } michael@0: michael@0: Matrix matrix2D; michael@0: Matrix4x4 result; michael@0: if (mManager->IsSnappingEffectiveTransforms() && michael@0: aTransform.Is2D(&matrix2D) && michael@0: gfx::Size(1.0, 1.0) <= ToSize(aSnapRect.Size()) && michael@0: matrix2D.PreservesAxisAlignedRectangles()) { michael@0: IntPoint transformedTopLeft = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopLeft())); michael@0: IntPoint transformedTopRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopRight())); michael@0: IntPoint transformedBottomRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.BottomRight())); michael@0: michael@0: Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect, michael@0: transformedTopLeft, transformedTopRight, transformedBottomRight); michael@0: michael@0: result = Matrix4x4::From2D(snappedMatrix); michael@0: if (aResidualTransform && !snappedMatrix.IsSingular()) { michael@0: // set aResidualTransform so that aResidual * snappedMatrix == matrix2D. michael@0: // (i.e., appying snappedMatrix after aResidualTransform gives the michael@0: // ideal transform. michael@0: Matrix snappedMatrixInverse = snappedMatrix; michael@0: snappedMatrixInverse.Invert(); michael@0: *aResidualTransform = matrix2D * snappedMatrixInverse; michael@0: } michael@0: } else { michael@0: result = aTransform; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static bool michael@0: AncestorLayerMayChangeTransform(Layer* aLayer) michael@0: { michael@0: for (Layer* l = aLayer; l; l = l->GetParent()) { michael@0: if (l->GetContentFlags() & Layer::CONTENT_MAY_CHANGE_TRANSFORM) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: Layer::MayResample() michael@0: { michael@0: Matrix transform2d; michael@0: return !GetEffectiveTransform().Is2D(&transform2d) || michael@0: ThebesMatrix(transform2d).HasNonIntegerTranslation() || michael@0: AncestorLayerMayChangeTransform(this); michael@0: } michael@0: michael@0: nsIntRect michael@0: Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect, michael@0: const gfx::Matrix* aWorldTransform) michael@0: { michael@0: ContainerLayer* container = GetParent(); michael@0: NS_ASSERTION(container, "This can't be called on the root!"); michael@0: michael@0: // Establish initial clip rect: it's either the one passed in, or michael@0: // if the parent has an intermediate surface, it's the extents of that surface. michael@0: nsIntRect currentClip; michael@0: if (container->UseIntermediateSurface()) { michael@0: currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size()); michael@0: } else { michael@0: currentClip = aCurrentScissorRect; michael@0: } michael@0: michael@0: const nsIntRect *clipRect = GetEffectiveClipRect(); michael@0: if (!clipRect) michael@0: return currentClip; michael@0: michael@0: if (clipRect->IsEmpty()) { michael@0: // We might have a non-translation transform in the container so we can't michael@0: // use the code path below. michael@0: return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); michael@0: } michael@0: michael@0: nsIntRect scissor = *clipRect; michael@0: if (!container->UseIntermediateSurface()) { michael@0: gfx::Matrix matrix; michael@0: DebugOnly is2D = container->GetEffectiveTransform().Is2D(&matrix); michael@0: // See DefaultComputeEffectiveTransforms below michael@0: NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(), michael@0: "Non preserves axis aligned transform with clipped child should have forced intermediate surface"); michael@0: gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height); michael@0: gfxRect trScissor = gfx::ThebesRect(matrix.TransformBounds(r)); michael@0: trScissor.Round(); michael@0: if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor)) { michael@0: return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); michael@0: } michael@0: michael@0: // Find the nearest ancestor with an intermediate surface michael@0: do { michael@0: container = container->GetParent(); michael@0: } while (container && !container->UseIntermediateSurface()); michael@0: } michael@0: if (container) { michael@0: scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft()); michael@0: } else if (aWorldTransform) { michael@0: gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height); michael@0: gfx::Rect trScissor = aWorldTransform->TransformBounds(r); michael@0: trScissor.Round(); michael@0: if (!gfxUtils::GfxRectToIntRect(ThebesRect(trScissor), &scissor)) michael@0: return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); michael@0: } michael@0: return currentClip.Intersect(scissor); michael@0: } michael@0: michael@0: const Matrix4x4 michael@0: Layer::GetTransform() const michael@0: { michael@0: Matrix4x4 transform = mTransform; michael@0: if (const ContainerLayer* c = AsContainerLayer()) { michael@0: transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f); michael@0: } michael@0: transform = transform * Matrix4x4().Scale(mPostXScale, mPostYScale, 1.0f); michael@0: return transform; michael@0: } michael@0: michael@0: const Matrix4x4 michael@0: Layer::GetLocalTransform() michael@0: { michael@0: Matrix4x4 transform; michael@0: if (LayerComposite* shadow = AsLayerComposite()) michael@0: transform = shadow->GetShadowTransform(); michael@0: else michael@0: transform = mTransform; michael@0: if (ContainerLayer* c = AsContainerLayer()) { michael@0: transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f); michael@0: } michael@0: transform = transform * Matrix4x4().Scale(mPostXScale, mPostYScale, 1.0f); michael@0: michael@0: return transform; michael@0: } michael@0: michael@0: void michael@0: Layer::ApplyPendingUpdatesForThisTransaction() michael@0: { michael@0: if (mPendingTransform && *mPendingTransform != mTransform) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this)); michael@0: mTransform = *mPendingTransform; michael@0: Mutated(); michael@0: } michael@0: mPendingTransform = nullptr; michael@0: michael@0: if (mPendingAnimations) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this)); michael@0: mPendingAnimations->SwapElements(mAnimations); michael@0: mPendingAnimations = nullptr; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: const float michael@0: Layer::GetLocalOpacity() michael@0: { michael@0: if (LayerComposite* shadow = AsLayerComposite()) michael@0: return shadow->GetShadowOpacity(); michael@0: return mOpacity; michael@0: } michael@0: michael@0: float michael@0: Layer::GetEffectiveOpacity() michael@0: { michael@0: float opacity = GetLocalOpacity(); michael@0: for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface(); michael@0: c = c->GetParent()) { michael@0: opacity *= c->GetLocalOpacity(); michael@0: } michael@0: return opacity; michael@0: } michael@0: michael@0: CompositionOp michael@0: Layer::GetEffectiveMixBlendMode() michael@0: { michael@0: if(mMixBlendMode != CompositionOp::OP_OVER) michael@0: return mMixBlendMode; michael@0: for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface(); michael@0: c = c->GetParent()) { michael@0: if(c->mMixBlendMode != CompositionOp::OP_OVER) michael@0: return c->mMixBlendMode; michael@0: } michael@0: michael@0: return mMixBlendMode; michael@0: } michael@0: michael@0: gfxContext::GraphicsOperator michael@0: Layer::DeprecatedGetEffectiveMixBlendMode() michael@0: { michael@0: return ThebesOp(GetEffectiveMixBlendMode()); michael@0: } michael@0: michael@0: void michael@0: Layer::ComputeEffectiveTransformForMaskLayer(const Matrix4x4& aTransformToSurface) michael@0: { michael@0: if (mMaskLayer) { michael@0: mMaskLayer->mEffectiveTransform = aTransformToSurface; michael@0: michael@0: #ifdef DEBUG michael@0: bool maskIs2D = mMaskLayer->GetTransform().CanDraw2D(); michael@0: NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!"); michael@0: #endif michael@0: mMaskLayer->mEffectiveTransform = mMaskLayer->GetTransform() * mMaskLayer->mEffectiveTransform; michael@0: } michael@0: } michael@0: michael@0: ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData) michael@0: : Layer(aManager, aImplData), michael@0: mFirstChild(nullptr), michael@0: mLastChild(nullptr), michael@0: mScrollHandoffParentId(FrameMetrics::NULL_SCROLL_ID), michael@0: mPreXScale(1.0f), michael@0: mPreYScale(1.0f), michael@0: mInheritedXScale(1.0f), michael@0: mInheritedYScale(1.0f), michael@0: mUseIntermediateSurface(false), michael@0: mSupportsComponentAlphaChildren(false), michael@0: mMayHaveReadbackChild(false) michael@0: { michael@0: mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT michael@0: } michael@0: michael@0: ContainerLayer::~ContainerLayer() {} michael@0: michael@0: bool michael@0: ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) michael@0: { michael@0: if(aChild->Manager() != Manager()) { michael@0: NS_ERROR("Child has wrong manager"); michael@0: return false; michael@0: } michael@0: if(aChild->GetParent()) { michael@0: NS_ERROR("aChild already in the tree"); michael@0: return false; michael@0: } michael@0: if (aChild->GetNextSibling() || aChild->GetPrevSibling()) { michael@0: NS_ERROR("aChild already has siblings?"); michael@0: return false; michael@0: } michael@0: if (aAfter && (aAfter->Manager() != Manager() || michael@0: aAfter->GetParent() != this)) michael@0: { michael@0: NS_ERROR("aAfter is not our child"); michael@0: return false; michael@0: } michael@0: michael@0: aChild->SetParent(this); michael@0: if (aAfter == mLastChild) { michael@0: mLastChild = aChild; michael@0: } michael@0: if (!aAfter) { michael@0: aChild->SetNextSibling(mFirstChild); michael@0: if (mFirstChild) { michael@0: mFirstChild->SetPrevSibling(aChild); michael@0: } michael@0: mFirstChild = aChild; michael@0: NS_ADDREF(aChild); michael@0: DidInsertChild(aChild); michael@0: return true; michael@0: } michael@0: michael@0: Layer* next = aAfter->GetNextSibling(); michael@0: aChild->SetNextSibling(next); michael@0: aChild->SetPrevSibling(aAfter); michael@0: if (next) { michael@0: next->SetPrevSibling(aChild); michael@0: } michael@0: aAfter->SetNextSibling(aChild); michael@0: NS_ADDREF(aChild); michael@0: DidInsertChild(aChild); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContainerLayer::RemoveChild(Layer *aChild) michael@0: { michael@0: if (aChild->Manager() != Manager()) { michael@0: NS_ERROR("Child has wrong manager"); michael@0: return false; michael@0: } michael@0: if (aChild->GetParent() != this) { michael@0: NS_ERROR("aChild not our child"); michael@0: return false; michael@0: } michael@0: michael@0: Layer* prev = aChild->GetPrevSibling(); michael@0: Layer* next = aChild->GetNextSibling(); michael@0: if (prev) { michael@0: prev->SetNextSibling(next); michael@0: } else { michael@0: this->mFirstChild = next; michael@0: } michael@0: if (next) { michael@0: next->SetPrevSibling(prev); michael@0: } else { michael@0: this->mLastChild = prev; michael@0: } michael@0: michael@0: aChild->SetNextSibling(nullptr); michael@0: aChild->SetPrevSibling(nullptr); michael@0: aChild->SetParent(nullptr); michael@0: michael@0: this->DidRemoveChild(aChild); michael@0: NS_RELEASE(aChild); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool michael@0: ContainerLayer::RepositionChild(Layer* aChild, Layer* aAfter) michael@0: { michael@0: if (aChild->Manager() != Manager()) { michael@0: NS_ERROR("Child has wrong manager"); michael@0: return false; michael@0: } michael@0: if (aChild->GetParent() != this) { michael@0: NS_ERROR("aChild not our child"); michael@0: return false; michael@0: } michael@0: if (aAfter && (aAfter->Manager() != Manager() || michael@0: aAfter->GetParent() != this)) michael@0: { michael@0: NS_ERROR("aAfter is not our child"); michael@0: return false; michael@0: } michael@0: if (aChild == aAfter) { michael@0: NS_ERROR("aChild cannot be the same as aAfter"); michael@0: return false; michael@0: } michael@0: michael@0: Layer* prev = aChild->GetPrevSibling(); michael@0: Layer* next = aChild->GetNextSibling(); michael@0: if (prev == aAfter) { michael@0: // aChild is already in the correct position, nothing to do. michael@0: return true; michael@0: } michael@0: if (prev) { michael@0: prev->SetNextSibling(next); michael@0: } else { michael@0: mFirstChild = next; michael@0: } michael@0: if (next) { michael@0: next->SetPrevSibling(prev); michael@0: } else { michael@0: mLastChild = prev; michael@0: } michael@0: if (!aAfter) { michael@0: aChild->SetPrevSibling(nullptr); michael@0: aChild->SetNextSibling(mFirstChild); michael@0: if (mFirstChild) { michael@0: mFirstChild->SetPrevSibling(aChild); michael@0: } michael@0: mFirstChild = aChild; michael@0: return true; michael@0: } michael@0: michael@0: Layer* afterNext = aAfter->GetNextSibling(); michael@0: if (afterNext) { michael@0: afterNext->SetPrevSibling(aChild); michael@0: } else { michael@0: mLastChild = aChild; michael@0: } michael@0: aAfter->SetNextSibling(aChild); michael@0: aChild->SetPrevSibling(aAfter); michael@0: aChild->SetNextSibling(afterNext); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) michael@0: { michael@0: aAttrs = ContainerLayerAttributes(GetFrameMetrics(), mScrollHandoffParentId, michael@0: mPreXScale, mPreYScale, michael@0: mInheritedXScale, mInheritedYScale); michael@0: } michael@0: michael@0: bool michael@0: ContainerLayer::HasMultipleChildren() michael@0: { michael@0: uint32_t count = 0; michael@0: for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) { michael@0: const nsIntRect *clipRect = child->GetEffectiveClipRect(); michael@0: if (clipRect && clipRect->IsEmpty()) michael@0: continue; michael@0: if (child->GetVisibleRegion().IsEmpty()) michael@0: continue; michael@0: ++count; michael@0: if (count > 1) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::SortChildrenBy3DZOrder(nsTArray& aArray) michael@0: { michael@0: nsAutoTArray toSort; michael@0: michael@0: for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) { michael@0: ContainerLayer* container = l->AsContainerLayer(); michael@0: if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) { michael@0: toSort.AppendElement(l); michael@0: } else { michael@0: if (toSort.Length() > 0) { michael@0: SortLayersBy3DZOrder(toSort); michael@0: aArray.MoveElementsFrom(toSort); michael@0: } michael@0: aArray.AppendElement(l); michael@0: } michael@0: } michael@0: if (toSort.Length() > 0) { michael@0: SortLayersBy3DZOrder(toSort); michael@0: aArray.MoveElementsFrom(toSort); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) michael@0: { michael@0: Matrix residual; michael@0: Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface; michael@0: idealTransform.ProjectTo2D(); michael@0: mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual); michael@0: michael@0: bool useIntermediateSurface; michael@0: if (GetMaskLayer()) { michael@0: useIntermediateSurface = true; michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: } else if (gfxUtils::sDumpPainting) { michael@0: useIntermediateSurface = true; michael@0: #endif michael@0: } else { michael@0: float opacity = GetEffectiveOpacity(); michael@0: if (opacity != 1.0f && HasMultipleChildren()) { michael@0: useIntermediateSurface = true; michael@0: } else { michael@0: useIntermediateSurface = false; michael@0: gfx::Matrix contTransform; michael@0: if (!mEffectiveTransform.Is2D(&contTransform) || michael@0: #ifdef MOZ_GFX_OPTIMIZE_MOBILE michael@0: !contTransform.PreservesAxisAlignedRectangles()) { michael@0: #else michael@0: gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) { michael@0: #endif michael@0: for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) { michael@0: const nsIntRect *clipRect = child->GetEffectiveClipRect(); michael@0: /* We can't (easily) forward our transform to children with a non-empty clip michael@0: * rect since it would need to be adjusted for the transform. See michael@0: * the calculations performed by CalculateScissorRect above. michael@0: * Nor for a child with a mask layer. michael@0: */ michael@0: if ((clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) || michael@0: child->GetMaskLayer()) { michael@0: useIntermediateSurface = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: mUseIntermediateSurface = useIntermediateSurface; michael@0: if (useIntermediateSurface) { michael@0: ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual)); michael@0: } else { michael@0: ComputeEffectiveTransformsForChildren(idealTransform); michael@0: } michael@0: michael@0: if (idealTransform.CanDraw2D()) { michael@0: ComputeEffectiveTransformForMaskLayer(aTransformToSurface); michael@0: } else { michael@0: ComputeEffectiveTransformForMaskLayer(Matrix4x4()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::ComputeEffectiveTransformsForChildren(const Matrix4x4& aTransformToSurface) michael@0: { michael@0: for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) { michael@0: l->ComputeEffectiveTransforms(aTransformToSurface); michael@0: } michael@0: } michael@0: michael@0: /* static */ bool michael@0: ContainerLayer::HasOpaqueAncestorLayer(Layer* aLayer) michael@0: { michael@0: for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) { michael@0: if (l->GetContentFlags() & Layer::CONTENT_OPAQUE) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::DidRemoveChild(Layer* aLayer) michael@0: { michael@0: ThebesLayer* tl = aLayer->AsThebesLayer(); michael@0: if (tl && tl->UsedForReadback()) { michael@0: for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) { michael@0: if (l->GetType() == TYPE_READBACK) { michael@0: static_cast(l)->NotifyThebesLayerRemoved(tl); michael@0: } michael@0: } michael@0: } michael@0: if (aLayer->GetType() == TYPE_READBACK) { michael@0: static_cast(aLayer)->NotifyRemoved(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerLayer::DidInsertChild(Layer* aLayer) michael@0: { michael@0: if (aLayer->GetType() == TYPE_READBACK) { michael@0: mMayHaveReadbackChild = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: RefLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) michael@0: { michael@0: aAttrs = RefLayerAttributes(GetReferentId()); michael@0: } michael@0: michael@0: /** michael@0: * StartFrameTimeRecording, together with StopFrameTimeRecording michael@0: * enable recording of frame intervals. michael@0: * michael@0: * To allow concurrent consumers, a cyclic array is used which serves all michael@0: * consumers, practically stateless with regard to consumers. michael@0: * michael@0: * To save resources, the buffer is allocated on first call to StartFrameTimeRecording michael@0: * and recording is paused if no consumer which called StartFrameTimeRecording is able michael@0: * to get valid results (because the cyclic buffer was overwritten since that call). michael@0: * michael@0: * To determine availability of the data upon StopFrameTimeRecording: michael@0: * - mRecording.mNextIndex increases on each PostPresent, and never resets. michael@0: * - Cyclic buffer position is realized as mNextIndex % bufferSize. michael@0: * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is called, michael@0: * the required start index is passed as an arg, and we're able to calculate the required michael@0: * length. If this length is bigger than bufferSize, it means data was overwritten. michael@0: * otherwise, we can return the entire sequence. michael@0: * - To determine if we need to pause, mLatestStartIndex is updated to mNextIndex michael@0: * on each call to StartFrameTimeRecording. If this index gets overwritten, michael@0: * it means that all earlier start indices obtained via StartFrameTimeRecording michael@0: * were also overwritten, hence, no point in recording, so pause. michael@0: * - mCurrentRunStartIndex indicates the oldest index of the recording after which michael@0: * the recording was not paused. If StopFrameTimeRecording is invoked with a start index michael@0: * older than this, it means that some frames were not recorded, so data is invalid. michael@0: */ michael@0: uint32_t michael@0: LayerManager::StartFrameTimeRecording(int32_t aBufferSize) michael@0: { michael@0: if (mRecording.mIsPaused) { michael@0: mRecording.mIsPaused = false; michael@0: michael@0: if (!mRecording.mIntervals.Length()) { // Initialize recording buffers michael@0: mRecording.mIntervals.SetLength(aBufferSize); michael@0: } michael@0: michael@0: // After being paused, recent values got invalid. Update them to now. michael@0: mRecording.mLastFrameTime = TimeStamp::Now(); michael@0: michael@0: // Any recording which started before this is invalid, since we were paused. michael@0: mRecording.mCurrentRunStartIndex = mRecording.mNextIndex; michael@0: } michael@0: michael@0: // If we'll overwrite this index, there are no more consumers with aStartIndex michael@0: // for which we're able to provide the full recording, so no point in keep recording. michael@0: mRecording.mLatestStartIndex = mRecording.mNextIndex; michael@0: return mRecording.mNextIndex; michael@0: } michael@0: michael@0: void michael@0: LayerManager::RecordFrame() michael@0: { michael@0: if (!mRecording.mIsPaused) { michael@0: TimeStamp now = TimeStamp::Now(); michael@0: uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length(); michael@0: mRecording.mIntervals[i] = static_cast((now - mRecording.mLastFrameTime) michael@0: .ToMilliseconds()); michael@0: mRecording.mNextIndex++; michael@0: mRecording.mLastFrameTime = now; michael@0: michael@0: if (mRecording.mNextIndex > (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) { michael@0: // We've just overwritten the most recent recording start -> pause. michael@0: mRecording.mIsPaused = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManager::PostPresent() michael@0: { michael@0: if (!mTabSwitchStart.IsNull()) { michael@0: Telemetry::Accumulate(Telemetry::FX_TAB_SWITCH_TOTAL_MS, michael@0: uint32_t((TimeStamp::Now() - mTabSwitchStart).ToMilliseconds())); michael@0: mTabSwitchStart = TimeStamp(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManager::StopFrameTimeRecording(uint32_t aStartIndex, michael@0: nsTArray& aFrameIntervals) michael@0: { michael@0: uint32_t bufferSize = mRecording.mIntervals.Length(); michael@0: uint32_t length = mRecording.mNextIndex - aStartIndex; michael@0: if (mRecording.mIsPaused || length > bufferSize || aStartIndex < mRecording.mCurrentRunStartIndex) { michael@0: // aStartIndex is too old. Also if aStartIndex was issued before mRecordingNextIndex overflowed (uint32_t) michael@0: // and stopped after the overflow (would happen once every 828 days of constant 60fps). michael@0: length = 0; michael@0: } michael@0: michael@0: if (!length) { michael@0: aFrameIntervals.Clear(); michael@0: return; // empty recording, return empty arrays. michael@0: } michael@0: // Set length in advance to avoid possibly repeated reallocations michael@0: aFrameIntervals.SetLength(length); michael@0: michael@0: uint32_t cyclicPos = aStartIndex % bufferSize; michael@0: for (uint32_t i = 0; i < length; i++, cyclicPos++) { michael@0: if (cyclicPos == bufferSize) { michael@0: cyclicPos = 0; michael@0: } michael@0: aFrameIntervals[i] = mRecording.mIntervals[cyclicPos]; michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManager::BeginTabSwitch() michael@0: { michael@0: mTabSwitchStart = TimeStamp::Now(); michael@0: } michael@0: michael@0: static nsACString& PrintInfo(nsACString& aTo, LayerComposite* aLayerComposite); michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: template michael@0: void WriteSnapshotLinkToDumpFile(T* aObj, FILE* aFile) michael@0: { michael@0: if (!aObj) { michael@0: return; michael@0: } michael@0: nsCString string(aObj->Name()); michael@0: string.Append("-"); michael@0: string.AppendInt((uint64_t)aObj); michael@0: fprintf_stderr(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading()); michael@0: } michael@0: michael@0: template michael@0: void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf) michael@0: { michael@0: nsRefPtr deprecatedSurf = michael@0: new gfxImageSurface(aSurf->GetData(), michael@0: ThebesIntSize(aSurf->GetSize()), michael@0: aSurf->Stride(), michael@0: SurfaceFormatToImageFormat(aSurf->GetFormat())); michael@0: nsCString string(aObj->Name()); michael@0: string.Append("-"); michael@0: string.AppendInt((uint64_t)aObj); michael@0: if (gfxUtils::sDumpPaintFile) { michael@0: fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); michael@0: } michael@0: deprecatedSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); michael@0: if (gfxUtils::sDumpPaintFile) { michael@0: fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); michael@0: } michael@0: } michael@0: michael@0: void WriteSnapshotToDumpFile(Layer* aLayer, DataSourceSurface* aSurf) michael@0: { michael@0: WriteSnapshotToDumpFile_internal(aLayer, aSurf); michael@0: } michael@0: michael@0: void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf) michael@0: { michael@0: WriteSnapshotToDumpFile_internal(aManager, aSurf); michael@0: } michael@0: michael@0: void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget) michael@0: { michael@0: RefPtr surf = aTarget->Snapshot(); michael@0: RefPtr dSurf = surf->GetDataSurface(); michael@0: WriteSnapshotToDumpFile_internal(aCompositor, dSurf); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) michael@0: { michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, "
  • "); michael@0: } michael@0: DumpSelf(aFile, aPrefix); michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) { michael@0: AsLayerComposite()->GetCompositableHost()->Dump(aFile, aPrefix, aDumpHtml); michael@0: } michael@0: #endif michael@0: michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, ""); michael@0: } michael@0: michael@0: if (Layer* mask = GetMaskLayer()) { michael@0: fprintf_stderr(aFile, "%s Mask layer:\n", aPrefix); michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " "; michael@0: mask->Dump(aFile, pfx.get(), aDumpHtml); michael@0: } michael@0: michael@0: if (Layer* kid = GetFirstChild()) { michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " "; michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, "
      "); michael@0: } michael@0: kid->Dump(aFile, pfx.get(), aDumpHtml); michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, "
    "); michael@0: } michael@0: } michael@0: michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, "
  • "); michael@0: } michael@0: if (Layer* next = GetNextSibling()) michael@0: next->Dump(aFile, aPrefix, aDumpHtml); michael@0: } michael@0: michael@0: void michael@0: Layer::DumpSelf(FILE* aFile, const char* aPrefix) michael@0: { michael@0: nsAutoCString str; michael@0: PrintInfo(str, aPrefix); michael@0: fprintf_stderr(aFile, "%s\n", str.get()); michael@0: } michael@0: michael@0: void michael@0: Layer::Log(const char* aPrefix) michael@0: { michael@0: if (!IsLogEnabled()) michael@0: return; michael@0: michael@0: LogSelf(aPrefix); michael@0: michael@0: if (Layer* kid = GetFirstChild()) { michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " "; michael@0: kid->Log(pfx.get()); michael@0: } michael@0: michael@0: if (Layer* next = GetNextSibling()) michael@0: next->Log(aPrefix); michael@0: } michael@0: michael@0: void michael@0: Layer::LogSelf(const char* aPrefix) michael@0: { michael@0: if (!IsLogEnabled()) michael@0: return; michael@0: michael@0: nsAutoCString str; michael@0: PrintInfo(str, aPrefix); michael@0: MOZ_LAYERS_LOG(("%s", str.get())); michael@0: michael@0: if (mMaskLayer) { michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " \\ MaskLayer "; michael@0: mMaskLayer->LogSelf(pfx.get()); michael@0: } michael@0: } michael@0: michael@0: nsACString& michael@0: Layer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: aTo += aPrefix; michael@0: aTo += nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this); michael@0: michael@0: ::PrintInfo(aTo, AsLayerComposite()); michael@0: michael@0: if (mUseClipRect) { michael@0: AppendToString(aTo, mClipRect, " [clip=", "]"); michael@0: } michael@0: if (1.0 != mPostXScale || 1.0 != mPostYScale) { michael@0: aTo.AppendPrintf(" [postScale=%g, %g]", mPostXScale, mPostYScale); michael@0: } michael@0: if (!mTransform.IsIdentity()) { michael@0: AppendToString(aTo, mTransform, " [transform=", "]"); michael@0: } michael@0: if (!mVisibleRegion.IsEmpty()) { michael@0: AppendToString(aTo, mVisibleRegion, " [visible=", "]"); michael@0: } else { michael@0: aTo += " [not visible]"; michael@0: } michael@0: if (!mEventRegions.mHitRegion.IsEmpty()) { michael@0: AppendToString(aTo, mEventRegions.mHitRegion, " [hitregion=", "]"); michael@0: } michael@0: if (!mEventRegions.mDispatchToContentHitRegion.IsEmpty()) { michael@0: AppendToString(aTo, mEventRegions.mDispatchToContentHitRegion, " [dispatchtocontentregion=", "]"); michael@0: } michael@0: if (1.0 != mOpacity) { michael@0: aTo.AppendPrintf(" [opacity=%g]", mOpacity); michael@0: } michael@0: if (GetContentFlags() & CONTENT_OPAQUE) { michael@0: aTo += " [opaqueContent]"; michael@0: } michael@0: if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) { michael@0: aTo += " [componentAlpha]"; michael@0: } michael@0: if (GetScrollbarDirection() == VERTICAL) { michael@0: aTo.AppendPrintf(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()); michael@0: } michael@0: if (GetScrollbarDirection() == HORIZONTAL) { michael@0: aTo.AppendPrintf(" [hscrollbar=%lld]", GetScrollbarTargetContainerId()); michael@0: } michael@0: if (GetIsFixedPosition()) { michael@0: aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f margin=%f,%f,%f,%f]", mAnchor.x, mAnchor.y, michael@0: mMargins.top, mMargins.right, mMargins.bottom, mMargins.left); michael@0: } michael@0: if (GetIsStickyPosition()) { michael@0: aTo.AppendPrintf(" [isStickyPosition scrollId=%d outer=%f,%f %fx%f " michael@0: "inner=%f,%f %fx%f]", mStickyPositionData->mScrollId, michael@0: mStickyPositionData->mOuter.x, mStickyPositionData->mOuter.y, michael@0: mStickyPositionData->mOuter.width, mStickyPositionData->mOuter.height, michael@0: mStickyPositionData->mInner.x, mStickyPositionData->mInner.y, michael@0: mStickyPositionData->mInner.width, mStickyPositionData->mInner.height); michael@0: } michael@0: if (mMaskLayer) { michael@0: aTo.AppendPrintf(" [mMaskLayer=%p]", mMaskLayer.get()); michael@0: } michael@0: michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: ThebesLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: if (!mValidRegion.IsEmpty()) { michael@0: AppendToString(aTo, mValidRegion, " [valid=", "]"); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: ContainerLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: if (!mFrameMetrics.IsDefault()) { michael@0: AppendToString(aTo, mFrameMetrics, " [metrics=", "]"); michael@0: } michael@0: if (mScrollHandoffParentId != FrameMetrics::NULL_SCROLL_ID) { michael@0: aTo.AppendPrintf(" [scrollParent=%llu]", mScrollHandoffParentId); michael@0: } michael@0: if (UseIntermediateSurface()) { michael@0: aTo += " [usesTmpSurf]"; michael@0: } michael@0: if (1.0 != mPreXScale || 1.0 != mPreYScale) { michael@0: aTo.AppendPrintf(" [preScale=%g, %g]", mPreXScale, mPreYScale); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: ColorLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: AppendToString(aTo, mColor, " [color=", "]"); michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: CanvasLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: if (mFilter != GraphicsFilter::FILTER_GOOD) { michael@0: AppendToString(aTo, mFilter, " [filter=", "]"); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: if (mFilter != GraphicsFilter::FILTER_GOOD) { michael@0: AppendToString(aTo, mFilter, " [filter=", "]"); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: RefLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: ContainerLayer::PrintInfo(aTo, aPrefix); michael@0: if (0 != mId) { michael@0: AppendToString(aTo, mId, " [id=", "]"); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: nsACString& michael@0: ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: Layer::PrintInfo(aTo, aPrefix); michael@0: AppendToString(aTo, mSize, " [size=", "]"); michael@0: if (mBackgroundLayer) { michael@0: AppendToString(aTo, mBackgroundLayer, " [backgroundLayer=", "]"); michael@0: AppendToString(aTo, mBackgroundLayerOffset, " [backgroundOffset=", "]"); michael@0: } else if (mBackgroundColor.a == 1.0) { michael@0: AppendToString(aTo, mBackgroundColor, " [backgroundColor=", "]"); michael@0: } else { michael@0: aTo += " [nobackground]"; michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: //-------------------------------------------------- michael@0: // LayerManager michael@0: michael@0: void michael@0: LayerManager::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) michael@0: { michael@0: FILE* file = FILEOrDefault(aFile); michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(file, ""); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(file, "
      "); michael@0: } michael@0: GetRoot()->Dump(file, pfx.get(), aDumpHtml); michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(file, "
    "); michael@0: } michael@0: fprintf_stderr(file, "\n"); michael@0: } michael@0: michael@0: void michael@0: LayerManager::DumpSelf(FILE* aFile, const char* aPrefix) michael@0: { michael@0: nsAutoCString str; michael@0: PrintInfo(str, aPrefix); michael@0: fprintf_stderr(FILEOrDefault(aFile), "%s\n", str.get()); michael@0: } michael@0: michael@0: void michael@0: LayerManager::Log(const char* aPrefix) michael@0: { michael@0: if (!IsLogEnabled()) michael@0: return; michael@0: michael@0: LogSelf(aPrefix); michael@0: michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " "; michael@0: if (!GetRoot()) { michael@0: MOZ_LAYERS_LOG(("%s(null)", pfx.get())); michael@0: return; michael@0: } michael@0: michael@0: GetRoot()->Log(pfx.get()); michael@0: } michael@0: michael@0: void michael@0: LayerManager::LogSelf(const char* aPrefix) michael@0: { michael@0: nsAutoCString str; michael@0: PrintInfo(str, aPrefix); michael@0: MOZ_LAYERS_LOG(("%s", str.get())); michael@0: } michael@0: michael@0: nsACString& michael@0: LayerManager::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: aTo += aPrefix; michael@0: return aTo += nsPrintfCString("%sLayerManager (0x%p)", Name(), this); michael@0: } michael@0: michael@0: /*static*/ void michael@0: LayerManager::InitLog() michael@0: { michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("Layers"); michael@0: } michael@0: michael@0: /*static*/ bool michael@0: LayerManager::IsLogEnabled() michael@0: { michael@0: NS_ABORT_IF_FALSE(!!sLog, michael@0: "layer manager must be created before logging is allowed"); michael@0: return PR_LOG_TEST(sLog, PR_LOG_DEBUG); michael@0: } michael@0: michael@0: static nsACString& michael@0: PrintInfo(nsACString& aTo, LayerComposite* aLayerComposite) michael@0: { michael@0: if (!aLayerComposite) { michael@0: return aTo; michael@0: } michael@0: if (const nsIntRect* clipRect = aLayerComposite->GetShadowClipRect()) { michael@0: AppendToString(aTo, *clipRect, " [shadow-clip=", "]"); michael@0: } michael@0: if (!aLayerComposite->GetShadowTransform().IsIdentity()) { michael@0: AppendToString(aTo, aLayerComposite->GetShadowTransform(), " [shadow-transform=", "]"); michael@0: } michael@0: if (!aLayerComposite->GetShadowVisibleRegion().IsEmpty()) { michael@0: AppendToString(aTo, aLayerComposite->GetShadowVisibleRegion(), " [shadow-visible=", "]"); michael@0: } michael@0: return aTo; michael@0: } michael@0: michael@0: void michael@0: SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget) michael@0: { michael@0: bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA); michael@0: if (aTarget->GetFormat() != SurfaceFormat::B8G8R8A8) { michael@0: aTarget->SetPermitSubpixelAA(permitSubpixelAA); michael@0: return; michael@0: } michael@0: michael@0: const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds(); michael@0: gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y), michael@0: Float(bounds.width), Float(bounds.height))); michael@0: transformedBounds.RoundOut(); michael@0: IntRect intTransformedBounds; michael@0: transformedBounds.ToIntRect(&intTransformedBounds); michael@0: permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || michael@0: aTarget->GetOpaqueRect().Contains(intTransformedBounds); michael@0: aTarget->SetPermitSubpixelAA(permitSubpixelAA); michael@0: } michael@0: michael@0: void michael@0: SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget) michael@0: { michael@0: if (!aTarget->IsCairo()) { michael@0: SetAntialiasingFlags(aLayer, aTarget->GetDrawTarget()); michael@0: return; michael@0: } michael@0: michael@0: bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA); michael@0: nsRefPtr surface = aTarget->CurrentSurface(); michael@0: if (surface->GetContentType() != gfxContentType::COLOR_ALPHA) { michael@0: // Destination doesn't have alpha channel; no need to set any special flags michael@0: surface->SetSubpixelAntialiasingEnabled(permitSubpixelAA); michael@0: return; michael@0: } michael@0: michael@0: const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds(); michael@0: permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || michael@0: surface->GetOpaqueRect().Contains( michael@0: aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))); michael@0: surface->SetSubpixelAntialiasingEnabled(permitSubpixelAA); michael@0: } michael@0: michael@0: PRLogModuleInfo* LayerManager::sLog; michael@0: michael@0: } // namespace layers michael@0: } // namespace mozilla