RetainedDisplayListBuilder.h (9040B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef RETAINEDDISPLAYLISTBUILDER_H_ 8 #define RETAINEDDISPLAYLISTBUILDER_H_ 9 10 #include "mozilla/EnumSet.h" 11 #include "mozilla/Maybe.h" 12 #include "nsDisplayList.h" 13 14 class nsWindowSizes; 15 16 namespace mozilla { 17 18 class nsDisplayItem; 19 class nsDisplayList; 20 21 /** 22 * RetainedDisplayListData contains frame invalidation information. 23 * Currently this is implemented as a map of frame pointers to flags. 24 */ 25 struct RetainedDisplayListData { 26 enum class FrameFlag : uint8_t { Modified, HasProps, HadWillChange }; 27 using FrameFlags = mozilla::EnumSet<FrameFlag, uint8_t>; 28 29 RetainedDisplayListData(); 30 31 /** 32 * Adds the frame to modified frames list. 33 */ 34 void AddModifiedFrame(nsIFrame* aFrame); 35 36 /** 37 * Removes all the frames from this RetainedDisplayListData. 38 */ 39 void Clear() { 40 mFrames.Clear(); 41 mModifiedFrameCount = 0; 42 } 43 44 /** 45 * Returns a mutable reference to flags set for the given |aFrame|. 46 */ 47 FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.LookupOrInsert(aFrame); } 48 49 /** 50 * Returns flags set for the given |aFrame|, or FrameFlags::None if the frame 51 * is not in this RetainedDisplayListData. 52 */ 53 FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); } 54 55 bool IsModified(nsIFrame* aFrame) const { 56 return GetFlags(aFrame).contains(FrameFlag::Modified); 57 } 58 59 bool HasProps(nsIFrame* aFrame) const { 60 return GetFlags(aFrame).contains(FrameFlag::HasProps); 61 } 62 63 /** 64 * Returns an iterator to the underlying frame storage. 65 */ 66 auto ConstIterator() { return mFrames.ConstIter(); } 67 68 /** 69 * Returns true if the modified frame limit has been reached. 70 */ 71 bool AtModifiedFrameLimit() { 72 return mModifiedFrameCount >= mModifiedFrameLimit; 73 } 74 75 bool GetModifiedFrameCount() { return mModifiedFrameCount; } 76 77 /** 78 * Removes the given |aFrame| from this RetainedDisplayListData. 79 */ 80 bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); } 81 82 private: 83 nsTHashMap<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames; 84 uint32_t mModifiedFrameCount = 0; 85 uint32_t mModifiedFrameLimit; // initialized to a pref value in constructor 86 }; 87 88 enum class PartialUpdateResult { Failed, NoChange, Updated }; 89 90 enum class PartialUpdateFailReason { 91 NA, 92 EmptyList, 93 RebuildLimit, 94 FrameType, 95 Disabled, 96 Content, 97 VisibleRect, 98 }; 99 100 struct RetainedDisplayListMetrics { 101 RetainedDisplayListMetrics() { Reset(); } 102 103 void Reset() { 104 mNewItems = 0; 105 mRebuiltItems = 0; 106 mRemovedItems = 0; 107 mReusedItems = 0; 108 mTotalItems = 0; 109 mPartialBuildDuration = 0; 110 mFullBuildDuration = 0; 111 mPartialUpdateFailReason = PartialUpdateFailReason::NA; 112 mPartialUpdateResult = PartialUpdateResult::NoChange; 113 } 114 115 void StartBuild() { mStartTime = mozilla::TimeStamp::Now(); } 116 117 void EndFullBuild() { mFullBuildDuration = Elapsed(); } 118 119 void EndPartialBuild(PartialUpdateResult aResult) { 120 mPartialBuildDuration = Elapsed(); 121 mPartialUpdateResult = aResult; 122 } 123 124 double Elapsed() { 125 return (mozilla::TimeStamp::Now() - mStartTime).ToMilliseconds(); 126 } 127 128 const char* FailReasonString() const { 129 switch (mPartialUpdateFailReason) { 130 case PartialUpdateFailReason::NA: 131 return "N/A"; 132 case PartialUpdateFailReason::EmptyList: 133 return "Empty list"; 134 case PartialUpdateFailReason::RebuildLimit: 135 return "Rebuild limit"; 136 case PartialUpdateFailReason::FrameType: 137 return "Frame type"; 138 case PartialUpdateFailReason::Disabled: 139 return "Disabled"; 140 case PartialUpdateFailReason::Content: 141 return "Content"; 142 case PartialUpdateFailReason::VisibleRect: 143 return "VisibleRect"; 144 default: 145 MOZ_ASSERT_UNREACHABLE("Enum value not handled!"); 146 } 147 } 148 149 unsigned int mNewItems; 150 unsigned int mRebuiltItems; 151 unsigned int mRemovedItems; 152 unsigned int mReusedItems; 153 unsigned int mTotalItems; 154 155 mozilla::TimeStamp mStartTime; 156 double mPartialBuildDuration; 157 double mFullBuildDuration; 158 PartialUpdateFailReason mPartialUpdateFailReason; 159 PartialUpdateResult mPartialUpdateResult; 160 }; 161 162 class RetainedDisplayListBuilder { 163 public: 164 RetainedDisplayListBuilder(nsIFrame* aReferenceFrame, 165 nsDisplayListBuilderMode aMode, bool aBuildCaret) 166 : mBuilder(aReferenceFrame, aMode, aBuildCaret, true), mList(&mBuilder) {} 167 ~RetainedDisplayListBuilder() { 168 mBuilder.SetIsDestroying(); 169 mList.DeleteAll(&mBuilder); 170 } 171 172 nsDisplayListBuilder* Builder() { return &mBuilder; } 173 174 nsDisplayList* List() { return &mList; } 175 176 RetainedDisplayListMetrics* Metrics() { return &mMetrics; } 177 178 RetainedDisplayListData* Data() { return &mData; } 179 180 PartialUpdateResult AttemptPartialUpdate(nscolor aBackstop); 181 182 /** 183 * Clears the modified state for frames in the retained display list data. 184 */ 185 void ClearFramesWithProps(); 186 187 void ClearRetainedData(); 188 189 void ClearReuseableDisplayItems() { mBuilder.ClearReuseableDisplayItems(); } 190 191 void AddSizeOfIncludingThis(nsWindowSizes&) const; 192 193 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder) 194 195 private: 196 void GetModifiedAndFramesWithProps(nsTArray<nsIFrame*>* aOutModifiedFrames, 197 nsTArray<nsIFrame*>* aOutFramesWithProps); 198 199 void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem); 200 201 /** 202 * A simple early exit heuristic to avoid slow partial display list rebuilds. 203 * Returns true if a partial display list build should be attempted. 204 */ 205 bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames); 206 207 /** 208 * Recursively pre-processes the old display list tree before building the 209 * new partial display lists, and serializes the old list into an array, 210 * recording indices on items for fast lookup during merging. Builds an 211 * initial linear DAG for the list if we don't have an existing one. Finds 212 * items that have a different AGR from the specified one, and marks them to 213 * also be built so that we get relative ordering correct. Passes 214 * aKeepLinked=true internally for sub-lists that can't be changed to keep the 215 * original list structure linked for fast re-use. 216 */ 217 bool PreProcessDisplayList( 218 RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated, 219 nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR, 220 nsIFrame* aOuterFrame = nullptr, uint32_t aCallerKey = 0, 221 uint32_t aNestingDepth = 0, bool aKeepLinked = false); 222 223 /** 224 * Merges items from aNewList into non-invalidated items from aOldList and 225 * stores the result in aOutList. 226 * 227 * aOuterItem is a pointer to an item that owns one of the lists, if 228 * available. If both lists are populated, then both outer items must not be 229 * invalidated, and identical, so either can be passed here. 230 * 231 * Returns true if changes were made, and the resulting display list (in 232 * aOutList) is different from aOldList. 233 */ 234 bool MergeDisplayLists( 235 nsDisplayList* aNewList, RetainedDisplayList* aOldList, 236 RetainedDisplayList* aOutList, 237 mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR, 238 nsDisplayItem* aOuterItem = nullptr); 239 240 bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames, 241 nsRect* aOutDirty, nsIFrame** aOutModifiedAGR, 242 nsTArray<nsIFrame*>& aOutFramesWithProps); 243 244 bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, 245 nsIFrame* aStopAtFrame, 246 nsTArray<nsIFrame*>& aOutFramesWithProps, 247 const bool aStopAtStackingContext, nsRect* aOutDirty, 248 nsIFrame** aOutModifiedAGR); 249 250 nsIFrame* RootReferenceFrame() { return mBuilder.RootReferenceFrame(); } 251 const nsIFrame* RootReferenceFrame() const { 252 return mBuilder.RootReferenceFrame(); 253 } 254 255 nsRect RootOverflowRect() const; 256 257 /** 258 * Tries to perform a simple partial display list build without display list 259 * merging. In this mode, only the top-level stacking context items and their 260 * contents are reused, when the frame subtree has not been modified. 261 */ 262 bool TrySimpleUpdate(const nsTArray<nsIFrame*>& aModifiedFrames, 263 nsTArray<nsIFrame*>& aOutFramesWithProps); 264 265 friend class MergeState; 266 267 nsDisplayListBuilder mBuilder; 268 RetainedDisplayList mList; 269 RetainedDisplayListMetrics mMetrics; 270 RetainedDisplayListData mData; 271 }; 272 273 namespace RDLUtils { 274 275 void AssertFrameSubtreeUnmodified(const nsIFrame* aFrame); 276 void AssertDisplayItemUnmodified(nsDisplayItem* aItem); 277 void AssertDisplayListUnmodified(nsDisplayList* aList); 278 279 } // namespace RDLUtils 280 } // namespace mozilla 281 282 #endif // RETAINEDDISPLAYLISTBUILDER_H_