mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-31 03:27:31 +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 "nsLayoutUtils.h"
|
||||||
#include "LayoutLogging.h"
|
#include "LayoutLogging.h"
|
||||||
#include "mozilla/RestyleManager.h"
|
#include "mozilla/RestyleManager.h"
|
||||||
|
#include "nsGridContainerFrame.h"
|
||||||
|
|
||||||
#include "nsIDOMNode.h"
|
#include "nsIDOMNode.h"
|
||||||
#include "nsISelection.h"
|
#include "nsISelection.h"
|
||||||
|
|
@ -4382,6 +4383,8 @@ nsFrame::MarkIntrinsicISizesDirty()
|
||||||
if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||||
nsFontInflationData::MarkFontInflationDataTextDirty(this);
|
nsFontInflationData::MarkFontInflationDataTextDirty(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveProperty(nsGridContainerFrame::CachedBAxisMeasurement::Prop());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */ nscoord
|
/* virtual */ nscoord
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ typedef nsTHashtable< nsPtrHashKey<nsIFrame> > FrameHashtable;
|
||||||
typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
|
typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
|
||||||
typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
|
typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
|
||||||
|
|
||||||
|
using GridItemCachedBAxisMeasurement = nsGridContainerFrame::CachedBAxisMeasurement;
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-sizing/#constraints
|
// https://drafts.csswg.org/css-sizing/#constraints
|
||||||
enum class SizingConstraint
|
enum class SizingConstraint
|
||||||
{
|
{
|
||||||
|
|
@ -3793,6 +3795,17 @@ MeasuringReflow(nsIFrame* aChild,
|
||||||
nsIFrame::ReflowChildFlags::NoMoveFrame |
|
nsIFrame::ReflowChildFlags::NoMoveFrame |
|
||||||
nsIFrame::ReflowChildFlags::NoSizeView |
|
nsIFrame::ReflowChildFlags::NoSizeView |
|
||||||
nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
|
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,
|
parent->ReflowChild(aChild, pc, childSize, childRI, wm,
|
||||||
LogicalPoint(wm), nsSize(), flags, childStatus);
|
LogicalPoint(wm), nsSize(), flags, childStatus);
|
||||||
parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
|
parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
|
||||||
|
|
@ -3800,6 +3813,22 @@ MeasuringReflow(nsIFrame* aChild,
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
|
parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
|
||||||
#endif
|
#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);
|
return childSize.BSize(wm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5916,7 +5945,7 @@ nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
|
||||||
if (child->GetType() != nsGkAtoms::placeholderFrame) {
|
if (child->GetType() != nsGkAtoms::placeholderFrame) {
|
||||||
info = &aState.mGridItems[aState.mIter.GridItemIndex()];
|
info = &aState.mGridItems[aState.mIter.GridItemIndex()];
|
||||||
}
|
}
|
||||||
ReflowInFlowChild(*aState.mIter, info, containerSize, Nothing(), nullptr,
|
ReflowInFlowChild(child, info, containerSize, Nothing(), nullptr,
|
||||||
aState, aContentArea, aDesiredSize, aStatus);
|
aState, aContentArea, aDesiredSize, aStatus);
|
||||||
MOZ_ASSERT(NS_FRAME_IS_COMPLETE(aStatus), "child should be complete "
|
MOZ_ASSERT(NS_FRAME_IS_COMPLETE(aStatus), "child should be complete "
|
||||||
"in unconstrained reflow");
|
"in unconstrained reflow");
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#ifndef nsGridContainerFrame_h___
|
#ifndef nsGridContainerFrame_h___
|
||||||
#define nsGridContainerFrame_h___
|
#define nsGridContainerFrame_h___
|
||||||
|
|
||||||
|
#include "mozilla/MathAlgorithms.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/TypeTraits.h"
|
#include "mozilla/TypeTraits.h"
|
||||||
#include "nsContainerFrame.h"
|
#include "nsContainerFrame.h"
|
||||||
|
|
@ -451,6 +452,104 @@ private:
|
||||||
// in case RemoveFrame removed all associated child frames.
|
// in case RemoveFrame removed all associated child frames.
|
||||||
bool mDidPushItemsBitMayLie;
|
bool mDidPushItemsBitMayLie;
|
||||||
#endif
|
#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___ */
|
#endif /* nsGridContainerFrame_h___ */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue