PersistentBufferProvider.cpp (23447B)
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 "mozilla/layers/KnowsCompositor.h" 10 #include "mozilla/layers/RemoteTextureMap.h" 11 #include "mozilla/layers/TextureClient.h" 12 #include "mozilla/layers/TextureForwarder.h" 13 #include "mozilla/layers/TextureRecorded.h" 14 #include "mozilla/gfx/gfxVars.h" 15 #include "mozilla/gfx/CanvasManagerChild.h" 16 #include "mozilla/gfx/DrawTargetWebgl.h" 17 #include "mozilla/gfx/Logging.h" 18 #include "mozilla/Maybe.h" 19 #include "mozilla/StaticPrefs_layers.h" 20 #include "pratom.h" 21 #include "gfxPlatform.h" 22 23 namespace mozilla { 24 25 using namespace gfx; 26 27 namespace layers { 28 29 PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt) 30 : mDrawTarget(aDt) { 31 MOZ_COUNT_CTOR(PersistentBufferProviderBasic); 32 } 33 34 PersistentBufferProviderBasic::~PersistentBufferProviderBasic() { 35 MOZ_COUNT_DTOR(PersistentBufferProviderBasic); 36 Destroy(); 37 } 38 39 already_AddRefed<gfx::DrawTarget> 40 PersistentBufferProviderBasic::BorrowDrawTarget( 41 const gfx::IntRect& aPersistedRect) { 42 MOZ_ASSERT(!mSnapshot); 43 RefPtr<gfx::DrawTarget> dt(mDrawTarget); 44 return dt.forget(); 45 } 46 47 bool PersistentBufferProviderBasic::ReturnDrawTarget( 48 already_AddRefed<gfx::DrawTarget> aDT) { 49 RefPtr<gfx::DrawTarget> dt(aDT); 50 MOZ_ASSERT(mDrawTarget == dt); 51 if (dt) { 52 // Since SkiaGL default to storing drawing command until flush 53 // we have to flush it before present. 54 dt->Flush(); 55 } 56 return true; 57 } 58 59 already_AddRefed<gfx::SourceSurface> 60 PersistentBufferProviderBasic::BorrowSnapshot(gfx::DrawTarget* aTarget) { 61 mSnapshot = mDrawTarget->Snapshot(); 62 RefPtr<SourceSurface> snapshot = mSnapshot; 63 return snapshot.forget(); 64 } 65 66 void PersistentBufferProviderBasic::ReturnSnapshot( 67 already_AddRefed<gfx::SourceSurface> aSnapshot) { 68 RefPtr<SourceSurface> snapshot = aSnapshot; 69 MOZ_ASSERT(!snapshot || snapshot == mSnapshot); 70 mSnapshot = nullptr; 71 } 72 73 void PersistentBufferProviderBasic::Destroy() { 74 mSnapshot = nullptr; 75 mDrawTarget = nullptr; 76 } 77 78 // static 79 already_AddRefed<PersistentBufferProviderBasic> 80 PersistentBufferProviderBasic::Create(gfx::IntSize aSize, 81 gfx::SurfaceFormat aFormat, 82 gfx::BackendType aBackend) { 83 RefPtr<DrawTarget> dt = 84 gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, 85 aFormat); 86 87 if (dt) { 88 // This is simply to ensure the DrawTarget gets initialized, and will detect 89 // a device reset, even if we're on the main thread. 90 dt->ClearRect(Rect(0, 0, 0, 0)); 91 } 92 93 if (!dt || !dt->IsValid()) { 94 return nullptr; 95 } 96 97 RefPtr<PersistentBufferProviderBasic> provider = 98 new PersistentBufferProviderBasic(dt); 99 100 return provider.forget(); 101 } 102 103 static already_AddRefed<TextureClient> CreateTexture( 104 KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat, 105 gfx::IntSize aSize, bool aWillReadFrequently = false, 106 bool aUseRemoteTexture = false) { 107 TextureAllocationFlags flags = ALLOC_DEFAULT; 108 if (aWillReadFrequently) { 109 flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE); 110 } 111 if (aUseRemoteTexture) { 112 flags = TextureAllocationFlags(flags | ALLOC_FORCE_REMOTE); 113 } 114 RefPtr<TextureClient> tc = TextureClient::CreateForDrawing( 115 aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas, 116 TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags); 117 return tc.forget(); 118 } 119 120 // static 121 already_AddRefed<PersistentBufferProviderAccelerated> 122 PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize, 123 gfx::SurfaceFormat aFormat, 124 KnowsCompositor* aKnowsCompositor) { 125 if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() || 126 !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) { 127 return nullptr; 128 } 129 130 if (!DrawTargetWebgl::CanCreate(aSize, aFormat)) { 131 #ifdef XP_WIN 132 // Direct2D acceleration does not require DrawTargetWebgl, but still 133 // requires PersistentBufferProviderAccelerated. 134 if (!TextureData::IsRemote(aKnowsCompositor, BackendSelector::Canvas, 135 aFormat, aSize)) { 136 return nullptr; 137 } 138 #else 139 return nullptr; 140 #endif 141 } 142 143 RefPtr<TextureClient> texture = CreateTexture( 144 aKnowsCompositor, aFormat, aSize, false, /* aUseRemoteTexture */ true); 145 if (!texture) { 146 return nullptr; 147 } 148 149 auto* recordedTextureData = 150 texture->GetInternalData()->AsRecordedTextureData(); 151 if (!recordedTextureData) { 152 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 153 gfxCriticalNoteOnce << "Expected RecordedTextureData"; 154 return nullptr; 155 } 156 157 RefPtr<PersistentBufferProviderAccelerated> provider = 158 new PersistentBufferProviderAccelerated( 159 recordedTextureData->mRemoteTextureOwnerId, texture); 160 return provider.forget(); 161 } 162 163 PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated( 164 RemoteTextureOwnerId aRemoteTextureOwnerId, 165 const RefPtr<TextureClient>& aTexture) 166 : mRemoteTextureOwnerId(aRemoteTextureOwnerId), mTexture(aTexture) { 167 MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated); 168 } 169 170 PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() { 171 MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated); 172 Destroy(); 173 } 174 175 void PersistentBufferProviderAccelerated::Destroy() { 176 mSnapshot = nullptr; 177 mDrawTarget = nullptr; 178 179 if (mTexture) { 180 if (mTexture->IsLocked()) { 181 MOZ_ASSERT(false); 182 mTexture->Unlock(); 183 } 184 mTexture = nullptr; 185 } 186 } 187 188 already_AddRefed<gfx::DrawTarget> 189 PersistentBufferProviderAccelerated::BorrowDrawTarget( 190 const gfx::IntRect& aPersistedRect) { 191 if (!mDrawTarget) { 192 if (aPersistedRect.IsEmpty()) { 193 mTexture->GetInternalData()->InvalidateContents(); 194 } 195 if (!mTexture->Lock(OpenMode::OPEN_READ_WRITE)) { 196 return nullptr; 197 } 198 mDrawTarget = mTexture->BorrowDrawTarget(); 199 if (!mDrawTarget || !mDrawTarget->IsValid()) { 200 mDrawTarget = nullptr; 201 mTexture->Unlock(); 202 return nullptr; 203 } 204 } 205 return do_AddRef(mDrawTarget); 206 } 207 208 bool PersistentBufferProviderAccelerated::ReturnDrawTarget( 209 already_AddRefed<gfx::DrawTarget> aDT) { 210 { 211 RefPtr<gfx::DrawTarget> dt(aDT); 212 MOZ_ASSERT(mDrawTarget == dt); 213 if (!mDrawTarget) { 214 return false; 215 } 216 mDrawTarget = nullptr; 217 } 218 mTexture->Unlock(); 219 return true; 220 } 221 222 already_AddRefed<gfx::SourceSurface> 223 PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) { 224 if (mDrawTarget) { 225 MOZ_ASSERT(mTexture->IsLocked()); 226 } else { 227 if (mTexture->IsLocked()) { 228 MOZ_ASSERT(false); 229 return nullptr; 230 } 231 if (!mTexture->Lock(OpenMode::OPEN_READ)) { 232 return nullptr; 233 } 234 } 235 mSnapshot = mTexture->BorrowSnapshot(); 236 return do_AddRef(mSnapshot); 237 } 238 239 void PersistentBufferProviderAccelerated::ReturnSnapshot( 240 already_AddRefed<gfx::SourceSurface> aSnapshot) { 241 RefPtr<SourceSurface> snapshot = aSnapshot; 242 MOZ_ASSERT(!snapshot || snapshot == mSnapshot); 243 snapshot = nullptr; 244 mTexture->ReturnSnapshot(mSnapshot.forget()); 245 if (!mDrawTarget) { 246 mTexture->Unlock(); 247 } 248 } 249 250 Maybe<SurfaceDescriptor> PersistentBufferProviderAccelerated::GetFrontBuffer() { 251 SurfaceDescriptor desc; 252 if (mTexture->GetInternalData()->Serialize(desc)) { 253 return Some(desc); 254 } 255 return Nothing(); 256 } 257 258 bool PersistentBufferProviderAccelerated::RequiresRefresh() const { 259 return mTexture->GetInternalData()->RequiresRefresh(); 260 } 261 262 already_AddRefed<FwdTransactionTracker> 263 PersistentBufferProviderAccelerated::UseCompositableForwarder( 264 CompositableForwarder* aForwarder) { 265 return mTexture->GetInternalData()->UseCompositableForwarder(aForwarder); 266 } 267 268 // static 269 already_AddRefed<PersistentBufferProviderShared> 270 PersistentBufferProviderShared::Create(gfx::IntSize aSize, 271 gfx::SurfaceFormat aFormat, 272 KnowsCompositor* aKnowsCompositor, 273 bool aWillReadFrequently, 274 const Maybe<uint64_t>& aWindowID) { 275 if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() || 276 !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) { 277 return nullptr; 278 } 279 280 if (!StaticPrefs::layers_shared_buffer_provider_enabled()) { 281 return nullptr; 282 } 283 284 #ifdef XP_WIN 285 // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to 286 // instability. 287 aWillReadFrequently = true; 288 #endif 289 290 RefPtr<TextureClient> texture = 291 CreateTexture(aKnowsCompositor, aFormat, aSize, aWillReadFrequently); 292 if (!texture) { 293 return nullptr; 294 } 295 296 RefPtr<PersistentBufferProviderShared> provider = 297 new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor, 298 texture, aWillReadFrequently, 299 aWindowID); 300 return provider.forget(); 301 } 302 303 PersistentBufferProviderShared::PersistentBufferProviderShared( 304 gfx::IntSize aSize, gfx::SurfaceFormat aFormat, 305 KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture, 306 bool aWillReadFrequently, const Maybe<uint64_t>& aWindowID) 307 : mSize(aSize), 308 mFormat(aFormat), 309 mKnowsCompositor(aKnowsCompositor), 310 mFront(Nothing()), 311 mWillReadFrequently(aWillReadFrequently), 312 mWindowID(aWindowID) { 313 MOZ_ASSERT(aKnowsCompositor); 314 if (mTextures.append(aTexture)) { 315 mBack = Some<uint32_t>(0); 316 } 317 318 // XXX KnowsCompositor could be used for mMaxAllowedTextures 319 if (gfxVars::UseWebRenderTripleBufferingWin()) { 320 ++mMaxAllowedTextures; 321 } 322 323 MOZ_COUNT_CTOR(PersistentBufferProviderShared); 324 } 325 326 PersistentBufferProviderShared::~PersistentBufferProviderShared() { 327 MOZ_COUNT_DTOR(PersistentBufferProviderShared); 328 329 if (IsActivityTracked()) { 330 if (auto* cm = CanvasManagerChild::Get()) { 331 cm->GetActiveResourceTracker()->RemoveObject(this); 332 } else { 333 MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!"); 334 } 335 } 336 337 Destroy(); 338 } 339 340 bool PersistentBufferProviderShared::SetKnowsCompositor( 341 KnowsCompositor* aKnowsCompositor, bool& aOutLostFrontTexture) { 342 MOZ_ASSERT(aKnowsCompositor); 343 MOZ_ASSERT(!aOutLostFrontTexture); 344 if (!aKnowsCompositor) { 345 return false; 346 } 347 348 if (mKnowsCompositor == aKnowsCompositor) { 349 // The forwarder should not change most of the time. 350 return true; 351 } 352 353 if (IsActivityTracked()) { 354 if (auto* cm = CanvasManagerChild::Get()) { 355 cm->GetActiveResourceTracker()->RemoveObject(this); 356 } else { 357 MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!"); 358 } 359 } 360 361 if (mKnowsCompositor->GetTextureForwarder() != 362 aKnowsCompositor->GetTextureForwarder() || 363 mKnowsCompositor->GetCompositorBackendType() != 364 aKnowsCompositor->GetCompositorBackendType()) { 365 // We are going to be used with an different and/or incompatible forwarder. 366 // This should be extremely rare. We have to copy the front buffer into a 367 // texture that is compatible with the new forwarder. 368 369 // Grab the current front buffer. 370 RefPtr<TextureClient> prevTexture = GetTexture(mFront); 371 372 // Get rid of everything else 373 Destroy(); 374 375 if (prevTexture && !prevTexture->IsValid()) { 376 aOutLostFrontTexture = true; 377 } else if (prevTexture && prevTexture->IsValid()) { 378 RefPtr<TextureClient> newTexture = 379 CreateTexture(aKnowsCompositor, mFormat, mSize, mWillReadFrequently); 380 381 MOZ_ASSERT(newTexture); 382 if (!newTexture) { 383 return false; 384 } 385 386 // If we early-return in one of the following branches, we will 387 // leave the buffer provider in an empty state, since we called 388 // Destroy. Not ideal but at least we won't try to use it with a 389 // an incompatible ipc channel. 390 391 if (!newTexture->Lock(OpenMode::OPEN_WRITE)) { 392 return false; 393 } 394 395 if (!prevTexture->Lock(OpenMode::OPEN_READ)) { 396 newTexture->Unlock(); 397 return false; 398 } 399 400 bool success = 401 prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr); 402 403 prevTexture->Unlock(); 404 newTexture->Unlock(); 405 406 if (!success) { 407 return false; 408 } 409 410 if (!mTextures.append(newTexture)) { 411 return false; 412 } 413 mFront = Some<uint32_t>(mTextures.length() - 1); 414 mBack = mFront; 415 } 416 } 417 418 mKnowsCompositor = aKnowsCompositor; 419 420 return true; 421 } 422 423 TextureClient* PersistentBufferProviderShared::GetTexture( 424 const Maybe<uint32_t>& aIndex) { 425 if (aIndex.isNothing() || !CheckIndex(aIndex.value())) { 426 return nullptr; 427 } 428 return mTextures[aIndex.value()]; 429 } 430 431 already_AddRefed<gfx::DrawTarget> 432 PersistentBufferProviderShared::BorrowDrawTarget( 433 const gfx::IntRect& aPersistedRect) { 434 if (!mKnowsCompositor->GetTextureForwarder() || 435 !mKnowsCompositor->GetTextureForwarder()->IPCOpen()) { 436 return nullptr; 437 } 438 439 auto* cm = CanvasManagerChild::Get(); 440 if (NS_WARN_IF(!cm)) { 441 return nullptr; 442 } 443 444 MOZ_ASSERT(!mSnapshot); 445 446 if (IsActivityTracked()) { 447 cm->GetActiveResourceTracker()->MarkUsed(this); 448 } else { 449 cm->GetActiveResourceTracker()->AddObject(this); 450 } 451 452 if (mDrawTarget) { 453 RefPtr<gfx::DrawTarget> dt(mDrawTarget); 454 return dt.forget(); 455 } 456 457 auto previousBackBuffer = mBack; 458 459 TextureClient* tex = GetTexture(mBack); 460 461 // First try to reuse the current back buffer. If we can do that it means 462 // we can skip copying its content to the new back buffer. 463 if (tex && tex->IsReadLocked()) { 464 // The back buffer is currently used by the compositor, we can't draw 465 // into it. 466 tex = nullptr; 467 } 468 469 if (!tex) { 470 // Try to grab an already allocated texture if any is available. 471 for (uint32_t i = 0; i < mTextures.length(); ++i) { 472 if (!mTextures[i]->IsReadLocked()) { 473 mBack = Some(i); 474 tex = mTextures[i]; 475 break; 476 } 477 } 478 } 479 480 if (!tex) { 481 // We have to allocate a new texture. 482 if (mTextures.length() >= mMaxAllowedTextures) { 483 // We should never need to buffer that many textures, something's wrong. 484 // In theory we throttle the main thread when the compositor can't keep 485 // up, so we shoud never get in a situation where we sent 4 textures to 486 // the compositor and the latter has not released any of them. In 487 // practice, though, the throttling mechanism appears to have some issues, 488 // especially when switching between layer managers (during tab-switch). 489 // To make sure we don't get too far ahead of the compositor, we send a 490 // sync ping to the compositor thread... 491 mKnowsCompositor->SyncWithCompositor(mWindowID); 492 // ...and try again. 493 for (uint32_t i = 0; i < mTextures.length(); ++i) { 494 if (!mTextures[i]->IsReadLocked()) { 495 gfxCriticalNote << "Managed to allocate after flush."; 496 mBack = Some(i); 497 tex = mTextures[i]; 498 break; 499 } 500 } 501 502 if (!tex) { 503 gfxCriticalNote << "Unexpected BufferProvider over-production."; 504 // It would be pretty bad to keep piling textures up at this point so we 505 // call NotifyInactive to remove some of our textures. 506 NotifyInactive(); 507 // Give up now. The caller can fall-back to a non-shared buffer 508 // provider. 509 return nullptr; 510 } 511 } 512 513 RefPtr<TextureClient> newTexture = 514 CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently); 515 516 MOZ_ASSERT(newTexture); 517 if (newTexture) { 518 if (mTextures.append(newTexture)) { 519 tex = newTexture; 520 mBack = Some<uint32_t>(mTextures.length() - 1); 521 } 522 } 523 } 524 525 if (!tex) { 526 return nullptr; 527 } 528 529 if (mPermanentBackBuffer) { 530 // If we have a permanent back buffer lock the selected one and switch to 531 // the permanent one before borrowing the DrawTarget. We will copy back into 532 // the selected one when ReturnDrawTarget is called, before we make it the 533 // new front buffer. 534 if (!tex->Lock(OpenMode::OPEN_WRITE)) { 535 return nullptr; 536 } 537 tex = mPermanentBackBuffer; 538 } else { 539 // Copy from the previous back buffer if required. 540 Maybe<TextureClientAutoLock> autoReadLock; 541 TextureClient* previous = nullptr; 542 if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) { 543 if (tex->HasSynchronization()) { 544 // We are about to read lock a texture that is in use by the compositor 545 // and has synchronization. To prevent possible future contention we 546 // switch to using a permanent back buffer. 547 mPermanentBackBuffer = CreateTexture(mKnowsCompositor, mFormat, mSize, 548 mWillReadFrequently); 549 if (!mPermanentBackBuffer) { 550 return nullptr; 551 } 552 if (!tex->Lock(OpenMode::OPEN_WRITE)) { 553 return nullptr; 554 } 555 tex = mPermanentBackBuffer; 556 } 557 558 previous = GetTexture(previousBackBuffer); 559 if (previous) { 560 autoReadLock.emplace(previous, OpenMode::OPEN_READ); 561 } 562 } 563 564 if (!tex->Lock(OpenMode::OPEN_READ_WRITE)) { 565 return nullptr; 566 } 567 568 if (autoReadLock.isSome() && autoReadLock->Succeeded() && previous) { 569 DebugOnly<bool> success = 570 previous->CopyToTextureClient(tex, &aPersistedRect, nullptr); 571 MOZ_ASSERT(success); 572 } 573 } 574 575 mDrawTarget = tex->BorrowDrawTarget(); 576 if (mDrawTarget) { 577 // This is simply to ensure the DrawTarget gets initialized, and will detect 578 // a device reset, even if we're on the main thread. 579 mDrawTarget->ClearRect(Rect(0, 0, 0, 0)); 580 581 if (!mDrawTarget->IsValid()) { 582 mDrawTarget = nullptr; 583 } 584 } 585 586 RefPtr<gfx::DrawTarget> dt(mDrawTarget); 587 return dt.forget(); 588 } 589 590 bool PersistentBufferProviderShared::ReturnDrawTarget( 591 already_AddRefed<gfx::DrawTarget> aDT) { 592 RefPtr<gfx::DrawTarget> dt(aDT); 593 MOZ_ASSERT(mDrawTarget == dt); 594 // Can't change the current front buffer while its snapshot is borrowed! 595 MOZ_ASSERT(!mSnapshot); 596 597 TextureClient* back = GetTexture(mBack); 598 MOZ_ASSERT(back); 599 600 mDrawTarget = nullptr; 601 dt = nullptr; 602 603 // If we have a permanent back buffer we have actually been drawing to that, 604 // so now we must copy to the shared one. 605 if (mPermanentBackBuffer && back) { 606 DebugOnly<bool> success = 607 mPermanentBackBuffer->CopyToTextureClient(back, nullptr, nullptr); 608 MOZ_ASSERT(success); 609 610 // Let our permanent back buffer know that we have finished drawing. 611 mPermanentBackBuffer->EndDraw(); 612 } 613 614 if (back) { 615 back->Unlock(); 616 mFront = mBack; 617 } 618 619 return !!back; 620 } 621 622 TextureClient* PersistentBufferProviderShared::GetTextureClient() { 623 // Can't access the front buffer while drawing. 624 MOZ_ASSERT(!mDrawTarget); 625 TextureClient* texture = GetTexture(mFront); 626 if (!texture) { 627 gfxCriticalNote 628 << "PersistentBufferProviderShared: front buffer unavailable"; 629 return nullptr; 630 } 631 632 // Sometimes, for example on tab switch, we re-forward our texture. So if we 633 // are and it is still read locked, then borrow and return our DrawTarget to 634 // force it to be copied to a texture that we will safely read lock. 635 if (texture->IsReadLocked()) { 636 RefPtr<DrawTarget> dt = 637 BorrowDrawTarget(IntRect(0, 0, mSize.width, mSize.height)); 638 639 // If we failed to borrow a DrawTarget then all our textures must be read 640 // locked or we failed to create one, so we'll just return the current front 641 // buffer even though that might lead to contention. 642 if (dt) { 643 ReturnDrawTarget(dt.forget()); 644 texture = GetTexture(mFront); 645 if (!texture) { 646 gfxCriticalNote 647 << "PersistentBufferProviderShared: front buffer unavailable"; 648 return nullptr; 649 } 650 } 651 } else { 652 // If it isn't read locked then make sure it is set as updated, so that we 653 // will always read lock even if we've forwarded the texture before. 654 texture->SetUpdated(); 655 } 656 657 return texture; 658 } 659 660 already_AddRefed<gfx::SourceSurface> 661 PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget* aTarget) { 662 // If we have a permanent back buffer we can always use that to snapshot. 663 if (mPermanentBackBuffer) { 664 mSnapshot = mPermanentBackBuffer->BorrowSnapshot(); 665 return do_AddRef(mSnapshot); 666 } 667 668 if (mDrawTarget) { 669 auto back = GetTexture(mBack); 670 if (NS_WARN_IF(!back) || NS_WARN_IF(!back->IsLocked())) { 671 return nullptr; 672 } 673 mSnapshot = back->BorrowSnapshot(); 674 return do_AddRef(mSnapshot); 675 } 676 677 auto front = GetTexture(mFront); 678 if (NS_WARN_IF(!front) || NS_WARN_IF(front->IsLocked())) { 679 return nullptr; 680 } 681 682 if (front->IsReadLocked() && front->HasSynchronization()) { 683 // We are about to read lock a texture that is in use by the compositor and 684 // has synchronization. To prevent possible future contention we switch to 685 // using a permanent back buffer. 686 mPermanentBackBuffer = 687 CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently); 688 if (!mPermanentBackBuffer || 689 !mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) { 690 return nullptr; 691 } 692 693 if (!front->Lock(OpenMode::OPEN_READ)) { 694 return nullptr; 695 } 696 697 DebugOnly<bool> success = 698 front->CopyToTextureClient(mPermanentBackBuffer, nullptr, nullptr); 699 MOZ_ASSERT(success); 700 front->Unlock(); 701 mSnapshot = mPermanentBackBuffer->BorrowSnapshot(); 702 return do_AddRef(mSnapshot); 703 } 704 705 if (!front->Lock(OpenMode::OPEN_READ)) { 706 return nullptr; 707 } 708 709 mSnapshot = front->BorrowSnapshot(); 710 711 return do_AddRef(mSnapshot); 712 } 713 714 void PersistentBufferProviderShared::ReturnSnapshot( 715 already_AddRefed<gfx::SourceSurface> aSnapshot) { 716 RefPtr<SourceSurface> snapshot = aSnapshot; 717 MOZ_ASSERT(!snapshot || snapshot == mSnapshot); 718 719 mSnapshot = nullptr; 720 snapshot = nullptr; 721 722 if (mDrawTarget || mPermanentBackBuffer) { 723 return; 724 } 725 726 auto front = GetTexture(mFront); 727 if (front) { 728 front->Unlock(); 729 } 730 } 731 732 void PersistentBufferProviderShared::NotifyInactive() { 733 ClearCachedResources(); 734 } 735 736 void PersistentBufferProviderShared::ClearCachedResources() { 737 RefPtr<TextureClient> front = GetTexture(mFront); 738 RefPtr<TextureClient> back = GetTexture(mBack); 739 740 // Clear all textures (except the front and back ones that we just kept). 741 mTextures.clear(); 742 mPermanentBackBuffer = nullptr; 743 744 if (back) { 745 if (mTextures.append(back)) { 746 mBack = Some<uint32_t>(0); 747 } 748 if (front == back) { 749 mFront = mBack; 750 } 751 } 752 753 if (front && front != back) { 754 if (mTextures.append(front)) { 755 mFront = Some<uint32_t>(mTextures.length() - 1); 756 } 757 } 758 } 759 760 void PersistentBufferProviderShared::Destroy() { 761 mSnapshot = nullptr; 762 mDrawTarget = nullptr; 763 764 if (mPermanentBackBuffer) { 765 mPermanentBackBuffer->Unlock(); 766 mPermanentBackBuffer = nullptr; 767 } 768 769 for (auto& texture : mTextures) { 770 if (texture && texture->IsLocked()) { 771 MOZ_ASSERT(false); 772 texture->Unlock(); 773 } 774 } 775 776 mTextures.clear(); 777 } 778 779 } // namespace layers 780 } // namespace mozilla