Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/client/TextureClientPool.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "TextureClientPool.h"
8
#include "CompositableClient.h"
9
#include "mozilla/layers/CompositableForwarder.h"
10
#include "mozilla/layers/TextureForwarder.h"
11
#include "mozilla/layers/TiledContentClient.h"
12
13
#include "gfxPrefs.h"
14
15
#include "nsComponentManagerUtils.h"
16
17
#define TCP_LOG(...)
18
//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
19
20
namespace mozilla {
21
namespace layers {
22
23
// We want to shrink to our maximum size of N unused tiles
24
// after a timeout to allow for short-term budget requirements
25
static void
26
ShrinkCallback(nsITimer *aTimer, void *aClosure)
27
0
{
28
0
  static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
29
0
}
30
31
// After a certain amount of inactivity, let's clear the pool so that
32
// we don't hold onto tiles needlessly. In general, allocations are
33
// cheap enough that re-allocating isn't an issue unless we're allocating
34
// at an inopportune time (e.g. mid-animation).
35
static void
36
ClearCallback(nsITimer *aTimer, void *aClosure)
37
0
{
38
0
  static_cast<TextureClientPool*>(aClosure)->Clear();
39
0
}
40
41
TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
42
                                     bool aSupportsTextureDirectMapping,
43
                                     int32_t aMaxTextureSize,
44
                                     gfx::SurfaceFormat aFormat,
45
                                     gfx::IntSize aSize,
46
                                     TextureFlags aFlags,
47
                                     uint32_t aShrinkTimeoutMsec,
48
                                     uint32_t aClearTimeoutMsec,
49
                                     uint32_t aInitialPoolSize,
50
                                     uint32_t aPoolUnusedSize,
51
                                     TextureForwarder* aAllocator)
52
  : mBackend(aLayersBackend)
53
  , mMaxTextureSize(aMaxTextureSize)
54
  , mFormat(aFormat)
55
  , mSize(aSize)
56
  , mFlags(aFlags)
57
  , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
58
  , mClearTimeoutMsec(aClearTimeoutMsec)
59
  , mInitialPoolSize(aInitialPoolSize)
60
  , mPoolUnusedSize(aPoolUnusedSize)
61
  , mOutstandingClients(0)
62
  , mSurfaceAllocator(aAllocator)
63
  , mDestroyed(false)
64
  , mSupportsTextureDirectMapping(aSupportsTextureDirectMapping)
65
0
{
66
0
  TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
67
0
      this, mInitialPoolSize);
68
0
  mShrinkTimer = NS_NewTimer();
69
0
  mClearTimer = NS_NewTimer();
70
0
  if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
71
0
    gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
72
0
  }
73
0
}
74
75
TextureClientPool::~TextureClientPool()
76
0
{
77
0
  mShrinkTimer->Cancel();
78
0
  mClearTimer->Cancel();
79
0
}
80
81
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
82
static bool TestClientPool(const char* what,
83
                           TextureClient* aClient,
84
                           TextureClientPool* aPool)
85
{
86
  if (!aClient || !aPool) {
87
    return false;
88
  }
89
90
  TextureClientPool* actual = aClient->mPoolTracker;
91
  bool ok = (actual == aPool);
92
  if (ok) {
93
    ok = (aClient->GetFormat() == aPool->GetFormat());
94
  }
95
96
  if (!ok) {
97
    if (actual) {
98
      gfxCriticalError() << "Pool error(" << what << "): "
99
                   << aPool << "-" << aPool->GetFormat() << ", "
100
                   << actual << "-" << actual->GetFormat() << ", "
101
                   << aClient->GetFormat();
102
      MOZ_CRASH("GFX: Crashing with actual");
103
    } else {
104
      gfxCriticalError() << "Pool error(" << what << "): "
105
                   << aPool << "-" << aPool->GetFormat() << ", nullptr, "
106
                   << aClient->GetFormat();
107
      MOZ_CRASH("GFX: Crashing without actual");
108
    }
109
  }
110
  return ok;
111
}
112
#endif
113
114
already_AddRefed<TextureClient>
115
TextureClientPool::GetTextureClient()
116
0
{
117
0
  // Try to fetch a client from the pool
118
0
  RefPtr<TextureClient> textureClient;
119
0
120
0
  // We initially allocate mInitialPoolSize for our pool. If we run
121
0
  // out of TextureClients, we allocate additional TextureClients to try and keep around
122
0
  // mPoolUnusedSize
123
0
  if (mTextureClients.empty()) {
124
0
    AllocateTextureClient();
125
0
  }
126
0
127
0
  if (mTextureClients.empty()) {
128
0
    // All our allocations failed, return nullptr
129
0
    return nullptr;
130
0
  }
131
0
132
0
  mOutstandingClients++;
133
0
  textureClient = mTextureClients.top();
134
0
  mTextureClients.pop();
135
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
136
  if (textureClient) {
137
    textureClient->mPoolTracker = this;
138
  }
139
  DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
140
  MOZ_ASSERT(ok);
141
#endif
142
  TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
143
0
      this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
144
0
145
0
  return textureClient.forget();
146
0
}
147
148
void
149
TextureClientPool::AllocateTextureClient()
150
0
{
151
0
  TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
152
0
      this, mOutstandingClients);
153
0
154
0
  TextureAllocationFlags allocFlags = ALLOC_DEFAULT;
155
0
156
0
  if (mSupportsTextureDirectMapping && std::max(mSize.width, mSize.height) <= mMaxTextureSize) {
157
0
    allocFlags = TextureAllocationFlags(allocFlags | ALLOC_ALLOW_DIRECT_MAPPING);
158
0
  }
159
0
160
0
  RefPtr<TextureClient> newClient;
161
0
  if (gfxPrefs::ForceShmemTiles()) {
162
0
    // gfx::BackendType::NONE means use the content backend
163
0
    newClient =
164
0
      TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
165
0
                                              mFormat, mSize,
166
0
                                              gfx::BackendType::NONE,
167
0
                                              mBackend,
168
0
                                              mFlags, allocFlags);
169
0
  } else {
170
0
    newClient =
171
0
      TextureClient::CreateForDrawing(mSurfaceAllocator,
172
0
                                      mFormat, mSize,
173
0
                                      mBackend,
174
0
                                      mMaxTextureSize,
175
0
                                      BackendSelector::Content,
176
0
                                      mFlags, allocFlags);
177
0
  }
178
0
179
0
  if (newClient) {
180
0
    mTextureClients.push(newClient);
181
0
  }
182
0
}
183
184
void
185
TextureClientPool::ResetTimers()
186
0
{
187
0
  // Shrink down if we're beyond our maximum size
188
0
  if (mShrinkTimeoutMsec &&
189
0
      mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
190
0
    TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
191
0
    mShrinkTimer->InitWithNamedFuncCallback(
192
0
      ShrinkCallback,
193
0
      this,
194
0
      mShrinkTimeoutMsec,
195
0
      nsITimer::TYPE_ONE_SHOT,
196
0
      "layers::TextureClientPool::ResetTimers");
197
0
  }
198
0
199
0
  // Clear pool after a period of inactivity to reduce memory consumption
200
0
  if (mClearTimeoutMsec) {
201
0
    TCP_LOG("TexturePool %p scheduling a clear\n", this);
202
0
    mClearTimer->InitWithNamedFuncCallback(
203
0
      ClearCallback,
204
0
      this,
205
0
      mClearTimeoutMsec,
206
0
      nsITimer::TYPE_ONE_SHOT,
207
0
      "layers::TextureClientPool::ResetTimers");
208
0
  }
209
0
}
210
211
void
212
TextureClientPool::ReturnTextureClient(TextureClient *aClient)
213
0
{
214
0
  if (!aClient || mDestroyed) {
215
0
    return;
216
0
  }
217
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
218
  DebugOnly<bool> ok = TestClientPool("return", aClient, this);
219
  MOZ_ASSERT(ok);
220
#endif
221
  // Add the client to the pool:
222
0
  MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
223
0
  mOutstandingClients--;
224
0
  mTextureClients.push(aClient);
225
0
  TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
226
0
      this, aClient, mTextureClients.size(), mOutstandingClients);
227
0
228
0
  ResetTimers();
229
0
}
230
231
void
232
TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
233
0
{
234
0
  if (!aClient || mDestroyed) {
235
0
    return;
236
0
  }
237
0
  MOZ_ASSERT(aClient->GetReadLock());
238
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
239
  DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
240
  MOZ_ASSERT(ok);
241
#endif
242
  mTextureClientsDeferred.push_back(aClient);
243
0
  TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
244
0
      this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
245
0
246
0
  ResetTimers();
247
0
}
248
249
void
250
TextureClientPool::ShrinkToMaximumSize()
251
0
{
252
0
  // We're over our desired maximum size, immediately shrink down to the
253
0
  // maximum.
254
0
  //
255
0
  // We cull from the deferred TextureClients first, as we can't reuse those
256
0
  // until they get returned.
257
0
  uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
258
0
259
0
  // If we have > mInitialPoolSize outstanding, then we want to keep around
260
0
  // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
261
0
  // outstanding, then keep around the entire initial pool size.
262
0
  uint32_t targetUnusedClients;
263
0
  if (mOutstandingClients > mInitialPoolSize) {
264
0
    targetUnusedClients = mPoolUnusedSize;
265
0
  } else {
266
0
    targetUnusedClients = mInitialPoolSize;
267
0
  }
268
0
269
0
  TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
270
0
      this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
271
0
272
0
  while (totalUnusedTextureClients > targetUnusedClients) {
273
0
    if (!mTextureClientsDeferred.empty()) {
274
0
      mOutstandingClients--;
275
0
      TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
276
0
          this, mTextureClientsDeferred.front().get(),
277
0
          mTextureClientsDeferred.size() - 1);
278
0
      mTextureClientsDeferred.pop_front();
279
0
    } else {
280
0
      TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
281
0
          this, mTextureClients.top().get(), mTextureClients.size() - 1);
282
0
      mTextureClients.pop();
283
0
    }
284
0
    totalUnusedTextureClients--;
285
0
  }
286
0
}
287
288
void
289
TextureClientPool::ReturnDeferredClients()
290
0
{
291
0
  if (mTextureClientsDeferred.empty()) {
292
0
    return;
293
0
  }
294
0
295
0
  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
296
0
      this, mTextureClientsDeferred.size());
297
0
298
0
  ReturnUnlockedClients();
299
0
  ShrinkToMaximumSize();
300
0
}
301
302
void
303
TextureClientPool::ReturnUnlockedClients()
304
0
{
305
0
  for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
306
0
    MOZ_ASSERT((*it)->GetReadLock()->AsNonBlockingLock()->GetReadCount() >= 1);
307
0
    // Last count is held by the lock itself.
308
0
    if (!(*it)->IsReadLocked()) {
309
0
      mTextureClients.push(*it);
310
0
      it = mTextureClientsDeferred.erase(it);
311
0
312
0
      MOZ_ASSERT(mOutstandingClients > 0);
313
0
      mOutstandingClients--;
314
0
    } else {
315
0
      it++;
316
0
    }
317
0
  }
318
0
}
319
320
void
321
TextureClientPool::ReportClientLost()
322
0
{
323
0
  MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
324
0
  mOutstandingClients--;
325
0
  TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
326
0
      this, mOutstandingClients);
327
0
}
328
329
void
330
TextureClientPool::Clear()
331
0
{
332
0
  TCP_LOG("TexturePool %p getting cleared\n", this);
333
0
  while (!mTextureClients.empty()) {
334
0
    TCP_LOG("TexturePool %p releasing client %p\n",
335
0
        this, mTextureClients.top().get());
336
0
    mTextureClients.pop();
337
0
  }
338
0
  while (!mTextureClientsDeferred.empty()) {
339
0
    MOZ_ASSERT(mOutstandingClients > 0);
340
0
    mOutstandingClients--;
341
0
    TCP_LOG("TexturePool %p releasing deferred client %p\n",
342
0
        this, mTextureClientsDeferred.front().get());
343
0
    mTextureClientsDeferred.pop_front();
344
0
  }
345
0
}
346
347
void TextureClientPool::Destroy()
348
0
{
349
0
  Clear();
350
0
  mDestroyed = true;
351
0
  mInitialPoolSize = 0;
352
0
  mPoolUnusedSize = 0;
353
0
}
354
355
} // namespace layers
356
} // namespace mozilla