Source code

Revision control

Other Tools

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 "PersistentBufferProvider.h"
8
9
#include "Layers.h"
10
#include "mozilla/layers/ShadowLayers.h"
11
#include "mozilla/layers/TextureClient.h"
12
#include "mozilla/gfx/gfxVars.h"
13
#include "mozilla/gfx/Logging.h"
14
#include "mozilla/StaticPrefs_layers.h"
15
#include "pratom.h"
16
#include "gfxPlatform.h"
17
18
namespace mozilla {
19
20
using namespace gfx;
21
22
namespace layers {
23
24
PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
25
: mDrawTarget(aDt) {
26
MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
27
}
28
29
PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
30
MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
31
Destroy();
32
}
33
34
already_AddRefed<gfx::DrawTarget>
35
PersistentBufferProviderBasic::BorrowDrawTarget(
36
const gfx::IntRect& aPersistedRect) {
37
MOZ_ASSERT(!mSnapshot);
38
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
39
return dt.forget();
40
}
41
42
bool PersistentBufferProviderBasic::ReturnDrawTarget(
43
already_AddRefed<gfx::DrawTarget> aDT) {
44
RefPtr<gfx::DrawTarget> dt(aDT);
45
MOZ_ASSERT(mDrawTarget == dt);
46
if (dt) {
47
// Since SkiaGL default to storing drawing command until flush
48
// we have to flush it before present.
49
dt->Flush();
50
}
51
return true;
52
}
53
54
already_AddRefed<gfx::SourceSurface>
55
PersistentBufferProviderBasic::BorrowSnapshot() {
56
mSnapshot = mDrawTarget->Snapshot();
57
RefPtr<SourceSurface> snapshot = mSnapshot;
58
return snapshot.forget();
59
}
60
61
void PersistentBufferProviderBasic::ReturnSnapshot(
62
already_AddRefed<gfx::SourceSurface> aSnapshot) {
63
RefPtr<SourceSurface> snapshot = aSnapshot;
64
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
65
mSnapshot = nullptr;
66
}
67
68
void PersistentBufferProviderBasic::Destroy() {
69
mSnapshot = nullptr;
70
mDrawTarget = nullptr;
71
}
72
73
// static
74
already_AddRefed<PersistentBufferProviderBasic>
75
PersistentBufferProviderBasic::Create(gfx::IntSize aSize,
76
gfx::SurfaceFormat aFormat,
77
gfx::BackendType aBackend) {
78
RefPtr<DrawTarget> dt =
79
gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize,
80
aFormat);
81
82
if (dt) {
83
// This is simply to ensure the DrawTarget gets initialized, and will detect
84
// a device reset, even if we're on the main thread.
85
dt->ClearRect(Rect(0, 0, 0, 0));
86
}
87
88
if (!dt || !dt->IsValid()) {
89
return nullptr;
90
}
91
92
RefPtr<PersistentBufferProviderBasic> provider =
93
new PersistentBufferProviderBasic(dt);
94
95
return provider.forget();
96
}
97
98
// static
99
already_AddRefed<PersistentBufferProviderShared>
100
PersistentBufferProviderShared::Create(gfx::IntSize aSize,
101
gfx::SurfaceFormat aFormat,
102
KnowsCompositor* aKnowsCompositor) {
103
if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
104
!aKnowsCompositor->GetTextureForwarder()->IPCOpen() ||
105
// Bug 1556433 - shared buffer provider and direct texture mapping do not
106
// synchronize properly
107
aKnowsCompositor->SupportsTextureDirectMapping()) {
108
return nullptr;
109
}
110
111
if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
112
return nullptr;
113
}
114
115
#ifdef XP_WIN
116
// Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
117
// instability, unless we are remoting the canvas drawing to the GPU process.
118
if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
119
BackendType::DIRECT2D1_1 &&
120
!TextureData::IsRemote(aKnowsCompositor->GetCompositorBackendType(),
121
BackendSelector::Canvas)) {
122
return nullptr;
123
}
124
#endif
125
126
RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
127
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
128
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
129
TextureAllocationFlags::ALLOC_DEFAULT);
130
131
if (!texture) {
132
return nullptr;
133
}
134
135
RefPtr<PersistentBufferProviderShared> provider =
136
new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
137
texture);
138
return provider.forget();
139
}
140
141
PersistentBufferProviderShared::PersistentBufferProviderShared(
142
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
143
KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture)
144
145
: mSize(aSize),
146
mFormat(aFormat),
147
mKnowsCompositor(aKnowsCompositor),
148
mFront(Nothing()) {
149
MOZ_ASSERT(aKnowsCompositor);
150
if (mTextures.append(aTexture)) {
151
mBack = Some<uint32_t>(0);
152
}
153
154
// If we are using webrender and our textures don't have an intermediate
155
// buffer, then we have to hold onto the textures for longer to make sure that
156
// the GPU has finished using them. So, we need to allow more TextureClients
157
// to be created.
158
if (!aTexture->HasIntermediateBuffer() && gfxVars::UseWebRender()) {
159
++mMaxAllowedTextures;
160
if (gfxVars::UseWebRenderTripleBufferingWin()) {
161
++mMaxAllowedTextures;
162
}
163
}
164
165
MOZ_COUNT_CTOR(PersistentBufferProviderShared);
166
}
167
168
PersistentBufferProviderShared::~PersistentBufferProviderShared() {
169
MOZ_COUNT_DTOR(PersistentBufferProviderShared);
170
171
if (IsActivityTracked()) {
172
mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
173
}
174
175
Destroy();
176
}
177
178
LayersBackend PersistentBufferProviderShared::GetType() {
179
if (mKnowsCompositor->GetCompositorBackendType() ==
180
LayersBackend::LAYERS_WR) {
181
return LayersBackend::LAYERS_WR;
182
} else {
183
return LayersBackend::LAYERS_CLIENT;
184
}
185
}
186
187
bool PersistentBufferProviderShared::SetKnowsCompositor(
188
KnowsCompositor* aKnowsCompositor) {
189
MOZ_ASSERT(aKnowsCompositor);
190
if (!aKnowsCompositor) {
191
return false;
192
}
193
194
if (mKnowsCompositor == aKnowsCompositor) {
195
// The forwarder should not change most of the time.
196
return true;
197
}
198
199
if (IsActivityTracked()) {
200
mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
201
}
202
203
if (mKnowsCompositor->GetTextureForwarder() !=
204
aKnowsCompositor->GetTextureForwarder() ||
205
mKnowsCompositor->GetCompositorBackendType() !=
206
aKnowsCompositor->GetCompositorBackendType()) {
207
// We are going to be used with an different and/or incompatible forwarder.
208
// This should be extremely rare. We have to copy the front buffer into a
209
// texture that is compatible with the new forwarder.
210
211
// Grab the current front buffer.
212
RefPtr<TextureClient> prevTexture = GetTexture(mFront);
213
214
// Get rid of everything else
215
Destroy();
216
217
if (prevTexture) {
218
RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
219
aKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
220
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
221
TextureAllocationFlags::ALLOC_DEFAULT);
222
223
MOZ_ASSERT(newTexture);
224
if (!newTexture) {
225
return false;
226
}
227
228
// If we early-return in one of the following branches, we will
229
// leave the buffer provider in an empty state, since we called
230
// Destroy. Not ideal but at least we won't try to use it with a
231
// an incompatible ipc channel.
232
233
if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
234
return false;
235
}
236
237
if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
238
newTexture->Unlock();
239
return false;
240
}
241
242
bool success =
243
prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
244
245
prevTexture->Unlock();
246
newTexture->Unlock();
247
248
if (!success) {
249
return false;
250
}
251
252
if (!mTextures.append(newTexture)) {
253
return false;
254
}
255
mFront = Some<uint32_t>(mTextures.length() - 1);
256
mBack = mFront;
257
}
258
}
259
260
mKnowsCompositor = aKnowsCompositor;
261
262
return true;
263
}
264
265
TextureClient* PersistentBufferProviderShared::GetTexture(
266
const Maybe<uint32_t>& aIndex) {
267
if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
268
return nullptr;
269
}
270
return mTextures[aIndex.value()];
271
}
272
273
already_AddRefed<gfx::DrawTarget>
274
PersistentBufferProviderShared::BorrowDrawTarget(
275
const gfx::IntRect& aPersistedRect) {
276
if (!mKnowsCompositor->GetTextureForwarder() ||
277
!mKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
278
return nullptr;
279
}
280
281
MOZ_ASSERT(!mSnapshot);
282
283
if (IsActivityTracked()) {
284
mKnowsCompositor->GetActiveResourceTracker()->MarkUsed(this);
285
} else {
286
mKnowsCompositor->GetActiveResourceTracker()->AddObject(this);
287
}
288
289
if (mDrawTarget) {
290
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
291
return dt.forget();
292
}
293
294
auto previousBackBuffer = mBack;
295
296
TextureClient* tex = GetTexture(mBack);
297
298
// First try to reuse the current back buffer. If we can do that it means
299
// we can skip copying its content to the new back buffer.
300
if ((mTextureLockIsUnreliable.isSome() &&
301
mTextureLockIsUnreliable == mBack) ||
302
(tex && tex->IsReadLocked())) {
303
// The back buffer is currently used by the compositor, we can't draw
304
// into it.
305
tex = nullptr;
306
}
307
308
if (!tex) {
309
// Try to grab an already allocated texture if any is available.
310
for (uint32_t i = 0; i < mTextures.length(); ++i) {
311
if (!mTextures[i]->IsReadLocked() &&
312
!(mTextureLockIsUnreliable.isSome() &&
313
mTextureLockIsUnreliable.ref() == i)) {
314
mBack = Some(i);
315
tex = mTextures[i];
316
break;
317
}
318
}
319
}
320
321
if (!tex) {
322
// We have to allocate a new texture.
323
if (mTextures.length() >= mMaxAllowedTextures) {
324
// We should never need to buffer that many textures, something's wrong.
325
// In theory we throttle the main thread when the compositor can't keep
326
// up, so we shoud never get in a situation where we sent 4 textures to
327
// the compositor and the latter has not released any of them. In
328
// practice, though, the throttling mechanism appears to have some issues,
329
// especially when switching between layer managers (during tab-switch).
330
// To make sure we don't get too far ahead of the compositor, we send a
331
// sync ping to the compositor thread...
332
mKnowsCompositor->SyncWithCompositor();
333
// ...and try again.
334
for (uint32_t i = 0; i < mTextures.length(); ++i) {
335
if (!mTextures[i]->IsReadLocked()) {
336
gfxCriticalNote << "Managed to allocate after flush.";
337
mBack = Some(i);
338
tex = mTextures[i];
339
break;
340
}
341
}
342
343
if (!tex) {
344
gfxCriticalNote << "Unexpected BufferProvider over-production.";
345
// It would be pretty bad to keep piling textures up at this point so we
346
// call NotifyInactive to remove some of our textures.
347
NotifyInactive();
348
// Give up now. The caller can fall-back to a non-shared buffer
349
// provider.
350
return nullptr;
351
}
352
}
353
354
RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
355
mKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
356
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
357
TextureAllocationFlags::ALLOC_DEFAULT);
358
359
MOZ_ASSERT(newTexture);
360
if (newTexture) {
361
if (mTextures.append(newTexture)) {
362
tex = newTexture;
363
mBack = Some<uint32_t>(mTextures.length() - 1);
364
}
365
}
366
}
367
368
if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
369
return nullptr;
370
}
371
372
// Clear dirty texture, since new back texture is selected.
373
mTextureLockIsUnreliable = Nothing();
374
375
mDrawTarget = tex->BorrowDrawTarget();
376
if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
377
if (mPreviousSnapshot) {
378
mDrawTarget->CopySurface(mPreviousSnapshot, aPersistedRect,
379
gfx::IntPoint(0, 0));
380
} else {
381
TextureClient* previous = GetTexture(previousBackBuffer);
382
if (previous && previous->Lock(OpenMode::OPEN_READ)) {
383
DebugOnly<bool> success =
384
previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
385
MOZ_ASSERT(success);
386
387
previous->Unlock();
388
}
389
}
390
}
391
mPreviousSnapshot = nullptr;
392
393
if (mDrawTarget) {
394
// This is simply to ensure the DrawTarget gets initialized, and will detect
395
// a device reset, even if we're on the main thread.
396
mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
397
398
if (!mDrawTarget->IsValid()) {
399
mDrawTarget = nullptr;
400
}
401
}
402
403
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
404
return dt.forget();
405
}
406
407
bool PersistentBufferProviderShared::ReturnDrawTarget(
408
already_AddRefed<gfx::DrawTarget> aDT) {
409
RefPtr<gfx::DrawTarget> dt(aDT);
410
MOZ_ASSERT(mDrawTarget == dt);
411
// Can't change the current front buffer while its snapshot is borrowed!
412
MOZ_ASSERT(!mSnapshot);
413
414
TextureClient* back = GetTexture(mBack);
415
MOZ_ASSERT(back);
416
417
// If our TextureClients have internal synchronization then, if locks are
418
// needed for reading and writing, this can cause locking issues with the
419
// compositor. To prevent this we take a snapshot when the DrawTarget is
420
// returned, so this can be used when our own BorrowSnapshot is called and
421
// also for copying to the next TextureClient. Using this snapshot outside of
422
// the locks is safe, because the TextureClient calls DetachAllSnapshots on
423
// its DrawTarget when we Unlock below.
424
if (back->HasSynchronization()) {
425
mPreviousSnapshot = back->BorrowSnapshot();
426
}
427
428
mDrawTarget = nullptr;
429
dt = nullptr;
430
431
if (back) {
432
back->Unlock();
433
mFront = mBack;
434
}
435
436
return !!back;
437
}
438
439
TextureClient* PersistentBufferProviderShared::GetTextureClient() {
440
// Can't access the front buffer while drawing.
441
MOZ_ASSERT(!mDrawTarget);
442
TextureClient* texture = GetTexture(mFront);
443
if (!texture) {
444
gfxCriticalNote
445
<< "PersistentBufferProviderShared: front buffer unavailable";
446
}
447
return texture;
448
}
449
450
already_AddRefed<gfx::SourceSurface>
451
PersistentBufferProviderShared::BorrowSnapshot() {
452
if (mPreviousSnapshot) {
453
mSnapshot = mPreviousSnapshot;
454
return do_AddRef(mSnapshot);
455
}
456
457
if (mDrawTarget) {
458
auto back = GetTexture(mBack);
459
MOZ_ASSERT(back && back->IsLocked());
460
mSnapshot = back->BorrowSnapshot();
461
return do_AddRef(mSnapshot);
462
}
463
464
auto front = GetTexture(mFront);
465
if (!front || front->IsLocked()) {
466
MOZ_ASSERT(false);
467
return nullptr;
468
}
469
470
if (!front->Lock(OpenMode::OPEN_READ)) {
471
return nullptr;
472
}
473
474
mSnapshot = front->BorrowSnapshot();
475
476
return do_AddRef(mSnapshot);
477
}
478
479
void PersistentBufferProviderShared::ReturnSnapshot(
480
already_AddRefed<gfx::SourceSurface> aSnapshot) {
481
RefPtr<SourceSurface> snapshot = aSnapshot;
482
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
483
484
mSnapshot = nullptr;
485
snapshot = nullptr;
486
487
if (mPreviousSnapshot || mDrawTarget) {
488
return;
489
}
490
491
auto front = GetTexture(mFront);
492
if (front) {
493
front->Unlock();
494
}
495
}
496
497
void PersistentBufferProviderShared::NotifyInactive() {
498
ClearCachedResources();
499
}
500
501
void PersistentBufferProviderShared::ClearCachedResources() {
502
RefPtr<TextureClient> front = GetTexture(mFront);
503
RefPtr<TextureClient> back = GetTexture(mBack);
504
505
// Clear all textures (except the front and back ones that we just kept).
506
mTextures.clear();
507
508
if (back) {
509
if (mTextures.append(back)) {
510
mBack = Some<uint32_t>(0);
511
}
512
if (front == back) {
513
mFront = mBack;
514
}
515
}
516
517
if (front && front != back) {
518
if (mTextures.append(front)) {
519
mFront = Some<uint32_t>(mTextures.length() - 1);
520
}
521
}
522
// Set front texture as dirty texture.
523
// The texture's read lock is unreliable after this function call.
524
mTextureLockIsUnreliable = mFront;
525
}
526
527
void PersistentBufferProviderShared::Destroy() {
528
mSnapshot = nullptr;
529
mPreviousSnapshot = nullptr;
530
mDrawTarget = nullptr;
531
532
for (auto& mTexture : mTextures) {
533
TextureClient* texture = mTexture;
534
if (texture && texture->IsLocked()) {
535
MOZ_ASSERT(false);
536
texture->Unlock();
537
}
538
}
539
540
mTextures.clear();
541
}
542
543
} // namespace layers
544
} // namespace mozilla