mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 16:58:38 +09:00
Issue #2694 - Improve nested grid layout performance by caching.
This improves performance by caching the grid items' block axis measurement, avoiding potentially exponential calculations and reflows. Resolves #2694
This commit is contained in:
parent
2da039daac
commit
25570dc572
3 changed files with 132 additions and 1 deletions
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsLayoutUtils.h"
|
||||
#include "LayoutLogging.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsISelection.h"
|
||||
|
|
@ -4382,6 +4383,8 @@ nsFrame::MarkIntrinsicISizesDirty()
|
|||
if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||
nsFontInflationData::MarkFontInflationDataTextDirty(this);
|
||||
}
|
||||
|
||||
RemoveProperty(nsGridContainerFrame::CachedBAxisMeasurement::Prop());
|
||||
}
|
||||
|
||||
/* virtual */ nscoord
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ typedef nsTHashtable< nsPtrHashKey<nsIFrame> > FrameHashtable;
|
|||
typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
|
||||
typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
|
||||
|
||||
using GridItemCachedBAxisMeasurement = nsGridContainerFrame::CachedBAxisMeasurement;
|
||||
|
||||
// https://drafts.csswg.org/css-sizing/#constraints
|
||||
enum class SizingConstraint
|
||||
{
|
||||
|
|
@ -3793,6 +3795,17 @@ MeasuringReflow(nsIFrame* aChild,
|
|||
nsIFrame::ReflowChildFlags::NoMoveFrame |
|
||||
nsIFrame::ReflowChildFlags::NoSizeView |
|
||||
nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
|
||||
|
||||
bool found;
|
||||
GridItemCachedBAxisMeasurement cachedMeasurement =
|
||||
aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop(), &found);
|
||||
if (found && cachedMeasurement.IsValidFor(aChild, aCBSize)) {
|
||||
childSize.BSize(wm) = cachedMeasurement.BSize();
|
||||
nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
|
||||
LogicalPoint(wm), nsSize(), flags);
|
||||
return cachedMeasurement.BSize();
|
||||
}
|
||||
|
||||
parent->ReflowChild(aChild, pc, childSize, childRI, wm,
|
||||
LogicalPoint(wm), nsSize(), flags, childStatus);
|
||||
parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
|
||||
|
|
@ -3800,6 +3813,22 @@ MeasuringReflow(nsIFrame* aChild,
|
|||
#ifdef DEBUG
|
||||
parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
|
||||
#endif
|
||||
|
||||
if (!found &&
|
||||
GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild, aCBSize)) {
|
||||
GridItemCachedBAxisMeasurement cachedMeasurement(aChild, aCBSize,
|
||||
childSize.BSize(wm));
|
||||
aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
|
||||
cachedMeasurement);
|
||||
} else if (found) {
|
||||
if (GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild,
|
||||
aCBSize)) {
|
||||
cachedMeasurement.Update(aChild, aCBSize, childSize.BSize(wm));
|
||||
} else {
|
||||
aChild->RemoveProperty(GridItemCachedBAxisMeasurement::Prop());
|
||||
}
|
||||
}
|
||||
|
||||
return childSize.BSize(wm);
|
||||
}
|
||||
|
||||
|
|
@ -5916,7 +5945,7 @@ nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
|
|||
if (child->GetType() != nsGkAtoms::placeholderFrame) {
|
||||
info = &aState.mGridItems[aState.mIter.GridItemIndex()];
|
||||
}
|
||||
ReflowInFlowChild(*aState.mIter, info, containerSize, Nothing(), nullptr,
|
||||
ReflowInFlowChild(child, info, containerSize, Nothing(), nullptr,
|
||||
aState, aContentArea, aDesiredSize, aStatus);
|
||||
MOZ_ASSERT(NS_FRAME_IS_COMPLETE(aStatus), "child should be complete "
|
||||
"in unconstrained reflow");
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#ifndef nsGridContainerFrame_h___
|
||||
#define nsGridContainerFrame_h___
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "nsContainerFrame.h"
|
||||
|
|
@ -451,6 +452,104 @@ private:
|
|||
// in case RemoveFrame removed all associated child frames.
|
||||
bool mDidPushItemsBitMayLie;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// This class caches the result for a grid item's block-axis measuring reflow.
|
||||
// This cache prevents us from doing exponential reflows in cases of deeply
|
||||
// nested grid frames.
|
||||
//
|
||||
// We store the cached value in the grid item's frame property table.
|
||||
//
|
||||
// We use the size of the grid area in the item's inline axis and
|
||||
// the item's block axis baseline padding for lookups and a unique-enough
|
||||
// set of properties.
|
||||
// We store the item's border-box BSize as value to be retrieved.
|
||||
class CachedBAxisMeasurement {
|
||||
public:
|
||||
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(Prop, CachedBAxisMeasurement)
|
||||
CachedBAxisMeasurement(const nsIFrame* aFrame, const LogicalSize& aCBSize,
|
||||
const nscoord aBSize)
|
||||
: mKey(aFrame, aCBSize), mBSize(aBSize) {}
|
||||
|
||||
CachedBAxisMeasurement() = default;
|
||||
|
||||
bool IsValidFor(const nsIFrame* aFrame, const LogicalSize& aCBSize) const {
|
||||
if (!CanCacheMeasurement(aFrame, aCBSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mKey == Key(aFrame, aCBSize);
|
||||
}
|
||||
|
||||
static bool CanCacheMeasurement(const nsIFrame* aFrame,
|
||||
const LogicalSize& aCBSize) {
|
||||
return Key::CanHash(aFrame, aCBSize);
|
||||
}
|
||||
|
||||
nscoord BSize() const { return mBSize; }
|
||||
|
||||
void Update(const nsIFrame* aFrame, const LogicalSize& aCBSize,
|
||||
const nscoord aBSize) {
|
||||
MOZ_ASSERT(CanCacheMeasurement(aFrame, aCBSize));
|
||||
mKey.mHashKey = Key::GenerateHash(aFrame, aCBSize);
|
||||
mBSize = aBSize;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Key {
|
||||
// mHashKey is generated by combining these 2 variables together
|
||||
// 1. The containing block size in the item's inline axis used
|
||||
// for measuring reflow
|
||||
// 2. The item's baseline padding property
|
||||
uint32_t mHashKey;
|
||||
|
||||
Key() = default;
|
||||
|
||||
Key(const nsIFrame* aFrame, const LogicalSize& aCBSize) {
|
||||
MOZ_ASSERT(CanHash(aFrame, aCBSize));
|
||||
mHashKey = GenerateHash(aFrame, aCBSize);
|
||||
}
|
||||
|
||||
void UpdateHash(const nsIFrame* aFrame, const LogicalSize& aCBSize) {
|
||||
MOZ_ASSERT(CanHash(aFrame, aCBSize));
|
||||
mHashKey = GenerateHash(aFrame, aCBSize);
|
||||
}
|
||||
|
||||
// Fast hash functions for this purpose only.
|
||||
static uint32_t GenerateHash(const nsIFrame* aFrame,
|
||||
const LogicalSize& aCBSize) {
|
||||
MOZ_ASSERT(CanHash(aFrame, aCBSize));
|
||||
|
||||
nscoord gridAreaISize = aCBSize.ISize(aFrame->GetWritingMode());
|
||||
nscoord bBaselinePaddingProperty =
|
||||
abs(aFrame->GetProperty(nsIFrame::BBaselinePadProperty()));
|
||||
|
||||
uint_fast8_t bitsNeededForISize = mozilla::FloorLog2(gridAreaISize) + 1;
|
||||
|
||||
return (gridAreaISize << (32 - bitsNeededForISize)) |
|
||||
bBaselinePaddingProperty;
|
||||
}
|
||||
|
||||
static bool CanHash(const nsIFrame* aFrame, const LogicalSize& aCBSize) {
|
||||
uint_fast8_t bitsNeededForISize =
|
||||
mozilla::FloorLog2(aCBSize.ISize(aFrame->GetWritingMode())) + 1;
|
||||
|
||||
uint_fast8_t bitsNeededForBBaselinePadding =
|
||||
mozilla::FloorLog2(
|
||||
abs(aFrame->GetProperty(nsIFrame::BBaselinePadProperty()))) +
|
||||
1;
|
||||
|
||||
return bitsNeededForISize + bitsNeededForBBaselinePadding <= 32;
|
||||
}
|
||||
|
||||
bool operator==(const Key& aOther) const {
|
||||
return mHashKey == aOther.mHashKey;
|
||||
}
|
||||
};
|
||||
|
||||
Key mKey;
|
||||
nscoord mBSize;
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* nsGridContainerFrame_h___ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue