diff options
Diffstat (limited to 'gfx/layers/wr/HitTestInfoManager.cpp')
-rw-r--r-- | gfx/layers/wr/HitTestInfoManager.cpp | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/gfx/layers/wr/HitTestInfoManager.cpp b/gfx/layers/wr/HitTestInfoManager.cpp new file mode 100644 index 0000000000..3365a92fbf --- /dev/null +++ b/gfx/layers/wr/HitTestInfoManager.cpp @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HitTestInfoManager.h" +#include "HitTestInfo.h" + +#include "nsDisplayList.h" + +#define DEBUG_HITTEST_INFO 0 +#if DEBUG_HITTEST_INFO +# define HITTEST_INFO_LOG(...) printf_stderr(__VA_ARGS__) +#else +# define HITTEST_INFO_LOG(...) +#endif + +namespace mozilla::layers { + +using ViewID = ScrollableLayerGuid::ViewID; + +/** + * TODO(miko): This used to be a performance bottle-neck, but it does not show + * up in profiles anymore, see bugs 1424637 and 1424968. + * A better way of doing this would be to store current app units per dev pixel + * in wr::DisplayListBuilder, and update it whenever display items that separate + * presshell boundaries are encountered. + */ +static int32_t GetAppUnitsFromDisplayItem(nsDisplayItem* aItem) { + nsIFrame* frame = aItem->Frame(); + MOZ_ASSERT(frame); + return frame->PresContext()->AppUnitsPerDevPixel(); +} + +static void CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder, + nsDisplayItem* aItem, const nsRect& aArea, + const gfx::CompositorHitTestInfo& aFlags, + const ViewID& aViewId) { + const Maybe<SideBits> sideBits = + aBuilder.GetContainingFixedPosSideBits(aItem->GetActiveScrolledRoot()); + + const LayoutDeviceRect devRect = + LayoutDeviceRect::FromAppUnits(aArea, GetAppUnitsFromDisplayItem(aItem)); + const wr::LayoutRect rect = wr::ToLayoutRect(devRect); + + aBuilder.PushHitTest(rect, rect, !aItem->BackfaceIsHidden(), aViewId, aFlags, + sideBits.valueOr(SideBits::eNone)); +} + +HitTestInfoManager::HitTestInfoManager() + : mArea(nsRect()), + mFlags(gfx::CompositorHitTestInvisibleToHit), + mViewId(ScrollableLayerGuid::NULL_SCROLL_ID), + mSpaceAndClipChain(wr::InvalidScrollNodeWithChain()) {} + +void HitTestInfoManager::Reset() { + mArea = nsRect(); + mFlags = gfx::CompositorHitTestInvisibleToHit; + mViewId = ScrollableLayerGuid::NULL_SCROLL_ID; + mSpaceAndClipChain = wr::InvalidScrollNodeWithChain(); + + HITTEST_INFO_LOG("* HitTestInfoManager::Reset\n"); +} + +bool HitTestInfoManager::ProcessItem( + nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder, + nsDisplayListBuilder* aDisplayListBuilder) { + MOZ_ASSERT(aItem); + + HITTEST_INFO_LOG("* HitTestInfoManager::ProcessItem(%d, %s, has=%d)\n", + getpid(), aItem->Frame()->ListTag().get(), + aItem->HasHitTestInfo()); + + if (MOZ_UNLIKELY(aItem->GetType() == DisplayItemType::TYPE_REMOTE)) { + // Remote frames might contain hit-test-info items inside (but those + // aren't processed by this process of course), so we can't optimize out the + // next hit-test info item because it might be on top of the iframe. + Reset(); + } + + if (!aItem->HasHitTestInfo()) { + return false; + } + + const HitTestInfo& hitTestInfo = aItem->GetHitTestInfo(); + const nsRect& area = hitTestInfo.Area(); + const gfx::CompositorHitTestInfo& flags = hitTestInfo.Info(); + + if (flags == gfx::CompositorHitTestInvisibleToHit || area.IsEmpty()) { + return false; + } + + const auto viewId = + hitTestInfo.GetViewId(aBuilder, aItem->GetActiveScrolledRoot()); + const auto spaceAndClipChain = aBuilder.CurrentSpaceAndClipChain(); + + if (!Update(area, flags, viewId, spaceAndClipChain)) { + // The previous hit test information is still valid. + return false; + } + + HITTEST_INFO_LOG("+ [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", area.x, + area.y, area.width, area.height, flags.serialize(), viewId); + + CreateWebRenderCommands(aBuilder, aItem, area, flags, viewId); + + return true; +} + +/** + * Updates the current hit testing information if necessary. + * Returns true if the hit testing information was changed. + */ +bool HitTestInfoManager::Update(const nsRect& aArea, + const gfx::CompositorHitTestInfo& aFlags, + const ViewID& aViewId, + const wr::WrSpaceAndClipChain& aSpaceAndClip) { + if (mViewId == aViewId && mFlags == aFlags && mArea.Contains(aArea) && + mSpaceAndClipChain == aSpaceAndClip) { + // The previous hit testing information can be reused. + HITTEST_INFO_LOG("s [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", aArea.x, + aArea.y, aArea.width, aArea.height, aFlags.serialize(), + aViewId); + return false; + } + + mArea = aArea; + mFlags = aFlags; + mViewId = aViewId; + mSpaceAndClipChain = aSpaceAndClip; + return true; +} + +} // namespace mozilla::layers |