Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/client/TextureClientRecycleAllocator.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 "gfxPlatform.h"
8
#include "ImageContainer.h"
9
#include "mozilla/layers/BufferTexture.h"
10
#include "mozilla/layers/ISurfaceAllocator.h"
11
#include "mozilla/layers/TextureForwarder.h"
12
#include "TextureClientRecycleAllocator.h"
13
14
namespace mozilla {
15
namespace layers {
16
17
// Used to keep TextureClient's reference count stable as not to disrupt recycling.
18
class TextureClientHolder
19
{
20
0
  ~TextureClientHolder() {}
21
public:
22
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
23
24
  explicit TextureClientHolder(TextureClient* aClient)
25
    : mTextureClient(aClient)
26
    , mWillRecycle(true)
27
0
  {}
28
29
  TextureClient* GetTextureClient()
30
0
  {
31
0
    return mTextureClient;
32
0
  }
33
34
  bool WillRecycle()
35
0
  {
36
0
    return mWillRecycle;
37
0
  }
38
39
  void ClearWillRecycle()
40
0
  {
41
0
    mWillRecycle = false;
42
0
  }
43
44
0
  void ClearTextureClient() { mTextureClient = nullptr; }
45
protected:
46
  RefPtr<TextureClient> mTextureClient;
47
  bool mWillRecycle;
48
};
49
50
class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
51
{
52
public:
53
  DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
54
                                       gfx::SurfaceFormat aFormat,
55
                                       gfx::IntSize aSize,
56
                                       BackendSelector aSelector,
57
                                       TextureFlags aTextureFlags,
58
                                       TextureAllocationFlags aAllocationFlags)
59
    : ITextureClientAllocationHelper(aFormat,
60
                                     aSize,
61
                                     aSelector,
62
                                     aTextureFlags,
63
                                     aAllocationFlags)
64
    , mAllocator(aAllocator)
65
0
  {}
66
67
  bool IsCompatible(TextureClient* aTextureClient) override
68
0
  {
69
0
    if (aTextureClient->GetFormat() != mFormat ||
70
0
        aTextureClient->GetSize() != mSize) {
71
0
      return false;
72
0
    }
73
0
    return true;
74
0
  }
75
76
  already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
77
0
  {
78
0
    return mAllocator->Allocate(mFormat,
79
0
                                mSize,
80
0
                                mSelector,
81
0
                                mTextureFlags,
82
0
                                mAllocationFlags);
83
0
  }
84
85
protected:
86
  TextureClientRecycleAllocator* mAllocator;
87
};
88
89
YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
90
                                                                       TextureFlags aTextureFlags)
91
  : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
92
                                   aData.mYSize,
93
                                   BackendSelector::Content,
94
                                   aTextureFlags,
95
                                   ALLOC_DEFAULT)
96
  , mData(aData)
97
0
{
98
0
}
99
100
bool
101
YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
102
0
{
103
0
  MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
104
0
105
0
  BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
106
0
  if (!bufferData ||
107
0
      aTextureClient->GetSize() != mData.mYSize ||
108
0
      bufferData->GetCbCrSize().isNothing() ||
109
0
      bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
110
0
      bufferData->GetYUVColorSpace().isNothing() ||
111
0
      bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
112
0
      bufferData->GetBitDepth().isNothing() ||
113
0
      bufferData->GetBitDepth().ref() != mData.mBitDepth ||
114
0
      bufferData->GetStereoMode().isNothing() ||
115
0
      bufferData->GetStereoMode().ref() != mData.mStereoMode) {
116
0
    return false;
117
0
  }
118
0
  return true;
119
0
}
120
121
already_AddRefed<TextureClient>
122
YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
123
0
{
124
0
  return TextureClient::CreateForYCbCr(aAllocator,
125
0
                                       mData.mYSize, mData.mYStride,
126
0
                                       mData.mCbCrSize, mData.mCbCrStride,
127
0
                                       mData.mStereoMode,
128
0
                                       mData.mYUVColorSpace,
129
0
                                       mData.mBitDepth,
130
0
                                       mTextureFlags);
131
0
}
132
133
TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
134
  : mSurfaceAllocator(aAllocator)
135
  , mMaxPooledSize(kMaxPooledSized)
136
  , mLock("TextureClientRecycleAllocatorImp.mLock")
137
  , mIsDestroyed(false)
138
0
{
139
0
}
140
141
TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
142
0
{
143
0
  MutexAutoLock lock(mLock);
144
0
  while (!mPooledClients.empty()) {
145
0
    mPooledClients.pop();
146
0
  }
147
0
  MOZ_ASSERT(mInUseClients.empty());
148
0
}
149
150
void
151
TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
152
0
{
153
0
  mMaxPooledSize = aMax;
154
0
}
155
156
already_AddRefed<TextureClient>
157
TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
158
                                               gfx::IntSize aSize,
159
                                               BackendSelector aSelector,
160
                                               TextureFlags aTextureFlags,
161
                                               TextureAllocationFlags aAllocFlags)
162
0
{
163
0
  MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
164
0
  DefaultTextureClientAllocationHelper helper(this,
165
0
                                              aFormat,
166
0
                                              aSize,
167
0
                                              aSelector,
168
0
                                              aTextureFlags,
169
0
                                              aAllocFlags);
170
0
  return CreateOrRecycle(helper);
171
0
}
172
173
already_AddRefed<TextureClient>
174
TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
175
0
{
176
0
  MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
177
0
178
0
  RefPtr<TextureClientHolder> textureHolder;
179
0
180
0
  {
181
0
    MutexAutoLock lock(mLock);
182
0
    if (mIsDestroyed) {
183
0
      return nullptr;
184
0
    }
185
0
    if (!mPooledClients.empty()) {
186
0
      textureHolder = mPooledClients.top();
187
0
      mPooledClients.pop();
188
0
      // If the texture's allocator is not open or a pooled TextureClient is
189
0
      // not compatible, release it.
190
0
      if (!textureHolder->GetTextureClient()->GetAllocator()->IPCOpen() ||
191
0
          !aHelper.IsCompatible(textureHolder->GetTextureClient())) {
192
0
        // Release TextureClient.
193
0
        RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
194
0
        textureHolder->ClearTextureClient();
195
0
        textureHolder = nullptr;
196
0
        mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
197
0
      } else {
198
0
        textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
199
0
      }
200
0
    }
201
0
  }
202
0
203
0
  if (!textureHolder) {
204
0
    // Allocate new TextureClient
205
0
    RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
206
0
    if (!texture) {
207
0
      return nullptr;
208
0
    }
209
0
    textureHolder = new TextureClientHolder(texture);
210
0
  }
211
0
212
0
  {
213
0
    MutexAutoLock lock(mLock);
214
0
    MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
215
0
    // Register TextureClient
216
0
    mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
217
0
  }
218
0
  RefPtr<TextureClient> client(textureHolder->GetTextureClient());
219
0
220
0
  // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
221
0
  // ref count drops to 1.
222
0
  client->SetRecycleAllocator(this);
223
0
  return client.forget();
224
0
}
225
226
already_AddRefed<TextureClient>
227
TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
228
                                        gfx::IntSize aSize,
229
                                        BackendSelector aSelector,
230
                                        TextureFlags aTextureFlags,
231
                                        TextureAllocationFlags aAllocFlags)
232
0
{
233
0
  return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
234
0
                                         aSelector, aTextureFlags, aAllocFlags);
235
0
}
236
237
void
238
TextureClientRecycleAllocator::ShrinkToMinimumSize()
239
0
{
240
0
  MutexAutoLock lock(mLock);
241
0
  while (!mPooledClients.empty()) {
242
0
    mPooledClients.pop();
243
0
  }
244
0
  // We can not clear using TextureClients safely.
245
0
  // Just clear WillRecycle here.
246
0
  std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
247
0
  for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
248
0
    RefPtr<TextureClientHolder> holder = it->second;
249
0
    holder->ClearWillRecycle();
250
0
  }
251
0
}
252
253
void
254
TextureClientRecycleAllocator::Destroy()
255
0
{
256
0
  MutexAutoLock lock(mLock);
257
0
  while (!mPooledClients.empty()) {
258
0
    mPooledClients.pop();
259
0
  }
260
0
  mIsDestroyed = true;
261
0
}
262
263
void
264
TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
265
0
{
266
0
  // Clearing the recycle allocator drops a reference, so make sure we stay alive
267
0
  // for the duration of this function.
268
0
  RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
269
0
  aClient->SetRecycleAllocator(nullptr);
270
0
271
0
  RefPtr<TextureClientHolder> textureHolder;
272
0
  {
273
0
    MutexAutoLock lock(mLock);
274
0
    if (mInUseClients.find(aClient) != mInUseClients.end()) {
275
0
      textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
276
0
      if (textureHolder->WillRecycle() &&
277
0
          !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
278
0
        mPooledClients.push(textureHolder);
279
0
      }
280
0
      mInUseClients.erase(aClient);
281
0
    }
282
0
  }
283
0
}
284
285
} // namespace layers
286
} // namespace mozilla