Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nullptr; 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/layers/SurfacePoolCA.h"
7
8
#import <CoreVideo/CVPixelBuffer.h>
9
10
#include <algorithm>
11
#include <unordered_set>
12
#include <utility>
13
14
#include "mozilla/StaticMutex.h"
15
#include "mozilla/StaticPrefs_gfx.h"
16
17
#include "GLContextCGL.h"
18
#include "MozFramebuffer.h"
19
20
namespace mozilla {
21
namespace layers {
22
23
using gfx::IntPoint;
24
using gfx::IntSize;
25
using gfx::IntRect;
26
using gfx::IntRegion;
27
using gl::GLContext;
28
using gl::GLContextCGL;
29
30
/* static */ RefPtr<SurfacePool> SurfacePool::Create(size_t aPoolSizeLimit) {
31
return new SurfacePoolCA(aPoolSizeLimit);
32
}
33
34
// SurfacePoolCA::LockedPool
35
36
SurfacePoolCA::LockedPool::LockedPool(size_t aPoolSizeLimit) : mPoolSizeLimit(aPoolSizeLimit) {}
37
38
SurfacePoolCA::LockedPool::~LockedPool() {
39
MOZ_RELEASE_ASSERT(mWrappers.empty(),
40
"Any outstanding wrappers should have kept the surface pool alive");
41
MOZ_RELEASE_ASSERT(mInUseEntries.empty(),
42
"Leak! No more surfaces should be in use at this point.");
43
// Remove all entries in mPendingEntries and mAvailableEntries.
44
MutateEntryStorage("Clear", {}, [&]() {
45
mPendingEntries.Clear();
46
mAvailableEntries.Clear();
47
});
48
}
49
50
RefPtr<SurfacePoolCAWrapperForGL> SurfacePoolCA::LockedPool::GetWrapperForGL(SurfacePoolCA* aPool,
51
GLContext* aGL) {
52
auto& wrapper = mWrappers[aGL];
53
if (!wrapper) {
54
wrapper = new SurfacePoolCAWrapperForGL(aPool, aGL);
55
}
56
return wrapper;
57
}
58
59
void SurfacePoolCA::LockedPool::DestroyGLResourcesForContext(GLContext* aGL) {
60
ForEachEntry([&](SurfacePoolEntry& entry) {
61
if (entry.mGLResources && entry.mGLResources->mGLContext == aGL) {
62
entry.mGLResources = Nothing();
63
}
64
});
65
mDepthBuffers.RemoveElementsBy(
66
[&](const DepthBufferEntry& entry) { return entry.mGLContext == aGL; });
67
}
68
69
template <typename F>
70
void SurfacePoolCA::LockedPool::MutateEntryStorage(const char* aMutationType,
71
const gfx::IntSize& aSize, F aFn) {
72
#ifdef MOZ_GECKO_PROFILER
73
size_t inUseCountBefore = mInUseEntries.size();
74
size_t pendingCountBefore = mPendingEntries.Length();
75
size_t availableCountBefore = mAvailableEntries.Length();
76
TimeStamp before = TimeStamp::NowUnfuzzed();
77
#endif
78
79
aFn();
80
81
#ifdef MOZ_GECKO_PROFILER
82
if (profiler_thread_is_being_profiled()) {
83
profiler_add_text_marker(
84
"SurfacePool",
85
nsPrintfCString("%d -> %d in use | %d -> %d waiting for | %d -> %d available | %s %dx%d | "
86
"%dMB total memory",
87
int(inUseCountBefore), int(mInUseEntries.size()), int(pendingCountBefore),
88
int(mPendingEntries.Length()), int(availableCountBefore),
89
int(mAvailableEntries.Length()), aMutationType, aSize.width, aSize.height,
90
int(EstimateTotalMemory() / 1000 / 1000)),
91
JS::ProfilingCategoryPair::GRAPHICS, before, TimeStamp::NowUnfuzzed());
92
}
93
#endif
94
}
95
96
template <typename F>
97
void SurfacePoolCA::LockedPool::ForEachEntry(F aFn) {
98
for (auto& iter : mInUseEntries) {
99
aFn(iter.second);
100
}
101
for (auto& entry : mPendingEntries) {
102
aFn(entry.mEntry);
103
}
104
for (auto& entry : mAvailableEntries) {
105
aFn(entry);
106
}
107
}
108
109
uint64_t SurfacePoolCA::LockedPool::EstimateTotalMemory() {
110
std::unordered_set<const gl::DepthAndStencilBuffer*> depthAndStencilBuffers;
111
uint64_t memBytes = 0;
112
113
ForEachEntry([&](const SurfacePoolEntry& entry) {
114
auto size = entry.mSize;
115
memBytes += size.width * 4 * size.height;
116
if (entry.mGLResources) {
117
const auto& fb = *entry.mGLResources->mFramebuffer;
118
if (const auto& buffer = fb.GetDepthAndStencilBuffer()) {
119
depthAndStencilBuffers.insert(buffer.get());
120
}
121
}
122
});
123
124
for (const auto& buffer : depthAndStencilBuffers) {
125
memBytes += buffer->EstimateMemory();
126
}
127
128
return memBytes;
129
}
130
131
bool SurfacePoolCA::LockedPool::CanRecycleSurfaceForRequest(const SurfacePoolEntry& aEntry,
132
const IntSize& aSize, GLContext* aGL) {
133
if (aEntry.mSize != aSize) {
134
return false;
135
}
136
if (aEntry.mGLResources) {
137
return aEntry.mGLResources->mGLContext == aGL;
138
}
139
return true;
140
}
141
142
CFTypeRefPtr<IOSurfaceRef> SurfacePoolCA::LockedPool::ObtainSurfaceFromPool(const IntSize& aSize,
143
GLContext* aGL) {
144
// Do a linear scan through mAvailableEntries to find an eligible suface, going from oldest to
145
// newest. The size of this array is limited, so the linear scan is fast.
146
auto iterToRecycle = std::find_if(mAvailableEntries.begin(), mAvailableEntries.end(),
147
[&](const SurfacePoolEntry& aEntry) {
148
return CanRecycleSurfaceForRequest(aEntry, aSize, aGL);
149
});
150
if (iterToRecycle != mAvailableEntries.end()) {
151
CFTypeRefPtr<IOSurfaceRef> surface = iterToRecycle->mIOSurface;
152
// Move the entry from mAvailableEntries to mInUseEntries.
153
MutateEntryStorage("Recycle", aSize, [&]() {
154
mInUseEntries.insert({surface, std::move(*iterToRecycle)});
155
mAvailableEntries.RemoveElementAt(iterToRecycle);
156
});
157
return surface;
158
}
159
160
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("IOSurface creation", GRAPHICS_TileAllocation,
161
nsPrintfCString("%dx%d", aSize.width, aSize.height));
162
CFTypeRefPtr<IOSurfaceRef> surface =
163
CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(IOSurfaceCreate((__bridge CFDictionaryRef) @{
164
(__bridge NSString*)kIOSurfaceWidth : @(aSize.width),
165
(__bridge NSString*)kIOSurfaceHeight : @(aSize.height),
166
(__bridge NSString*)kIOSurfacePixelFormat : @(kCVPixelFormatType_32BGRA),
167
(__bridge NSString*)kIOSurfaceBytesPerElement : @(4),
168
}));
169
if (surface) {
170
// Create a new entry in mInUseEntries.
171
MutateEntryStorage("Create", aSize, [&]() {
172
mInUseEntries.insert({surface, SurfacePoolEntry{aSize, surface, {}}});
173
});
174
}
175
return surface;
176
}
177
178
void SurfacePoolCA::LockedPool::ReturnSurfaceToPool(CFTypeRefPtr<IOSurfaceRef> aSurface) {
179
auto inUseEntryIter = mInUseEntries.find(aSurface);
180
MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
181
if (IOSurfaceIsInUse(aSurface.get())) {
182
// Move the entry from mInUseEntries to mPendingEntries.
183
MutateEntryStorage("Start waiting for", IntSize(inUseEntryIter->second.mSize), [&]() {
184
mPendingEntries.AppendElement(
185
PendingSurfaceEntry{std::move(inUseEntryIter->second), mCollectionGeneration, 0});
186
mInUseEntries.erase(inUseEntryIter);
187
});
188
} else {
189
// Move the entry from mInUseEntries to mAvailableEntries.
190
MutateEntryStorage("Retain", IntSize(inUseEntryIter->second.mSize), [&]() {
191
mAvailableEntries.AppendElement(std::move(inUseEntryIter->second));
192
mInUseEntries.erase(inUseEntryIter);
193
});
194
}
195
}
196
197
void SurfacePoolCA::LockedPool::EnforcePoolSizeLimit() {
198
// Enforce the pool size limit, removing least-recently-used entries as necessary.
199
while (mAvailableEntries.Length() > mPoolSizeLimit) {
200
MutateEntryStorage("Evict", IntSize(mAvailableEntries[0].mSize),
201
[&]() { mAvailableEntries.RemoveElementAt(0); });
202
}
203
}
204
205
uint64_t SurfacePoolCA::LockedPool::CollectPendingSurfaces(uint64_t aCheckGenerationsUpTo) {
206
mCollectionGeneration++;
207
208
// Loop from back to front, potentially deleting items as we iterate.
209
// mPendingEntries is used as a set; the order of its items is not meaningful.
210
size_t i = mPendingEntries.Length();
211
while (i) {
212
i -= 1;
213
auto& pendingSurf = mPendingEntries[i];
214
if (pendingSurf.mPreviousCheckGeneration > aCheckGenerationsUpTo) {
215
continue;
216
}
217
// Check if the window server is still using the surface. As long as it is doing that, we cannot
218
// move the surface to mAvailableSurfaces because anything we draw to it could reach the screen
219
// in a place where we don't expect it.
220
if (IOSurfaceIsInUse(pendingSurf.mEntry.mIOSurface.get())) {
221
// The surface is still in use. Update mPreviousCheckGeneration and mCheckCount.
222
pendingSurf.mPreviousCheckGeneration = mCollectionGeneration;
223
pendingSurf.mCheckCount++;
224
if (pendingSurf.mCheckCount >= 30) {
225
// The window server has been holding on to this surface for an unreasonably long time. This
226
// is known to happen sometimes, for example in occluded windows or after a GPU switch. In
227
// that case, release our references to the surface so that it's Not Our Problem anymore.
228
// Remove the entry from mPendingEntries.
229
MutateEntryStorage("Eject", IntSize(pendingSurf.mEntry.mSize),
230
[&]() { mPendingEntries.RemoveElementAt(i); });
231
}
232
} else {
233
// The surface has become unused!
234
// Move the entry from mPendingEntries to mAvailableEntries.
235
MutateEntryStorage("Stop waiting for", IntSize(pendingSurf.mEntry.mSize), [&]() {
236
mAvailableEntries.AppendElement(std::move(pendingSurf.mEntry));
237
mPendingEntries.RemoveElementAt(i);
238
});
239
}
240
}
241
return mCollectionGeneration;
242
}
243
244
void SurfacePoolCA::LockedPool::OnWrapperDestroyed(gl::GLContext* aGL,
245
SurfacePoolCAWrapperForGL* aWrapper) {
246
if (aGL) {
247
DestroyGLResourcesForContext(aGL);
248
}
249
250
auto iter = mWrappers.find(aGL);
251
MOZ_RELEASE_ASSERT(iter != mWrappers.end());
252
MOZ_RELEASE_ASSERT(iter->second == aWrapper, "Only one SurfacePoolCAWrapperForGL object should "
253
"exist for each GLContext* at any time");
254
mWrappers.erase(iter);
255
}
256
257
Maybe<GLuint> SurfacePoolCA::LockedPool::GetFramebufferForSurface(
258
CFTypeRefPtr<IOSurfaceRef> aSurface, GLContext* aGL, bool aNeedsDepthBuffer) {
259
MOZ_RELEASE_ASSERT(aGL);
260
261
auto inUseEntryIter = mInUseEntries.find(aSurface);
262
MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
263
264
SurfacePoolEntry& entry = inUseEntryIter->second;
265
if (entry.mGLResources) {
266
// We have an existing framebuffer.
267
MOZ_RELEASE_ASSERT(entry.mGLResources->mGLContext == aGL,
268
"Recycled surface that still had GL resources from a different GL context. "
269
"This shouldn't happen.");
270
if (!aNeedsDepthBuffer || entry.mGLResources->mFramebuffer->HasDepth()) {
271
return Some(entry.mGLResources->mFramebuffer->mFB);
272
}
273
}
274
275
// No usable existing framebuffer, we need to create one.
276
277
#ifdef MOZ_GECKO_PROFILER
278
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
279
"Framebuffer creation", GRAPHICS_TileAllocation,
280
nsPrintfCString("%dx%d", entry.mSize.width, entry.mSize.height));
281
#endif
282
283
RefPtr<GLContextCGL> cgl = GLContextCGL::Cast(aGL);
284
MOZ_RELEASE_ASSERT(cgl, "Unexpected GLContext type");
285
286
if (!aGL->MakeCurrent()) {
287
// Context may have been destroyed.
288
return {};
289
}
290
291
GLuint tex = aGL->CreateTexture();
292
{
293
const gl::ScopedBindTexture bindTex(aGL, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
294
CGLTexImageIOSurface2D(cgl->GetCGLContext(), LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_RGBA,
295
entry.mSize.width, entry.mSize.height, LOCAL_GL_BGRA,
296
LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, entry.mIOSurface.get(), 0);
297
}
298
299
auto fb = CreateFramebufferForTexture(aGL, entry.mSize, tex, aNeedsDepthBuffer);
300
if (!fb) {
301
// Framebuffer completeness check may have failed.
302
return {};
303
}
304
305
GLuint fbo = fb->mFB;
306
entry.mGLResources = Some(GLResourcesForSurface{aGL, std::move(fb)});
307
return Some(fbo);
308
}
309
310
RefPtr<gl::DepthAndStencilBuffer> SurfacePoolCA::LockedPool::GetDepthBufferForSharing(
311
GLContext* aGL, const IntSize& aSize) {
312
// Clean out entries for which the weak pointer has become null.
313
mDepthBuffers.RemoveElementsBy([&](const DepthBufferEntry& entry) { return !entry.mBuffer; });
314
315
for (const auto& entry : mDepthBuffers) {
316
if (entry.mGLContext == aGL && entry.mSize == aSize) {
317
return entry.mBuffer.get();
318
}
319
}
320
return nullptr;
321
}
322
323
UniquePtr<gl::MozFramebuffer> SurfacePoolCA::LockedPool::CreateFramebufferForTexture(
324
GLContext* aGL, const IntSize& aSize, GLuint aTexture, bool aNeedsDepthBuffer) {
325
if (aNeedsDepthBuffer) {
326
// Try to find an existing depth buffer of aSize in aGL and create a framebuffer that shares it.
327
if (auto buffer = GetDepthBufferForSharing(aGL, aSize)) {
328
return gl::MozFramebuffer::CreateForBackingWithSharedDepthAndStencil(
329
aSize, 0, LOCAL_GL_TEXTURE_RECTANGLE_ARB, aTexture, buffer);
330
}
331
}
332
333
// No depth buffer needed or we didn't find one. Create a framebuffer with a new depth buffer and
334
// store a weak pointer to the new depth buffer in mDepthBuffers.
335
UniquePtr<gl::MozFramebuffer> fb = gl::MozFramebuffer::CreateForBacking(
336
aGL, aSize, 0, aNeedsDepthBuffer, LOCAL_GL_TEXTURE_RECTANGLE_ARB, aTexture);
337
if (fb && fb->GetDepthAndStencilBuffer()) {
338
mDepthBuffers.AppendElement(DepthBufferEntry{aGL, aSize, fb->GetDepthAndStencilBuffer().get()});
339
}
340
341
return fb;
342
}
343
344
// SurfacePoolHandleCA
345
346
SurfacePoolHandleCA::SurfacePoolHandleCA(RefPtr<SurfacePoolCAWrapperForGL>&& aPoolWrapper,
347
uint64_t aCurrentCollectionGeneration)
348
: mPoolWrapper(aPoolWrapper),
349
mPreviousFrameCollectionGeneration(
350
"SurfacePoolHandleCA::mPreviousFrameCollectionGeneration") {
351
auto generation = mPreviousFrameCollectionGeneration.Lock();
352
*generation = aCurrentCollectionGeneration;
353
}
354
355
SurfacePoolHandleCA::~SurfacePoolHandleCA() {}
356
357
void SurfacePoolHandleCA::OnBeginFrame() {
358
auto generation = mPreviousFrameCollectionGeneration.Lock();
359
*generation = mPoolWrapper->mPool->CollectPendingSurfaces(*generation);
360
}
361
362
void SurfacePoolHandleCA::OnEndFrame() { mPoolWrapper->mPool->EnforcePoolSizeLimit(); }
363
364
CFTypeRefPtr<IOSurfaceRef> SurfacePoolHandleCA::ObtainSurfaceFromPool(const IntSize& aSize) {
365
return mPoolWrapper->mPool->ObtainSurfaceFromPool(aSize, mPoolWrapper->mGL);
366
}
367
368
void SurfacePoolHandleCA::ReturnSurfaceToPool(CFTypeRefPtr<IOSurfaceRef> aSurface) {
369
mPoolWrapper->mPool->ReturnSurfaceToPool(aSurface);
370
}
371
372
Maybe<GLuint> SurfacePoolHandleCA::GetFramebufferForSurface(CFTypeRefPtr<IOSurfaceRef> aSurface,
373
bool aNeedsDepthBuffer) {
374
return mPoolWrapper->mPool->GetFramebufferForSurface(aSurface, mPoolWrapper->mGL,
375
aNeedsDepthBuffer);
376
}
377
378
// SurfacePoolCA
379
380
SurfacePoolCA::SurfacePoolCA(size_t aPoolSizeLimit)
381
: mPool(LockedPool(aPoolSizeLimit), "SurfacePoolCA::mPool") {}
382
383
SurfacePoolCA::~SurfacePoolCA() {}
384
385
RefPtr<SurfacePoolHandle> SurfacePoolCA::GetHandleForGL(GLContext* aGL) {
386
RefPtr<SurfacePoolCAWrapperForGL> wrapper;
387
uint64_t collectionGeneration = 0;
388
{
389
auto pool = mPool.Lock();
390
wrapper = pool->GetWrapperForGL(this, aGL);
391
collectionGeneration = pool->mCollectionGeneration;
392
}
393
394
// Run the SurfacePoolHandleCA constructor outside of the lock so that the
395
// mPool lock and the handle's lock are always ordered the same way.
396
return new SurfacePoolHandleCA(std::move(wrapper), collectionGeneration);
397
}
398
399
void SurfacePoolCA::DestroyGLResourcesForContext(GLContext* aGL) {
400
auto pool = mPool.Lock();
401
pool->DestroyGLResourcesForContext(aGL);
402
}
403
404
CFTypeRefPtr<IOSurfaceRef> SurfacePoolCA::ObtainSurfaceFromPool(const IntSize& aSize,
405
GLContext* aGL) {
406
auto pool = mPool.Lock();
407
return pool->ObtainSurfaceFromPool(aSize, aGL);
408
}
409
410
void SurfacePoolCA::ReturnSurfaceToPool(CFTypeRefPtr<IOSurfaceRef> aSurface) {
411
auto pool = mPool.Lock();
412
pool->ReturnSurfaceToPool(aSurface);
413
}
414
415
uint64_t SurfacePoolCA::CollectPendingSurfaces(uint64_t aCheckGenerationsUpTo) {
416
auto pool = mPool.Lock();
417
return pool->CollectPendingSurfaces(aCheckGenerationsUpTo);
418
}
419
void SurfacePoolCA::EnforcePoolSizeLimit() {
420
auto pool = mPool.Lock();
421
pool->EnforcePoolSizeLimit();
422
}
423
424
Maybe<GLuint> SurfacePoolCA::GetFramebufferForSurface(CFTypeRefPtr<IOSurfaceRef> aSurface,
425
GLContext* aGL, bool aNeedsDepthBuffer) {
426
auto pool = mPool.Lock();
427
return pool->GetFramebufferForSurface(aSurface, aGL, aNeedsDepthBuffer);
428
}
429
430
void SurfacePoolCA::OnWrapperDestroyed(gl::GLContext* aGL, SurfacePoolCAWrapperForGL* aWrapper) {
431
auto pool = mPool.Lock();
432
return pool->OnWrapperDestroyed(aGL, aWrapper);
433
}
434
435
} // namespace layers
436
} // namespace mozilla