mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			279 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#ifndef RETAINEDDISPLAYLISTBUILDER_H_
 | 
						|
#define RETAINEDDISPLAYLISTBUILDER_H_
 | 
						|
 | 
						|
#include "nsDisplayList.h"
 | 
						|
#include "mozilla/Maybe.h"
 | 
						|
#include "mozilla/EnumSet.h"
 | 
						|
 | 
						|
class nsWindowSizes;
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
class nsDisplayItem;
 | 
						|
class nsDisplayList;
 | 
						|
 | 
						|
/**
 | 
						|
 * RetainedDisplayListData contains frame invalidation information.
 | 
						|
 * Currently this is implemented as a map of frame pointers to flags.
 | 
						|
 */
 | 
						|
struct RetainedDisplayListData {
 | 
						|
  enum class FrameFlag : uint8_t { Modified, HasProps, HadWillChange };
 | 
						|
  using FrameFlags = mozilla::EnumSet<FrameFlag, uint8_t>;
 | 
						|
 | 
						|
  RetainedDisplayListData();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adds the frame to modified frames list.
 | 
						|
   */
 | 
						|
  void AddModifiedFrame(nsIFrame* aFrame);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Removes all the frames from this RetainedDisplayListData.
 | 
						|
   */
 | 
						|
  void Clear() {
 | 
						|
    mFrames.Clear();
 | 
						|
    mModifiedFrameCount = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a mutable reference to flags set for the given |aFrame|.
 | 
						|
   */
 | 
						|
  FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.LookupOrInsert(aFrame); }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns flags set for the given |aFrame|, or FrameFlags::None if the frame
 | 
						|
   * is not in this RetainedDisplayListData.
 | 
						|
   */
 | 
						|
  FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); }
 | 
						|
 | 
						|
  bool IsModified(nsIFrame* aFrame) const {
 | 
						|
    return GetFlags(aFrame).contains(FrameFlag::Modified);
 | 
						|
  }
 | 
						|
 | 
						|
  bool HasProps(nsIFrame* aFrame) const {
 | 
						|
    return GetFlags(aFrame).contains(FrameFlag::HasProps);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns an iterator to the underlying frame storage.
 | 
						|
   */
 | 
						|
  auto ConstIterator() { return mFrames.ConstIter(); }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns true if the modified frame limit has been reached.
 | 
						|
   */
 | 
						|
  bool AtModifiedFrameLimit() {
 | 
						|
    return mModifiedFrameCount >= mModifiedFrameLimit;
 | 
						|
  }
 | 
						|
 | 
						|
  bool GetModifiedFrameCount() { return mModifiedFrameCount; }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Removes the given |aFrame| from this RetainedDisplayListData.
 | 
						|
   */
 | 
						|
  bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); }
 | 
						|
 | 
						|
 private:
 | 
						|
  nsTHashMap<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames;
 | 
						|
  uint32_t mModifiedFrameCount = 0;
 | 
						|
  uint32_t mModifiedFrameLimit;  // initialized to a pref value in constructor
 | 
						|
};
 | 
						|
 | 
						|
enum class PartialUpdateResult { Failed, NoChange, Updated };
 | 
						|
 | 
						|
enum class PartialUpdateFailReason {
 | 
						|
  NA,
 | 
						|
  EmptyList,
 | 
						|
  RebuildLimit,
 | 
						|
  FrameType,
 | 
						|
  Disabled,
 | 
						|
  Content,
 | 
						|
  VisibleRect,
 | 
						|
};
 | 
						|
 | 
						|
struct RetainedDisplayListMetrics {
 | 
						|
  RetainedDisplayListMetrics() { Reset(); }
 | 
						|
 | 
						|
  void Reset() {
 | 
						|
    mNewItems = 0;
 | 
						|
    mRebuiltItems = 0;
 | 
						|
    mRemovedItems = 0;
 | 
						|
    mReusedItems = 0;
 | 
						|
    mTotalItems = 0;
 | 
						|
    mPartialBuildDuration = 0;
 | 
						|
    mFullBuildDuration = 0;
 | 
						|
    mPartialUpdateFailReason = PartialUpdateFailReason::NA;
 | 
						|
    mPartialUpdateResult = PartialUpdateResult::NoChange;
 | 
						|
  }
 | 
						|
 | 
						|
  void StartBuild() { mStartTime = mozilla::TimeStamp::Now(); }
 | 
						|
 | 
						|
  void EndFullBuild() { mFullBuildDuration = Elapsed(); }
 | 
						|
 | 
						|
  void EndPartialBuild(PartialUpdateResult aResult) {
 | 
						|
    mPartialBuildDuration = Elapsed();
 | 
						|
    mPartialUpdateResult = aResult;
 | 
						|
  }
 | 
						|
 | 
						|
  double Elapsed() {
 | 
						|
    return (mozilla::TimeStamp::Now() - mStartTime).ToMilliseconds();
 | 
						|
  }
 | 
						|
 | 
						|
  const char* FailReasonString() const {
 | 
						|
    switch (mPartialUpdateFailReason) {
 | 
						|
      case PartialUpdateFailReason::NA:
 | 
						|
        return "N/A";
 | 
						|
      case PartialUpdateFailReason::EmptyList:
 | 
						|
        return "Empty list";
 | 
						|
      case PartialUpdateFailReason::RebuildLimit:
 | 
						|
        return "Rebuild limit";
 | 
						|
      case PartialUpdateFailReason::FrameType:
 | 
						|
        return "Frame type";
 | 
						|
      case PartialUpdateFailReason::Disabled:
 | 
						|
        return "Disabled";
 | 
						|
      case PartialUpdateFailReason::Content:
 | 
						|
        return "Content";
 | 
						|
      case PartialUpdateFailReason::VisibleRect:
 | 
						|
        return "VisibleRect";
 | 
						|
      default:
 | 
						|
        MOZ_ASSERT_UNREACHABLE("Enum value not handled!");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  unsigned int mNewItems;
 | 
						|
  unsigned int mRebuiltItems;
 | 
						|
  unsigned int mRemovedItems;
 | 
						|
  unsigned int mReusedItems;
 | 
						|
  unsigned int mTotalItems;
 | 
						|
 | 
						|
  mozilla::TimeStamp mStartTime;
 | 
						|
  double mPartialBuildDuration;
 | 
						|
  double mFullBuildDuration;
 | 
						|
  PartialUpdateFailReason mPartialUpdateFailReason;
 | 
						|
  PartialUpdateResult mPartialUpdateResult;
 | 
						|
};
 | 
						|
 | 
						|
class RetainedDisplayListBuilder {
 | 
						|
 public:
 | 
						|
  RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
 | 
						|
                             nsDisplayListBuilderMode aMode, bool aBuildCaret)
 | 
						|
      : mBuilder(aReferenceFrame, aMode, aBuildCaret, true), mList(&mBuilder) {}
 | 
						|
  ~RetainedDisplayListBuilder() { mList.DeleteAll(&mBuilder); }
 | 
						|
 | 
						|
  nsDisplayListBuilder* Builder() { return &mBuilder; }
 | 
						|
 | 
						|
  nsDisplayList* List() { return &mList; }
 | 
						|
 | 
						|
  RetainedDisplayListMetrics* Metrics() { return &mMetrics; }
 | 
						|
 | 
						|
  RetainedDisplayListData* Data() { return &mData; }
 | 
						|
 | 
						|
  PartialUpdateResult AttemptPartialUpdate(nscolor aBackstop);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Clears the modified state for frames in the retained display list data.
 | 
						|
   */
 | 
						|
  void ClearFramesWithProps();
 | 
						|
 | 
						|
  void ClearRetainedData();
 | 
						|
 | 
						|
  void ClearReuseableDisplayItems() { mBuilder.ClearReuseableDisplayItems(); }
 | 
						|
 | 
						|
  void AddSizeOfIncludingThis(nsWindowSizes&) const;
 | 
						|
 | 
						|
  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 | 
						|
 | 
						|
 private:
 | 
						|
  void GetModifiedAndFramesWithProps(nsTArray<nsIFrame*>* aOutModifiedFrames,
 | 
						|
                                     nsTArray<nsIFrame*>* aOutFramesWithProps);
 | 
						|
 | 
						|
  void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 | 
						|
 | 
						|
  /**
 | 
						|
   * A simple early exit heuristic to avoid slow partial display list rebuilds.
 | 
						|
   * Returns true if a partial display list build should be attempted.
 | 
						|
   */
 | 
						|
  bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Recursively pre-processes the old display list tree before building the
 | 
						|
   * new partial display lists, and serializes the old list into an array,
 | 
						|
   * recording indices on items for fast lookup during merging. Builds an
 | 
						|
   * initial linear DAG for the list if we don't have an existing one. Finds
 | 
						|
   * items that have a different AGR from the specified one, and marks them to
 | 
						|
   * also be built so that we get relative ordering correct. Passes
 | 
						|
   * aKeepLinked=true internally for sub-lists that can't be changed to keep the
 | 
						|
   * original list structure linked for fast re-use.
 | 
						|
   */
 | 
						|
  bool PreProcessDisplayList(
 | 
						|
      RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated,
 | 
						|
      nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR,
 | 
						|
      nsIFrame* aOuterFrame = nullptr, uint32_t aCallerKey = 0,
 | 
						|
      uint32_t aNestingDepth = 0, bool aKeepLinked = false);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Merges items from aNewList into non-invalidated items from aOldList and
 | 
						|
   * stores the result in aOutList.
 | 
						|
   *
 | 
						|
   * aOuterItem is a pointer to an item that owns one of the lists, if
 | 
						|
   * available. If both lists are populated, then both outer items must not be
 | 
						|
   * invalidated, and identical, so either can be passed here.
 | 
						|
   *
 | 
						|
   * Returns true if changes were made, and the resulting display list (in
 | 
						|
   * aOutList) is different from aOldList.
 | 
						|
   */
 | 
						|
  bool MergeDisplayLists(
 | 
						|
      nsDisplayList* aNewList, RetainedDisplayList* aOldList,
 | 
						|
      RetainedDisplayList* aOutList,
 | 
						|
      mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
 | 
						|
      nsDisplayItem* aOuterItem = nullptr);
 | 
						|
 | 
						|
  bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
 | 
						|
                            nsRect* aOutDirty, nsIFrame** aOutModifiedAGR,
 | 
						|
                            nsTArray<nsIFrame*>& aOutFramesWithProps);
 | 
						|
 | 
						|
  bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
 | 
						|
                    nsIFrame* aStopAtFrame,
 | 
						|
                    nsTArray<nsIFrame*>& aOutFramesWithProps,
 | 
						|
                    const bool aStopAtStackingContext, nsRect* aOutDirty,
 | 
						|
                    nsIFrame** aOutModifiedAGR);
 | 
						|
 | 
						|
  nsIFrame* RootReferenceFrame() { return mBuilder.RootReferenceFrame(); }
 | 
						|
  const nsIFrame* RootReferenceFrame() const {
 | 
						|
    return mBuilder.RootReferenceFrame();
 | 
						|
  }
 | 
						|
 | 
						|
  nsRect RootOverflowRect() const;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Tries to perform a simple partial display list build without display list
 | 
						|
   * merging. In this mode, only the top-level stacking context items and their
 | 
						|
   * contents are reused, when the frame subtree has not been modified.
 | 
						|
   */
 | 
						|
  bool TrySimpleUpdate(const nsTArray<nsIFrame*>& aModifiedFrames,
 | 
						|
                       nsTArray<nsIFrame*>& aOutFramesWithProps);
 | 
						|
 | 
						|
  friend class MergeState;
 | 
						|
 | 
						|
  nsDisplayListBuilder mBuilder;
 | 
						|
  RetainedDisplayList mList;
 | 
						|
  RetainedDisplayListMetrics mMetrics;
 | 
						|
  RetainedDisplayListData mData;
 | 
						|
};
 | 
						|
 | 
						|
namespace RDLUtils {
 | 
						|
 | 
						|
void AssertFrameSubtreeUnmodified(const nsIFrame* aFrame);
 | 
						|
void AssertDisplayItemUnmodified(nsDisplayItem* aItem);
 | 
						|
void AssertDisplayListUnmodified(nsDisplayList* aList);
 | 
						|
 | 
						|
}  // namespace RDLUtils
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
#endif  // RETAINEDDISPLAYLISTBUILDER_H_
 |