Bug 1544948 helps us skip the merging phase for definitely-unchanged sublists, but we still have to dereference every item during PreProcessDisplayList.
We could be significantly faster if we could skip this work entirely for unchanged lists.
ProcessDisplayList does a bunch of work that we'd need to avoid:
- It serializes the display list into mOldItems.
With bug 1544948, this isn't used if we skip the merge. The only time it matters is if we remove items with a deleted frame, and we need the mOldItems array to have a placeholder in that location. If there are no items with a deleted frame, then we really don't need this at all.
- It detects items that belong to a frame that has been deleted, and removes them.
I think we'd probably want a way to detect if a sublist has (or might have) items with deleted frames, and still descend into those.
* Whenever a frame with display items is deleted (RemoveDisplayItemDataForDeletion), set a flag on the root to mark that we need to do a full PPDL next time.
* Use RetainedDisplayListData to store a flag for the deleted frame (or its parent?) so that we know to call MarkFrameForDisplayIfVisible which triggers PPDL to descend into list.
* Mark the parent of the deleted frame as modified. This isn't great as it triggers building new items, and the parent's area might be large.
- Detects items that return false for CanBeReused and removes them.
Harder, maybe something similar to what we end up doing for deleted frames?
- Calls RestoreState on items and lists.
This is FrameLayerBuilder specific code, that clear any mutations FLB made to the item when it was painted and gets it back to a clean slate. Getting rid of FLB would be one solution, but I also think FLB could do this when it encounters a reused item (checking for nsDisplayItem::IsReused during ProcessDisplayItems?).
- (after bug 1544948) Calls SetIsReused(true), and UpdateBounds.
SetIsReused is a FLB optimization that lets it skip geometry comparisons for items that are reused. I think FLB could just set this to true on items when it finishes with them the first time (FrameLayerBuilder::ComputeGeometryChangeForItem), so that ones that do get reused have it set.
Most of UpdateBounds just recomputes the bounds of a list, and isn't needed if we're not going to change the list. The initial bit on nsDisplayWrapList::UpdateBounds is the hard bit, since it adjusts the clip based on the current ASR. This again shouldn't be needed if we haven't changed, but RestoreState will have un-done it. We could maybe just make this computation lazy in getters, or put it into RestoreState itself.
- Looks for async-AGRs that don't match the passed in one, and marks those frames for rebuild.
I think we could keep a flag on container items, or lists to note that state where all descendant items have the same AGR as the container item. We'd have to track this and set it during DL building, and during merging. We have something similar with ASRs, which using AutoContainerASRTracker during building, and Update(Container)ASR during merging.
We also need to find sub-lists that have a new specified AGR, but I think the bug 1544948 check for MarkFrameForDisplayIfVisible is sufficient. ProcessFrameInternal only sets an AGR to look for on an ancestor of a modified frame, and MarkFrameForDisplayIfVisible is set on modified frames and their ancestors.
Bug 1544948 takes https://mattwoodrow.github.io/dl-test/dl-test.html?count=20000&layer=flattened&siblings=5 from 29ms of DL building to 5ms. Basically all of that time is in PPDL, and I think we could skip most of it this way.