summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/DisplayItemCache.h
blob: a44b4a52f571c73525a318697e39d5ca61a45b28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/* -*- 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 GFX_DISPLAY_ITEM_CACHE_H
#define GFX_DISPLAY_ITEM_CACHE_H

#include "mozilla/webrender/WebRenderAPI.h"
#include "nsTArray.h"

namespace mozilla {

class nsDisplayList;
class nsDisplayListBuilder;
class nsPaintedDisplayItem;

namespace wr {
class DisplayListBuilder;
}  // namespace wr

namespace layers {

class CacheStats {
 public:
  CacheStats() = default;

  void Reset() { mCached = mReused = mTotal = 0; }

  void Print() {
    static uint64_t avgC = 1;
    static uint64_t avgR = 1;
    static uint64_t avgT = 1;

    avgC += mCached;
    avgR += mReused;
    avgT += mTotal;

    printf("Cached: %zu (avg: %f), Reused: %zu (avg: %f), Total: %zu\n",
           mCached, (double)avgC / (double)avgT, mReused,
           (double)avgR / (double)avgT, mTotal);
  }

  void AddCached() { mCached++; }
  void AddReused() { mReused++; }
  void AddTotal() { mTotal++; }

 private:
  size_t mCached = 0;
  size_t mReused = 0;
  size_t mTotal = 0;
};

/**
 * DisplayItemCache keeps track of which Gecko display items have already had
 * their respective WebRender display items sent to WebRender backend.
 *
 * Ideally creating the WR display items for a Gecko display item would not
 * depend on any external state. However currently pipeline id, clip id, and
 * spatial id can change between display lists, even if the Gecko display items
 * have not. This state is tracked by DisplayItemCache.
 */
class DisplayItemCache final {
 public:
  DisplayItemCache();

  /**
   * Clears the cache.
   */
  void Clear();

  /**
   * Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|.
   */
  void SetCapacity(const size_t aInitialSize, const size_t aMaximumSize);

  /**
   * Sets the display list used by the cache.
   */
  void SetDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayList* aList);

  /**
   * Sets the pipeline id used by the cache.
   */
  void SetPipelineId(const wr::PipelineId& aPipelineId);

  /**
   * Enables caching immediately if the cache is valid, and display list is set.
   */
  void SkipWaitingForPartialDisplayList() {
    mCaching = mDisplayList && !mInvalid;
  }

  /**
   * Returns true if display item caching is enabled, otherwise false.
   */
  bool IsEnabled() const { return !mSuppressed && mMaximumSize > 0; }

  /**
   * Suppress display item caching. This doesn't clear any existing cached
   * items or change the underlying capacity, it just makes IsEnabled() return
   * false.
   * It will also make CanReuseItem return false for the duration of the
   * suppression.
   */
  bool SetSuppressed(bool aSuppressed) {
    if (aSuppressed == mSuppressed) {
      return mSuppressed;
    }
    mSuppressed = aSuppressed;
    return !mSuppressed;
  }

  /**
   * Returns true if there are no cached items, otherwise false.
   */
  bool IsEmpty() const { return mFreeSlots.Length() == CurrentSize(); }

  /**
   * Returns true if the cache has reached the maximum size, otherwise false.
   */
  bool IsFull() const {
    return mFreeSlots.IsEmpty() && CurrentSize() == mMaximumSize;
  }

  /**
   * Returns the current cache size.
   */
  size_t CurrentSize() const { return mSlots.Length(); }

  /**
   * If there are free slots in the cache, assigns a cache slot to the given
   * display item |aItem| and returns it. Otherwise returns Nothing().
   */
  Maybe<uint16_t> AssignSlot(nsPaintedDisplayItem* aItem);

  /**
   * Marks the slot with the given |slotIndex| occupied and used.
   * Also stores the current space and clipchain |aSpaceAndClip|.
   */
  void MarkSlotOccupied(uint16_t slotIndex,
                        const wr::WrSpaceAndClipChain& aSpaceAndClip);

  /**
   * Returns the slot index of the the given display item |aItem|, if the item
   * can be reused. The current space and clipchain |aSpaceAndClip| is used to
   * check whether the cached item is still valid.
   * If the item cannot be reused, returns Nothing().
   */
  Maybe<uint16_t> CanReuseItem(nsPaintedDisplayItem* aItem,
                               const wr::WrSpaceAndClipChain& aSpaceAndClip);

  CacheStats& Stats() { return mCacheStats; }

 private:
  struct Slot {
    Slot() : mSpaceAndClip{}, mOccupied(false), mUsed(false) {}

    wr::WrSpaceAndClipChain mSpaceAndClip;
    bool mOccupied;
    bool mUsed;
  };

  void FreeUnusedSlots();
  Maybe<uint16_t> GetNextFreeSlot();
  bool GrowIfPossible();
  void UpdateState();

  // The lifetime of display lists exceed the lifetime of DisplayItemCache.
  // This pointer stores the address of the display list that is using this
  // cache, and it is only used for pointer comparisons.
  nsDisplayList* mDisplayList;

  size_t mMaximumSize;
  nsTArray<Slot> mSlots;
  nsTArray<uint16_t> mFreeSlots;

  wr::PipelineId mPipelineId;
  bool mCaching;
  bool mInvalid;
  bool mSuppressed;

  CacheStats mCacheStats;
};

class MOZ_RAII AutoDisplayItemCacheSuppressor {
 public:
  explicit AutoDisplayItemCacheSuppressor(DisplayItemCache* aCache)
      : mCache(aCache) {
    mWasSuppressed = mCache->SetSuppressed(true);
  }

  // Note that this restores the original state rather than unconditionally
  // unsuppressing the cache for future-proofing/robustification. Currently
  // we only ever use this RAII in one non-recursive function, but we might
  // decide to expand its usage to other scenarios and end up with nested
  // suppressions, in which case restoring the state back to what we found it
  // is better.
  ~AutoDisplayItemCacheSuppressor() { mCache->SetSuppressed(mWasSuppressed); }

 private:
  DisplayItemCache* mCache;
  bool mWasSuppressed;
};

}  // namespace layers
}  // namespace mozilla

#endif /* GFX_DISPLAY_ITEM_CACHE_H */