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 "nsDisplayList.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/TypedEnumBits.h" 13 14 class nsWindowSizes; 15 16 /** 17 * RetainedDisplayListData contains frame invalidation information. It is stored 18 * in root frames, and used by RetainedDisplayListBuilder. 19 * Currently this is implemented as a map of frame pointers to flags. 20 */ 21 struct RetainedDisplayListData { 22 NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListData, RetainedDisplayListData) 23 24 enum class FrameFlags : uint8_t { 25 None = 0, 26 Modified = 1 << 0, 27 HasProps = 1 << 1, 28 HadWillChange = 1 << 2 29 }; 30 RetainedDisplayListDataRetainedDisplayListData31 RetainedDisplayListData() : mModifiedFramesCount(0) {} 32 33 /** 34 * Adds the frame to modified frames list. 35 */ 36 void AddModifiedFrame(nsIFrame* aFrame); 37 38 /** 39 * Removes all the frames from this RetainedDisplayListData. 40 */ ClearRetainedDisplayListData41 void Clear() { 42 mFrames.Clear(); 43 mModifiedFramesCount = 0; 44 } 45 46 /** 47 * Returns a mutable reference to flags set for the given |aFrame|. If the 48 * frame does not exist in this RetainedDisplayListData, it is added with 49 * default constructible flags FrameFlags::None. 50 */ FlagsRetainedDisplayListData51 FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.LookupOrInsert(aFrame); } 52 53 /** 54 * Returns flags set for the given |aFrame|, or FrameFlags::None if the frame 55 * is not in this RetainedDisplayListData. 56 */ GetFlagsRetainedDisplayListData57 FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); } 58 59 /** 60 * Returns an iterator to the underlying frame storage. 61 */ ConstIteratorRetainedDisplayListData62 auto ConstIterator() { return mFrames.ConstIter(); } 63 64 /** 65 * Returns the count of modified frames in this RetainedDisplayListData. 66 */ ModifiedFramesCountRetainedDisplayListData67 uint32_t ModifiedFramesCount() const { return mModifiedFramesCount; } 68 69 /** 70 * Removes the given |aFrame| from this RetainedDisplayListData. 71 */ RemoveRetainedDisplayListData72 bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); } 73 74 private: 75 nsTHashMap<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames; 76 uint32_t mModifiedFramesCount; 77 }; 78 79 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(RetainedDisplayListData::FrameFlags) 80 81 /** 82 * Returns RetainedDisplayListData property for the given |aRootFrame|, or 83 * nullptr if the property is not set. 84 */ 85 RetainedDisplayListData* GetRetainedDisplayListData(nsIFrame* aRootFrame); 86 87 /** 88 * Returns RetainedDisplayListData property for the given |aRootFrame|. Creates 89 * and sets a new RetainedDisplayListData property if it is not already set. 90 */ 91 RetainedDisplayListData* GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame); 92 93 enum class PartialUpdateResult { Failed, NoChange, Updated }; 94 95 enum class PartialUpdateFailReason { 96 NA, 97 EmptyList, 98 RebuildLimit, 99 FrameType, 100 Disabled, 101 Content, 102 VisibleRect, 103 }; 104 105 struct RetainedDisplayListMetrics { RetainedDisplayListMetricsRetainedDisplayListMetrics106 RetainedDisplayListMetrics() { Reset(); } 107 ResetRetainedDisplayListMetrics108 void Reset() { 109 mNewItems = 0; 110 mRebuiltItems = 0; 111 mRemovedItems = 0; 112 mReusedItems = 0; 113 mTotalItems = 0; 114 mPartialBuildDuration = 0; 115 mFullBuildDuration = 0; 116 mPartialUpdateFailReason = PartialUpdateFailReason::NA; 117 mPartialUpdateResult = PartialUpdateResult::NoChange; 118 } 119 StartBuildRetainedDisplayListMetrics120 void StartBuild() { mStartTime = mozilla::TimeStamp::Now(); } 121 EndFullBuildRetainedDisplayListMetrics122 void EndFullBuild() { mFullBuildDuration = Elapsed(); } 123 EndPartialBuildRetainedDisplayListMetrics124 void EndPartialBuild(PartialUpdateResult aResult) { 125 mPartialBuildDuration = Elapsed(); 126 mPartialUpdateResult = aResult; 127 } 128 ElapsedRetainedDisplayListMetrics129 double Elapsed() { 130 return (mozilla::TimeStamp::Now() - mStartTime).ToMilliseconds(); 131 } 132 FailReasonStringRetainedDisplayListMetrics133 const char* FailReasonString() const { 134 switch (mPartialUpdateFailReason) { 135 case PartialUpdateFailReason::NA: 136 return "N/A"; 137 case PartialUpdateFailReason::EmptyList: 138 return "Empty list"; 139 case PartialUpdateFailReason::RebuildLimit: 140 return "Rebuild limit"; 141 case PartialUpdateFailReason::FrameType: 142 return "Frame type"; 143 case PartialUpdateFailReason::Disabled: 144 return "Disabled"; 145 case PartialUpdateFailReason::Content: 146 return "Content"; 147 case PartialUpdateFailReason::VisibleRect: 148 return "VisibleRect"; 149 default: 150 MOZ_ASSERT_UNREACHABLE("Enum value not handled!"); 151 } 152 } 153 154 unsigned int mNewItems; 155 unsigned int mRebuiltItems; 156 unsigned int mRemovedItems; 157 unsigned int mReusedItems; 158 unsigned int mTotalItems; 159 160 mozilla::TimeStamp mStartTime; 161 double mPartialBuildDuration; 162 double mFullBuildDuration; 163 PartialUpdateFailReason mPartialUpdateFailReason; 164 PartialUpdateResult mPartialUpdateResult; 165 }; 166 167 struct RetainedDisplayListBuilder { RetainedDisplayListBuilderRetainedDisplayListBuilder168 RetainedDisplayListBuilder(nsIFrame* aReferenceFrame, 169 nsDisplayListBuilderMode aMode, bool aBuildCaret) 170 : mBuilder(aReferenceFrame, aMode, aBuildCaret, true) {} ~RetainedDisplayListBuilderRetainedDisplayListBuilder171 ~RetainedDisplayListBuilder() { mList.DeleteAll(&mBuilder); } 172 BuilderRetainedDisplayListBuilder173 nsDisplayListBuilder* Builder() { return &mBuilder; } 174 ListRetainedDisplayListBuilder175 nsDisplayList* List() { return &mList; } 176 MetricsRetainedDisplayListBuilder177 RetainedDisplayListMetrics* Metrics() { return &mMetrics; } 178 179 PartialUpdateResult AttemptPartialUpdate(nscolor aBackstop); 180 181 /** 182 * Iterates through the display list builder reference frame document and 183 * subdocuments, and clears the modified frame lists from the root frames. 184 * Also clears the frame properties set by RetainedDisplayListBuilder for all 185 * the frames in the modified frame lists. 186 */ 187 void ClearFramesWithProps(); 188 void AddSizeOfIncludingThis(nsWindowSizes&) const; 189 190 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder) 191 192 private: 193 void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem); 194 195 /** 196 * Invalidates the current and previous caret frame if they have changed. 197 */ 198 void InvalidateCaretFramesIfNeeded(); 199 200 /** 201 * A simple early exit heuristic to avoid slow partial display list rebuilds. 202 * Returns true if a partial display list build should be attempted. 203 */ 204 bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames); 205 206 /** 207 * Recursively pre-processes the old display list tree before building the 208 * new partial display lists, and serializes the old list into an array, 209 * recording indices on items for fast lookup during merging. Builds an 210 * initial linear DAG for the list if we don't have an existing one. Finds 211 * items that have a different AGR from the specified one, and marks them to 212 * also be built so that we get relative ordering correct. Passes 213 * aKeepLinked=true internally for sub-lists that can't be changed to keep the 214 * original list structure linked for fast re-use. 215 */ 216 bool PreProcessDisplayList(RetainedDisplayList* aList, 217 AnimatedGeometryRoot* aAGR, 218 PartialUpdateResult& aUpdated, 219 nsIFrame* aOuterFrame = nullptr, 220 uint32_t aCallerKey = 0, 221 uint32_t aNestingDepth = 0, 222 bool aKeepLinked = false); 223 224 /** 225 * Merges items from aNewList into non-invalidated items from aOldList and 226 * stores the result in aOutList. 227 * 228 * aOuterItem is a pointer to an item that owns one of the lists, if 229 * available. If both lists are populated, then both outer items must not be 230 * invalidated, and identical, so either can be passed here. 231 * 232 * Returns true if changes were made, and the resulting display list (in 233 * aOutList) is different from aOldList. 234 */ 235 bool MergeDisplayLists( 236 nsDisplayList* aNewList, RetainedDisplayList* aOldList, 237 RetainedDisplayList* aOutList, 238 mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR, 239 nsDisplayItem* aOuterItem = nullptr); 240 241 bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames, 242 nsRect* aOutDirty, 243 AnimatedGeometryRoot** aOutModifiedAGR, 244 nsTArray<nsIFrame*>& aOutFramesWithProps); 245 246 bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, 247 nsIFrame* aStopAtFrame, 248 nsTArray<nsIFrame*>& aOutFramesWithProps, 249 const bool aStopAtStackingContext, nsRect* aOutDirty, 250 AnimatedGeometryRoot** aOutModifiedAGR); 251 252 friend class MergeState; 253 254 nsDisplayListBuilder mBuilder; 255 RetainedDisplayList mList; 256 nsRect mPreviousVisibleRect; 257 WeakFrame mPreviousCaret; 258 RetainedDisplayListMetrics mMetrics; 259 }; 260 261 #endif // RETAINEDDISPLAYLISTBUILDER_H_ 262