Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / gfx / layers / PersistentBufferProvider.cpp
blob72dbcd9468ae6812acf9233418c0dc2c798224b0
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/. */
7 #include "PersistentBufferProvider.h"
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"
18 namespace mozilla {
20 using namespace gfx;
22 namespace layers {
24 PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
25 : mDrawTarget(aDt) {
26 MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
29 PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
30 MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
31 Destroy();
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();
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();
51 return true;
54 already_AddRefed<gfx::SourceSurface>
55 PersistentBufferProviderBasic::BorrowSnapshot() {
56 mSnapshot = mDrawTarget->Snapshot();
57 RefPtr<SourceSurface> snapshot = mSnapshot;
58 return snapshot.forget();
61 void PersistentBufferProviderBasic::ReturnSnapshot(
62 already_AddRefed<gfx::SourceSurface> aSnapshot) {
63 RefPtr<SourceSurface> snapshot = aSnapshot;
64 MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
65 mSnapshot = nullptr;
68 void PersistentBufferProviderBasic::Destroy() {
69 mSnapshot = nullptr;
70 mDrawTarget = nullptr;
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);
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));
88 if (!dt || !dt->IsValid()) {
89 return nullptr;
92 RefPtr<PersistentBufferProviderBasic> provider =
93 new PersistentBufferProviderBasic(dt);
95 return provider.forget();
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;
111 if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
112 return nullptr;
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, BackendSelector::Canvas)) {
121 return nullptr;
123 #endif
125 RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
126 aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
127 TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
128 TextureAllocationFlags::ALLOC_DEFAULT);
130 if (!texture) {
131 return nullptr;
134 RefPtr<PersistentBufferProviderShared> provider =
135 new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
136 texture);
137 return provider.forget();
140 PersistentBufferProviderShared::PersistentBufferProviderShared(
141 gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
142 KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture)
144 : mSize(aSize),
145 mFormat(aFormat),
146 mKnowsCompositor(aKnowsCompositor),
147 mFront(Nothing()) {
148 MOZ_ASSERT(aKnowsCompositor);
149 if (mTextures.append(aTexture)) {
150 mBack = Some<uint32_t>(0);
153 // If we are using webrender and our textures don't have an intermediate
154 // buffer, then we have to hold onto the textures for longer to make sure that
155 // the GPU has finished using them. So, we need to allow more TextureClients
156 // to be created.
157 if (!aTexture->HasIntermediateBuffer() && gfxVars::UseWebRender()) {
158 ++mMaxAllowedTextures;
159 if (gfxVars::UseWebRenderTripleBufferingWin()) {
160 ++mMaxAllowedTextures;
164 MOZ_COUNT_CTOR(PersistentBufferProviderShared);
167 PersistentBufferProviderShared::~PersistentBufferProviderShared() {
168 MOZ_COUNT_DTOR(PersistentBufferProviderShared);
170 if (IsActivityTracked()) {
171 mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
174 Destroy();
177 LayersBackend PersistentBufferProviderShared::GetType() {
178 if (mKnowsCompositor->GetCompositorBackendType() ==
179 LayersBackend::LAYERS_WR) {
180 return LayersBackend::LAYERS_WR;
181 } else {
182 return LayersBackend::LAYERS_CLIENT;
186 bool PersistentBufferProviderShared::SetKnowsCompositor(
187 KnowsCompositor* aKnowsCompositor) {
188 MOZ_ASSERT(aKnowsCompositor);
189 if (!aKnowsCompositor) {
190 return false;
193 if (mKnowsCompositor == aKnowsCompositor) {
194 // The forwarder should not change most of the time.
195 return true;
198 if (IsActivityTracked()) {
199 mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
202 if (mKnowsCompositor->GetTextureForwarder() !=
203 aKnowsCompositor->GetTextureForwarder() ||
204 mKnowsCompositor->GetCompositorBackendType() !=
205 aKnowsCompositor->GetCompositorBackendType()) {
206 // We are going to be used with an different and/or incompatible forwarder.
207 // This should be extremely rare. We have to copy the front buffer into a
208 // texture that is compatible with the new forwarder.
210 // Grab the current front buffer.
211 RefPtr<TextureClient> prevTexture = GetTexture(mFront);
213 // Get rid of everything else
214 Destroy();
216 if (prevTexture) {
217 RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
218 aKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
219 TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
220 TextureAllocationFlags::ALLOC_DEFAULT);
222 MOZ_ASSERT(newTexture);
223 if (!newTexture) {
224 return false;
227 // If we early-return in one of the following branches, we will
228 // leave the buffer provider in an empty state, since we called
229 // Destroy. Not ideal but at least we won't try to use it with a
230 // an incompatible ipc channel.
232 if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
233 return false;
236 if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
237 newTexture->Unlock();
238 return false;
241 bool success =
242 prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
244 prevTexture->Unlock();
245 newTexture->Unlock();
247 if (!success) {
248 return false;
251 if (!mTextures.append(newTexture)) {
252 return false;
254 mFront = Some<uint32_t>(mTextures.length() - 1);
255 mBack = mFront;
259 mKnowsCompositor = aKnowsCompositor;
261 return true;
264 TextureClient* PersistentBufferProviderShared::GetTexture(
265 const Maybe<uint32_t>& aIndex) {
266 if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
267 return nullptr;
269 return mTextures[aIndex.value()];
272 already_AddRefed<gfx::DrawTarget>
273 PersistentBufferProviderShared::BorrowDrawTarget(
274 const gfx::IntRect& aPersistedRect) {
275 if (!mKnowsCompositor->GetTextureForwarder() ||
276 !mKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
277 return nullptr;
280 MOZ_ASSERT(!mSnapshot);
282 if (IsActivityTracked()) {
283 mKnowsCompositor->GetActiveResourceTracker()->MarkUsed(this);
284 } else {
285 mKnowsCompositor->GetActiveResourceTracker()->AddObject(this);
288 if (mDrawTarget) {
289 RefPtr<gfx::DrawTarget> dt(mDrawTarget);
290 return dt.forget();
293 auto previousBackBuffer = mBack;
295 TextureClient* tex = GetTexture(mBack);
297 // First try to reuse the current back buffer. If we can do that it means
298 // we can skip copying its content to the new back buffer.
299 if ((mTextureLockIsUnreliable.isSome() &&
300 mTextureLockIsUnreliable == mBack) ||
301 (tex && tex->IsReadLocked())) {
302 // The back buffer is currently used by the compositor, we can't draw
303 // into it.
304 tex = nullptr;
307 if (!tex) {
308 // Try to grab an already allocated texture if any is available.
309 for (uint32_t i = 0; i < mTextures.length(); ++i) {
310 if (!mTextures[i]->IsReadLocked() &&
311 !(mTextureLockIsUnreliable.isSome() &&
312 mTextureLockIsUnreliable.ref() == i)) {
313 mBack = Some(i);
314 tex = mTextures[i];
315 break;
320 if (!tex) {
321 // We have to allocate a new texture.
322 if (mTextures.length() >= mMaxAllowedTextures) {
323 // We should never need to buffer that many textures, something's wrong.
324 // In theory we throttle the main thread when the compositor can't keep
325 // up, so we shoud never get in a situation where we sent 4 textures to
326 // the compositor and the latter has not released any of them. In
327 // practice, though, the throttling mechanism appears to have some issues,
328 // especially when switching between layer managers (during tab-switch).
329 // To make sure we don't get too far ahead of the compositor, we send a
330 // sync ping to the compositor thread...
331 mKnowsCompositor->SyncWithCompositor();
332 // ...and try again.
333 for (uint32_t i = 0; i < mTextures.length(); ++i) {
334 if (!mTextures[i]->IsReadLocked()) {
335 gfxCriticalNote << "Managed to allocate after flush.";
336 mBack = Some(i);
337 tex = mTextures[i];
338 break;
342 if (!tex) {
343 gfxCriticalNote << "Unexpected BufferProvider over-production.";
344 // It would be pretty bad to keep piling textures up at this point so we
345 // call NotifyInactive to remove some of our textures.
346 NotifyInactive();
347 // Give up now. The caller can fall-back to a non-shared buffer
348 // provider.
349 return nullptr;
353 RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
354 mKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
355 TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
356 TextureAllocationFlags::ALLOC_DEFAULT);
358 MOZ_ASSERT(newTexture);
359 if (newTexture) {
360 if (mTextures.append(newTexture)) {
361 tex = newTexture;
362 mBack = Some<uint32_t>(mTextures.length() - 1);
367 if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
368 return nullptr;
371 // Clear dirty texture, since new back texture is selected.
372 mTextureLockIsUnreliable = Nothing();
374 mDrawTarget = tex->BorrowDrawTarget();
375 if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
376 if (mPreviousSnapshot) {
377 mDrawTarget->CopySurface(mPreviousSnapshot, aPersistedRect,
378 gfx::IntPoint(0, 0));
379 } else {
380 TextureClient* previous = GetTexture(previousBackBuffer);
381 if (previous && previous->Lock(OpenMode::OPEN_READ)) {
382 DebugOnly<bool> success =
383 previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
384 MOZ_ASSERT(success);
386 previous->Unlock();
390 mPreviousSnapshot = nullptr;
392 if (mDrawTarget) {
393 // This is simply to ensure the DrawTarget gets initialized, and will detect
394 // a device reset, even if we're on the main thread.
395 mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
397 if (!mDrawTarget->IsValid()) {
398 mDrawTarget = nullptr;
402 RefPtr<gfx::DrawTarget> dt(mDrawTarget);
403 return dt.forget();
406 bool PersistentBufferProviderShared::ReturnDrawTarget(
407 already_AddRefed<gfx::DrawTarget> aDT) {
408 RefPtr<gfx::DrawTarget> dt(aDT);
409 MOZ_ASSERT(mDrawTarget == dt);
410 // Can't change the current front buffer while its snapshot is borrowed!
411 MOZ_ASSERT(!mSnapshot);
413 TextureClient* back = GetTexture(mBack);
414 MOZ_ASSERT(back);
416 // If our TextureClients have internal synchronization then, if locks are
417 // needed for reading and writing, this can cause locking issues with the
418 // compositor. To prevent this we take a snapshot when the DrawTarget is
419 // returned, so this can be used when our own BorrowSnapshot is called and
420 // also for copying to the next TextureClient. Using this snapshot outside of
421 // the locks is safe, because the TextureClient calls DetachAllSnapshots on
422 // its DrawTarget when we Unlock below.
423 if (back->HasSynchronization()) {
424 mPreviousSnapshot = back->BorrowSnapshot();
427 mDrawTarget = nullptr;
428 dt = nullptr;
430 if (back) {
431 back->Unlock();
432 mFront = mBack;
435 return !!back;
438 TextureClient* PersistentBufferProviderShared::GetTextureClient() {
439 // Can't access the front buffer while drawing.
440 MOZ_ASSERT(!mDrawTarget);
441 TextureClient* texture = GetTexture(mFront);
442 if (!texture) {
443 gfxCriticalNote
444 << "PersistentBufferProviderShared: front buffer unavailable";
446 return texture;
449 already_AddRefed<gfx::SourceSurface>
450 PersistentBufferProviderShared::BorrowSnapshot() {
451 if (mPreviousSnapshot) {
452 mSnapshot = mPreviousSnapshot;
453 return do_AddRef(mSnapshot);
456 if (mDrawTarget) {
457 auto back = GetTexture(mBack);
458 MOZ_ASSERT(back && back->IsLocked());
459 mSnapshot = back->BorrowSnapshot();
460 return do_AddRef(mSnapshot);
463 auto front = GetTexture(mFront);
464 if (!front || front->IsLocked()) {
465 MOZ_ASSERT(false);
466 return nullptr;
469 if (!front->Lock(OpenMode::OPEN_READ)) {
470 return nullptr;
473 mSnapshot = front->BorrowSnapshot();
475 return do_AddRef(mSnapshot);
478 void PersistentBufferProviderShared::ReturnSnapshot(
479 already_AddRefed<gfx::SourceSurface> aSnapshot) {
480 RefPtr<SourceSurface> snapshot = aSnapshot;
481 MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
483 mSnapshot = nullptr;
484 snapshot = nullptr;
486 if (mPreviousSnapshot || mDrawTarget) {
487 return;
490 auto front = GetTexture(mFront);
491 if (front) {
492 front->Unlock();
496 void PersistentBufferProviderShared::NotifyInactive() {
497 ClearCachedResources();
500 void PersistentBufferProviderShared::ClearCachedResources() {
501 RefPtr<TextureClient> front = GetTexture(mFront);
502 RefPtr<TextureClient> back = GetTexture(mBack);
504 // Clear all textures (except the front and back ones that we just kept).
505 mTextures.clear();
507 if (back) {
508 if (mTextures.append(back)) {
509 mBack = Some<uint32_t>(0);
511 if (front == back) {
512 mFront = mBack;
516 if (front && front != back) {
517 if (mTextures.append(front)) {
518 mFront = Some<uint32_t>(mTextures.length() - 1);
521 // Set front texture as dirty texture.
522 // The texture's read lock is unreliable after this function call.
523 mTextureLockIsUnreliable = mFront;
526 void PersistentBufferProviderShared::Destroy() {
527 mSnapshot = nullptr;
528 mPreviousSnapshot = nullptr;
529 mDrawTarget = nullptr;
531 for (auto& mTexture : mTextures) {
532 TextureClient* texture = mTexture;
533 if (texture && texture->IsLocked()) {
534 MOZ_ASSERT(false);
535 texture->Unlock();
539 mTextures.clear();
542 } // namespace layers
543 } // namespace mozilla