Coverage Report

Created: 2018-09-25 14:53

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