TextureClient.cpp (60869B)
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 "mozilla/layers/TextureClient.h" 8 9 #include <stdint.h> // for uint8_t, uint32_t, etc 10 11 #include "BufferTexture.h" 12 #include "IPDLActor.h" 13 #include "ImageContainer.h" // for PlanarYCbCrData, etc 14 #include "MainThreadUtils.h" 15 #include "gfx2DGlue.h" 16 #include "gfxPlatform.h" // for gfxPlatform 17 #include "gfxUtils.h" // for gfxUtils::GetAsLZ4Base64Str 18 #include "mozilla/Atomics.h" 19 #include "mozilla/Mutex.h" 20 #include "mozilla/ProfilerLabels.h" 21 #include "mozilla/SchedulerGroup.h" 22 #include "mozilla/StaticPrefs_gfx.h" 23 #include "mozilla/StaticPrefs_layers.h" 24 #include "mozilla/gfx/2D.h" 25 #include "mozilla/gfx/DataSurfaceHelpers.h" // for CreateDataSourceSurfaceByCloning 26 #include "mozilla/gfx/Logging.h" // for gfxDebug 27 #include "mozilla/gfx/gfxVars.h" 28 #include "mozilla/ipc/CrossProcessSemaphore.h" 29 #include "mozilla/layers/CanvasRenderer.h" 30 #include "mozilla/layers/CompositableForwarder.h" 31 #include "mozilla/layers/ISurfaceAllocator.h" 32 #include "mozilla/layers/ImageBridgeChild.h" 33 #include "mozilla/layers/ImageDataSerializer.h" 34 #include "mozilla/layers/PTextureChild.h" 35 #include "mozilla/layers/SynchronousTask.h" 36 #include "mozilla/layers/TextureClientOGL.h" 37 #include "mozilla/layers/TextureClientRecycleAllocator.h" 38 #include "mozilla/layers/TextureRecorded.h" 39 #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc 40 #include "nsISerialEventTarget.h" 41 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc 42 #include "nsPrintfCString.h" // for nsPrintfCString 43 44 #ifdef XP_WIN 45 # include "gfx2DGlue.h" 46 # include "gfxWindowsPlatform.h" 47 # include "mozilla/gfx/DeviceManagerDx.h" 48 # include "mozilla/layers/TextureD3D11.h" 49 #endif 50 #ifdef MOZ_WIDGET_GTK 51 # include <gtk/gtkx.h> 52 # include "gfxPlatformGtk.h" 53 #endif 54 #ifdef MOZ_WAYLAND 55 # include "mozilla/widget/nsWaylandDisplay.h" 56 #endif 57 58 #ifdef XP_MACOSX 59 # include "mozilla/layers/MacIOSurfaceTextureClientOGL.h" 60 #endif 61 62 #if 0 63 # define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__) 64 #else 65 # define RECYCLE_LOG(...) \ 66 do { \ 67 } while (0) 68 #endif 69 70 namespace mozilla::layers { 71 72 using namespace mozilla::ipc; 73 using namespace mozilla::gl; 74 using namespace mozilla::gfx; 75 76 struct TextureDeallocParams { 77 TextureData* data = nullptr; 78 RefPtr<TextureChild> actor; 79 RefPtr<TextureReadLock> readLock; 80 RefPtr<LayersIPCChannel> allocator; 81 bool clientDeallocation = false; 82 bool syncDeallocation = false; 83 84 TextureDeallocParams() = default; 85 TextureDeallocParams(const TextureDeallocParams&) = delete; 86 TextureDeallocParams& operator=(const TextureDeallocParams&) = delete; 87 88 TextureDeallocParams(TextureDeallocParams&& aOther) 89 : data(aOther.data), 90 actor(std::move(aOther.actor)), 91 readLock(std::move(aOther.readLock)), 92 allocator(std::move(aOther.allocator)), 93 clientDeallocation(aOther.clientDeallocation), 94 syncDeallocation(aOther.syncDeallocation) { 95 aOther.data = nullptr; 96 } 97 98 TextureDeallocParams& operator=(TextureDeallocParams&& aOther) { 99 data = aOther.data; 100 aOther.data = nullptr; 101 actor = std::move(aOther.actor); 102 readLock = std::move(aOther.readLock); 103 allocator = std::move(aOther.allocator); 104 clientDeallocation = aOther.clientDeallocation; 105 syncDeallocation = aOther.syncDeallocation; 106 return *this; 107 } 108 }; 109 110 void DeallocateTextureClient(TextureDeallocParams& params); 111 112 /** 113 * TextureChild is the content-side incarnation of the PTexture IPDL actor. 114 * 115 * TextureChild is used to synchronize a texture client and its corresponding 116 * TextureHost if needed (a TextureClient that is not shared with the compositor 117 * does not have a TextureChild) 118 * 119 * During the deallocation phase, a TextureChild may hold its recently destroyed 120 * TextureClient's data until the compositor side confirmed that it is safe to 121 * deallocte or recycle the it. 122 */ 123 class TextureChild final : PTextureChild { 124 ~TextureChild() { 125 // We should have deallocated mTextureData in ActorDestroy 126 MOZ_ASSERT(!mTextureData); 127 MOZ_ASSERT_IF(!mOwnerCalledDestroy, !mTextureClient); 128 } 129 130 public: 131 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild) 132 133 TextureChild() 134 : mCompositableForwarder(nullptr), 135 mTextureForwarder(nullptr), 136 mTextureClient(nullptr), 137 mTextureData(nullptr), 138 mDestroyed(false), 139 mIPCOpen(false), 140 mOwnsTextureData(false), 141 mOwnerCalledDestroy(false), 142 mUsesImageBridge(false) {} 143 144 mozilla::ipc::IPCResult Recv__delete__() override { return IPC_OK(); } 145 146 LayersIPCChannel* GetAllocator() { return mTextureForwarder; } 147 148 void ActorDestroy(ActorDestroyReason why) override; 149 150 bool IPCOpen() const { return mIPCOpen; } 151 152 void Lock() const { 153 if (mUsesImageBridge) { 154 mLock.Enter(); 155 } 156 } 157 158 void Unlock() const { 159 if (mUsesImageBridge) { 160 mLock.Leave(); 161 } 162 } 163 164 private: 165 // AddIPDLReference and ReleaseIPDLReference are only to be called by 166 // CreateIPDLActor and DestroyIPDLActor, respectively. We intentionally make 167 // them private to prevent misuse. The purpose of these methods is to be aware 168 // of when the IPC system around this actor goes down: mIPCOpen is then set to 169 // false. 170 void AddIPDLReference() { 171 MOZ_ASSERT(mIPCOpen == false); 172 mIPCOpen = true; 173 AddRef(); 174 } 175 void ReleaseIPDLReference() { 176 MOZ_ASSERT(mIPCOpen == false); 177 Release(); 178 } 179 180 /// The normal way to destroy the actor. 181 /// 182 /// This will asynchronously send a Destroy message to the parent actor, whom 183 /// will send the delete message. 184 void Destroy(const TextureDeallocParams& aParams); 185 186 // This lock is used order to prevent several threads to access the 187 // TextureClient's data concurrently. In particular, it prevents shutdown 188 // code to destroy a texture while another thread is reading or writing into 189 // it. 190 // In most places, the lock is held in short and bounded scopes in which we 191 // don't block on any other resource. There are few exceptions to this, which 192 // are discussed below. 193 // 194 // The locking pattern of TextureClient may in some case upset deadlock 195 // detection tools such as TSan. Typically our tile rendering code will lock 196 // all of its tiles, render into them and unlock them all right after that, 197 // which looks something like: 198 // 199 // Lock tile A 200 // Lock tile B 201 // Lock tile C 202 // Apply drawing commands to tiles A, B and C 203 // Unlock tile A 204 // Unlock tile B 205 // Unlock tile C 206 // 207 // And later, we may end up rendering a tile buffer that has the same tiles, 208 // in a different order, for example: 209 // 210 // Lock tile B 211 // Lock tile A 212 // Lock tile D 213 // Apply drawing commands to tiles A, B and D 214 // Unlock tile B 215 // Unlock tile A 216 // Unlock tile D 217 // 218 // This is because textures being expensive to create, we recycle them as much 219 // as possible and they may reappear in the tile buffer in a different order. 220 // 221 // Unfortunately this is not very friendly to TSan's analysis, which will see 222 // that B was once locked while A was locked, and then A locked while B was 223 // locked. TSan identifies this as a potential dead-lock which would be the 224 // case if this kind of inconsistent and dependent locking order was happening 225 // concurrently. 226 // In the case of TextureClient, dependent locking only ever happens on the 227 // thread that draws into the texture (let's call it the producer thread). 228 // Other threads may call into a method that can lock the texture in a short 229 // and bounded scope inside of which it is not allowed to do anything that 230 // could cause the thread to block. A given texture can only have one producer 231 // thread. 232 // 233 // Another example of TSan-unfriendly locking pattern is when copying a 234 // texture into another, which also never happens outside of the producer 235 // thread. Copying A into B looks like this: 236 // 237 // Lock texture B 238 // Lock texture A 239 // Copy A into B 240 // Unlock A 241 // Unlock B 242 // 243 // In a given frame we may need to copy A into B and in another frame copy 244 // B into A. For example A and B can be the Front and Back buffers, 245 // alternating roles and the copy is needed to avoid the cost of re-drawing 246 // the valid region. 247 // 248 // The important rule is that all of the dependent locking must occur only 249 // in the texture's producer thread to avoid deadlocks. 250 mutable gfx::CriticalSection mLock; 251 252 RefPtr<CompositableForwarder> mCompositableForwarder; 253 RefPtr<TextureForwarder> mTextureForwarder; 254 255 TextureClient* mTextureClient; 256 TextureData* mTextureData; 257 Atomic<bool> mDestroyed; 258 bool mIPCOpen; 259 bool mOwnsTextureData; 260 bool mOwnerCalledDestroy; 261 bool mUsesImageBridge; 262 263 friend class TextureClient; 264 friend void DeallocateTextureClient(TextureDeallocParams& params); 265 }; 266 267 static inline gfx::BackendType BackendTypeForBackendSelector( 268 LayersBackend aLayersBackend, BackendSelector aSelector) { 269 switch (aSelector) { 270 case BackendSelector::Canvas: 271 return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); 272 case BackendSelector::Content: 273 return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend); 274 default: 275 MOZ_ASSERT_UNREACHABLE("Unknown backend selector"); 276 return gfx::BackendType::NONE; 277 } 278 }; 279 280 static TextureType ChooseTextureType(gfx::SurfaceFormat aFormat, 281 gfx::IntSize aSize, 282 KnowsCompositor* aKnowsCompositor, 283 BackendSelector aSelector, 284 TextureAllocationFlags aAllocFlags) { 285 LayersBackend layersBackend = aKnowsCompositor->GetCompositorBackendType(); 286 gfx::BackendType moz2DBackend = 287 BackendTypeForBackendSelector(layersBackend, aSelector); 288 (void)moz2DBackend; 289 290 #ifdef XP_MACOSX 291 if (StaticPrefs::gfx_use_iosurface_textures_AtStartup() && 292 !aKnowsCompositor->UsingSoftwareWebRender()) { 293 return TextureType::MacIOSurface; 294 } 295 #endif 296 297 #ifdef MOZ_WIDGET_ANDROID 298 if (StaticPrefs::gfx_use_surfacetexture_textures_AtStartup() && 299 !aKnowsCompositor->UsingSoftwareWebRender()) { 300 return TextureType::AndroidNativeWindow; 301 } 302 #endif 303 304 return TextureType::Unknown; 305 } 306 307 TextureType PreferredCanvasTextureType(KnowsCompositor* aKnowsCompositor) { 308 return ChooseTextureType(gfx::SurfaceFormat::R8G8B8A8, {1, 1}, 309 aKnowsCompositor, BackendSelector::Canvas, 310 TextureAllocationFlags::ALLOC_DEFAULT); 311 } 312 313 /* static */ 314 TextureData* TextureData::Create(TextureType aTextureType, 315 gfx::SurfaceFormat aFormat, 316 const gfx::IntSize& aSize, 317 TextureAllocationFlags aAllocFlags, 318 gfx::BackendType aBackendType) { 319 switch (aTextureType) { 320 #ifdef XP_WIN 321 case TextureType::D3D11: 322 return D3D11TextureData::Create(aSize, aFormat, aAllocFlags); 323 #endif 324 325 #ifdef XP_MACOSX 326 case TextureType::MacIOSurface: 327 return MacIOSurfaceTextureData::Create(aSize, aFormat, aBackendType); 328 #endif 329 #ifdef MOZ_WIDGET_ANDROID 330 case TextureType::AndroidNativeWindow: 331 return AndroidNativeWindowTextureData::Create(aSize, aFormat); 332 #endif 333 default: 334 return nullptr; 335 } 336 } 337 338 /* static */ 339 TextureData* TextureData::Create(TextureForwarder* aAllocator, 340 gfx::SurfaceFormat aFormat, gfx::IntSize aSize, 341 KnowsCompositor* aKnowsCompositor, 342 BackendSelector aSelector, 343 TextureFlags aTextureFlags, 344 TextureAllocationFlags aAllocFlags) { 345 TextureType textureType = ChooseTextureType(aFormat, aSize, aKnowsCompositor, 346 aSelector, aAllocFlags); 347 348 if (aAllocFlags & ALLOC_FORCE_REMOTE) { 349 RefPtr<CanvasChild> canvasChild = aAllocator->GetCanvasChild(); 350 if (canvasChild) { 351 TextureType webglTextureType = 352 TexTypeForWebgl(aKnowsCompositor, /* aIsWebglOop */ true); 353 if (canvasChild->EnsureRecorder(aSize, aFormat, textureType, 354 webglTextureType)) { 355 return new RecordedTextureData(canvasChild.forget(), aSize, aFormat, 356 textureType, webglTextureType); 357 } 358 } 359 // If we must be remote, but there is no canvas child, then falling back 360 // is not possible. 361 return nullptr; 362 } 363 364 gfx::BackendType moz2DBackend = gfx::BackendType::NONE; 365 366 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) 367 moz2DBackend = BackendTypeForBackendSelector( 368 aKnowsCompositor->GetCompositorBackendType(), aSelector); 369 #endif 370 371 return TextureData::Create(textureType, aFormat, aSize, aAllocFlags, 372 moz2DBackend); 373 } 374 375 /* static */ 376 bool TextureData::IsRemote(KnowsCompositor* aKnowsCompositor, 377 BackendSelector aSelector, 378 gfx::SurfaceFormat aFormat, gfx::IntSize aSize) { 379 if (aSelector != BackendSelector::Canvas || !gfxPlatform::UseRemoteCanvas()) { 380 return false; 381 } 382 383 TextureType textureType = 384 ChooseTextureType(aFormat, aSize, aKnowsCompositor, aSelector, 385 TextureAllocationFlags::ALLOC_DEFAULT); 386 387 switch (textureType) { 388 case TextureType::D3D11: 389 return true; 390 default: 391 return false; 392 } 393 } 394 395 static void DestroyTextureData(TextureData* aTextureData, 396 LayersIPCChannel* aAllocator, bool aDeallocate) { 397 if (!aTextureData) { 398 return; 399 } 400 401 if (aDeallocate) { 402 aTextureData->Deallocate(aAllocator); 403 } else { 404 aTextureData->Forget(aAllocator); 405 } 406 delete aTextureData; 407 } 408 409 void TextureChild::ActorDestroy(ActorDestroyReason why) { 410 AUTO_PROFILER_LABEL("TextureChild::ActorDestroy", GRAPHICS); 411 MOZ_ASSERT(mIPCOpen); 412 mIPCOpen = false; 413 414 if (mTextureData) { 415 DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData); 416 mTextureData = nullptr; 417 } 418 } 419 420 void TextureChild::Destroy(const TextureDeallocParams& aParams) { 421 MOZ_ASSERT(!mOwnerCalledDestroy); 422 if (mOwnerCalledDestroy) { 423 return; 424 } 425 426 mOwnerCalledDestroy = true; 427 428 if (!IPCOpen()) { 429 DestroyTextureData(aParams.data, aParams.allocator, 430 aParams.clientDeallocation); 431 return; 432 } 433 434 // DestroyTextureData will be called by TextureChild::ActorDestroy 435 mTextureData = aParams.data; 436 mOwnsTextureData = aParams.clientDeallocation; 437 438 if (!mCompositableForwarder || 439 !mCompositableForwarder->DestroyInTransaction(this)) { 440 this->SendDestroy(); 441 } 442 } 443 444 /* static */ 445 Atomic<uint64_t> TextureClient::sSerialCounter(0); 446 447 /// The logic for synchronizing a TextureClient's deallocation goes here. 448 /// 449 /// This funciton takes care of dispatching work to the right thread using 450 /// a synchronous proxy if needed, and handles client/host deallocation. 451 void DeallocateTextureClient(TextureDeallocParams& params) { 452 if (!params.actor && !params.readLock && !params.data) { 453 // Nothing to do 454 return; 455 } 456 457 TextureChild* actor = params.actor; 458 nsCOMPtr<nsISerialEventTarget> ipdlThread; 459 460 if (params.allocator) { 461 ipdlThread = params.allocator->GetThread(); 462 if (!ipdlThread) { 463 // An allocator with no thread means we are too late in the shutdown 464 // sequence. 465 gfxCriticalError() << "Texture deallocated too late during shutdown"; 466 return; 467 } 468 } 469 470 // First make sure that the work is happening on the IPDL thread. 471 if (ipdlThread && !ipdlThread->IsOnCurrentThread()) { 472 if (params.syncDeallocation) { 473 bool done = false; 474 ReentrantMonitor barrier MOZ_UNANNOTATED("DeallocateTextureClient"); 475 ReentrantMonitorAutoEnter autoMon(barrier); 476 ipdlThread->Dispatch(NS_NewRunnableFunction( 477 "DeallocateTextureClientSyncProxyRunnable", [&]() { 478 DeallocateTextureClient(params); 479 ReentrantMonitorAutoEnter autoMonInner(barrier); 480 done = true; 481 barrier.NotifyAll(); 482 })); 483 while (!done) { 484 barrier.Wait(); 485 } 486 } else { 487 ipdlThread->Dispatch( 488 NS_NewRunnableFunction("DeallocateTextureClientRunnable", 489 [params = std::move(params)]() mutable { 490 DeallocateTextureClient(params); 491 })); 492 } 493 // The work has been forwarded to the IPDL thread, we are done. 494 return; 495 } 496 497 // Below this line, we are either in the IPDL thread or ther is no IPDL 498 // thread anymore. 499 500 if (!ipdlThread) { 501 // If we don't have a thread we can't know for sure that we are in 502 // the IPDL thread and use the LayersIPCChannel. 503 // This should ideally not happen outside of gtest, but some shutdown 504 // raciness could put us in this situation. 505 params.allocator = nullptr; 506 } 507 508 if (params.readLock) { 509 // This should be the last reference to the object, which will destroy it. 510 params.readLock = nullptr; 511 } 512 513 if (!actor) { 514 // We don't have an IPDL actor, probably because we destroyed the 515 // TextureClient before sharing it with the compositor. It means the data 516 // cannot be owned by the TextureHost since we never created the 517 // TextureHost... 518 DestroyTextureData(params.data, params.allocator, /* aDeallocate */ true); 519 return; 520 } 521 522 actor->Destroy(params); 523 } 524 525 void TextureClient::Destroy() { 526 // Async paints should have been flushed by now. 527 MOZ_RELEASE_ASSERT(mPaintThreadRefs == 0); 528 529 if (mActor && !mIsLocked) { 530 mActor->Lock(); 531 } 532 533 mBorrowedDrawTarget = nullptr; 534 mBorrowedSnapshot = false; 535 536 RefPtr<TextureChild> actor = std::move(mActor); 537 538 RefPtr<TextureReadLock> readLock; 539 { 540 MutexAutoLock lock(mMutex); 541 readLock = std::move(mReadLock); 542 } 543 544 if (actor && !actor->mDestroyed.compareExchange(false, true)) { 545 actor->Unlock(); 546 actor = nullptr; 547 } 548 549 TextureData* data = mData; 550 mData = nullptr; 551 552 if (data || actor || readLock) { 553 TextureDeallocParams params; 554 params.actor = std::move(actor); 555 params.readLock = std::move(readLock); 556 params.allocator = mAllocator; 557 params.clientDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT); 558 params.data = data; 559 // At the moment we always deallocate synchronously when deallocating on the 560 // client side, but having asynchronous deallocate in some of the cases will 561 // be a worthwhile optimization. 562 params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT); 563 564 // Release the lock before calling DeallocateTextureClient because the 565 // latter may wait for the main thread which could create a dead-lock. 566 567 if (params.actor) { 568 params.actor->Unlock(); 569 } 570 571 DeallocateTextureClient(params); 572 } 573 } 574 575 void TextureClient::LockActor() const { 576 if (mActor) { 577 mActor->Lock(); 578 } 579 } 580 581 void TextureClient::UnlockActor() const { 582 if (mActor) { 583 mActor->Unlock(); 584 } 585 } 586 587 void TextureClient::EnsureHasReadLock() { 588 if (mFlags & TextureFlags::NON_BLOCKING_READ_LOCK) { 589 MOZ_ASSERT(!(mFlags & TextureFlags::BLOCKING_READ_LOCK)); 590 EnableReadLock(); 591 } else if (mFlags & TextureFlags::BLOCKING_READ_LOCK) { 592 MOZ_ASSERT(!(mFlags & TextureFlags::NON_BLOCKING_READ_LOCK)); 593 EnableBlockingReadLock(); 594 } 595 } 596 597 bool TextureClient::IsReadLocked() { 598 if (!ShouldReadLock()) { 599 return false; 600 } 601 602 nsCOMPtr<nsISerialEventTarget> thread; 603 604 { 605 MutexAutoLock lock(mMutex); 606 if (mReadLock) { 607 MOZ_ASSERT(mReadLock->AsNonBlockingLock(), 608 "Can only check locked for non-blocking locks!"); 609 return mReadLock->AsNonBlockingLock()->GetReadCount() > 1; 610 } 611 612 thread = mAllocator->GetThread(); 613 if (!thread) { 614 // We must be in the process of shutting down. 615 return false; 616 } 617 618 if (thread->IsOnCurrentThread()) { 619 EnsureHasReadLock(); 620 if (NS_WARN_IF(!mReadLock)) { 621 MOZ_ASSERT(!mAllocator->IPCOpen()); 622 return false; 623 } 624 MOZ_ASSERT(mReadLock->AsNonBlockingLock(), 625 "Can only check locked for non-blocking locks!"); 626 return mReadLock->AsNonBlockingLock()->GetReadCount() > 1; 627 } 628 } 629 630 MOZ_ASSERT(mAllocator->UsesImageBridge()); 631 632 bool result = false; 633 SynchronousTask task("TextureClient::IsReadLocked"); 634 thread->Dispatch(NS_NewRunnableFunction("TextureClient::IsReadLocked", [&]() { 635 AutoCompleteTask complete(&task); 636 result = IsReadLocked(); 637 })); 638 task.Wait(); 639 640 return result; 641 } 642 643 bool TextureClient::TryReadLock() { 644 if (!ShouldReadLock()) { 645 return true; 646 } 647 648 nsCOMPtr<nsISerialEventTarget> thread; 649 650 { 651 MutexAutoLock lock(mMutex); 652 if (mIsReadLocked) { 653 return true; 654 } 655 656 if (mReadLock) { 657 if (mReadLock->AsNonBlockingLock() && 658 mReadLock->AsNonBlockingLock()->GetReadCount() > 1) { 659 return false; 660 } 661 662 if (!mReadLock->TryReadLock(TimeDuration::FromMilliseconds(500))) { 663 return false; 664 } 665 666 mIsReadLocked = true; 667 return true; 668 } 669 670 thread = mAllocator->GetThread(); 671 if (!thread) { 672 // We must be in the process of shutting down. 673 return false; 674 } 675 676 if (thread->IsOnCurrentThread()) { 677 EnsureHasReadLock(); 678 679 if (NS_WARN_IF(!mReadLock)) { 680 MOZ_ASSERT(!mAllocator->IPCOpen()); 681 return false; 682 } 683 684 if (mReadLock->AsNonBlockingLock() && 685 mReadLock->AsNonBlockingLock()->GetReadCount() > 1) { 686 return false; 687 } 688 689 if (!mReadLock->TryReadLock(TimeDuration::FromMilliseconds(500))) { 690 return false; 691 } 692 693 mIsReadLocked = true; 694 return true; 695 } 696 } 697 698 MOZ_ASSERT(mAllocator->UsesImageBridge()); 699 700 bool result = false; 701 SynchronousTask task("TextureClient::TryReadLock"); 702 thread->Dispatch(NS_NewRunnableFunction("TextureClient::TryReadLock", [&]() { 703 AutoCompleteTask complete(&task); 704 result = TryReadLock(); 705 })); 706 task.Wait(); 707 708 return result; 709 } 710 711 void TextureClient::ReadUnlock() { 712 if (!ShouldReadLock()) { 713 return; 714 } 715 716 MutexAutoLock lock(mMutex); 717 718 if (!mIsReadLocked) { 719 return; 720 } 721 722 MOZ_ASSERT(mReadLock); 723 mReadLock->ReadUnlock(); 724 mIsReadLocked = false; 725 } 726 727 bool TextureClient::Lock(OpenMode aMode) { 728 if (NS_WARN_IF(!IsValid())) { 729 return false; 730 } 731 if (NS_WARN_IF(mIsLocked)) { 732 return mOpenMode == aMode; 733 } 734 735 if ((aMode & OpenMode::OPEN_WRITE || !mInfo.canConcurrentlyReadLock) && 736 !TryReadLock()) { 737 // Only warn if attempting to write. Attempting to read is acceptable usage. 738 if (aMode & OpenMode::OPEN_WRITE) { 739 NS_WARNING( 740 "Attempt to Lock a texture that is being read by the compositor!"); 741 } 742 return false; 743 } 744 745 LockActor(); 746 747 mIsLocked = mData->Lock(aMode); 748 mOpenMode = aMode; 749 750 auto format = GetFormat(); 751 if (mIsLocked && CanExposeDrawTarget() && 752 (aMode & OpenMode::OPEN_READ_WRITE) == OpenMode::OPEN_READ_WRITE && 753 NS_IsMainThread() && 754 // the formats that we apparently expect, in the cairo backend. Any other 755 // format will trigger an assertion in GfxFormatToCairoFormat. 756 (format == SurfaceFormat::A8R8G8B8_UINT32 || 757 format == SurfaceFormat::X8R8G8B8_UINT32 || 758 format == SurfaceFormat::A8 || format == SurfaceFormat::R5G6B5_UINT16)) { 759 if (!BorrowDrawTarget()) { 760 // Failed to get a DrawTarget, means we won't be able to write into the 761 // texture, might as well fail now. 762 Unlock(); 763 return false; 764 } 765 } 766 767 if (!mIsLocked) { 768 UnlockActor(); 769 ReadUnlock(); 770 } 771 772 return mIsLocked; 773 } 774 775 void TextureClient::Unlock() { 776 MOZ_ASSERT(IsValid()); 777 MOZ_ASSERT(mIsLocked); 778 if (!IsValid() || !mIsLocked) { 779 return; 780 } 781 782 if (mBorrowedDrawTarget) { 783 if (mOpenMode & OpenMode::OPEN_WRITE) { 784 mBorrowedDrawTarget->Flush(); 785 } 786 787 mData->ReturnDrawTarget(mBorrowedDrawTarget.forget()); 788 } 789 mBorrowedSnapshot = false; 790 791 if (mOpenMode & OpenMode::OPEN_WRITE) { 792 mUpdated = true; 793 } 794 795 if (mData) { 796 mData->Unlock(); 797 } 798 mIsLocked = false; 799 mOpenMode = OpenMode::OPEN_NONE; 800 801 UnlockActor(); 802 ReadUnlock(); 803 } 804 805 void TextureClient::EnableReadLock() { 806 MOZ_ASSERT(ShouldReadLock()); 807 if (!mReadLock && mAllocator->GetTileLockAllocator()) { 808 mReadLock = NonBlockingTextureReadLock::Create(mAllocator); 809 } 810 } 811 812 void TextureClient::OnPrepareForwardToHost() { 813 if (!ShouldReadLock()) { 814 return; 815 } 816 817 MutexAutoLock lock(mMutex); 818 if (NS_WARN_IF(!mReadLock)) { 819 MOZ_ASSERT(!mAllocator->IPCOpen(), "Should have created readlock already!"); 820 MOZ_ASSERT(!mIsPendingForwardReadLocked); 821 return; 822 } 823 824 if (mIsPendingForwardReadLocked) { 825 return; 826 } 827 828 mReadLock->ReadLock(); 829 mIsPendingForwardReadLocked = true; 830 } 831 832 void TextureClient::OnAbandonForwardToHost() { 833 if (!ShouldReadLock()) { 834 return; 835 } 836 837 MutexAutoLock lock(mMutex); 838 if (!mReadLock || !mIsPendingForwardReadLocked) { 839 return; 840 } 841 842 mReadLock->ReadUnlock(); 843 mIsPendingForwardReadLocked = false; 844 } 845 846 bool TextureClient::OnForwardedToHost() { 847 if (mData) { 848 mData->OnForwardedToHost(); 849 } 850 851 if (!ShouldReadLock()) { 852 return false; 853 } 854 855 MutexAutoLock lock(mMutex); 856 EnsureHasReadLock(); 857 858 if (NS_WARN_IF(!mReadLock)) { 859 MOZ_ASSERT(!mAllocator->IPCOpen()); 860 return false; 861 } 862 863 if (!mUpdated) { 864 if (mIsPendingForwardReadLocked) { 865 mIsPendingForwardReadLocked = false; 866 mReadLock->ReadUnlock(); 867 } 868 return false; 869 } 870 871 mUpdated = false; 872 873 if (mIsPendingForwardReadLocked) { 874 // We have successfully forwarded, just clear the flag and let the 875 // TextureHost be responsible for unlocking. 876 mIsPendingForwardReadLocked = false; 877 } else { 878 // Otherwise we did not need to readlock in advance, so do so now. We do 879 // this on behalf of the TextureHost. 880 mReadLock->ReadLock(); 881 } 882 883 return true; 884 } 885 886 TextureClient::~TextureClient() { 887 // TextureClients should be kept alive while there are references on the 888 // paint thread. 889 MOZ_ASSERT(mPaintThreadRefs == 0); 890 mReadLock = nullptr; 891 Destroy(); 892 } 893 894 void TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface) { 895 MOZ_ASSERT(IsValid()); 896 MOZ_ASSERT(mIsLocked); 897 MOZ_ASSERT(aSurface); 898 // If you run into this assertion, make sure the texture was locked write-only 899 // rather than read-write. 900 MOZ_ASSERT(!mBorrowedDrawTarget); 901 902 // XXX - It would be better to first try the DrawTarget approach and fallback 903 // to the backend-specific implementation because the latter will usually do 904 // an expensive read-back + cpu-side copy if the texture is on the gpu. 905 // There is a bug with the DrawTarget approach, though specific to reading 906 // back from WebGL (where R and B channel end up inverted) to figure out 907 // first. 908 if (mData->UpdateFromSurface(aSurface)) { 909 return; 910 } 911 if (CanExposeDrawTarget() && NS_IsMainThread()) { 912 RefPtr<DrawTarget> dt = BorrowDrawTarget(); 913 914 MOZ_ASSERT(dt); 915 if (dt) { 916 dt->CopySurface(aSurface, 917 gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()), 918 gfx::IntPoint(0, 0)); 919 return; 920 } 921 } 922 NS_WARNING("TextureClient::UpdateFromSurface failed"); 923 } 924 925 already_AddRefed<TextureClient> TextureClient::CreateSimilar( 926 LayersBackend aLayersBackend, TextureFlags aFlags, 927 TextureAllocationFlags aAllocFlags) const { 928 MOZ_ASSERT(IsValid()); 929 930 MOZ_ASSERT(!mIsLocked); 931 if (mIsLocked) { 932 return nullptr; 933 } 934 935 LockActor(); 936 TextureData* data = 937 mData->CreateSimilar(mAllocator, aLayersBackend, aFlags, aAllocFlags); 938 UnlockActor(); 939 940 if (!data) { 941 return nullptr; 942 } 943 944 return MakeAndAddRef<TextureClient>(data, aFlags, mAllocator); 945 } 946 947 gfx::DrawTarget* TextureClient::BorrowDrawTarget() { 948 MOZ_ASSERT(IsValid()); 949 MOZ_ASSERT(mIsLocked); 950 // TODO- We can't really assert that at the moment because there is code that 951 // Borrows the DrawTarget, just to get a snapshot, which is legit in term of 952 // OpenMode but we should have a way to get a SourceSurface directly instead. 953 // MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE); 954 955 if (!IsValid() || !mIsLocked) { 956 return nullptr; 957 } 958 959 if (!mBorrowedDrawTarget) { 960 mBorrowedDrawTarget = mData->BorrowDrawTarget(); 961 } 962 963 return mBorrowedDrawTarget; 964 } 965 966 void TextureClient::EndDraw() { 967 MOZ_ASSERT(mOpenMode & OpenMode::OPEN_READ_WRITE); 968 969 // Because EndDraw is used when we are not unlocking this TextureClient at the 970 // end of a transaction, we need to Flush and DetachAllSnapshots to ensure any 971 // dependents are updated. 972 mBorrowedDrawTarget->Flush(); 973 mData->ReturnDrawTarget(mBorrowedDrawTarget.forget()); 974 975 mBorrowedSnapshot = false; 976 mData->EndDraw(); 977 } 978 979 void TextureData::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) { 980 RefPtr<gfx::DrawTarget> dt(aDT); 981 dt->DetachAllSnapshots(); 982 } 983 984 already_AddRefed<gfx::SourceSurface> TextureClient::BorrowSnapshot() { 985 MOZ_ASSERT(mIsLocked); 986 987 RefPtr<gfx::SourceSurface> surface = mData->BorrowSnapshot(); 988 if (surface) { 989 mBorrowedSnapshot = true; 990 } else { 991 RefPtr<gfx::DrawTarget> drawTarget = BorrowDrawTarget(); 992 if (!drawTarget) { 993 return nullptr; 994 } 995 surface = drawTarget->Snapshot(); 996 } 997 998 return surface.forget(); 999 } 1000 1001 void TextureData::ReturnSnapshot( 1002 already_AddRefed<gfx::SourceSurface> aSnapshot) { 1003 RefPtr<gfx::SourceSurface> snapshot(aSnapshot); 1004 } 1005 1006 void TextureClient::ReturnSnapshot( 1007 already_AddRefed<gfx::SourceSurface> aSnapshot) { 1008 RefPtr<gfx::SourceSurface> snapshot = aSnapshot; 1009 if (mBorrowedSnapshot) { 1010 mData->ReturnSnapshot(snapshot.forget()); 1011 mBorrowedSnapshot = false; 1012 } 1013 } 1014 1015 bool TextureClient::BorrowMappedData(MappedTextureData& aMap) { 1016 MOZ_ASSERT(IsValid()); 1017 1018 // TODO - SharedRGBImage just accesses the buffer without properly locking 1019 // the texture. It's bad. 1020 // MOZ_ASSERT(mIsLocked); 1021 // if (!mIsLocked) { 1022 // return nullptr; 1023 //} 1024 1025 return mData ? mData->BorrowMappedData(aMap) : false; 1026 } 1027 1028 bool TextureClient::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) { 1029 MOZ_ASSERT(IsValid()); 1030 1031 return mData ? mData->BorrowMappedYCbCrData(aMap) : false; 1032 } 1033 1034 bool TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) { 1035 MOZ_ASSERT(IsValid()); 1036 1037 return mData ? mData->Serialize(aOutDescriptor) : false; 1038 } 1039 1040 // static 1041 PTextureChild* TextureClient::CreateIPDLActor() { 1042 TextureChild* c = new TextureChild(); 1043 c->AddIPDLReference(); 1044 return c; 1045 } 1046 1047 // static 1048 bool TextureClient::DestroyIPDLActor(PTextureChild* actor) { 1049 static_cast<TextureChild*>(actor)->ReleaseIPDLReference(); 1050 return true; 1051 } 1052 1053 // static 1054 already_AddRefed<TextureClient> TextureClient::AsTextureClient( 1055 PTextureChild* actor) { 1056 if (!actor) { 1057 return nullptr; 1058 } 1059 1060 TextureChild* tc = static_cast<TextureChild*>(actor); 1061 1062 tc->Lock(); 1063 1064 // Since TextureClient may be destroyed asynchronously with respect to its 1065 // IPDL actor, we must acquire a reference within a lock. The mDestroyed bit 1066 // tells us whether or not the main thread has disconnected the TextureClient 1067 // from its actor. 1068 if (tc->mDestroyed) { 1069 tc->Unlock(); 1070 return nullptr; 1071 } 1072 1073 RefPtr<TextureClient> texture = tc->mTextureClient; 1074 tc->Unlock(); 1075 1076 return texture.forget(); 1077 } 1078 1079 bool TextureClient::IsSharedWithCompositor() const { 1080 return mActor && mActor->IPCOpen(); 1081 } 1082 1083 void TextureClient::AddFlags(TextureFlags aFlags) { 1084 MOZ_ASSERT( 1085 !IsSharedWithCompositor() || 1086 ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient())); 1087 mFlags |= aFlags; 1088 } 1089 1090 void TextureClient::RemoveFlags(TextureFlags aFlags) { 1091 MOZ_ASSERT( 1092 !IsSharedWithCompositor() || 1093 ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient())); 1094 mFlags &= ~aFlags; 1095 } 1096 1097 void TextureClient::RecycleTexture(TextureFlags aFlags) { 1098 MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE); 1099 MOZ_ASSERT(!mIsLocked); 1100 1101 mAddedToCompositableClient = false; 1102 if (mFlags != aFlags) { 1103 mFlags = aFlags; 1104 } 1105 } 1106 1107 void TextureClient::SetAddedToCompositableClient() { 1108 if (!mAddedToCompositableClient) { 1109 mAddedToCompositableClient = true; 1110 if (!(GetFlags() & TextureFlags::RECYCLE)) { 1111 return; 1112 } 1113 MOZ_ASSERT(!mIsLocked); 1114 LockActor(); 1115 if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) { 1116 mActor->SendRecycleTexture(mFlags); 1117 } 1118 UnlockActor(); 1119 } 1120 } 1121 1122 static void CancelTextureClientNotifyNotUsed(uint64_t aTextureId, 1123 LayersIPCChannel* aAllocator) { 1124 if (!aAllocator) { 1125 return; 1126 } 1127 nsCOMPtr<nsISerialEventTarget> thread = aAllocator->GetThread(); 1128 if (!thread) { 1129 return; 1130 } 1131 if (thread->IsOnCurrentThread()) { 1132 aAllocator->CancelWaitForNotifyNotUsed(aTextureId); 1133 } else { 1134 thread->Dispatch(NewRunnableFunction( 1135 "CancelTextureClientNotifyNotUsedRunnable", 1136 CancelTextureClientNotifyNotUsed, aTextureId, aAllocator)); 1137 } 1138 } 1139 1140 void TextureClient::CancelWaitForNotifyNotUsed() { 1141 if (GetFlags() & TextureFlags::RECYCLE) { 1142 CancelTextureClientNotifyNotUsed(mSerial, GetAllocator()); 1143 return; 1144 } 1145 } 1146 1147 /* static */ 1148 void TextureClient::TextureClientRecycleCallback(TextureClient* aClient, 1149 void* aClosure) { 1150 MOZ_ASSERT(aClient->GetRecycleAllocator()); 1151 aClient->GetRecycleAllocator()->RecycleTextureClient(aClient); 1152 } 1153 1154 void TextureClient::SetRecycleAllocator( 1155 ITextureClientRecycleAllocator* aAllocator) { 1156 mRecycleAllocator = aAllocator; 1157 if (aAllocator) { 1158 SetRecycleCallback(TextureClientRecycleCallback, nullptr); 1159 } else { 1160 ClearRecycleCallback(); 1161 } 1162 } 1163 1164 bool TextureClient::InitIPDLActor(CompositableForwarder* aForwarder) { 1165 MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetThread() == 1166 mAllocator->GetThread()); 1167 1168 if (mActor && !mActor->IPCOpen()) { 1169 return false; 1170 } 1171 1172 if (mActor && !mActor->mDestroyed) { 1173 CompositableForwarder* currentFwd = mActor->mCompositableForwarder; 1174 TextureForwarder* currentTexFwd = mActor->mTextureForwarder; 1175 if (currentFwd != aForwarder) { 1176 // It's a bit iffy but right now ShadowLayerForwarder inherits 1177 // TextureForwarder even though it should not. 1178 // ShadowLayerForwarder::GetTextureForwarder actually returns a pointer to 1179 // the CompositorBridgeChild. It's Ok for a texture to move from a 1180 // ShadowLayerForwarder to another, but not form a CompositorBridgeChild 1181 // to another (they use different channels). 1182 if (currentTexFwd && currentTexFwd != aForwarder->GetTextureForwarder()) { 1183 gfxCriticalError() 1184 << "Attempt to move a texture to a different channel CF."; 1185 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1186 return false; 1187 } 1188 if (currentFwd && currentFwd->GetCompositorBackendType() != 1189 aForwarder->GetCompositorBackendType()) { 1190 gfxCriticalError() 1191 << "Attempt to move a texture to different compositor backend."; 1192 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1193 return false; 1194 } 1195 mActor->mCompositableForwarder = aForwarder; 1196 mActor->mUsesImageBridge = 1197 aForwarder->GetTextureForwarder()->UsesImageBridge(); 1198 } 1199 return true; 1200 } 1201 MOZ_ASSERT(!mActor || mActor->mDestroyed, 1202 "Cannot use a texture on several IPC channels."); 1203 1204 SurfaceDescriptor desc; 1205 if (!ToSurfaceDescriptor(desc)) { 1206 return false; 1207 } 1208 1209 // Try external image id allocation. 1210 mExternalImageId = 1211 aForwarder->GetTextureForwarder()->GetNextExternalImageId(); 1212 1213 ReadLockDescriptor readLockDescriptor = null_t(); 1214 1215 { 1216 MutexAutoLock lock(mMutex); 1217 EnsureHasReadLock(); 1218 if (mReadLock) { 1219 mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid()); 1220 } 1221 } 1222 1223 PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture( 1224 desc, std::move(readLockDescriptor), 1225 aForwarder->GetCompositorBackendType(), GetFlags(), 1226 dom::ContentParentId(), mSerial, mExternalImageId); 1227 1228 if (!actor) { 1229 gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", " 1230 << static_cast<int32_t>( 1231 aForwarder->GetCompositorBackendType()) 1232 << ", " << static_cast<uint32_t>(GetFlags()) << ", " 1233 << mSerial; 1234 return false; 1235 } 1236 1237 mActor = static_cast<TextureChild*>(actor); 1238 mActor->mCompositableForwarder = aForwarder; 1239 mActor->mTextureForwarder = aForwarder->GetTextureForwarder(); 1240 mActor->mTextureClient = this; 1241 1242 // If the TextureClient is already locked, we have to lock TextureChild's 1243 // mutex since it will be unlocked in TextureClient::Unlock. 1244 if (mIsLocked) { 1245 LockActor(); 1246 } 1247 1248 return mActor->IPCOpen(); 1249 } 1250 1251 bool TextureClient::InitIPDLActor(KnowsCompositor* aKnowsCompositor, 1252 const dom::ContentParentId& aContentId) { 1253 MOZ_ASSERT(aKnowsCompositor && 1254 aKnowsCompositor->GetTextureForwarder()->GetThread() == 1255 mAllocator->GetThread()); 1256 TextureForwarder* fwd = aKnowsCompositor->GetTextureForwarder(); 1257 if (mActor && !mActor->mDestroyed) { 1258 CompositableForwarder* currentFwd = mActor->mCompositableForwarder; 1259 TextureForwarder* currentTexFwd = mActor->mTextureForwarder; 1260 1261 if (currentFwd) { 1262 gfxCriticalError() 1263 << "Attempt to remove a texture from a CompositableForwarder."; 1264 return false; 1265 } 1266 1267 if (currentTexFwd && currentTexFwd != fwd) { 1268 gfxCriticalError() 1269 << "Attempt to move a texture to a different channel TF."; 1270 return false; 1271 } 1272 mActor->mTextureForwarder = fwd; 1273 return true; 1274 } 1275 MOZ_ASSERT(!mActor || mActor->mDestroyed, 1276 "Cannot use a texture on several IPC channels."); 1277 1278 SurfaceDescriptor desc; 1279 if (!ToSurfaceDescriptor(desc)) { 1280 return false; 1281 } 1282 1283 // Try external image id allocation. 1284 mExternalImageId = 1285 aKnowsCompositor->GetTextureForwarder()->GetNextExternalImageId(); 1286 1287 ReadLockDescriptor readLockDescriptor = null_t(); 1288 { 1289 MutexAutoLock lock(mMutex); 1290 EnsureHasReadLock(); 1291 if (mReadLock) { 1292 mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid()); 1293 } 1294 } 1295 1296 PTextureChild* actor = 1297 fwd->CreateTexture(desc, std::move(readLockDescriptor), 1298 aKnowsCompositor->GetCompositorBackendType(), 1299 GetFlags(), aContentId, mSerial, mExternalImageId); 1300 if (!actor) { 1301 gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", " 1302 << static_cast<int32_t>( 1303 aKnowsCompositor->GetCompositorBackendType()) 1304 << ", " << static_cast<uint32_t>(GetFlags()) << ", " 1305 << mSerial; 1306 return false; 1307 } 1308 1309 mActor = static_cast<TextureChild*>(actor); 1310 mActor->mTextureForwarder = fwd; 1311 mActor->mTextureClient = this; 1312 1313 // If the TextureClient is already locked, we have to lock TextureChild's 1314 // mutex since it will be unlocked in TextureClient::Unlock. 1315 if (mIsLocked) { 1316 LockActor(); 1317 } 1318 1319 return mActor->IPCOpen(); 1320 } 1321 1322 PTextureChild* TextureClient::GetIPDLActor() { return mActor; } 1323 1324 // static 1325 already_AddRefed<TextureClient> TextureClient::CreateForDrawing( 1326 KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, 1327 BackendSelector aSelector, TextureFlags aTextureFlags, 1328 TextureAllocationFlags aAllocFlags) { 1329 return TextureClient::CreateForDrawing(aAllocator->GetTextureForwarder(), 1330 aFormat, aSize, aAllocator, aSelector, 1331 aTextureFlags, aAllocFlags); 1332 } 1333 1334 // static 1335 already_AddRefed<TextureClient> TextureClient::CreateForDrawing( 1336 TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat, 1337 gfx::IntSize aSize, KnowsCompositor* aKnowsCompositor, 1338 BackendSelector aSelector, TextureFlags aTextureFlags, 1339 TextureAllocationFlags aAllocFlags) { 1340 LayersBackend layersBackend = aKnowsCompositor->GetCompositorBackendType(); 1341 gfx::BackendType moz2DBackend = 1342 BackendTypeForBackendSelector(layersBackend, aSelector); 1343 1344 // also test the validity of aAllocator 1345 if (!aAllocator || !aAllocator->IPCOpen()) { 1346 return nullptr; 1347 } 1348 1349 if (!gfx::Factory::AllowedSurfaceSize(aSize)) { 1350 return nullptr; 1351 } 1352 1353 TextureData* data = 1354 TextureData::Create(aAllocator, aFormat, aSize, aKnowsCompositor, 1355 aSelector, aTextureFlags, aAllocFlags); 1356 1357 if (data) { 1358 return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator); 1359 } 1360 if (aAllocFlags & ALLOC_FORCE_REMOTE) { 1361 // If we must be remote, but allocation failed, then don't fall back. 1362 return nullptr; 1363 } 1364 1365 // Can't do any better than a buffer texture client. 1366 return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize, 1367 moz2DBackend, layersBackend, 1368 aTextureFlags, aAllocFlags); 1369 } 1370 1371 // static 1372 already_AddRefed<TextureClient> TextureClient::CreateFromSurface( 1373 KnowsCompositor* aAllocator, gfx::SourceSurface* aSurface, 1374 BackendSelector aSelector, TextureFlags aTextureFlags, 1375 TextureAllocationFlags aAllocFlags) { 1376 // also test the validity of aAllocator 1377 if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) { 1378 return nullptr; 1379 } 1380 1381 gfx::IntSize size = aSurface->GetSize(); 1382 1383 if (!gfx::Factory::AllowedSurfaceSize(size)) { 1384 return nullptr; 1385 } 1386 1387 // Fall back to using UpdateFromSurface 1388 1389 TextureAllocationFlags allocFlags = 1390 TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE); 1391 RefPtr<TextureClient> client = 1392 CreateForDrawing(aAllocator, aSurface->GetFormat(), size, aSelector, 1393 aTextureFlags, allocFlags); 1394 if (!client) { 1395 return nullptr; 1396 } 1397 1398 TextureClientAutoLock autoLock(client, OpenMode::OPEN_WRITE_ONLY); 1399 if (!autoLock.Succeeded()) { 1400 return nullptr; 1401 } 1402 1403 client->UpdateFromSurface(aSurface); 1404 return client.forget(); 1405 } 1406 1407 // static 1408 already_AddRefed<TextureClient> TextureClient::CreateForRawBufferAccess( 1409 KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, 1410 gfx::BackendType aMoz2DBackend, TextureFlags aTextureFlags, 1411 TextureAllocationFlags aAllocFlags) { 1412 return CreateForRawBufferAccess( 1413 aAllocator->GetTextureForwarder(), aFormat, aSize, aMoz2DBackend, 1414 aAllocator->GetCompositorBackendType(), aTextureFlags, aAllocFlags); 1415 } 1416 1417 // static 1418 already_AddRefed<TextureClient> TextureClient::CreateForRawBufferAccess( 1419 LayersIPCChannel* aAllocator, gfx::SurfaceFormat aFormat, 1420 gfx::IntSize aSize, gfx::BackendType aMoz2DBackend, 1421 LayersBackend aLayersBackend, TextureFlags aTextureFlags, 1422 TextureAllocationFlags aAllocFlags) { 1423 // also test the validity of aAllocator 1424 if (!aAllocator || !aAllocator->IPCOpen()) { 1425 return nullptr; 1426 } 1427 1428 if (!gfx::Factory::AllowedSurfaceSize(aSize)) { 1429 return nullptr; 1430 } 1431 1432 if (aFormat == SurfaceFormat::B8G8R8X8) { 1433 // Skia doesn't support RGBX, so ensure we clear the buffer for the proper 1434 // alpha values. 1435 aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER); 1436 } 1437 1438 // We ignore the backend type if we get here. It should only be Skia. 1439 // Therefore it is safe to force the buffer to be Skia. 1440 NS_WARNING_ASSERTION(aMoz2DBackend == gfx::BackendType::SKIA, 1441 "Unsupported TextureClient backend type"); 1442 1443 // For future changes, check aAllocFlags aAllocFlags & ALLOC_DO_NOT_ACCELERATE 1444 TextureData* texData = BufferTextureData::Create( 1445 aSize, aFormat, gfx::BackendType::SKIA, aLayersBackend, aTextureFlags, 1446 aAllocFlags, aAllocator); 1447 if (!texData) { 1448 return nullptr; 1449 } 1450 1451 return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator); 1452 } 1453 1454 // static 1455 already_AddRefed<TextureClient> TextureClient::CreateForYCbCr( 1456 KnowsCompositor* aAllocator, const gfx::IntRect& aDisplay, 1457 const gfx::IntSize& aYSize, uint32_t aYStride, 1458 const gfx::IntSize& aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, 1459 gfx::ColorDepth aColorDepth, gfx::YUVColorSpace aYUVColorSpace, 1460 gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling, 1461 TextureFlags aTextureFlags) { 1462 if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) { 1463 return nullptr; 1464 } 1465 1466 if (!gfx::Factory::AllowedSurfaceSize(aYSize)) { 1467 return nullptr; 1468 } 1469 1470 TextureData* data = BufferTextureData::CreateForYCbCr( 1471 aAllocator, aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, 1472 aStereoMode, aColorDepth, aYUVColorSpace, aColorRange, aSubsampling, 1473 aTextureFlags); 1474 if (!data) { 1475 return nullptr; 1476 } 1477 1478 return MakeAndAddRef<TextureClient>(data, aTextureFlags, 1479 aAllocator->GetTextureForwarder()); 1480 } 1481 1482 TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags, 1483 LayersIPCChannel* aAllocator) 1484 : AtomicRefCountedWithFinalize("TextureClient"), 1485 mMutex("TextureClient::mMutex"), 1486 mAllocator(aAllocator), 1487 mActor(nullptr), 1488 mData(aData), 1489 mFlags(aFlags), 1490 mOpenMode(OpenMode::OPEN_NONE), 1491 mIsLocked(false), 1492 mIsReadLocked(false), 1493 mUpdated(false), 1494 mAddedToCompositableClient(false), 1495 mFwdTransactionId(0), 1496 mSerial(++sSerialCounter) { 1497 mData->FillInfo(mInfo); 1498 mFlags |= mData->GetTextureFlags(); 1499 } 1500 1501 bool TextureClient::CopyToTextureClient(TextureClient* aTarget, 1502 const gfx::IntRect* aRect, 1503 const gfx::IntPoint* aPoint) { 1504 MOZ_ASSERT(IsLocked()); 1505 MOZ_ASSERT(aTarget->IsLocked()); 1506 1507 if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) { 1508 return false; 1509 } 1510 1511 RefPtr<DrawTarget> destinationTarget = aTarget->BorrowDrawTarget(); 1512 if (!destinationTarget) { 1513 gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in " 1514 "BorrowDrawTarget"; 1515 return false; 1516 } 1517 1518 RefPtr<DrawTarget> sourceTarget = BorrowDrawTarget(); 1519 if (!sourceTarget) { 1520 gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in " 1521 "BorrowDrawTarget"; 1522 return false; 1523 } 1524 1525 RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot(); 1526 destinationTarget->CopySurface( 1527 source, aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()), 1528 aPoint ? *aPoint : gfx::IntPoint(0, 0)); 1529 return true; 1530 } 1531 1532 already_AddRefed<gfx::DataSourceSurface> TextureClient::GetAsSurface() { 1533 if (!Lock(OpenMode::OPEN_READ)) { 1534 return nullptr; 1535 } 1536 RefPtr<gfx::DataSourceSurface> data; 1537 { // scope so that the DrawTarget is destroyed before Unlock() 1538 RefPtr<gfx::DrawTarget> dt = BorrowDrawTarget(); 1539 if (dt) { 1540 RefPtr<gfx::SourceSurface> surf = dt->Snapshot(); 1541 if (surf) { 1542 data = surf->GetDataSurface(); 1543 } 1544 } 1545 } 1546 Unlock(); 1547 return data.forget(); 1548 } 1549 1550 void TextureClient::GetSurfaceDescriptorRemoteDecoder( 1551 SurfaceDescriptorRemoteDecoder* const aOutDesc) { 1552 const auto handle = GetSerial(); 1553 1554 RemoteDecoderVideoSubDescriptor subDesc = null_t(); 1555 MOZ_RELEASE_ASSERT(mData); 1556 mData->GetSubDescriptor(&subDesc); 1557 1558 *aOutDesc = SurfaceDescriptorRemoteDecoder( 1559 handle, std::move(subDesc), Nothing(), 1560 SurfaceDescriptorRemoteDecoderId::GetNext()); 1561 } 1562 1563 class MemoryTextureReadLock : public NonBlockingTextureReadLock { 1564 public: 1565 MemoryTextureReadLock(); 1566 1567 virtual ~MemoryTextureReadLock(); 1568 1569 bool ReadLock() override; 1570 1571 int32_t ReadUnlock() override; 1572 1573 int32_t GetReadCount() override; 1574 1575 LockType GetType() override { return TYPE_NONBLOCKING_MEMORY; } 1576 1577 bool IsValid() const override { return true; }; 1578 1579 bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override; 1580 1581 Atomic<int32_t> mReadCount; 1582 }; 1583 1584 // The cross-prcess implementation of TextureReadLock. 1585 // 1586 // Since we don't use cross-process reference counting for the ReadLock objects, 1587 // we use the lock's internal counter as a way to know when to deallocate the 1588 // underlying shmem section: when the counter is equal to 1, it means that the 1589 // lock is not "held" (the texture is writable), when the counter is equal to 0 1590 // it means that we can safely deallocate the shmem section without causing a 1591 // race condition with the other process. 1592 class ShmemTextureReadLock : public NonBlockingTextureReadLock { 1593 public: 1594 struct ShmReadLockInfo { 1595 int32_t readCount; 1596 }; 1597 1598 explicit ShmemTextureReadLock(LayersIPCChannel* aAllocator); 1599 1600 virtual ~ShmemTextureReadLock(); 1601 1602 bool ReadLock() override; 1603 1604 int32_t ReadUnlock() override; 1605 1606 int32_t GetReadCount() override; 1607 1608 bool IsValid() const override { return mAllocSuccess; }; 1609 1610 LockType GetType() override { return TYPE_NONBLOCKING_SHMEM; } 1611 1612 bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override; 1613 1614 mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; } 1615 1616 explicit ShmemTextureReadLock( 1617 const mozilla::layers::ShmemSection& aShmemSection) 1618 : mShmemSection(aShmemSection), mAllocSuccess(true) { 1619 MOZ_COUNT_CTOR(ShmemTextureReadLock); 1620 } 1621 1622 ShmReadLockInfo* GetShmReadLockInfoPtr() { 1623 return reinterpret_cast<ShmReadLockInfo*>( 1624 mShmemSection.shmem().get<char>() + mShmemSection.offset()); 1625 } 1626 1627 RefPtr<LayersIPCChannel> mClientAllocator; 1628 mozilla::layers::ShmemSection mShmemSection; 1629 bool mAllocSuccess; 1630 }; 1631 1632 class CrossProcessSemaphoreReadLock : public TextureReadLock { 1633 public: 1634 CrossProcessSemaphoreReadLock() 1635 : mSemaphore(CrossProcessSemaphore::Create("TextureReadLock", 1)), 1636 mShared(false) {} 1637 explicit CrossProcessSemaphoreReadLock(CrossProcessSemaphoreHandle aHandle) 1638 : mSemaphore(CrossProcessSemaphore::Create(std::move(aHandle))), 1639 mShared(false) {} 1640 1641 bool ReadLock() override { 1642 if (!IsValid()) { 1643 return false; 1644 } 1645 return mSemaphore->Wait(); 1646 } 1647 bool TryReadLock(TimeDuration aTimeout) override { 1648 if (!IsValid()) { 1649 return false; 1650 } 1651 return mSemaphore->Wait(Some(aTimeout)); 1652 } 1653 int32_t ReadUnlock() override { 1654 if (!IsValid()) { 1655 return 1; 1656 } 1657 mSemaphore->Signal(); 1658 return 1; 1659 } 1660 bool IsValid() const override { return !!mSemaphore; } 1661 1662 bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override; 1663 1664 LockType GetType() override { return TYPE_CROSS_PROCESS_SEMAPHORE; } 1665 1666 UniquePtr<CrossProcessSemaphore> mSemaphore; 1667 bool mShared; 1668 }; 1669 1670 // static 1671 already_AddRefed<TextureReadLock> TextureReadLock::Deserialize( 1672 ReadLockDescriptor&& aDescriptor, ISurfaceAllocator* aAllocator) { 1673 switch (aDescriptor.type()) { 1674 case ReadLockDescriptor::TUntrustedShmemSection: { 1675 const UntrustedShmemSection& untrusted = 1676 aDescriptor.get_UntrustedShmemSection(); 1677 Maybe<ShmemSection> section = ShmemSection::FromUntrusted(untrusted); 1678 if (section.isNothing()) { 1679 return nullptr; 1680 } 1681 return MakeAndAddRef<ShmemTextureReadLock>(section.value()); 1682 } 1683 case ReadLockDescriptor::Tuintptr_t: { 1684 if (!aAllocator->IsSameProcess()) { 1685 // Trying to use a memory based lock instead of a shmem based one in 1686 // the cross-process case is a bad security violation. 1687 NS_ERROR( 1688 "A client process may be trying to peek at the host's address " 1689 "space!"); 1690 return nullptr; 1691 } 1692 RefPtr<TextureReadLock> lock = 1693 reinterpret_cast<MemoryTextureReadLock*>(aDescriptor.get_uintptr_t()); 1694 1695 MOZ_ASSERT(lock); 1696 if (lock) { 1697 // The corresponding AddRef is in MemoryTextureReadLock::Serialize 1698 lock.get()->Release(); 1699 } 1700 1701 return lock.forget(); 1702 } 1703 case ReadLockDescriptor::TCrossProcessSemaphoreDescriptor: { 1704 return MakeAndAddRef<CrossProcessSemaphoreReadLock>( 1705 std::move(aDescriptor.get_CrossProcessSemaphoreDescriptor().sem())); 1706 } 1707 case ReadLockDescriptor::Tnull_t: { 1708 return nullptr; 1709 } 1710 default: { 1711 MOZ_DIAGNOSTIC_CRASH( 1712 "Invalid descriptor in TextureReadLock::Deserialize"); 1713 } 1714 } 1715 return nullptr; 1716 } 1717 // static 1718 already_AddRefed<TextureReadLock> NonBlockingTextureReadLock::Create( 1719 LayersIPCChannel* aAllocator) { 1720 if (aAllocator->IsSameProcess()) { 1721 // If our compositor is in the same process, we can save some cycles by not 1722 // using shared memory. 1723 return MakeAndAddRef<MemoryTextureReadLock>(); 1724 } 1725 1726 return MakeAndAddRef<ShmemTextureReadLock>(aAllocator); 1727 } 1728 1729 MemoryTextureReadLock::MemoryTextureReadLock() : mReadCount(1) { 1730 MOZ_COUNT_CTOR(MemoryTextureReadLock); 1731 } 1732 1733 MemoryTextureReadLock::~MemoryTextureReadLock() { 1734 // One read count that is added in constructor. 1735 MOZ_ASSERT(mReadCount == 1); 1736 MOZ_COUNT_DTOR(MemoryTextureReadLock); 1737 } 1738 1739 bool MemoryTextureReadLock::Serialize(ReadLockDescriptor& aOutput, 1740 base::ProcessId aOther) { 1741 // AddRef here and Release when receiving on the host side to make sure the 1742 // reference count doesn't go to zero before the host receives the message. 1743 // see TextureReadLock::Deserialize 1744 this->AddRef(); 1745 aOutput = ReadLockDescriptor(uintptr_t(this)); 1746 return true; 1747 } 1748 1749 bool MemoryTextureReadLock::ReadLock() { 1750 ++mReadCount; 1751 return true; 1752 } 1753 1754 int32_t MemoryTextureReadLock::ReadUnlock() { 1755 int32_t readCount = --mReadCount; 1756 MOZ_ASSERT(readCount >= 0); 1757 1758 return readCount; 1759 } 1760 1761 int32_t MemoryTextureReadLock::GetReadCount() { return mReadCount; } 1762 1763 ShmemTextureReadLock::ShmemTextureReadLock(LayersIPCChannel* aAllocator) 1764 : mClientAllocator(aAllocator), mAllocSuccess(false) { 1765 MOZ_COUNT_CTOR(ShmemTextureReadLock); 1766 MOZ_ASSERT(mClientAllocator); 1767 MOZ_ASSERT(mClientAllocator->GetTileLockAllocator()); 1768 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) 1769 if (mClientAllocator->GetTileLockAllocator()->AllocShmemSection( 1770 MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) { 1771 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1772 info->readCount = 1; 1773 mAllocSuccess = true; 1774 } 1775 } 1776 1777 ShmemTextureReadLock::~ShmemTextureReadLock() { 1778 if (mClientAllocator) { 1779 // Release one read count that is added in constructor. 1780 // The count is kept for calling GetReadCount() by TextureClientPool. 1781 ReadUnlock(); 1782 } 1783 MOZ_COUNT_DTOR(ShmemTextureReadLock); 1784 } 1785 1786 bool ShmemTextureReadLock::Serialize(ReadLockDescriptor& aOutput, 1787 base::ProcessId aOther) { 1788 aOutput = ReadLockDescriptor(GetShmemSection().AsUntrusted()); 1789 return true; 1790 } 1791 1792 bool ShmemTextureReadLock::ReadLock() { 1793 if (!mAllocSuccess) { 1794 return false; 1795 } 1796 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1797 PR_ATOMIC_INCREMENT(&info->readCount); 1798 return true; 1799 } 1800 1801 int32_t ShmemTextureReadLock::ReadUnlock() { 1802 if (!mAllocSuccess) { 1803 return 0; 1804 } 1805 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1806 int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount); 1807 MOZ_ASSERT(readCount >= 0); 1808 if (readCount > 0) { 1809 return readCount; 1810 } 1811 if (mClientAllocator) { 1812 if (nsCOMPtr<nsISerialEventTarget> thread = mClientAllocator->GetThread()) { 1813 if (thread->IsOnCurrentThread()) { 1814 if (auto* tileLockAllocator = 1815 mClientAllocator->GetTileLockAllocator()) { 1816 tileLockAllocator->DeallocShmemSection(mShmemSection); 1817 return readCount; 1818 } 1819 } else { 1820 thread->Dispatch(NS_NewRunnableFunction( 1821 __func__, 1822 [shmemSection = std::move(mShmemSection), 1823 clientAllocator = std::move(mClientAllocator)]() mutable { 1824 if (auto* tileLockAllocator = 1825 clientAllocator->GetTileLockAllocator()) { 1826 tileLockAllocator->DeallocShmemSection(shmemSection); 1827 } else { 1828 // we are on the compositor process, or IPC is down. 1829 FixedSizeSmallShmemSectionAllocator::FreeShmemSection( 1830 shmemSection); 1831 } 1832 })); 1833 return readCount; 1834 } 1835 } 1836 } 1837 // we are on the compositor process, or IPC is down. 1838 FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mShmemSection); 1839 return readCount; 1840 } 1841 1842 int32_t ShmemTextureReadLock::GetReadCount() { 1843 if (!mAllocSuccess) { 1844 return 0; 1845 } 1846 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1847 return info->readCount; 1848 } 1849 1850 bool CrossProcessSemaphoreReadLock::Serialize(ReadLockDescriptor& aOutput, 1851 base::ProcessId aOther) { 1852 if (!mShared && IsValid()) { 1853 aOutput = ReadLockDescriptor( 1854 CrossProcessSemaphoreDescriptor(mSemaphore->CloneHandle())); 1855 mSemaphore->CloseHandle(); 1856 mShared = true; 1857 return true; 1858 } else { 1859 return mShared; 1860 } 1861 } 1862 1863 void TextureClient::EnableBlockingReadLock() { 1864 MOZ_ASSERT(ShouldReadLock()); 1865 if (!mReadLock) { 1866 mReadLock = new CrossProcessSemaphoreReadLock(); 1867 } 1868 } 1869 1870 bool UpdateYCbCrTextureClient(TextureClient* aTexture, 1871 const PlanarYCbCrData& aData) { 1872 MOZ_ASSERT(aTexture); 1873 MOZ_ASSERT(aTexture->IsLocked()); 1874 MOZ_ASSERT(aTexture->GetFormat() == gfx::SurfaceFormat::YUV420, 1875 "This textureClient can only use YCbCr data"); 1876 MOZ_ASSERT(!aTexture->IsImmutable()); 1877 MOZ_ASSERT(aTexture->IsValid()); 1878 MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip); 1879 1880 MappedYCbCrTextureData mapped; 1881 if (!aTexture->BorrowMappedYCbCrData(mapped)) { 1882 NS_WARNING("Failed to extract YCbCr info!"); 1883 return false; 1884 } 1885 1886 uint32_t bytesPerPixel = 1887 BytesPerPixel(SurfaceFormatForColorDepth(aData.mColorDepth)); 1888 MappedYCbCrTextureData srcData; 1889 srcData.y.data = aData.mYChannel; 1890 srcData.y.size = aData.YDataSize(); 1891 srcData.y.stride = aData.mYStride; 1892 srcData.y.skip = aData.mYSkip; 1893 srcData.y.bytesPerPixel = bytesPerPixel; 1894 srcData.cb.data = aData.mCbChannel; 1895 srcData.cb.size = aData.CbCrDataSize(); 1896 srcData.cb.stride = aData.mCbCrStride; 1897 srcData.cb.skip = aData.mCbSkip; 1898 srcData.cb.bytesPerPixel = bytesPerPixel; 1899 srcData.cr.data = aData.mCrChannel; 1900 srcData.cr.size = aData.CbCrDataSize(); 1901 srcData.cr.stride = aData.mCbCrStride; 1902 srcData.cr.skip = aData.mCrSkip; 1903 srcData.cr.bytesPerPixel = bytesPerPixel; 1904 srcData.metadata = nullptr; 1905 1906 if (!srcData.CopyInto(mapped)) { 1907 NS_WARNING("Failed to copy image data!"); 1908 return false; 1909 } 1910 1911 if (TextureRequiresLocking(aTexture->GetFlags())) { 1912 // We don't have support for proper locking yet, so we'll 1913 // have to be immutable instead. 1914 aTexture->MarkImmutable(); 1915 } 1916 return true; 1917 } 1918 1919 already_AddRefed<TextureClient> TextureClient::CreateWithData( 1920 TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator) { 1921 if (!aData) { 1922 return nullptr; 1923 } 1924 return MakeAndAddRef<TextureClient>(aData, aFlags, aAllocator); 1925 } 1926 1927 template <class PixelDataType> 1928 static void copyData(PixelDataType* aDst, 1929 const MappedYCbCrChannelData& aChannelDst, 1930 PixelDataType* aSrc, 1931 const MappedYCbCrChannelData& aChannelSrc) { 1932 uint8_t* srcByte = reinterpret_cast<uint8_t*>(aSrc); 1933 const int32_t srcSkip = aChannelSrc.skip + 1; 1934 uint8_t* dstByte = reinterpret_cast<uint8_t*>(aDst); 1935 const int32_t dstSkip = aChannelDst.skip + 1; 1936 for (int32_t i = 0; i < aChannelSrc.size.height; ++i) { 1937 for (int32_t j = 0; j < aChannelSrc.size.width; ++j) { 1938 *aDst = *aSrc; 1939 aSrc += srcSkip; 1940 aDst += dstSkip; 1941 } 1942 srcByte += aChannelSrc.stride; 1943 aSrc = reinterpret_cast<PixelDataType*>(srcByte); 1944 dstByte += aChannelDst.stride; 1945 aDst = reinterpret_cast<PixelDataType*>(dstByte); 1946 } 1947 } 1948 1949 bool MappedYCbCrChannelData::CopyInto(MappedYCbCrChannelData& aDst) { 1950 if (!data || !aDst.data || size != aDst.size) { 1951 return false; 1952 } 1953 1954 if (stride == aDst.stride && skip == aDst.skip) { 1955 // fast path! 1956 // We assume that the padding in the destination is there for alignment 1957 // purposes and doesn't contain useful data. 1958 memcpy(aDst.data, data, stride * size.height); 1959 return true; 1960 } 1961 1962 if (aDst.skip == 0 && skip == 0) { 1963 // fast-ish path 1964 for (int32_t i = 0; i < size.height; ++i) { 1965 memcpy(aDst.data + i * aDst.stride, data + i * stride, 1966 size.width * bytesPerPixel); 1967 } 1968 return true; 1969 } 1970 1971 MOZ_ASSERT(bytesPerPixel == 1 || bytesPerPixel == 2); 1972 // slow path 1973 if (bytesPerPixel == 1) { 1974 copyData(aDst.data, aDst, data, *this); 1975 } else if (bytesPerPixel == 2) { 1976 copyData(reinterpret_cast<uint16_t*>(aDst.data), aDst, 1977 reinterpret_cast<uint16_t*>(data), *this); 1978 } 1979 return true; 1980 } 1981 1982 } // namespace mozilla::layers