Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxGradientCache.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/gfx/2D.h"
7
#include "nsTArray.h"
8
#include "PLDHashTable.h"
9
#include "nsExpirationTracker.h"
10
#include "nsClassHashtable.h"
11
#include "mozilla/SystemGroup.h"
12
#include "gfxGradientCache.h"
13
#include <time.h>
14
15
namespace mozilla {
16
namespace gfx {
17
18
using namespace mozilla;
19
20
struct GradientCacheKey : public PLDHashEntryHdr {
21
  typedef const GradientCacheKey& KeyType;
22
  typedef const GradientCacheKey* KeyTypePointer;
23
  enum { ALLOW_MEMMOVE = true };
24
  const nsTArray<GradientStop> mStops;
25
  ExtendMode mExtend;
26
  BackendType mBackendType;
27
28
  GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
29
    : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
30
0
  { }
31
32
  explicit GradientCacheKey(const GradientCacheKey* aOther)
33
    : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
34
0
  { }
35
36
0
  GradientCacheKey(GradientCacheKey&& aOther) = default;
37
38
  union FloatUint32
39
  {
40
    float    f;
41
    uint32_t u;
42
  };
43
44
  static PLDHashNumber
45
  HashKey(const KeyTypePointer aKey)
46
0
  {
47
0
    PLDHashNumber hash = 0;
48
0
    FloatUint32 convert;
49
0
    hash = AddToHash(hash, int(aKey->mBackendType));
50
0
    hash = AddToHash(hash, int(aKey->mExtend));
51
0
    for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
52
0
      hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
53
0
      // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
54
0
      convert.f = aKey->mStops[i].offset;
55
0
      hash = AddToHash(hash, convert.f ? convert.u : 0);
56
0
    }
57
0
    return hash;
58
0
  }
59
60
  bool KeyEquals(KeyTypePointer aKey) const
61
0
  {
62
0
    bool sameStops = true;
63
0
    if (aKey->mStops.Length() != mStops.Length()) {
64
0
      sameStops = false;
65
0
    } else {
66
0
      for (uint32_t i = 0; i < mStops.Length(); i++) {
67
0
        if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
68
0
            mStops[i].offset != aKey->mStops[i].offset) {
69
0
          sameStops = false;
70
0
          break;
71
0
        }
72
0
      }
73
0
    }
74
0
75
0
    return sameStops &&
76
0
           (aKey->mBackendType == mBackendType) &&
77
0
           (aKey->mExtend == mExtend);
78
0
  }
79
  static KeyTypePointer KeyToPointer(KeyType aKey)
80
0
  {
81
0
    return &aKey;
82
0
  }
83
};
84
85
/**
86
 * This class is what is cached. It need to be allocated in an object separated
87
 * to the cache entry to be able to be tracked by the nsExpirationTracker.
88
 * */
89
struct GradientCacheData {
90
  GradientCacheData(GradientStops* aStops, GradientCacheKey&& aKey)
91
    : mStops(aStops),
92
      mKey(std::move(aKey))
93
0
  {}
94
95
  GradientCacheData(GradientCacheData&& aOther) = default;
96
97
0
  nsExpirationState *GetExpirationState() {
98
0
    return &mExpirationState;
99
0
  }
100
101
  nsExpirationState mExpirationState;
102
  const RefPtr<GradientStops> mStops;
103
  GradientCacheKey mKey;
104
};
105
106
/**
107
 * This class implements a cache with no maximum size, that retains the
108
 * gfxPatterns used to draw the gradients.
109
 *
110
 * The key is the nsStyleGradient that defines the gradient, and the size of the
111
 * gradient.
112
 *
113
 * The value is the gfxPattern, and whether or not we perform an optimization
114
 * based on the actual gradient property.
115
 *
116
 * An entry stays in the cache as long as it is used often. As long as a cache
117
 * entry is in the cache, all the references it has are guaranteed to be valid:
118
 * the nsStyleRect for the key, the gfxPattern for the value.
119
 */
120
class GradientCache final : public nsExpirationTracker<GradientCacheData,4>
121
{
122
  public:
123
    GradientCache()
124
      : nsExpirationTracker<GradientCacheData,4>(MAX_GENERATION_MS,
125
                                                 "GradientCache",
126
                                                 SystemGroup::EventTargetFor(TaskCategory::Other))
127
0
    {
128
0
      srand(time(nullptr));
129
0
    }
130
131
    virtual void NotifyExpired(GradientCacheData* aObject) override
132
0
    {
133
0
      // This will free the gfxPattern.
134
0
      RemoveObject(aObject);
135
0
      mHashEntries.Remove(aObject->mKey);
136
0
    }
137
138
    GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
139
0
    {
140
0
      GradientCacheData* gradient =
141
0
        mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
142
0
143
0
      if (gradient) {
144
0
        MarkUsed(gradient);
145
0
      }
146
0
147
0
      return gradient;
148
0
    }
149
150
    // Returns true if we successfully register the gradient in the cache, false
151
    // otherwise.
152
    bool RegisterEntry(GradientCacheData* aValue)
153
0
    {
154
0
      nsresult rv = AddObject(aValue);
155
0
      if (NS_FAILED(rv)) {
156
0
        // We are OOM, and we cannot track this object. We don't want stall
157
0
        // entries in the hash table (since the expiration tracker is responsible
158
0
        // for removing the cache entries), so we avoid putting that entry in the
159
0
        // table, which is a good things considering we are short on memory
160
0
        // anyway, we probably don't want to retain things.
161
0
        return false;
162
0
      }
163
0
      mHashEntries.Put(aValue->mKey, aValue);
164
0
      return true;
165
0
    }
166
167
  protected:
168
    static const uint32_t MAX_GENERATION_MS = 10000;
169
    /**
170
     * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
171
     * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
172
     */
173
    nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
174
};
175
176
static GradientCache* gGradientCache = nullptr;
177
178
GradientStops *
179
gfxGradientCache::GetGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
180
0
{
181
0
  if (!gGradientCache) {
182
0
    gGradientCache = new GradientCache();
183
0
  }
184
0
  GradientCacheData* cached =
185
0
    gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType());
186
0
  if (cached && cached->mStops) {
187
0
    if (!cached->mStops->IsValid()) {
188
0
      gGradientCache->NotifyExpired(cached);
189
0
    } else {
190
0
      return cached->mStops;
191
0
    }
192
0
  }
193
0
194
0
  return nullptr;
195
0
}
196
197
already_AddRefed<GradientStops>
198
gfxGradientCache::GetOrCreateGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
199
0
{
200
0
  if (aDT->IsRecording()) {
201
0
    return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
202
0
  }
203
0
204
0
  RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
205
0
  if (!gs) {
206
0
    gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
207
0
    if (!gs) {
208
0
      return nullptr;
209
0
    }
210
0
    GradientCacheData *cached =
211
0
      new GradientCacheData(gs, GradientCacheKey(aStops, aExtend,
212
0
                                                 aDT->GetBackendType()));
213
0
    if (!gGradientCache->RegisterEntry(cached)) {
214
0
      delete cached;
215
0
    }
216
0
  }
217
0
  return gs.forget();
218
0
}
219
220
void
221
gfxGradientCache::PurgeAllCaches()
222
0
{
223
0
  if (gGradientCache) {
224
0
    gGradientCache->AgeAllGenerations();
225
0
  }
226
0
}
227
228
void
229
gfxGradientCache::Shutdown()
230
0
{
231
0
  delete gGradientCache;
232
0
  gGradientCache = nullptr;
233
0
}
234
235
} // namespace gfx
236
} // namespace mozilla