From d8b3579dd96b561510dcbcdc097429207a910251 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 18 Mar 2016 18:38:57 -0700 Subject: [PATCH] Bug 1257315 (Part 1) - Add a visualization of visibility tracking to the APZ minimap. r=botond,mattwoodrow --- dom/ipc/TabChild.h | 2 + gfx/layers/FrameMetrics.h | 6 + .../composite/ContainerLayerComposite.cpp | 38 ++++- gfx/layers/composite/LayerManagerComposite.h | 19 +++ gfx/layers/ipc/CompositorChild.cpp | 11 ++ gfx/layers/ipc/CompositorChild.h | 2 + gfx/layers/ipc/CompositorParent.cpp | 28 ++++ gfx/layers/ipc/CompositorParent.h | 3 + gfx/layers/ipc/PCompositor.ipdl | 7 + gfx/thebes/gfxPrefs.h | 1 + layout/base/nsPresShell.cpp | 140 +++++++++++++++++- layout/base/nsPresShell.h | 19 ++- modules/libpref/init/all.js | 1 + 13 files changed, 267 insertions(+), 10 deletions(-) diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index feb2e8f87fbe..7b04efd1433a 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -539,6 +539,8 @@ public: static TabChild* GetFrom(nsIPresShell* aPresShell); static TabChild* GetFrom(uint64_t aLayersId); + uint64_t LayersId() { return mLayersId; } + void DidComposite(uint64_t aTransactionId, const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd); diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index bf38c6acb0d2..556d16572ec3 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -8,6 +8,7 @@ #include // for uint32_t, uint64_t #include "Units.h" // for CSSRect, CSSPixel, etc +#include "mozilla/HashFunctions.h" // for HashGeneric #include "mozilla/Maybe.h" #include "mozilla/gfx/BasePoint.h" // for BasePoint #include "mozilla/gfx/Rect.h" // for RoundedIn @@ -866,6 +867,11 @@ struct ScrollableLayerGuid { } return false; } + + uint32_t Hash() const + { + return HashGeneric(mLayersId, mPresShellId, mScrollId); + } }; template diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index afdaae3a74b6..79d015cb17e2 100755 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -496,11 +496,12 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager, const int verticalPadding = 10; const int horizontalPadding = 5; gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f); - gfx::Color tileActiveColor(1, 1, 1, 0.5f); + gfx::Color tileActiveColor(1, 1, 1, 0.4f); gfx::Color tileBorderColor(0, 0, 0, 0.1f); gfx::Color pageBorderColor(0, 0, 0); gfx::Color displayPortColor(0, 1.f, 0); - gfx::Color viewPortColor(0, 0, 1.f); + gfx::Color viewPortColor(0, 0, 1.f, 0.3f); + gfx::Color visibilityColor(1.f, 0, 0); // Rects const FrameMetrics& fm = aLayer->GetFrameMetrics(0); @@ -551,12 +552,45 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager, } */ + // Render the scrollable area. r = transform.TransformBounds(scrollRect.ToUnknownRect()); compositor->SlowDrawRect(r, pageBorderColor, clipRect, aContainer->GetEffectiveTransform()); + + // If enabled, render information about visibility. + if (gfxPrefs::APZMinimapVisibilityEnabled()) { + // Retrieve the APZC scrollable layer guid, which we'll use to get the + // appropriate visibility information from the layer manager. + AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0); + MOZ_ASSERT(controller); + + ScrollableLayerGuid guid = controller->GetGuid(); + + // Get the approximately visible region. + static CSSIntRegion emptyRegion; + CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid); + if (!visibleRegion) { + visibleRegion = &emptyRegion; + } + + // Iterate through and draw the rects in the region. + for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter(); + !iterator.Done(); + iterator.Next()) + { + CSSIntRect rect = iterator.Get(); + LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel(); + Rect r = transform.TransformBounds(scaledRect.ToUnknownRect()); + compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform()); + } + } + + // Render the displayport. r = transform.TransformBounds(dp.ToUnknownRect()); compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform()); r = transform.TransformBounds(dp.ToUnknownRect()); compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform()); + + // Render the viewport. r = transform.TransformBounds(viewRect.ToUnknownRect()); compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2); } diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h index 2430b8a2e631..a80e0a147630 100644 --- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -220,6 +220,20 @@ public: mInvalidRegion.Or(mInvalidRegion, aRegion); } + void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) + { + CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid); + MOZ_ASSERT(regionForScrollFrame); + + *regionForScrollFrame = aRegion; + } + + CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid) + { + return mVisibleRegions.Get(aGuid); + } + Compositor* GetCompositor() const { return mCompositor; @@ -355,6 +369,11 @@ private: gfx::IntRect mTargetBounds; nsIntRegion mInvalidRegion; + + typedef nsClassHashtable, + CSSIntRegion> VisibleRegions; + VisibleRegions mVisibleRegions; + UniquePtr mFPS; bool mInTransaction; diff --git a/gfx/layers/ipc/CompositorChild.cpp b/gfx/layers/ipc/CompositorChild.cpp index d11ca478bb8c..217ffc9641a6 100644 --- a/gfx/layers/ipc/CompositorChild.cpp +++ b/gfx/layers/ipc/CompositorChild.cpp @@ -704,6 +704,17 @@ CompositorChild::SendRequestNotifyAfterRemotePaint() return PCompositorChild::SendRequestNotifyAfterRemotePaint(); } +bool +CompositorChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + MOZ_ASSERT(mCanSend); + if (!mCanSend) { + return true; + } + return PCompositorChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CompositorChild.h b/gfx/layers/ipc/CompositorChild.h index d9716a4c22e4..884e63b10287 100644 --- a/gfx/layers/ipc/CompositorChild.h +++ b/gfx/layers/ipc/CompositorChild.h @@ -126,6 +126,8 @@ public: bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray* intervals); bool SendNotifyRegionInvalidated(const nsIntRegion& region); bool SendRequestNotifyAfterRemotePaint(); + bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const mozilla::CSSIntRegion& aRegion); private: // Private destructor, to discourage deletion outside of Release(): diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index af4a0e8e6763..f25812957d47 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -938,6 +938,19 @@ CompositorParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex, return true; } +bool +CompositorParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion); + + // We need to recomposite to update the minimap. + ScheduleComposition(); + } + return true; +} + void CompositorParent::ActorDestroy(ActorDestroyReason why) { @@ -1931,6 +1944,21 @@ public: virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; } virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; } virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray* intervals) override { return true; } + + virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) override + { + CompositorParent* parent; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + parent = sIndirectLayerTrees[aGuid.mLayersId].mParent; + } + if (parent) { + return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion); + } + return true; + } + virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override { *aWidth = gfxPlatform::GetPlatform()->GetTileWidth(); diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index a570bf41a787..b28f7f5c0d31 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -251,6 +251,9 @@ public: // @see CrossProcessCompositorParent::RecvRequestNotifyAfterRemotePaint virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; }; + virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) override; + virtual void ActorDestroy(ActorDestroyReason why) override; virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, diff --git a/gfx/layers/ipc/PCompositor.ipdl b/gfx/layers/ipc/PCompositor.ipdl index 4c2375ba0e86..47e1b8e0bd8a 100644 --- a/gfx/layers/ipc/PCompositor.ipdl +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -17,10 +17,12 @@ using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/Comp using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h"; using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h"; using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using mozilla::CSSIntRegion from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using mozilla::LayoutDeviceIntRegion from "Units.h"; using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; @@ -178,6 +180,11 @@ parent: */ async RequestNotifyAfterRemotePaint(); + // The child sends a region containing rects associated with the provided + // scrollable layer GUID that the child considers 'approximately visible'. + // We visualize this information in the APZ minimap. + async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region); + child: // Send back Compositor Frame Metrics from APZCs so tiled layers can // update progressively. diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 906c89e4cc4e..f2bc80b463c7 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -167,6 +167,7 @@ private: DECL_GFX_PREF(Once, "apz.max_velocity_queue_size", APZMaxVelocityQueueSize, uint32_t, 5); DECL_GFX_PREF(Live, "apz.min_skate_speed", APZMinSkateSpeed, float, 1.0f); DECL_GFX_PREF(Live, "apz.minimap.enabled", APZMinimap, bool, false); + DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled", APZMinimapVisibilityEnabled, bool, false); DECL_GFX_PREF(Live, "apz.overscroll.enabled", APZOverscrollEnabled, bool, false); DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f); DECL_GFX_PREF(Live, "apz.overscroll.spring_friction", APZOverscrollSpringFriction, float, 0.015f); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 85fb419037e9..51b3519c76a7 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -159,6 +159,7 @@ #endif +#include "mozilla/layers/CompositorChild.h" #include "GeckoProfiler.h" #include "gfxPlatform.h" #include "Layers.h" @@ -5530,13 +5531,65 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) } } +static void +AddFrameToVisibleRegions(nsIFrame* aFrame, + nsViewManager* aViewManager, + Maybe& aVisibleRegions) +{ + if (!aVisibleRegions) { + return; + } + + MOZ_ASSERT(aFrame); + MOZ_ASSERT(aViewManager); + + // Retrieve the view ID for this frame (which we obtain from the enclosing + // scrollable frame). + nsIScrollableFrame* scrollableFrame = + nsLayoutUtils::GetNearestScrollableFrame(aFrame, + nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE | + nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT); + if (!scrollableFrame) { + return; + } + + nsIFrame* scrollableFrameAsFrame = do_QueryFrame(scrollableFrame); + MOZ_ASSERT(scrollableFrameAsFrame); + + nsIContent* scrollableFrameContent = scrollableFrameAsFrame->GetContent(); + if (!scrollableFrameContent) { + return; + } + + ViewID viewID; + if (!nsLayoutUtils::FindIDFor(scrollableFrameContent, &viewID)) { + return ; + } + + // Update the visible region for the appropriate view ID. + nsRect frameRectInScrolledFrameSpace = aFrame->GetVisualOverflowRect(); + nsLayoutUtils::TransformResult result = + nsLayoutUtils::TransformRect(aFrame, + scrollableFrame->GetScrolledFrame(), + frameRectInScrolledFrameSpace); + if (result != nsLayoutUtils::TransformResult::TRANSFORM_SUCCEEDED) { + return; + } + + CSSIntRegion* regionForView = aVisibleRegions->LookupOrAdd(viewID); + MOZ_ASSERT(regionForView); + + regionForView->OrWith(CSSPixel::FromAppUnitsRounded(frameRectInScrolledFrameSpace)); +} + /* static */ void -PresShell::MarkImagesInListVisible(const nsDisplayList& aList) +PresShell::MarkImagesInListVisible(const nsDisplayList& aList, + Maybe& aVisibleRegions) { for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { nsDisplayList* sublist = item->GetChildren(); if (sublist) { - MarkImagesInListVisible(*sublist); + MarkImagesInListVisible(*sublist, aVisibleRegions); continue; } nsIFrame* f = item->Frame(); @@ -5553,6 +5606,8 @@ PresShell::MarkImagesInListVisible(const nsDisplayList& aList) // content was added to mVisibleImages, so we need to increment its visible count content->IncrementVisibleCount(); } + + AddFrameToVisibleRegions(f, presShell->mViewManager, aVisibleRegions); } } } @@ -5611,6 +5666,53 @@ DecrementVisibleCount(nsTHashtable>& aIm } } +static void +NotifyCompositorOfVisibleRegionsChange(PresShell* aPresShell, + const Maybe& aRegions) +{ + if (!aRegions) { + return; + } + + MOZ_ASSERT(aPresShell); + + // Retrieve the layers ID and pres shell ID. + TabChild* tabChild = TabChild::GetFrom(aPresShell); + if (!tabChild) { + return; + } + + const uint64_t layersId = tabChild->LayersId(); + const uint32_t presShellId = aPresShell->GetPresShellId(); + + // Retrieve the CompositorChild. + LayerManager* layerManager = aPresShell->GetLayerManager(); + if (!layerManager) { + return; + } + + ClientLayerManager* clientLayerManager = layerManager->AsClientLayerManager(); + if (!clientLayerManager) { + return; + } + + CompositorChild* compositorChild = clientLayerManager->GetCompositorChild(); + if (!compositorChild) { + return; + } + + // Send the approximately visible regions to the compositor. + for (auto iter = aRegions->ConstIter(); !iter.Done(); iter.Next()) { + const ViewID viewId = iter.Key(); + const CSSIntRegion* region = iter.UserData(); + MOZ_ASSERT(region); + + const ScrollableLayerGuid guid(layersId, presShellId, viewId); + + compositorChild->SendNotifyApproximatelyVisibleRegion(guid, *region); + } +} + void PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList) { @@ -5620,9 +5722,22 @@ PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList) // oldVisibleImages. nsTHashtable< nsRefPtrHashKey > oldVisibleImages; mVisibleImages.SwapElements(oldVisibleImages); - MarkImagesInListVisible(aList); + + // If we're visualizing visible regions, create a VisibleRegions object to + // store information about them. The functions we call will populate this + // object and send it to the compositor only if it's Some(), so we don't + // need to check the prefs everywhere. + Maybe visibleRegions; + if (gfxPrefs::APZMinimap() && gfxPrefs::APZMinimapVisibilityEnabled()) { + visibleRegions.emplace(); + } + + MarkImagesInListVisible(aList, visibleRegions); + DecrementVisibleCount(oldVisibleImages, nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION, this); + + NotifyCompositorOfVisibleRegionsChange(this, visibleRegions); } /* static */ void @@ -5656,6 +5771,7 @@ PresShell::ClearVisibleImagesList(uint32_t aNonvisibleAction) void PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect, + Maybe& aVisibleRegions, bool aRemoveOnly /* = false */) { MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell"); @@ -5669,6 +5785,8 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, // content was added to mVisibleImages, so we need to increment its visible count content->IncrementVisibleCount(); } + + AddFrameToVisibleRegions(aFrame, mViewManager, aVisibleRegions); } nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame); @@ -5737,7 +5855,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, } } } - MarkImagesInSubtreeVisible(child, r); + MarkImagesInSubtreeVisible(child, r, aVisibleRegions); } } } @@ -5764,14 +5882,26 @@ PresShell::RebuildImageVisibility(nsRect* aRect, nsTHashtable< nsRefPtrHashKey > oldVisibleImages; mVisibleImages.SwapElements(oldVisibleImages); + // If we're visualizing visible regions, create a VisibleRegions object to + // store information about them. The functions we call will populate this + // object and send it to the compositor only if it's Some(), so we don't + // need to check the prefs everywhere. + Maybe visibleRegions; + if (gfxPrefs::APZMinimap() && gfxPrefs::APZMinimapVisibilityEnabled()) { + visibleRegions.emplace(); + } + nsRect vis(nsPoint(0, 0), rootFrame->GetSize()); if (aRect) { vis = *aRect; } - MarkImagesInSubtreeVisible(rootFrame, vis, aRemoveOnly); + + MarkImagesInSubtreeVisible(rootFrame, vis, visibleRegions, aRemoveOnly); DecrementVisibleCount(oldVisibleImages, nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION, this); + + NotifyCompositorOfVisibleRegionsChange(this, visibleRegions); } void diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 21dfe3349454..de61ca1758d6 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -52,7 +52,14 @@ class nsPresShellEventCB; class nsAutoCauseReflowNotifier; namespace mozilla { + class EventDispatchingCallback; + +// A hash table type for tracking visible regions, for use by the visibility +// code in PresShell. The mapping is from view IDs to regions in the +// coordinate system of that view's scrolled frame. +typedef nsClassHashtable VisibleRegions; + } // namespace mozilla // 250ms. This is actually pref-controlled, but we use this value if we fail @@ -65,6 +72,9 @@ class PresShell final : public nsIPresShell, public nsIObserver, public nsSupportsWeakReference { + template using Maybe = mozilla::Maybe; + using VisibleRegions = mozilla::VisibleRegions; + public: PresShell(); @@ -468,7 +478,7 @@ protected: : mResolution(aPresShell->mResolution) , mRenderFlags(aPresShell->mRenderFlags) { } - mozilla::Maybe mResolution; + Maybe mResolution; RenderFlags mRenderFlags; }; @@ -740,8 +750,11 @@ protected: void ClearVisibleImagesList(uint32_t aNonvisibleAction); static void ClearImageVisibilityVisited(nsView* aView, bool aClear); - static void MarkImagesInListVisible(const nsDisplayList& aList); - void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect, + static void MarkImagesInListVisible(const nsDisplayList& aList, + Maybe& aVisibleRegions); + void MarkImagesInSubtreeVisible(nsIFrame* aFrame, + const nsRect& aRect, + Maybe& aVisibleRegions, bool aRemoveOnly = false); // Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 2d52adb92b7f..7b79ecbf67b1 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -585,6 +585,7 @@ pref("apz.max_velocity_inches_per_ms", "-1.0"); pref("apz.max_velocity_queue_size", 5); pref("apz.min_skate_speed", "1.0"); pref("apz.minimap.enabled", false); +pref("apz.minimap.visibility.enabled", false); pref("apz.overscroll.enabled", false); pref("apz.overscroll.min_pan_distance_ratio", "1.0"); pref("apz.overscroll.spring_friction", "0.015");