From 7fe025ae6494e126347481d1a7f4c76b971a6e83 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Mon, 15 Jan 2018 10:15:50 +1100 Subject: [PATCH] Bug 1430494 - Skip new-list loop and its related operations if list is empty - r=mattwoodrow MozReview-Commit-ID: LkY2lldl7Al --- .../painting/RetainedDisplayListBuilder.cpp | 182 +++++++++--------- 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp index 5f59d6dcdd09..5125eb66d115 100644 --- a/layout/painting/RetainedDisplayListBuilder.cpp +++ b/layout/painting/RetainedDisplayListBuilder.cpp @@ -411,115 +411,117 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList, } }; - // Build a hashtable of items in the old list so we can look for them quickly. - // We have similar data in the nsIFrame DisplayItems() property, but it doesn't - // know which display list items are in, and we only want to match items in - // this list. - nsDataHashtable oldListLookup(aOldList->Count()); - - for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) { - i->SetReused(false); - - if (!aNewList->IsEmpty()) { - oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i); + if (!aNewList->IsEmpty()) { + // Build a hashtable of items in the old list so we can look for them quickly. + // We have similar data in the nsIFrame DisplayItems() property, but it doesn't + // know which display list items are in, and we only want to match items in + // this list. + nsDataHashtable oldListLookup(aOldList->Count()); + + for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) { + i->SetReused(false); + + if (!aNewList->IsEmpty()) { + oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i); + } } - } - nsDataHashtable newListLookup(aNewList->Count()); - for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) { + nsDataHashtable newListLookup(aNewList->Count()); + for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) { #ifdef DEBUG - if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) { - MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d", - i->Name(), i->Frame(), - static_cast(i->GetType()), i->GetPerFrameKey()); - } + if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) { + MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d", + i->Name(), i->Frame(), + static_cast(i->GetType()), i->GetPerFrameKey()); + } #endif - newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i); - } + newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i); + } - while (nsDisplayItem* newItem = aNewList->RemoveBottom()) { - if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) { - // The new item has a matching counterpart in the old list that we haven't yet reached, - // so copy all valid items from the old list into the merged list until we get to the - // matched item. - nsDisplayItem* old = nullptr; - while ((old = aOldList->GetBottom()) && old != oldItem) { - if (IsAnyAncestorModified(old->FrameForInvalidation())) { - // The old item is invalid, discard it. - oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() }); - aOldList->RemoveBottom(); - old->Destroy(&mBuilder); - } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) { - // This old item is also in the new list, but we haven't got to it yet. - // Stop now, and we'll deal with it when we get to the new entry. - break; - } else { - // Recurse into the child list (without a matching new list) to - // ensure that we find and remove any invalidated items. - if (old->GetChildren()) { - nsDisplayList empty; - Maybe containerASRForChildren; - MergeDisplayLists(&empty, old->GetChildren(), - old->GetChildren(), containerASRForChildren); - UpdateASR(old, containerASRForChildren); - old->UpdateBounds(&mBuilder); + while (nsDisplayItem* newItem = aNewList->RemoveBottom()) { + if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) { + // The new item has a matching counterpart in the old list that we haven't yet reached, + // so copy all valid items from the old list into the merged list until we get to the + // matched item. + nsDisplayItem* old = nullptr; + while ((old = aOldList->GetBottom()) && old != oldItem) { + if (IsAnyAncestorModified(old->FrameForInvalidation())) { + // The old item is invalid, discard it. + oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() }); + aOldList->RemoveBottom(); + old->Destroy(&mBuilder); + } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) { + // This old item is also in the new list, but we haven't got to it yet. + // Stop now, and we'll deal with it when we get to the new entry. + break; + } else { + // Recurse into the child list (without a matching new list) to + // ensure that we find and remove any invalidated items. + if (old->GetChildren()) { + nsDisplayList empty; + Maybe containerASRForChildren; + MergeDisplayLists(&empty, old->GetChildren(), + old->GetChildren(), containerASRForChildren); + UpdateASR(old, containerASRForChildren); + old->UpdateBounds(&mBuilder); + } + aOldList->RemoveBottom(); + ReuseItem(old); } + } + bool destroy = false; + if (old == oldItem) { + // If we advanced the old list until the matching item then we can pop + // the matching item off the old list and make sure we clean it up. aOldList->RemoveBottom(); - ReuseItem(old); + destroy = true; + } else { + // If we didn't get to the matching item, then mark the old item + // as being reused (since we're adding the new version to the new + // list now) so that we don't add it twice at the end. + oldItem->SetReused(true); } - } - bool destroy = false; - if (old == oldItem) { - // If we advanced the old list until the matching item then we can pop - // the matching item off the old list and make sure we clean it up. - aOldList->RemoveBottom(); - destroy = true; - } else { - // If we didn't get to the matching item, then mark the old item - // as being reused (since we're adding the new version to the new - // list now) so that we don't add it twice at the end. - oldItem->SetReused(true); - } - // Recursively merge any child lists, destroy the old item and add - // the new one to the list. - if (!destroy && - oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS && - !IsAnyAncestorModified(oldItem->FrameForInvalidation())) { - // Event regions items don't have anything interesting other than - // the lists of regions and frames, so we have no need to use the - // newer item. Always use the old item instead since we assume it's - // likely to have the bigger lists and merging will be quicker. - MergeLayerEventRegions(oldItem, newItem); - ReuseItem(oldItem); - newItem->Destroy(&mBuilder); - } else { - if (!IsAnyAncestorModified(oldItem->FrameForInvalidation()) && - oldItem->GetChildren()) { - MOZ_ASSERT(newItem->GetChildren()); - Maybe containerASRForChildren; - MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(), - newItem->GetChildren(), containerASRForChildren); - UpdateASR(newItem, containerASRForChildren); - newItem->UpdateBounds(&mBuilder); - } + // Recursively merge any child lists, destroy the old item and add + // the new one to the list. + if (!destroy && + oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS && + !IsAnyAncestorModified(oldItem->FrameForInvalidation())) { + // Event regions items don't have anything interesting other than + // the lists of regions and frames, so we have no need to use the + // newer item. Always use the old item instead since we assume it's + // likely to have the bigger lists and merging will be quicker. + MergeLayerEventRegions(oldItem, newItem); + ReuseItem(oldItem); + newItem->Destroy(&mBuilder); + } else { + if (!IsAnyAncestorModified(oldItem->FrameForInvalidation()) && + oldItem->GetChildren()) { + MOZ_ASSERT(newItem->GetChildren()); + Maybe containerASRForChildren; + MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(), + newItem->GetChildren(), containerASRForChildren); + UpdateASR(newItem, containerASRForChildren); + newItem->UpdateBounds(&mBuilder); + } - if (destroy) { - oldItem->Destroy(&mBuilder); + if (destroy) { + oldItem->Destroy(&mBuilder); + } + UseItem(newItem); } + } else { + // If there was no matching item in the old list, then we only need to + // add the new item to the merged list. UseItem(newItem); } - } else { - // If there was no matching item in the old list, then we only need to - // add the new item to the merged list. - UseItem(newItem); } } // Reuse the remaining valid items from the old display list. while (nsDisplayItem* old = aOldList->RemoveBottom()) { if (!IsAnyAncestorModified(old->FrameForInvalidation()) && - !old->IsReused()) { + (!old->IsReused() || aNewList->IsEmpty())) { if (old->GetChildren()) { // We are calling MergeDisplayLists() to ensure that the display items // with modified or deleted children will be correctly handled.