author | Chris Jones <jones.chris.g@gmail.com> |
Wed, 07 Nov 2012 19:51:55 -0800 | |
changeset 112665 | ea85f4e3fc2de25e94235875648923f562c7252f |
parent 112664 | de95b48ab6e14d4d8d91ef14dfc3098fe5a352be |
child 112666 | c0d1ffbe9a87102cbba20b533c6a3f8bc208efe4 |
push id | 23833 |
push user | emorley@mozilla.com |
push date | Thu, 08 Nov 2012 10:20:57 +0000 |
treeherder | mozilla-central@e0d7b394462b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mattwoodrow, roc |
bugs | 806029 |
milestone | 19.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1787,16 +1787,32 @@ TabChild::NotifyPainted() } bool TabChild::IsAsyncPanZoomEnabled() { return mScrolling == ASYNC_PAN_ZOOM; } +void +TabChild::MakeVisible() +{ + if (mWidget) { + mWidget->Show(true); + } +} + +void +TabChild::MakeHidden() +{ + if (mWidget) { + mWidget->Show(false); + } +} + NS_IMETHODIMP TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult) { if (mTabChildGlobal) { NS_ADDREF(*aResult = mTabChildGlobal); return NS_OK; } *aResult = nullptr;
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -287,16 +287,24 @@ public: void GetDPI(float* aDPI); void SetBackgroundColor(const nscolor& aColor); void NotifyPainted(); bool IsAsyncPanZoomEnabled(); + /** + * Signal to this TabChild that it should be made visible: + * activated widget, retained layer tree, etc. (Respectively, + * made not visible.) + */ + void MakeVisible(); + void MakeHidden(); + protected: virtual PRenderFrameChild* AllocPRenderFrame(ScrollingBehavior* aScrolling, LayersBackend* aBackend, int32_t* aMaxTextureSize, uint64_t* aLayersId) MOZ_OVERRIDE; virtual bool DeallocPRenderFrame(PRenderFrameChild* aFrame) MOZ_OVERRIDE; virtual bool RecvDestroy() MOZ_OVERRIDE;
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -426,16 +426,34 @@ public: * manager. */ LayerUserData* GetUserData(void* aKey) { return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey))); } /** + * Must be called outside of a layers transaction. + * + * For the subtree rooted at |aSubtree|, this attempts to free up + * any free-able resources like retained buffers, but may do nothing + * at all. After this call, the layer tree is left in an undefined + * state; the layers in |aSubtree|'s subtree may no longer have + * buffers with valid content and may no longer be able to draw + * their visible and valid regions. + * + * In general, a painting or forwarding transaction on |this| must + * complete on the tree before it returns to a valid state. + * + * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree| + * is null. |aSubtree|'s manager must be this. + */ + virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} + + /** * Flag the next paint as the first for a document. */ virtual void SetIsFirstPaint() {} // We always declare the following logging symbols, because it's // extremely tricky to conditionally declare them. However, for // ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial // definitions in Layers.cpp.
--- a/gfx/layers/basic/BasicCanvasLayer.cpp +++ b/gfx/layers/basic/BasicCanvasLayer.cpp @@ -311,16 +311,21 @@ public: { DestroyBackBuffer(); MOZ_COUNT_DTOR(BasicShadowableCanvasLayer); } virtual void Initialize(const Data& aData); virtual void Paint(gfxContext* aContext, Layer* aMaskLayer); + virtual void ClearCachedResources() MOZ_OVERRIDE + { + DestroyBackBuffer(); + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = CanvasLayerAttributes(mFilter); } virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; }
--- a/gfx/layers/basic/BasicImageLayer.cpp +++ b/gfx/layers/basic/BasicImageLayer.cpp @@ -176,16 +176,21 @@ public: virtual ~BasicShadowableImageLayer() { DestroyBackBuffer(); MOZ_COUNT_DTOR(BasicShadowableImageLayer); } virtual void Paint(gfxContext* aContext, Layer* aMaskLayer); + virtual void ClearCachedResources() MOZ_OVERRIDE + { + DestroyBackBuffer(); + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = ImageLayerAttributes(mFilter, mForceSingleTile); } virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; }
--- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -993,22 +993,24 @@ BasicLayerManager::PaintLayer(gfxContext aTarget->Rectangle(destRect, true); aTarget->Clip(); FlushGroup(paintContext, needsClipToVisibleRegion); } } } void -BasicLayerManager::ClearCachedResources() +BasicLayerManager::ClearCachedResources(Layer* aSubtree) { - if (mRoot) { + MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this); + if (aSubtree) { + ClearLayer(aSubtree); + } else if (mRoot) { ClearLayer(mRoot); } - mCachedSurface.Expire(); } void BasicLayerManager::ClearLayer(Layer* aLayer) { ToData(aLayer)->ClearCachedResources(); for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { @@ -1278,16 +1280,26 @@ BasicShadowLayerManager::IsCompositingCh } void BasicShadowLayerManager::SetIsFirstPaint() { ShadowLayerForwarder::SetIsFirstPaint(); } +void +BasicShadowLayerManager::ClearCachedResources(Layer* aSubtree) +{ + MOZ_ASSERT(!HasShadowManager() || !aSubtree); + if (PLayersChild* manager = GetShadowManager()) { + manager->SendClearCachedResources(); + } + BasicLayerManager::ClearCachedResources(aSubtree); +} + bool BasicShadowLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, gfx::Rect& aViewport, float& aScaleX, float& aScaleY) { #ifdef MOZ_WIDGET_ANDROID Layer* primaryScrollable = GetPrimaryScrollableLayer();
--- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -133,18 +133,18 @@ public: gfxContext* GetTarget() { return mTarget; } void SetTarget(gfxContext* aTarget) { mUsingDefaultTarget = false; mTarget = aTarget; } bool IsRetained() { return mWidget != nullptr; } #ifdef MOZ_LAYERS_HAVE_LOG virtual const char* Name() const { return "Basic"; } #endif // MOZ_LAYERS_HAVE_LOG - // Clear the cached contents of this layer. - void ClearCachedResources(); + // Clear the cached contents of this layer tree. + virtual void ClearCachedResources(Layer* aSubtree = nullptr) MOZ_OVERRIDE; void SetTransactionIncomplete() { mTransactionIncomplete = true; } bool IsTransactionIncomplete() { return mTransactionIncomplete; } already_AddRefed<gfxContext> PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion, bool* aNeedsClipToVisibleRegion); already_AddRefed<gfxContext> PushGroupWithCachedSurface(gfxContext *aTarget, @@ -266,16 +266,20 @@ public: bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); } virtual bool IsCompositingCheap(); virtual bool HasShadowManagerInternal() const { return HasShadowManager(); } virtual void SetIsFirstPaint() MOZ_OVERRIDE; + // Drop cached resources and ask our shadow manager to do the same, + // if we have one. + virtual void ClearCachedResources(Layer* aSubtree = nullptr) MOZ_OVERRIDE; + void SetRepeatTransaction() { mRepeatTransaction = true; } bool IsRepeatTransaction() { return mIsRepeatTransaction; } /** * Called for each iteration of a progressive tile update. Fills * aViewport, aScaleX and aScaleY with the current scale and viewport * being used to composite the layers in this manager, to determine what area
--- a/gfx/layers/basic/BasicThebesLayer.h +++ b/gfx/layers/basic/BasicThebesLayer.h @@ -126,16 +126,25 @@ public: virtual ~BasicShadowableThebesLayer(); virtual void PaintThebes(gfxContext* aContext, Layer* aMaskLayer, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, ReadbackProcessor* aReadback); + virtual void ClearCachedResources() MOZ_OVERRIDE + { + BasicThebesLayer::ClearCachedResources(); + DestroyBackBuffer(); + // Don't try to read back from this, it soon may be invalid. + mROFrontBuffer = null_t(); + mFrontAndBackBufferDiffer = false; + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = ThebesLayerAttributes(GetValidRegion()); } virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; }
--- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -323,13 +323,17 @@ parent: // for a particular document. sync Update(Edit[] cset, TargetConfig targetConfig, bool isFirstPaint) returns (EditReply[] reply); // We don't need to send a sync transaction if // no transaction operate require a swap. async UpdateNoSwap(Edit[] cset, TargetConfig targetConfig, bool isFirstPaint); + // Drop any front buffers that might be retained on the compositor + // side. + async ClearCachedResources(); + async __delete__(); }; } // namespace layers } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -476,16 +476,28 @@ ShadowLayersParent::RecvUpdate(const Inf if (compositeTime > 15) { printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", compositeTime); } #endif return true; } +bool +ShadowLayersParent::RecvClearCachedResources() +{ + if (mRoot) { + // NB: |mRoot| here is the *child* context's root. In this parent + // context, it's just a subtree root. We need to scope the clear + // of resources to exactly that subtree, so we specify it here. + mLayerManager->ClearCachedResources(mRoot); + } + return true; +} + PGrallocBufferParent* ShadowLayersParent::AllocPGrallocBuffer(const gfxIntSize& aSize, const gfxContentType& aContent, MaybeMagicGrallocBufferHandle* aOutHandle) { #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC return GrallocBufferActor::Create(aSize, aContent, aOutHandle); #else
--- a/gfx/layers/ipc/ShadowLayersParent.h +++ b/gfx/layers/ipc/ShadowLayersParent.h @@ -51,16 +51,18 @@ protected: const TargetConfig& targetConfig, const bool& isFirstPaint, EditReplyArray* reply) MOZ_OVERRIDE; virtual bool RecvUpdateNoSwap(const EditArray& cset, const TargetConfig& targetConfig, const bool& isFirstPaint) MOZ_OVERRIDE; + virtual bool RecvClearCachedResources() MOZ_OVERRIDE; + virtual PGrallocBufferParent* AllocPGrallocBuffer(const gfxIntSize& aSize, const gfxContentType& aContent, MaybeMagicGrallocBufferHandle* aOutHandle) MOZ_OVERRIDE; virtual bool DeallocPGrallocBuffer(PGrallocBufferParent* actor) MOZ_OVERRIDE; virtual PLayerParent* AllocPLayer() MOZ_OVERRIDE; virtual bool DeallocPLayer(PLayerParent* actor) MOZ_OVERRIDE;
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp +++ b/gfx/layers/opengl/ContainerLayerOGL.cpp @@ -488,12 +488,13 @@ ShadowRefLayerOGL::RenderLayer(int aPrev const nsIntPoint& aOffset) { ContainerRender(this, aPreviousFrameBuffer, aOffset, mOGLManager); } void ShadowRefLayerOGL::CleanupResources() { + MOZ_ASSERT(!mFirstChild); } } /* layers */ } /* mozilla */
--- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -295,16 +295,19 @@ LayerManagerOGL::LayerManagerOGL(nsIWidg : mWidget(aWidget) , mWidgetSize(-1, -1) , mSurfaceSize(aSurfaceWidth, aSurfaceHeight) , mBackBufferFBO(0) , mBackBufferTexture(0) , mBackBufferSize(-1, -1) , mHasBGRA(0) , mIsRenderingToEGLSurface(aIsRenderingToEGLSurface) +#ifdef DEBUG + , mMaybeInvalidTree(false) +#endif { } LayerManagerOGL::~LayerManagerOGL() { Destroy(); } @@ -589,22 +592,28 @@ LayerManagerOGL::SetClippingRegion(const { mClippingRegion = aClippingRegion; } void LayerManagerOGL::BeginTransaction() { mInTransaction = true; +#ifdef DEBUG + mMaybeInvalidTree = false; +#endif } void LayerManagerOGL::BeginTransactionWithTarget(gfxContext *aTarget) { mInTransaction = true; +#ifdef DEBUG + mMaybeInvalidTree = false; +#endif #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); @@ -612,16 +621,20 @@ LayerManagerOGL::BeginTransactionWithTar } mTarget = aTarget; } bool LayerManagerOGL::EndEmptyTransaction(EndTransactionFlags aFlags) { + // NB: this makes the somewhat bogus assumption that pure + // compositing txns don't call BeginTransaction(), because that's + // the behavior of CompositorParent. + MOZ_ASSERT(!mMaybeInvalidTree); mInTransaction = false; if (!mRoot) return false; EndTransaction(nullptr, nullptr, aFlags); return true; } @@ -734,25 +747,60 @@ LayerManagerOGL::CreateCanvasLayer() NS_WARNING("Call on destroyed layer manager"); return nullptr; } nsRefPtr<CanvasLayer> layer = new CanvasLayerOGL(this); return layer.forget(); } +static LayerOGL* +ToLayerOGL(Layer* aLayer) +{ + return static_cast<LayerOGL*>(aLayer->ImplData()); +} + +static void ClearSubtree(Layer* aLayer) +{ + ToLayerOGL(aLayer)->CleanupResources(); + for (Layer* child = aLayer->GetFirstChild(); child; + child = child->GetNextSibling()) { + ClearSubtree(child); + } +} + +void +LayerManagerOGL::ClearCachedResources(Layer* aSubtree) +{ + MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this); + Layer* subtree = aSubtree ? aSubtree : mRoot.get(); + if (!subtree) { + return; + } + + ClearSubtree(subtree); +#ifdef DEBUG + // If this subtree is reachable from the root layer, then it's + // possibly onscreen, and the resource clear means that composites + // until the next received transaction may draw garbage to the + // framebuffer. + for(; subtree && subtree != mRoot; subtree = subtree->GetParent()); + mMaybeInvalidTree = (subtree == mRoot); +#endif // DEBUG +} + LayerOGL* LayerManagerOGL::RootLayer() const { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } - return static_cast<LayerOGL*>(mRoot->ImplData()); + return ToLayerOGL(mRoot); } bool LayerManagerOGL::sDrawFPS = false; bool LayerManagerOGL::sFrameCounter = false; static uint16_t sFrameCount = 0; void FPSState::DrawFrameCounter(GLContext* context)
--- a/gfx/layers/opengl/LayerManagerOGL.h +++ b/gfx/layers/opengl/LayerManagerOGL.h @@ -140,16 +140,18 @@ public: virtual already_AddRefed<ShadowRefLayer> CreateShadowRefLayer(); virtual LayersBackend GetBackendType() { return LAYERS_OPENGL; } virtual void GetBackendName(nsAString& name) { name.AssignLiteral("OpenGL"); } virtual already_AddRefed<gfxASurface> CreateOptimalMaskSurface(const gfxIntSize &aSize); + virtual void ClearCachedResources(Layer* aSubtree = nullptr) MOZ_OVERRIDE; + /** * Helper methods. */ void MakeCurrent(bool aForce = false) { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } @@ -459,16 +461,23 @@ private: const gfx3DMatrix& aTransform); /* Thebes layer callbacks; valid at the end of a transaciton, * while rendering */ DrawThebesLayerCallback mThebesLayerCallback; void *mThebesLayerCallbackData; gfxMatrix mWorldMatrix; nsAutoPtr<FPSState> mFPS; +#ifdef DEBUG + // NB: only interesting when this is a purely compositing layer + // manager. True after possibly onscreen layers have had their + // cached resources cleared outside of a transaction, and before the + // next forwarded transaction that re-validates their buffers. + bool mMaybeInvalidTree; +#endif static bool sDrawFPS; static bool sFrameCounter; }; /** * General information and tree management for OGL layers. */
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9065,16 +9065,48 @@ PresShell::SetIsActive(bool aIsActive) #ifdef ACCESSIBILITY if (aIsActive) { nsAccessibilityService* accService = AccService(); if (accService) { accService->PresShellActivated(this); } } #endif + + // We have this odd special case here because remote content behaves + // differently from same-process content when "hidden". In + // desktop-type "browser UIs", hidden "tabs" have documents that are + // part of the chrome tree. When the tabs are hidden, their content + // is no longer part of the visible document tree, and the layers + // for the content are naturally released. + // + // Remote content is its own top-level tree in its subprocess. When + // it's "hidden", there's no transaction in which the document + // thinks it's not visible, so layers can be retained forever. This + // is problematic when those layers uselessly hold on to precious + // resources like directly texturable memory. + // + // PresShell::SetIsActive() is the first C++ entry point at which we + // (i) know that our parent process wants our content to be hidden; + // and (ii) has easy access to the TabChild. So we use this + // notification to signal the TabChild to drop its layer tree and + // stop trying to repaint. + if (TabChild* tab = GetTabChildFrom(this)) { + if (aIsActive) { + tab->MakeVisible(); + if (nsIFrame* root = mFrameConstructor->GetRootFrame()) { + FrameLayerBuilder::InvalidateAllLayersForFrame( + nsLayoutUtils::GetDisplayRootFrame(root)); + root->SchedulePaint(); + } + } else { + tab->MakeHidden(); + } + } + return rv; } /* * Determines the current image locking state. Called when one of the * dependent factors changes. */ nsresult
--- a/widget/xpwidgets/PuppetWidget.cpp +++ b/widget/xpwidgets/PuppetWidget.cpp @@ -157,18 +157,27 @@ NS_IMETHODIMP PuppetWidget::Show(bool aState) { NS_ASSERTION(mEnabled, "does it make sense to Show()/Hide() a disabled widget?"); bool wasVisible = mVisible; mVisible = aState; + if (mChild) { + mChild->mVisible = aState; + } + + if (!mVisible && mLayerManager) { + mLayerManager->ClearCachedResources(); + } + if (!wasVisible && mVisible) { Resize(mBounds.width, mBounds.height, false); + Invalidate(mBounds); } return NS_OK; } NS_IMETHODIMP PuppetWidget::Resize(int32_t aWidth, int32_t aHeight, @@ -543,16 +552,22 @@ NS_IMETHODIMP PuppetWidget::PaintTask::Run() { if (mWidget) { mWidget->Paint(); } return NS_OK; } +bool +PuppetWidget::NeedsPaint() +{ + return mVisible; +} + float PuppetWidget::GetDPI() { if (mDPI < 0) { NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the DPI from!"); mTabChild->GetDPI(&mDPI); }
--- a/widget/xpwidgets/PuppetWidget.h +++ b/widget/xpwidgets/PuppetWidget.h @@ -167,16 +167,18 @@ public: } // Gets the DPI of the screen corresponding to this widget. // Contacts the parent process which gets the DPI from the // proper widget there. TODO: Handle DPI changes that happen // later on. virtual float GetDPI(); + virtual bool NeedsPaint() MOZ_OVERRIDE; + virtual TabChild* GetOwningTabChild() MOZ_OVERRIDE { return mTabChild; } private: nsresult Paint(); void SetChild(PuppetWidget* aChild); nsresult IMEEndComposition(bool aCancel);