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 "LayerManagerComposite.h" michael@0: #include // for size_t michael@0: #include // for uint16_t, uint32_t michael@0: #include "CanvasLayerComposite.h" // for CanvasLayerComposite michael@0: #include "ColorLayerComposite.h" // for ColorLayerComposite michael@0: #include "Composer2D.h" // for Composer2D michael@0: #include "CompositableHost.h" // for CompositableHost michael@0: #include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc michael@0: #include "FPSCounter.h" // for FPSState, FPSCounter michael@0: #include "FrameMetrics.h" // for FrameMetrics michael@0: #include "GeckoProfiler.h" // for profiler_set_frame_number, etc michael@0: #include "ImageLayerComposite.h" // for ImageLayerComposite michael@0: #include "Layers.h" // for Layer, ContainerLayer, etc michael@0: #include "ThebesLayerComposite.h" // for ThebesLayerComposite michael@0: #include "TiledLayerBuffer.h" // for TiledLayerComposer michael@0: #include "Units.h" // for ScreenIntRect michael@0: #include "gfx2DGlue.h" // for ToMatrix4x4 michael@0: #include "gfx3DMatrix.h" // for gfx3DMatrix michael@0: #include "gfxPrefs.h" // for gfxPrefs michael@0: #ifdef XP_MACOSX michael@0: #include "gfxPlatformMac.h" michael@0: #endif michael@0: #include "gfxRect.h" // for gfxRect michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/RefPtr.h" // for RefPtr, TemporaryRef michael@0: #include "mozilla/gfx/2D.h" // for DrawTarget michael@0: #include "mozilla/gfx/Matrix.h" // for Matrix4x4 michael@0: #include "mozilla/gfx/Point.h" // for IntSize, Point michael@0: #include "mozilla/gfx/Rect.h" // for Rect michael@0: #include "mozilla/gfx/Types.h" // for Color, SurfaceFormat michael@0: #include "mozilla/layers/Compositor.h" // for Compositor michael@0: #include "mozilla/layers/CompositorTypes.h" michael@0: #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc michael@0: #include "mozilla/layers/LayersTypes.h" // for etc michael@0: #include "ipc/ShadowLayerUtils.h" michael@0: #include "mozilla/mozalloc.h" // for operator new, etc michael@0: #include "nsAutoPtr.h" // for nsRefPtr michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc michael@0: #include "nsISupportsImpl.h" // for Layer::AddRef, etc michael@0: #include "nsIWidget.h" // for nsIWidget michael@0: #include "nsPoint.h" // for nsIntPoint michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsRegion.h" // for nsIntRegion, etc michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include michael@0: #endif michael@0: #include "GeckoProfiler.h" michael@0: #include "TextRenderer.h" // for TextRenderer michael@0: michael@0: class gfxContext; michael@0: struct nsIntSize; michael@0: michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: class ImageLayer; michael@0: michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::gl; michael@0: michael@0: static LayerComposite* michael@0: ToLayerComposite(Layer* aLayer) michael@0: { michael@0: return static_cast(aLayer->ImplData()); michael@0: } michael@0: michael@0: static void ClearSubtree(Layer* aLayer) michael@0: { michael@0: ToLayerComposite(aLayer)->CleanupResources(); michael@0: for (Layer* child = aLayer->GetFirstChild(); child; michael@0: child = child->GetNextSibling()) { michael@0: ClearSubtree(child); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::ClearCachedResources(Layer* aSubtree) michael@0: { michael@0: MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this); michael@0: Layer* subtree = aSubtree ? aSubtree : mRoot.get(); michael@0: if (!subtree) { michael@0: return; michael@0: } michael@0: michael@0: ClearSubtree(subtree); michael@0: // FIXME [bjacob] michael@0: // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here. michael@0: // Do we need that? michael@0: } michael@0: michael@0: /** michael@0: * LayerManagerComposite michael@0: */ michael@0: LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor) michael@0: : mCompositor(aCompositor) michael@0: , mInTransaction(false) michael@0: , mIsCompositorReady(false) michael@0: , mDebugOverlayWantsNextFrame(false) michael@0: , mGeometryChanged(true) michael@0: { michael@0: mTextRenderer = new TextRenderer(aCompositor); michael@0: MOZ_ASSERT(aCompositor); michael@0: } michael@0: michael@0: LayerManagerComposite::~LayerManagerComposite() michael@0: { michael@0: Destroy(); michael@0: } michael@0: michael@0: michael@0: bool michael@0: LayerManagerComposite::Initialize() michael@0: { michael@0: bool result = mCompositor->Initialize(); michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::Destroy() michael@0: { michael@0: if (!mDestroyed) { michael@0: mCompositor->GetWidget()->CleanupWindowEffects(); michael@0: if (mRoot) { michael@0: RootLayer()->Destroy(); michael@0: } michael@0: mRoot = nullptr; michael@0: michael@0: mCompositor->Destroy(); michael@0: michael@0: mDestroyed = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect) michael@0: { michael@0: mRenderBounds = aRect; michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::BeginTransaction() michael@0: { michael@0: mInTransaction = true; michael@0: michael@0: if (!mCompositor->Ready()) { michael@0: return; michael@0: } michael@0: michael@0: mIsCompositorReady = true; michael@0: michael@0: if (Compositor::GetBackend() == LayersBackend::LAYERS_OPENGL || michael@0: Compositor::GetBackend() == LayersBackend::LAYERS_BASIC) { michael@0: mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget) michael@0: { michael@0: mInTransaction = true; michael@0: michael@0: if (!mCompositor->Ready()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef MOZ_LAYERS_HAVE_LOG michael@0: MOZ_LAYERS_LOG(("[----- BeginTransaction")); michael@0: Log(); michael@0: #endif michael@0: michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return; michael@0: } michael@0: michael@0: mIsCompositorReady = true; michael@0: mCompositor->SetTargetContext(aTarget); michael@0: mTarget = aTarget; michael@0: } michael@0: michael@0: bool michael@0: LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags) michael@0: { michael@0: NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?"); michael@0: if (!mRoot) { michael@0: mInTransaction = false; michael@0: mIsCompositorReady = false; michael@0: return false; michael@0: } michael@0: michael@0: EndTransaction(nullptr, nullptr); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::EndTransaction(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags aFlags) michael@0: { michael@0: NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?"); michael@0: NS_ASSERTION(!aCallback && !aCallbackData, "Not expecting callbacks here"); michael@0: mInTransaction = false; michael@0: michael@0: if (!mIsCompositorReady) { michael@0: return; michael@0: } michael@0: mIsCompositorReady = false; michael@0: michael@0: #ifdef MOZ_LAYERS_HAVE_LOG michael@0: MOZ_LAYERS_LOG((" ----- (beginning paint)")); michael@0: Log(); michael@0: #endif michael@0: michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return; michael@0: } michael@0: michael@0: if (mRoot && mClonedLayerTreeProperties) { michael@0: nsIntRegion invalid = michael@0: mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged); michael@0: mClonedLayerTreeProperties = nullptr; michael@0: michael@0: mInvalidRegion.Or(mInvalidRegion, invalid); michael@0: } else { michael@0: mInvalidRegion.Or(mInvalidRegion, mRenderBounds); michael@0: } michael@0: michael@0: if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { michael@0: if (aFlags & END_NO_COMPOSITE) { michael@0: // Apply pending tree updates before recomputing effective michael@0: // properties. michael@0: mRoot->ApplyPendingUpdatesToSubtree(); michael@0: } michael@0: michael@0: // The results of our drawing always go directly into a pixel buffer, michael@0: // so we don't need to pass any global transform here. michael@0: mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); michael@0: michael@0: Render(); michael@0: mGeometryChanged = false; michael@0: } michael@0: michael@0: mCompositor->SetTargetContext(nullptr); michael@0: mTarget = nullptr; michael@0: michael@0: #ifdef MOZ_LAYERS_HAVE_LOG michael@0: Log(); michael@0: MOZ_LAYERS_LOG(("]----- EndTransaction")); michael@0: #endif michael@0: } michael@0: michael@0: TemporaryRef michael@0: LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize) michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateThebesLayer() michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateContainerLayer() michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateImageLayer() michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateColorLayer() michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateCanvasLayer() michael@0: { michael@0: NS_RUNTIMEABORT("Should only be called on the drawing side"); michael@0: return nullptr; michael@0: } michael@0: michael@0: LayerComposite* michael@0: LayerManagerComposite::RootLayer() const michael@0: { michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return ToLayerComposite(mRoot); michael@0: } michael@0: michael@0: // Size of the builtin font. michael@0: static const float FontHeight = 7.f; michael@0: static const float FontWidth = 4.f; michael@0: static const float FontStride = 4.f; michael@0: michael@0: // Scale the font when drawing it to the viewport for better readability. michael@0: static const float FontScaleX = 2.f; michael@0: static const float FontScaleY = 3.f; michael@0: michael@0: static void DrawDigits(unsigned int aValue, michael@0: int aOffsetX, int aOffsetY, michael@0: Compositor* aCompositor, michael@0: EffectChain& aEffectChain) michael@0: { michael@0: if (aValue > 999) { michael@0: aValue = 999; michael@0: } michael@0: michael@0: unsigned int divisor = 100; michael@0: float textureWidth = FontWidth * 10; michael@0: gfx::Float opacity = 1; michael@0: gfx::Matrix4x4 transform; michael@0: transform.Scale(FontScaleX, FontScaleY, 1); michael@0: michael@0: for (size_t n = 0; n < 3; ++n) { michael@0: unsigned int digit = aValue % (divisor * 10) / divisor; michael@0: divisor /= 10; michael@0: michael@0: RefPtr texturedEffect = static_cast(aEffectChain.mPrimaryEffect.get()); michael@0: texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f); michael@0: michael@0: Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight); michael@0: Rect clipRect = Rect(0, 0, 300, 100); michael@0: aCompositor->DrawQuad(drawRect, clipRect, michael@0: aEffectChain, opacity, transform); michael@0: } michael@0: } michael@0: michael@0: void FPSState::DrawFPS(TimeStamp aNow, michael@0: unsigned int aFillRatio, michael@0: Compositor* aCompositor) michael@0: { michael@0: if (!mFPSTextureSource) { michael@0: const char *text = michael@0: " " michael@0: " XXX XX XXX XXX X X XXX XXX XXX XXX XXX" michael@0: " X X X X X X X X X X X X X X" michael@0: " X X X XXX XXX XXX XXX XXX X XXX XXX" michael@0: " X X X X X X X X X X X X X" michael@0: " XXX XXX XXX XXX X XXX XXX X XXX X" michael@0: " "; michael@0: michael@0: // Convert the text encoding above to RGBA. michael@0: int w = FontWidth * 10; michael@0: int h = FontHeight; michael@0: uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t)); michael@0: for (int i = 0; i < h; i++) { michael@0: for (int j = 0; j < w; j++) { michael@0: uint32_t purple = 0xfff000ff; michael@0: uint32_t white = 0xffffffff; michael@0: buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white; michael@0: } michael@0: } michael@0: michael@0: int bytesPerPixel = 4; michael@0: RefPtr fpsSurface = Factory::CreateWrappingDataSourceSurface( michael@0: reinterpret_cast(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8); michael@0: mFPSTextureSource = aCompositor->CreateDataTextureSource(); michael@0: mFPSTextureSource->Update(fpsSurface); michael@0: } michael@0: michael@0: EffectChain effectChain; michael@0: effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mFPSTextureSource, Filter::POINT); michael@0: michael@0: unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow)); michael@0: unsigned int txnFps = unsigned(mTransactionFps.GetFpsAt(aNow)); michael@0: michael@0: DrawDigits(fps, 0, 0, aCompositor, effectChain); michael@0: DrawDigits(txnFps, FontWidth * 4, 0, aCompositor, effectChain); michael@0: DrawDigits(aFillRatio, FontWidth * 8, 0, aCompositor, effectChain); michael@0: } michael@0: michael@0: static uint16_t sFrameCount = 0; michael@0: void michael@0: LayerManagerComposite::RenderDebugOverlay(const Rect& aBounds) michael@0: { michael@0: if (gfxPrefs::LayersDrawFPS()) { michael@0: if (!mFPS) { michael@0: mFPS = new FPSState(); michael@0: } michael@0: michael@0: float fillRatio = mCompositor->GetFillRatio(); michael@0: mFPS->DrawFPS(TimeStamp::Now(), unsigned(fillRatio), mCompositor); michael@0: } else { michael@0: mFPS = nullptr; michael@0: } michael@0: michael@0: if (gfxPrefs::DrawFrameCounter()) { michael@0: profiler_set_frame_number(sFrameCount); michael@0: michael@0: uint16_t frameNumber = sFrameCount; michael@0: const uint16_t bitWidth = 3; michael@0: float opacity = 1.0; michael@0: gfx::Rect clip(0,0, bitWidth*16, bitWidth); michael@0: for (size_t i = 0; i < 16; i++) { michael@0: michael@0: gfx::Color bitColor; michael@0: if ((frameNumber >> i) & 0x1) { michael@0: bitColor = gfx::Color(0, 0, 0, 1.0); michael@0: } else { michael@0: bitColor = gfx::Color(1.0, 1.0, 1.0, 1.0); michael@0: } michael@0: EffectChain effects; michael@0: effects.mPrimaryEffect = new EffectSolidColor(bitColor); michael@0: mCompositor->DrawQuad(gfx::Rect(bitWidth*i, 0, bitWidth, bitWidth), michael@0: clip, michael@0: effects, michael@0: opacity, michael@0: gfx::Matrix4x4()); michael@0: } michael@0: // We intentionally overflow at 2^16. michael@0: sFrameCount++; michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::Render() michael@0: { michael@0: PROFILER_LABEL("LayerManagerComposite", "Render"); michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return; michael@0: } michael@0: michael@0: if (gfxPrefs::LayersDump()) { michael@0: this->Dump(); michael@0: } michael@0: michael@0: /** Our more efficient but less powerful alter ego, if one is available. */ michael@0: nsRefPtr composer2D = mCompositor->GetWidget()->GetComposer2D(); michael@0: michael@0: if (!mTarget && composer2D && composer2D->TryRender(mRoot, mWorldMatrix, mGeometryChanged)) { michael@0: if (mFPS) { michael@0: double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now()); michael@0: if (gfxPrefs::LayersDrawFPS()) { michael@0: printf_stderr("HWComposer: FPS is %g\n", fps); michael@0: } michael@0: } michael@0: mCompositor->EndFrameForExternalComposition(mWorldMatrix); michael@0: return; michael@0: } michael@0: michael@0: { michael@0: PROFILER_LABEL("LayerManagerComposite", "PreRender"); michael@0: if (!mCompositor->GetWidget()->PreRender(this)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: nsIntRect clipRect; michael@0: Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); michael@0: Rect actualBounds; michael@0: if (mRoot->GetClipRect()) { michael@0: clipRect = *mRoot->GetClipRect(); michael@0: WorldTransformRect(clipRect); michael@0: Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); michael@0: mCompositor->BeginFrame(mInvalidRegion, &rect, mWorldMatrix, bounds, nullptr, &actualBounds); michael@0: } else { michael@0: gfx::Rect rect; michael@0: mCompositor->BeginFrame(mInvalidRegion, nullptr, mWorldMatrix, bounds, &rect, &actualBounds); michael@0: clipRect = nsIntRect(rect.x, rect.y, rect.width, rect.height); michael@0: } michael@0: michael@0: // Reset the invalid region now that we've begun compositing. michael@0: mInvalidRegion.SetEmpty(); michael@0: michael@0: if (actualBounds.IsEmpty()) { michael@0: mCompositor->GetWidget()->PostRender(this); michael@0: return; michael@0: } michael@0: michael@0: // Allow widget to render a custom background. michael@0: mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x, michael@0: actualBounds.y, michael@0: actualBounds.width, michael@0: actualBounds.height)); michael@0: michael@0: // Render our layers. michael@0: RootLayer()->RenderLayer(clipRect); michael@0: michael@0: if (!mRegionToClear.IsEmpty()) { michael@0: nsIntRegionRectIterator iter(mRegionToClear); michael@0: const nsIntRect *r; michael@0: while ((r = iter.Next())) { michael@0: mCompositor->ClearRect(Rect(r->x, r->y, r->width, r->height)); michael@0: } michael@0: } michael@0: michael@0: // Allow widget to render a custom foreground. michael@0: mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x, michael@0: actualBounds.y, michael@0: actualBounds.width, michael@0: actualBounds.height)); michael@0: michael@0: // Debugging michael@0: RenderDebugOverlay(actualBounds); michael@0: michael@0: { michael@0: PROFILER_LABEL("LayerManagerComposite", "EndFrame"); michael@0: mCompositor->EndFrame(); michael@0: mCompositor->SetFBAcquireFence(mRoot); michael@0: } michael@0: michael@0: mCompositor->GetWidget()->PostRender(this); michael@0: michael@0: RecordFrame(); michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::SetWorldTransform(const gfx::Matrix& aMatrix) michael@0: { michael@0: NS_ASSERTION(aMatrix.PreservesAxisAlignedRectangles(), michael@0: "SetWorldTransform only accepts matrices that satisfy PreservesAxisAlignedRectangles"); michael@0: NS_ASSERTION(!aMatrix.HasNonIntegerScale(), michael@0: "SetWorldTransform only accepts matrices with integer scale"); michael@0: michael@0: mWorldMatrix = aMatrix; michael@0: } michael@0: michael@0: gfx::Matrix& michael@0: LayerManagerComposite::GetWorldTransform(void) michael@0: { michael@0: return mWorldMatrix; michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::WorldTransformRect(nsIntRect& aRect) michael@0: { michael@0: gfx::Rect grect(aRect.x, aRect.y, aRect.width, aRect.height); michael@0: grect = mWorldMatrix.TransformBounds(grect); michael@0: aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height()); michael@0: } michael@0: michael@0: static void michael@0: SubtractTransformedRegion(nsIntRegion& aRegion, michael@0: const nsIntRegion& aRegionToSubtract, michael@0: const gfx3DMatrix& aTransform) michael@0: { michael@0: if (aRegionToSubtract.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: // For each rect in the region, find out its bounds in screen space and michael@0: // subtract it from the screen region. michael@0: nsIntRegionRectIterator it(aRegionToSubtract); michael@0: while (const nsIntRect* rect = it.Next()) { michael@0: gfxRect incompleteRect = aTransform.TransformBounds(gfxRect(*rect)); michael@0: aRegion.Sub(aRegion, nsIntRect(incompleteRect.x, michael@0: incompleteRect.y, michael@0: incompleteRect.width, michael@0: incompleteRect.height)); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer, michael@0: nsIntRegion& aScreenRegion, michael@0: nsIntRegion& aLowPrecisionScreenRegion, michael@0: const gfx3DMatrix& aTransform) michael@0: { michael@0: if (aLayer->GetOpacity() <= 0.f || michael@0: (aScreenRegion.IsEmpty() && aLowPrecisionScreenRegion.IsEmpty())) { michael@0: return; michael@0: } michael@0: michael@0: // If the layer's a container, recurse into all of its children michael@0: ContainerLayer* container = aLayer->AsContainerLayer(); michael@0: if (container) { michael@0: // Accumulate the transform of intermediate surfaces michael@0: gfx3DMatrix transform = aTransform; michael@0: if (container->UseIntermediateSurface()) { michael@0: gfx::To3DMatrix(aLayer->GetEffectiveTransform(), transform); michael@0: transform.PreMultiply(aTransform); michael@0: } michael@0: for (Layer* child = aLayer->GetFirstChild(); child; michael@0: child = child->GetNextSibling()) { michael@0: ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // Only thebes layers can be incomplete michael@0: ThebesLayer* thebesLayer = aLayer->AsThebesLayer(); michael@0: if (!thebesLayer) { michael@0: return; michael@0: } michael@0: michael@0: // See if there's any incomplete rendering michael@0: nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion(); michael@0: incompleteRegion.Sub(incompleteRegion, thebesLayer->GetValidRegion()); michael@0: michael@0: if (!incompleteRegion.IsEmpty()) { michael@0: // Calculate the transform to get between screen and layer space michael@0: gfx3DMatrix transformToScreen; michael@0: To3DMatrix(aLayer->GetEffectiveTransform(), transformToScreen); michael@0: transformToScreen.PreMultiply(aTransform); michael@0: michael@0: SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen); michael@0: michael@0: // See if there's any incomplete low-precision rendering michael@0: TiledLayerComposer* composer = nullptr; michael@0: LayerComposite* shadow = aLayer->AsLayerComposite(); michael@0: if (shadow) { michael@0: composer = shadow->GetTiledLayerComposer(); michael@0: if (composer) { michael@0: incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion()); michael@0: if (!incompleteRegion.IsEmpty()) { michael@0: SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // If we can't get a valid low precision region, assume it's the same as michael@0: // the high precision region. michael@0: if (!composer) { michael@0: SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_ANDROID_OMTC michael@0: static float michael@0: GetDisplayportCoverage(const CSSRect& aDisplayPort, michael@0: const gfx3DMatrix& aTransformToScreen, michael@0: const nsIntRect& aScreenRect) michael@0: { michael@0: gfxRect transformedDisplayport = michael@0: aTransformToScreen.TransformBounds(gfxRect(aDisplayPort.x, michael@0: aDisplayPort.y, michael@0: aDisplayPort.width, michael@0: aDisplayPort.height)); michael@0: transformedDisplayport.RoundOut(); michael@0: nsIntRect displayport = nsIntRect(transformedDisplayport.x, michael@0: transformedDisplayport.y, michael@0: transformedDisplayport.width, michael@0: transformedDisplayport.height); michael@0: if (!displayport.Contains(aScreenRect)) { michael@0: nsIntRegion coveredRegion; michael@0: coveredRegion.And(aScreenRect, displayport); michael@0: return coveredRegion.Area() / (float)(aScreenRect.width * aScreenRect.height); michael@0: } michael@0: michael@0: return 1.0f; michael@0: } michael@0: #endif // MOZ_ANDROID_OMTC michael@0: michael@0: float michael@0: LayerManagerComposite::ComputeRenderIntegrity() michael@0: { michael@0: // We only ever have incomplete rendering when progressive tiles are enabled. michael@0: Layer* root = GetRoot(); michael@0: if (!gfxPrefs::UseProgressiveTilePainting() || !root) { michael@0: return 1.f; michael@0: } michael@0: michael@0: const FrameMetrics& rootMetrics = root->AsContainerLayer()->GetFrameMetrics(); michael@0: nsIntRect screenRect(rootMetrics.mCompositionBounds.x, michael@0: rootMetrics.mCompositionBounds.y, michael@0: rootMetrics.mCompositionBounds.width, michael@0: rootMetrics.mCompositionBounds.height); michael@0: michael@0: float lowPrecisionMultiplier = 1.0f; michael@0: float highPrecisionMultiplier = 1.0f; michael@0: michael@0: #ifdef MOZ_ANDROID_OMTC michael@0: // Use the transform on the primary scrollable layer and its FrameMetrics michael@0: // to find out how much of the viewport the current displayport covers michael@0: Layer* primaryScrollable = GetPrimaryScrollableLayer(); michael@0: if (primaryScrollable) { michael@0: // This is derived from the code in michael@0: // AsyncCompositionManager::TransformScrollableLayer michael@0: const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); michael@0: gfx3DMatrix transform; michael@0: gfx::To3DMatrix(primaryScrollable->GetEffectiveTransform(), transform); michael@0: transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1); michael@0: michael@0: // Clip the screen rect to the document bounds michael@0: gfxRect documentBounds = michael@0: transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x, michael@0: metrics.mScrollableRect.y - metrics.GetScrollOffset().y, michael@0: metrics.mScrollableRect.width, michael@0: metrics.mScrollableRect.height)); michael@0: documentBounds.RoundOut(); michael@0: screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y, michael@0: documentBounds.width, documentBounds.height)); michael@0: michael@0: // If the screen rect is empty, the user has scrolled entirely into michael@0: // over-scroll and so we can be considered to have full integrity. michael@0: if (screenRect.IsEmpty()) { michael@0: return 1.0f; michael@0: } michael@0: michael@0: // Work out how much of the critical display-port covers the screen michael@0: bool hasLowPrecision = false; michael@0: if (!metrics.mCriticalDisplayPort.IsEmpty()) { michael@0: hasLowPrecision = true; michael@0: highPrecisionMultiplier = michael@0: GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect); michael@0: } michael@0: michael@0: // Work out how much of the display-port covers the screen michael@0: if (!metrics.mDisplayPort.IsEmpty()) { michael@0: if (hasLowPrecision) { michael@0: lowPrecisionMultiplier = michael@0: GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); michael@0: } else { michael@0: lowPrecisionMultiplier = highPrecisionMultiplier = michael@0: GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // If none of the screen is covered, we have zero integrity. michael@0: if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) { michael@0: return 0.0f; michael@0: } michael@0: #endif // MOZ_ANDROID_OMTC michael@0: michael@0: nsIntRegion screenRegion(screenRect); michael@0: nsIntRegion lowPrecisionScreenRegion(screenRect); michael@0: gfx3DMatrix transform; michael@0: ComputeRenderIntegrityInternal(root, screenRegion, michael@0: lowPrecisionScreenRegion, transform); michael@0: michael@0: if (!screenRegion.IsEqual(screenRect)) { michael@0: // Calculate the area of the region. All rects in an nsRegion are michael@0: // non-overlapping. michael@0: float screenArea = screenRect.width * screenRect.height; michael@0: float highPrecisionIntegrity = screenRegion.Area() / screenArea; michael@0: float lowPrecisionIntegrity = 1.f; michael@0: if (!lowPrecisionScreenRegion.IsEqual(screenRect)) { michael@0: lowPrecisionIntegrity = lowPrecisionScreenRegion.Area() / screenArea; michael@0: } michael@0: michael@0: return ((highPrecisionIntegrity * highPrecisionMultiplier) + michael@0: (lowPrecisionIntegrity * lowPrecisionMultiplier)) / 2; michael@0: } michael@0: michael@0: return 1.f; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateThebesLayerComposite() michael@0: { michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new ThebesLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateContainerLayerComposite() michael@0: { michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new ContainerLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateImageLayerComposite() michael@0: { michael@0: if (mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new ImageLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateColorLayerComposite() michael@0: { michael@0: if (LayerManagerComposite::mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new ColorLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateCanvasLayerComposite() michael@0: { michael@0: if (LayerManagerComposite::mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new CanvasLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerComposite::CreateRefLayerComposite() michael@0: { michael@0: if (LayerManagerComposite::mDestroyed) { michael@0: NS_WARNING("Call on destroyed layer manager"); michael@0: return nullptr; michael@0: } michael@0: return nsRefPtr(new RefLayerComposite(this)).forget(); michael@0: } michael@0: michael@0: LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer, michael@0: EffectChain& aEffects, michael@0: bool aIs3D) michael@0: : mCompositable(nullptr) michael@0: { michael@0: if (!aMaskLayer) { michael@0: return; michael@0: } michael@0: michael@0: mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost(); michael@0: if (!mCompositable) { michael@0: NS_WARNING("Mask layer with no compositable host"); michael@0: return; michael@0: } michael@0: michael@0: if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform(), aIs3D)) { michael@0: mCompositable = nullptr; michael@0: } michael@0: } michael@0: michael@0: LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect() michael@0: { michael@0: if (!mCompositable) { michael@0: return; michael@0: } michael@0: michael@0: mCompositable->RemoveMaskEffect(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: LayerManagerComposite::CreateDrawTarget(const IntSize &aSize, michael@0: SurfaceFormat aFormat) michael@0: { michael@0: #ifdef XP_MACOSX michael@0: // We don't want to accelerate if the surface is too small which indicates michael@0: // that it's likely used for an icon/static image. We also don't want to michael@0: // accelerate anything that is above the maximum texture size of weakest gpu. michael@0: // Safari uses 5000 area as the minimum for acceleration, we decided 64^2 is more logical. michael@0: bool useAcceleration = aSize.width <= 4096 && aSize.height <= 4096 && michael@0: aSize.width > 64 && aSize.height > 64 && michael@0: gfxPlatformMac::GetPlatform()->UseAcceleratedCanvas(); michael@0: if (useAcceleration) { michael@0: return Factory::CreateDrawTarget(BackendType::COREGRAPHICS_ACCELERATED, michael@0: aSize, aFormat); michael@0: } michael@0: #endif michael@0: return LayerManager::CreateDrawTarget(aSize, aFormat); michael@0: } michael@0: michael@0: LayerComposite::LayerComposite(LayerManagerComposite *aManager) michael@0: : mCompositeManager(aManager) michael@0: , mCompositor(aManager->GetCompositor()) michael@0: , mShadowOpacity(1.0) michael@0: , mUseShadowClipRect(false) michael@0: , mShadowTransformSetByAnimation(false) michael@0: , mDestroyed(false) michael@0: , mLayerComposited(false) michael@0: { } michael@0: michael@0: LayerComposite::~LayerComposite() michael@0: { michael@0: } michael@0: michael@0: void michael@0: LayerComposite::Destroy() michael@0: { michael@0: if (!mDestroyed) { michael@0: mDestroyed = true; michael@0: CleanupResources(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize) michael@0: { michael@0: return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width, michael@0: aSize.height)); michael@0: } michael@0: michael@0: void michael@0: LayerManagerComposite::NotifyShadowTreeTransaction() michael@0: { michael@0: if (mFPS) { michael@0: mFPS->NotifyShadowTreeTransaction(); michael@0: } michael@0: } michael@0: michael@0: #ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS michael@0: michael@0: /*static*/ bool michael@0: LayerManagerComposite::SupportsDirectTexturing() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: /*static*/ void michael@0: LayerManagerComposite::PlatformSyncBeforeReplyUpdate() michael@0: { michael@0: } michael@0: michael@0: #endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS) michael@0: michael@0: } /* layers */ michael@0: } /* mozilla */