ImageContainer.cpp (42348B)
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 "ImageContainer.h" 8 9 #include <string.h> // for memcpy, memset 10 11 #include "GLImages.h" // for SurfaceTextureImage 12 #include "MediaInfo.h" // VideoInfo::Rotation 13 #include "YCbCrUtils.h" // for YCbCr conversions 14 #include "gfx2DGlue.h" 15 #include "gfxPlatform.h" // for gfxPlatform 16 #include "gfxUtils.h" // for gfxUtils 17 #include "GPUVideoImage.h" 18 #include "libyuv.h" 19 #include "mozilla/CheckedInt.h" 20 #include "mozilla/ProfilerLabels.h" 21 #include "mozilla/RefPtr.h" // for already_AddRefed 22 #include "mozilla/StaticPrefs_layers.h" 23 #include "mozilla/gfx/2D.h" 24 #include "mozilla/gfx/gfxVars.h" 25 #include "mozilla/gfx/Swizzle.h" 26 #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc 27 #include "mozilla/layers/CompositorTypes.h" 28 #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild 29 #include "mozilla/layers/ImageClient.h" // for ImageClient 30 #include "mozilla/layers/ImageDataSerializer.h" // for SurfaceDescriptorBuffer 31 #include "mozilla/layers/LayersMessages.h" 32 #include "mozilla/layers/SharedPlanarYCbCrImage.h" 33 #include "mozilla/layers/SharedRGBImage.h" 34 #include "mozilla/layers/TextureClientRecycleAllocator.h" 35 #include "nsProxyRelease.h" 36 #include "nsISupportsUtils.h" // for NS_IF_ADDREF 37 38 #ifdef XP_DARWIN 39 # include "MacIOSurfaceImage.h" 40 #endif 41 42 #ifdef XP_WIN 43 # include <d3d10_1.h> 44 45 # include "gfxWindowsPlatform.h" 46 # include "mozilla/gfx/DeviceManagerDx.h" 47 # include "mozilla/layers/D3D11ShareHandleImage.h" 48 # include "mozilla/layers/D3D11YCbCrImage.h" 49 #endif 50 51 namespace mozilla::layers { 52 53 using namespace mozilla::gfx; 54 using namespace mozilla::ipc; 55 56 Atomic<int32_t> Image::sSerialCounter(0); 57 58 Atomic<uint32_t> ImageContainer::sGenerationCounter(0); 59 60 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc, 61 const gfx::IntSize& aSize, int32_t aStride, 62 int32_t aSkip); 63 64 RefPtr<PlanarYCbCrImage> ImageFactory::CreatePlanarYCbCrImage( 65 const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) { 66 return new RecyclingPlanarYCbCrImage(aRecycleBin); 67 } 68 69 BufferRecycleBin::BufferRecycleBin() 70 : mLock("mozilla.layers.BufferRecycleBin.mLock") 71 // This member is only valid when the bin is not empty and will be 72 // properly initialized in RecycleBuffer, but initializing it here avoids 73 // static analysis noise. 74 , 75 mRecycledBufferSize(0) {} 76 77 void BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer, 78 uint32_t aSize) { 79 MutexAutoLock lock(mLock); 80 81 if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) { 82 mRecycledBuffers.Clear(); 83 } 84 mRecycledBufferSize = aSize; 85 mRecycledBuffers.AppendElement(std::move(aBuffer)); 86 } 87 88 UniquePtr<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize) { 89 MutexAutoLock lock(mLock); 90 91 if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) { 92 return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]); 93 } 94 95 return mRecycledBuffers.PopLastElement(); 96 } 97 98 void BufferRecycleBin::ClearRecycledBuffers() { 99 MutexAutoLock lock(mLock); 100 if (!mRecycledBuffers.IsEmpty()) { 101 mRecycledBuffers.Clear(); 102 } 103 mRecycledBufferSize = 0; 104 } 105 106 ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer) 107 : mLock("mozilla.layers.ImageContainerListener.mLock"), 108 mImageContainer(aImageContainer) {} 109 110 ImageContainerListener::~ImageContainerListener() = default; 111 112 void ImageContainerListener::NotifyComposite( 113 const ImageCompositeNotification& aNotification) { 114 MutexAutoLock lock(mLock); 115 if (mImageContainer) { 116 mImageContainer->NotifyComposite(aNotification); 117 } 118 } 119 120 void ImageContainerListener::NotifyDropped(uint32_t aDropped) { 121 MutexAutoLock lock(mLock); 122 if (mImageContainer) { 123 mImageContainer->NotifyDropped(aDropped); 124 } 125 } 126 127 void ImageContainerListener::ClearImageContainer() { 128 MutexAutoLock lock(mLock); 129 mImageContainer = nullptr; 130 } 131 132 void ImageContainerListener::DropImageClient() { 133 MutexAutoLock lock(mLock); 134 if (mImageContainer) { 135 mImageContainer->DropImageClient(); 136 } 137 } 138 139 already_AddRefed<ImageClient> ImageContainer::GetImageClient() { 140 RecursiveMutexAutoLock mon(mRecursiveMutex); 141 EnsureImageClient(); 142 RefPtr<ImageClient> imageClient = mImageClient; 143 return imageClient.forget(); 144 } 145 146 void ImageContainer::DropImageClient() { 147 RecursiveMutexAutoLock mon(mRecursiveMutex); 148 if (mImageClient) { 149 mImageClient->ClearCachedResources(); 150 mImageClient = nullptr; 151 } 152 } 153 154 void ImageContainer::EnsureImageClient() { 155 // If we're not forcing a new ImageClient, then we can skip this if we don't 156 // have an existing ImageClient, or if the existing one belongs to an IPC 157 // actor that is still open. 158 if (!mIsAsync) { 159 return; 160 } 161 if (mImageClient && 162 mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) { 163 return; 164 } 165 166 RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton(); 167 if (!imageBridge) { 168 return; 169 } 170 171 mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this); 172 if (mImageClient) { 173 mAsyncContainerHandle = mImageClient->GetAsyncHandle(); 174 } else { 175 // It's okay to drop the async container handle since the ImageBridgeChild 176 // is going to die anyway. 177 mAsyncContainerHandle = CompositableHandle(); 178 } 179 } 180 181 ImageContainer::ImageContainer(ImageUsageType aUsageType, Mode aFlag) 182 : mUsageType(aUsageType), 183 mIsAsync(aFlag == ASYNCHRONOUS), 184 mRecursiveMutex("ImageContainer.mRecursiveMutex"), 185 mGenerationCounter(++sGenerationCounter), 186 mPaintCount(0), 187 mDroppedImageCount(0), 188 mImageFactory(new ImageFactory()), 189 mRotation(VideoRotation::kDegree_0), 190 mRecycleBin(new BufferRecycleBin()), 191 mCurrentProducerID(-1) { 192 if (aFlag == ASYNCHRONOUS) { 193 mNotifyCompositeListener = new ImageContainerListener(this); 194 EnsureImageClient(); 195 } 196 } 197 198 ImageContainer::~ImageContainer() { 199 if (mNotifyCompositeListener) { 200 mNotifyCompositeListener->ClearImageContainer(); 201 } 202 if (mAsyncContainerHandle) { 203 if (RefPtr<ImageBridgeChild> imageBridge = 204 ImageBridgeChild::GetSingleton()) { 205 imageBridge->ForgetImageContainer(mAsyncContainerHandle); 206 } 207 } 208 } 209 210 /* static */ nsresult Image::AllocateSurfaceDescriptorBufferRgb( 211 const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, uint8_t*& aOutBuffer, 212 SurfaceDescriptorBuffer& aSdBuffer, int32_t& aStride, 213 const std::function<layers::MemoryOrShmem(uint32_t)>& aAllocate) { 214 aStride = ImageDataSerializer::ComputeRGBStride(aFormat, aSize.width); 215 size_t length = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat); 216 217 if (aStride <= 0 || length == 0) { 218 return NS_ERROR_INVALID_ARG; 219 } 220 221 aSdBuffer.desc() = RGBDescriptor(aSize, aFormat); 222 aSdBuffer.data() = aAllocate(length); 223 224 const layers::MemoryOrShmem& memOrShmem = aSdBuffer.data(); 225 switch (memOrShmem.type()) { 226 case layers::MemoryOrShmem::Tuintptr_t: 227 aOutBuffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t()); 228 break; 229 case layers::MemoryOrShmem::TShmem: 230 aOutBuffer = memOrShmem.get_Shmem().get<uint8_t>(); 231 break; 232 default: 233 return NS_ERROR_OUT_OF_MEMORY; 234 } 235 236 MOZ_ASSERT(aOutBuffer); 237 return NS_OK; 238 } 239 240 nsresult Image::BuildSurfaceDescriptorBuffer( 241 SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags, 242 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) { 243 RefPtr<SourceSurface> surface = GetAsSourceSurface(); 244 if (NS_WARN_IF(!surface)) { 245 return NS_ERROR_NOT_AVAILABLE; 246 } 247 248 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); 249 if (NS_WARN_IF(!dataSurface)) { 250 return NS_ERROR_FAILURE; 251 } 252 253 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ); 254 if (NS_WARN_IF(!map.IsMapped())) { 255 return NS_ERROR_FAILURE; 256 } 257 258 SurfaceFormat format = dataSurface->GetFormat(); 259 IntSize size = dataSurface->GetSize(); 260 uint8_t* output = nullptr; 261 int32_t stride = 0; 262 nsresult rv = AllocateSurfaceDescriptorBufferRgb( 263 size, format, output, aSdBuffer, stride, aAllocate); 264 if (NS_WARN_IF(NS_FAILED(rv))) { 265 return rv; 266 } 267 268 if (NS_WARN_IF(!SwizzleData(map.GetData(), map.GetStride(), format, output, 269 stride, format, size))) { 270 return NS_ERROR_FAILURE; 271 } 272 273 return NS_OK; 274 } 275 276 nsresult Image::BuildSurfaceDescriptorGPUVideoOrBuffer( 277 SurfaceDescriptor& aSd, BuildSdbFlags aFlags, 278 const Maybe<VideoBridgeSource>& aDest, 279 const std::function<MemoryOrShmem(uint32_t)>& aAllocate, 280 const std::function<void(MemoryOrShmem&&)>& aFree) { 281 if (auto* gpuImage = AsGPUVideoImage()) { 282 if (auto maybeSd = gpuImage->GetDesc()) { 283 if (!aDest || 284 (maybeSd->type() == SurfaceDescriptor::TSurfaceDescriptorGPUVideo && 285 maybeSd->get_SurfaceDescriptorGPUVideo() 286 .get_SurfaceDescriptorRemoteDecoder() 287 .source() == aDest)) { 288 aSd = std::move(*maybeSd); 289 return NS_OK; 290 } 291 } 292 } 293 294 SurfaceDescriptorBuffer sdb; 295 nsresult rv = BuildSurfaceDescriptorBuffer(sdb, aFlags, aAllocate); 296 if (NS_FAILED(rv)) { 297 if (sdb.data().type() != MemoryOrShmem::Type::T__None) { 298 aFree(std::move(sdb.data())); 299 } 300 return rv; 301 } 302 303 aSd = std::move(sdb); 304 return NS_OK; 305 } 306 307 Maybe<SurfaceDescriptor> Image::GetDesc() { return GetDescFromTexClient(); } 308 309 Maybe<SurfaceDescriptor> Image::GetDescFromTexClient( 310 TextureClient* const tcOverride) { 311 RefPtr<TextureClient> tc = tcOverride; 312 if (!tcOverride) { 313 tc = GetTextureClient(nullptr); 314 } 315 if (!tc) { 316 return {}; 317 } 318 319 const auto& tcd = tc->GetInternalData(); 320 321 SurfaceDescriptor ret; 322 if (!tcd->Serialize(ret)) { 323 return {}; 324 } 325 return Some(ret); 326 } 327 328 already_AddRefed<ImageContainerListener> 329 ImageContainer::GetImageContainerListener() const { 330 MOZ_ASSERT(InImageBridgeChildThread()); 331 return do_AddRef(mNotifyCompositeListener); 332 } 333 334 RefPtr<PlanarYCbCrImage> ImageContainer::CreatePlanarYCbCrImage() { 335 RecursiveMutexAutoLock lock(mRecursiveMutex); 336 EnsureImageClient(); 337 if (mImageClient && mImageClient->AsImageClientSingle()) { 338 return new SharedPlanarYCbCrImage(mImageClient); 339 } 340 if (mRecycleAllocator) { 341 return new SharedPlanarYCbCrImage(mRecycleAllocator); 342 } 343 return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin); 344 } 345 346 RefPtr<SharedRGBImage> ImageContainer::CreateSharedRGBImage() { 347 RecursiveMutexAutoLock lock(mRecursiveMutex); 348 EnsureImageClient(); 349 if (mImageClient && mImageClient->AsImageClientSingle()) { 350 return new SharedRGBImage(mImageClient); 351 } 352 if (mRecycleAllocator) { 353 return new SharedRGBImage(mRecycleAllocator); 354 } 355 return nullptr; 356 } 357 358 void ImageContainer::SetCurrentImageInternal( 359 const nsTArray<NonOwningImage>& aImages) { 360 RecursiveMutexAutoLock lock(mRecursiveMutex); 361 362 mGenerationCounter = ++sGenerationCounter; 363 364 if (!aImages.IsEmpty()) { 365 NS_ASSERTION(mCurrentImages.IsEmpty() || 366 mCurrentImages[0].mProducerID != aImages[0].mProducerID || 367 mCurrentImages[0].mFrameID <= aImages[0].mFrameID, 368 "frame IDs shouldn't go backwards"); 369 if (aImages[0].mProducerID != mCurrentProducerID) { 370 mCurrentProducerID = aImages[0].mProducerID; 371 } 372 for (auto& img : mCurrentImages) { 373 img.mImage->OnAbandonForwardToHost(); 374 } 375 } 376 377 nsTArray<OwningImage> newImages; 378 379 for (uint32_t i = 0; i < aImages.Length(); ++i) { 380 NS_ASSERTION(aImages[i].mImage, "image can't be null"); 381 NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1, 382 "Multiple images require timestamps"); 383 if (i > 0) { 384 NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp, 385 "Timestamps must not decrease"); 386 NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID, 387 "FrameIDs must increase"); 388 NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID, 389 "ProducerIDs must be the same"); 390 } 391 OwningImage* img = newImages.AppendElement(); 392 img->mImage = aImages[i].mImage; 393 img->mTimeStamp = aImages[i].mTimeStamp; 394 img->mProcessingDuration = aImages[i].mProcessingDuration; 395 img->mMediaTime = aImages[i].mMediaTime; 396 img->mWebrtcCaptureTime = aImages[i].mWebrtcCaptureTime; 397 img->mWebrtcReceiveTime = aImages[i].mWebrtcReceiveTime; 398 img->mRtpTimestamp = aImages[i].mRtpTimestamp; 399 img->mFrameID = aImages[i].mFrameID; 400 img->mProducerID = aImages[i].mProducerID; 401 for (const auto& oldImg : mCurrentImages) { 402 if (oldImg.mFrameID == img->mFrameID && 403 oldImg.mProducerID == img->mProducerID) { 404 img->mComposited = oldImg.mComposited; 405 break; 406 } 407 } 408 img->mImage->OnPrepareForwardToHost(); 409 } 410 411 mCurrentImages = std::move(newImages); 412 } 413 414 void ImageContainer::ClearImagesFromImageBridge() { 415 RecursiveMutexAutoLock lock(mRecursiveMutex); 416 SetCurrentImageInternal(nsTArray<NonOwningImage>()); 417 } 418 419 void ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages) { 420 AUTO_PROFILER_LABEL("ImageContainer::SetCurrentImages", GRAPHICS); 421 MOZ_ASSERT(!aImages.IsEmpty()); 422 MOZ_ASSERT(mUsageType == ImageUsageType::Canvas || 423 mUsageType == ImageUsageType::OffscreenCanvas || 424 mUsageType == ImageUsageType::VideoFrameContainer); 425 426 RecursiveMutexAutoLock lock(mRecursiveMutex); 427 if (mIsAsync) { 428 if (RefPtr<ImageBridgeChild> imageBridge = 429 ImageBridgeChild::GetSingleton()) { 430 imageBridge->UpdateImageClient(this); 431 } 432 } 433 SetCurrentImageInternal(aImages); 434 } 435 436 void ImageContainer::ClearImagesInHost(ClearImagesType aType) { 437 MOZ_ASSERT(mIsAsync); 438 if (!mIsAsync) { 439 return; 440 } 441 442 mRecursiveMutex.Lock(); 443 if (mImageClient) { 444 RefPtr<ImageClient> imageClient = mImageClient; 445 mRecursiveMutex.Unlock(); 446 447 // Let ImageClient clear Images(TextureClients). This doesn't return 448 // until ImageBridge has called ImageClient::ClearImagesInHost. 449 if (RefPtr<ImageBridgeChild> imageBridge = 450 ImageBridgeChild::GetSingleton()) { 451 imageBridge->ClearImagesInHost(imageClient, this, aType); 452 } 453 return; 454 } 455 456 SetCurrentImageInternal(nsTArray<NonOwningImage>()); 457 mRecursiveMutex.Unlock(); 458 } 459 460 void ImageContainer::ClearCachedResources() { 461 RecursiveMutexAutoLock lock(mRecursiveMutex); 462 if (mImageClient && mImageClient->AsImageClientSingle()) { 463 if (!mImageClient->HasTextureClientRecycler()) { 464 return; 465 } 466 mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize(); 467 return; 468 } 469 return mRecycleBin->ClearRecycledBuffers(); 470 } 471 472 void ImageContainer::SetCurrentImageInTransaction(Image* aImage) { 473 AutoTArray<NonOwningImage, 1> images; 474 images.AppendElement(NonOwningImage(aImage)); 475 SetCurrentImagesInTransaction(images); 476 } 477 478 void ImageContainer::SetCurrentImagesInTransaction( 479 const nsTArray<NonOwningImage>& aImages) { 480 MOZ_ASSERT(!mIsAsync); 481 MOZ_ASSERT(mUsageType == ImageUsageType::WebRenderFallbackData); 482 483 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); 484 NS_ASSERTION(!HasImageClient(), 485 "Should use async image transfer with ImageBridge."); 486 487 SetCurrentImageInternal(aImages); 488 } 489 490 bool ImageContainer::IsAsync() const { return mIsAsync; } 491 492 CompositableHandle ImageContainer::GetAsyncContainerHandle() { 493 NS_ASSERTION(IsAsync(), 494 "Shared image ID is only relevant to async ImageContainers"); 495 RecursiveMutexAutoLock mon(mRecursiveMutex); 496 NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID"); 497 EnsureImageClient(); 498 return mAsyncContainerHandle; 499 } 500 501 bool ImageContainer::HasCurrentImage() { 502 RecursiveMutexAutoLock lock(mRecursiveMutex); 503 504 return !mCurrentImages.IsEmpty(); 505 } 506 507 void ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages, 508 uint32_t* aGenerationCounter) { 509 RecursiveMutexAutoLock lock(mRecursiveMutex); 510 511 aImages->Assign(mCurrentImages); 512 if (aGenerationCounter) { 513 *aGenerationCounter = mGenerationCounter; 514 } 515 } 516 517 gfx::IntSize ImageContainer::GetCurrentSize() { 518 RecursiveMutexAutoLock lock(mRecursiveMutex); 519 520 if (mCurrentImages.IsEmpty()) { 521 return gfx::IntSize(0, 0); 522 } 523 524 return mCurrentImages[0].mImage->GetSize(); 525 } 526 527 void ImageContainer::NotifyComposite( 528 const ImageCompositeNotification& aNotification) { 529 RecursiveMutexAutoLock lock(mRecursiveMutex); 530 531 // An image composition notification is sent the first time a particular 532 // image is composited by an ImageHost. Thus, every time we receive such 533 // a notification, a new image has been painted. 534 ++mPaintCount; 535 536 if (aNotification.producerID() == mCurrentProducerID) { 537 for (auto& img : mCurrentImages) { 538 if (img.mFrameID == aNotification.frameID()) { 539 img.mComposited = true; 540 } 541 } 542 } 543 544 if (!aNotification.imageTimeStamp().IsNull()) { 545 mPaintDelay = aNotification.firstCompositeTimeStamp() - 546 aNotification.imageTimeStamp(); 547 } 548 } 549 550 void ImageContainer::NotifyDropped(uint32_t aDropped) { 551 mDroppedImageCount += aDropped; 552 } 553 554 void ImageContainer::EnsureRecycleAllocatorForRDD( 555 KnowsCompositor* aKnowsCompositor) { 556 MOZ_ASSERT(!mIsAsync); 557 MOZ_ASSERT(XRE_IsRDDProcess()); 558 559 RecursiveMutexAutoLock lock(mRecursiveMutex); 560 MOZ_ASSERT(!mImageClient); 561 562 if (mRecycleAllocator && 563 aKnowsCompositor == mRecycleAllocator->GetKnowsCompositor()) { 564 return; 565 } 566 567 if (!StaticPrefs::layers_recycle_allocator_rdd_AtStartup()) { 568 return; 569 } 570 571 static const uint32_t MAX_POOLED_VIDEO_COUNT = 5; 572 573 mRecycleAllocator = 574 new layers::TextureClientRecycleAllocator(aKnowsCompositor); 575 mRecycleAllocator->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT); 576 } 577 578 #ifdef XP_WIN 579 already_AddRefed<D3D11RecycleAllocator> 580 ImageContainer::GetD3D11RecycleAllocator(KnowsCompositor* aKnowsCompositor, 581 gfx::SurfaceFormat aPreferredFormat) { 582 MOZ_ASSERT(aKnowsCompositor); 583 584 if (!aKnowsCompositor->SupportsD3D11()) { 585 return nullptr; 586 } 587 588 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice(); 589 if (!device) { 590 return nullptr; 591 } 592 593 RecursiveMutexAutoLock lock(mRecursiveMutex); 594 if (mD3D11RecycleAllocator && mD3D11RecycleAllocator->mDevice == device && 595 mD3D11RecycleAllocator->GetKnowsCompositor() == aKnowsCompositor) { 596 return do_AddRef(mD3D11RecycleAllocator); 597 } 598 599 mD3D11RecycleAllocator = 600 new D3D11RecycleAllocator(aKnowsCompositor, device, aPreferredFormat); 601 602 if (device != DeviceManagerDx::Get()->GetCompositorDevice()) { 603 RefPtr<SyncObjectClient> syncObject = 604 SyncObjectClient::CreateSyncObjectClient( 605 aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle, 606 device); 607 mD3D11RecycleAllocator->SetSyncObject(syncObject); 608 } 609 610 return do_AddRef(mD3D11RecycleAllocator); 611 } 612 613 already_AddRefed<D3D11YCbCrRecycleAllocator> 614 ImageContainer::GetD3D11YCbCrRecycleAllocator( 615 KnowsCompositor* aKnowsCompositor) { 616 if (!aKnowsCompositor->SupportsD3D11() || 617 !gfx::DeviceManagerDx::Get()->GetImageDevice()) { 618 return nullptr; 619 } 620 621 RecursiveMutexAutoLock lock(mRecursiveMutex); 622 if (mD3D11YCbCrRecycleAllocator && 623 aKnowsCompositor == mD3D11YCbCrRecycleAllocator->GetKnowsCompositor()) { 624 return do_AddRef(mD3D11YCbCrRecycleAllocator); 625 } 626 627 mD3D11YCbCrRecycleAllocator = 628 new D3D11YCbCrRecycleAllocator(aKnowsCompositor); 629 return do_AddRef(mD3D11YCbCrRecycleAllocator); 630 } 631 #endif 632 633 #ifdef XP_DARWIN 634 already_AddRefed<MacIOSurfaceRecycleAllocator> 635 ImageContainer::GetMacIOSurfaceRecycleAllocator() { 636 RecursiveMutexAutoLock lock(mRecursiveMutex); 637 if (!mMacIOSurfaceRecycleAllocator) { 638 mMacIOSurfaceRecycleAllocator = new MacIOSurfaceRecycleAllocator(); 639 } 640 641 return do_AddRef(mMacIOSurfaceRecycleAllocator); 642 } 643 #endif 644 645 // - 646 // https://searchfox.org/mozilla-central/source/dom/media/ipc/RemoteImageHolder.cpp#46 647 648 Maybe<PlanarYCbCrData> PlanarYCbCrData::From( 649 const SurfaceDescriptorBuffer& sdb) { 650 if (sdb.desc().type() != BufferDescriptor::TYCbCrDescriptor) { 651 return {}; 652 } 653 const YCbCrDescriptor& yuvDesc = sdb.desc().get_YCbCrDescriptor(); 654 655 Maybe<Range<uint8_t>> buffer; 656 const MemoryOrShmem& memOrShmem = sdb.data(); 657 switch (memOrShmem.type()) { 658 case MemoryOrShmem::Tuintptr_t: 659 gfxCriticalError() << "PlanarYCbCrData::From SurfaceDescriptorBuffer " 660 "w/uintptr_t unsupported."; 661 break; 662 case MemoryOrShmem::TShmem: 663 buffer.emplace(memOrShmem.get_Shmem().Range<uint8_t>()); 664 break; 665 default: 666 MOZ_ASSERT(false, "Unknown MemoryOrShmem type"); 667 break; 668 } 669 if (!buffer) { 670 return {}; 671 } 672 673 PlanarYCbCrData yuvData; 674 yuvData.mYStride = AssertedCast<int32_t>(yuvDesc.yStride()); 675 yuvData.mCbCrStride = AssertedCast<int32_t>(yuvDesc.cbCrStride()); 676 // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor 677 yuvData.mYSkip = yuvData.mCbSkip = yuvData.mCrSkip = 0; 678 yuvData.mPictureRect = yuvDesc.display(); 679 yuvData.mStereoMode = yuvDesc.stereoMode(); 680 yuvData.mColorDepth = yuvDesc.colorDepth(); 681 yuvData.mYUVColorSpace = yuvDesc.yUVColorSpace(); 682 yuvData.mColorRange = yuvDesc.colorRange(); 683 yuvData.mChromaSubsampling = yuvDesc.chromaSubsampling(); 684 685 const auto GetPlanePtr = [&](const uint32_t beginOffset, 686 const gfx::IntSize size, 687 const int32_t stride) -> uint8_t* { 688 if (size.width > stride) return nullptr; 689 auto bytesNeeded = CheckedInt<uintptr_t>(stride) * 690 size.height; // Don't accept `stride*(h-1)+w`. 691 bytesNeeded += beginOffset; 692 if (!bytesNeeded.isValid() || bytesNeeded.value() > buffer->length()) { 693 gfxCriticalError() 694 << "PlanarYCbCrData::From asked for out-of-bounds plane data."; 695 return nullptr; 696 } 697 return (buffer->begin() + beginOffset).get(); 698 }; 699 yuvData.mYChannel = 700 GetPlanePtr(yuvDesc.yOffset(), yuvDesc.ySize(), yuvData.mYStride); 701 yuvData.mCbChannel = 702 GetPlanePtr(yuvDesc.cbOffset(), yuvDesc.cbCrSize(), yuvData.mCbCrStride); 703 yuvData.mCrChannel = 704 GetPlanePtr(yuvDesc.crOffset(), yuvDesc.cbCrSize(), yuvData.mCbCrStride); 705 706 if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || 707 yuvDesc.ySize().width < 0 || yuvDesc.ySize().height < 0 || 708 yuvDesc.cbCrSize().width < 0 || yuvDesc.cbCrSize().height < 0 || 709 yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel || 710 !yuvData.mCbChannel || !yuvData.mCrChannel) { 711 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << "," 712 << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", " 713 << yuvDesc.ySize().width << "," << yuvDesc.ySize().height 714 << ", " << yuvDesc.cbCrSize().width << "," 715 << yuvDesc.cbCrSize().height << ", " << yuvData.mYStride 716 << "," << yuvData.mCbCrStride << ", " 717 << yuvData.mYChannel << "," << yuvData.mCbChannel << "," 718 << yuvData.mCrChannel; 719 return {}; 720 } 721 722 return Some(yuvData); 723 } 724 725 Maybe<PlanarYCbCrData> PlanarYCbCrData::From( 726 const VideoData::YCbCrBuffer& yuvDesc) { 727 constexpr int YPlane = 0; 728 constexpr int CbPlane = 1; 729 constexpr int CrPlane = 2; 730 731 PlanarYCbCrData yuvData; 732 yuvData.mYStride = yuvDesc.mPlanes[YPlane].mStride; 733 yuvData.mCbCrStride = yuvDesc.mPlanes[CbPlane].mStride; 734 yuvData.mYSkip = yuvDesc.mPlanes[YPlane].mSkip; 735 yuvData.mCbSkip = yuvDesc.mPlanes[CbPlane].mSkip; 736 yuvData.mCrSkip = yuvDesc.mPlanes[CrPlane].mSkip; 737 yuvData.mPictureRect = gfx::IntRect(0, 0, yuvDesc.mPlanes[YPlane].mWidth, 738 yuvDesc.mPlanes[YPlane].mHeight); 739 yuvData.mColorDepth = yuvDesc.mColorDepth; 740 yuvData.mYUVColorSpace = yuvDesc.mYUVColorSpace; 741 yuvData.mColorRange = yuvDesc.mColorRange; 742 yuvData.mChromaSubsampling = yuvDesc.mChromaSubsampling; 743 yuvData.mYChannel = yuvDesc.mPlanes[YPlane].mData; 744 yuvData.mCbChannel = yuvDesc.mPlanes[CbPlane].mData; 745 yuvData.mCrChannel = yuvDesc.mPlanes[CrPlane].mData; 746 747 if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || 748 yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel || 749 !yuvData.mCbChannel || !yuvData.mCrChannel) { 750 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << "," 751 << yuvData.mCbSkip << "," << yuvData.mCrSkip << "," 752 << yuvData.mYStride << "," << yuvData.mCbCrStride << ", " 753 << yuvData.mYChannel << "," << yuvData.mCbChannel << "," 754 << yuvData.mCrChannel; 755 return {}; 756 } 757 758 return Some(yuvData); 759 } 760 761 // - 762 763 PlanarYCbCrImage::PlanarYCbCrImage() 764 : Image(nullptr, ImageFormat::PLANAR_YCBCR), 765 mOffscreenFormat(SurfaceFormat::UNKNOWN), 766 mBufferSize(0) {} 767 768 nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer( 769 SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags, 770 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) { 771 const PlanarYCbCrData* pdata = GetData(); 772 MOZ_ASSERT(pdata, "must have PlanarYCbCrData"); 773 MOZ_ASSERT(pdata->mYSkip == 0 && pdata->mCbSkip == 0 && pdata->mCrSkip == 0, 774 "YCbCrDescriptor doesn't hold skip values"); 775 776 if (aFlags & BuildSdbFlags::RgbOnly) { 777 gfx::IntSize size(mSize); 778 auto format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat()); 779 gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size); 780 781 uint8_t* buffer = nullptr; 782 int32_t stride = 0; 783 nsresult rv = AllocateSurfaceDescriptorBufferRgb( 784 size, format, buffer, aSdBuffer, stride, aAllocate); 785 if (NS_WARN_IF(NS_FAILED(rv))) { 786 return rv; 787 } 788 789 // If we can copy directly from the surface, let's do that to avoid the YUV 790 // to RGB conversion. 791 if (mSourceSurface && mSourceSurface->GetSize() == size) { 792 DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::READ); 793 if (map.IsMapped() && SwizzleData(map.GetData(), map.GetStride(), 794 mSourceSurface->GetFormat(), buffer, 795 stride, format, size)) { 796 return NS_OK; 797 } 798 } 799 800 rv = gfx::ConvertYCbCrToRGB(mData, format, size, buffer, stride); 801 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to convert YUV into RGB data"); 802 return rv; 803 } 804 805 auto ySize = pdata->YDataSize(); 806 auto cbcrSize = pdata->CbCrDataSize(); 807 uint32_t yOffset; 808 uint32_t cbOffset; 809 uint32_t crOffset; 810 ImageDataSerializer::ComputeYCbCrOffsets(pdata->mYStride, ySize.height, 811 pdata->mCbCrStride, cbcrSize.height, 812 yOffset, cbOffset, crOffset); 813 814 uint32_t bufferSize = ImageDataSerializer::ComputeYCbCrBufferSize( 815 ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride, yOffset, cbOffset, 816 crOffset); 817 818 aSdBuffer.data() = aAllocate(bufferSize); 819 820 uint8_t* buffer = nullptr; 821 const MemoryOrShmem& memOrShmem = aSdBuffer.data(); 822 switch (memOrShmem.type()) { 823 case MemoryOrShmem::Tuintptr_t: 824 buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t()); 825 break; 826 case MemoryOrShmem::TShmem: 827 buffer = memOrShmem.get_Shmem().get<uint8_t>(); 828 break; 829 default: 830 buffer = nullptr; 831 break; 832 } 833 if (!buffer) { 834 return NS_ERROR_OUT_OF_MEMORY; 835 } 836 837 aSdBuffer.desc() = YCbCrDescriptor( 838 pdata->mPictureRect, ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride, 839 yOffset, cbOffset, crOffset, pdata->mStereoMode, pdata->mColorDepth, 840 pdata->mYUVColorSpace, pdata->mColorRange, pdata->mChromaSubsampling); 841 842 CopyPlane(buffer + yOffset, pdata->mYChannel, ySize, pdata->mYStride, 843 pdata->mYSkip); 844 CopyPlane(buffer + cbOffset, pdata->mCbChannel, cbcrSize, pdata->mCbCrStride, 845 pdata->mCbSkip); 846 CopyPlane(buffer + crOffset, pdata->mCrChannel, cbcrSize, pdata->mCbCrStride, 847 pdata->mCrSkip); 848 return NS_OK; 849 } 850 851 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() { 852 if (mBuffer) { 853 mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize); 854 } 855 } 856 857 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis( 858 MallocSizeOf aMallocSizeOf) const { 859 // Ignoring: 860 // - mData - just wraps mBuffer 861 // - Surfaces should be reported under gfx-surfaces-*: 862 // - mSourceSurface 863 // - Base class: 864 // - mImplData is not used 865 // Not owned: 866 // - mRecycleBin 867 size_t size = aMallocSizeOf(mBuffer.get()); 868 869 // Could add in the future: 870 // - mBackendData (from base class) 871 872 return size; 873 } 874 875 UniquePtr<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize) { 876 return mRecycleBin->GetBuffer(aSize); 877 } 878 879 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc, 880 const gfx::IntSize& aSize, int32_t aStride, 881 int32_t aSkip) { 882 int32_t height = aSize.height; 883 int32_t width = aSize.width; 884 885 MOZ_RELEASE_ASSERT(width <= aStride); 886 887 if (!aSkip) { 888 // Fast path: planar input. 889 memcpy(aDst, aSrc, height * aStride); 890 } else { 891 for (int y = 0; y < height; ++y) { 892 const uint8_t* src = aSrc; 893 uint8_t* dst = aDst; 894 // Slow path 895 for (int x = 0; x < width; ++x) { 896 *dst++ = *src++; 897 src += aSkip; 898 } 899 aSrc += aStride; 900 aDst += aStride; 901 } 902 } 903 } 904 905 nsresult RecyclingPlanarYCbCrImage::CopyData(const Data& aData) { 906 // update buffer size 907 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize 908 auto ySize = aData.YDataSize(); 909 auto cbcrSize = aData.CbCrDataSize(); 910 const auto checkedSize = 911 CheckedInt<uint32_t>(aData.mCbCrStride) * cbcrSize.height * 2 + 912 CheckedInt<uint32_t>(aData.mYStride) * ySize.height * 913 (aData.mAlpha ? 2 : 1); 914 915 if (!checkedSize.isValid()) return NS_ERROR_INVALID_ARG; 916 917 const auto size = checkedSize.value(); 918 919 // get new buffer 920 mBuffer = AllocateBuffer(size); 921 if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY; 922 923 // update buffer size 924 mBufferSize = size; 925 926 mData = aData; // mAlpha will be set if aData has it 927 mData.mYChannel = mBuffer.get(); 928 mData.mCbChannel = mData.mYChannel + mData.mYStride * ySize.height; 929 mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * cbcrSize.height; 930 mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0; 931 932 CopyPlane(mData.mYChannel, aData.mYChannel, ySize, aData.mYStride, 933 aData.mYSkip); 934 CopyPlane(mData.mCbChannel, aData.mCbChannel, cbcrSize, aData.mCbCrStride, 935 aData.mCbSkip); 936 CopyPlane(mData.mCrChannel, aData.mCrChannel, cbcrSize, aData.mCbCrStride, 937 aData.mCrSkip); 938 if (aData.mAlpha) { 939 MOZ_ASSERT(mData.mAlpha); 940 mData.mAlpha->mChannel = 941 mData.mCrChannel + mData.mCbCrStride * cbcrSize.height; 942 CopyPlane(mData.mAlpha->mChannel, aData.mAlpha->mChannel, ySize, 943 aData.mYStride, aData.mYSkip); 944 } 945 946 mSize = aData.mPictureRect.Size(); 947 mOrigin = aData.mPictureRect.TopLeft(); 948 return NS_OK; 949 } 950 951 gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const { 952 return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat() 953 : mOffscreenFormat; 954 } 955 956 nsresult PlanarYCbCrImage::AdoptData(const Data& aData) { 957 mData = aData; 958 mSize = aData.mPictureRect.Size(); 959 mOrigin = aData.mPictureRect.TopLeft(); 960 return NS_OK; 961 } 962 963 already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() { 964 if (mSourceSurface) { 965 RefPtr<gfx::SourceSurface> surface(mSourceSurface); 966 return surface.forget(); 967 } 968 969 gfx::IntSize size(mSize); 970 gfx::SurfaceFormat format = 971 gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat()); 972 gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size); 973 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION || 974 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) { 975 NS_ERROR("Illegal image dest width or height"); 976 return nullptr; 977 } 978 979 RefPtr<gfx::DataSourceSurface> surface = 980 gfx::Factory::CreateDataSourceSurface(size, format); 981 if (NS_WARN_IF(!surface)) { 982 return nullptr; 983 } 984 985 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE); 986 if (NS_WARN_IF(!mapping.IsMapped())) { 987 return nullptr; 988 } 989 990 if (NS_WARN_IF(NS_FAILED(gfx::ConvertYCbCrToRGB( 991 mData, format, size, mapping.GetData(), mapping.GetStride())))) { 992 MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data"); 993 return nullptr; 994 } 995 996 mSourceSurface = surface; 997 998 return surface.forget(); 999 } 1000 1001 PlanarYCbCrImage::~PlanarYCbCrImage() { 1002 NS_ReleaseOnMainThread("PlanarYCbCrImage::mSourceSurface", 1003 mSourceSurface.forget()); 1004 } 1005 1006 NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {} 1007 1008 NVImage::~NVImage() { 1009 NS_ReleaseOnMainThread("NVImage::mSourceSurface", mSourceSurface.forget()); 1010 } 1011 1012 IntSize NVImage::GetSize() const { return mSize; } 1013 1014 IntRect NVImage::GetPictureRect() const { return mData.mPictureRect; } 1015 1016 already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() { 1017 if (mSourceSurface) { 1018 RefPtr<gfx::SourceSurface> surface(mSourceSurface); 1019 return surface.forget(); 1020 } 1021 1022 // Convert the current NV12 or NV21 data to YUV420P so that we can follow the 1023 // logics in PlanarYCbCrImage::GetAsSourceSurface(). 1024 auto ySize = mData.YDataSize(); 1025 auto cbcrSize = mData.CbCrDataSize(); 1026 const int bufferLength = 1027 ySize.height * mData.mYStride + cbcrSize.height * cbcrSize.width * 2; 1028 UniquePtr<uint8_t[]> buffer(new uint8_t[bufferLength]); 1029 1030 Data aData = mData; 1031 aData.mCbCrStride = cbcrSize.width; 1032 aData.mCbSkip = 0; 1033 aData.mCrSkip = 0; 1034 aData.mYChannel = buffer.get(); 1035 aData.mCbChannel = aData.mYChannel + ySize.height * aData.mYStride; 1036 aData.mCrChannel = aData.mCbChannel + cbcrSize.height * aData.mCbCrStride; 1037 1038 if (mData.mCbChannel < mData.mCrChannel) { // NV12 1039 libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel, 1040 mData.mCbCrStride, aData.mYChannel, aData.mYStride, 1041 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel, 1042 aData.mCbCrStride, ySize.width, ySize.height); 1043 } else { // NV21 1044 libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel, 1045 mData.mCbCrStride, aData.mYChannel, aData.mYStride, 1046 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel, 1047 aData.mCbCrStride, ySize.width, ySize.height); 1048 } 1049 1050 // The logics in PlanarYCbCrImage::GetAsSourceSurface(). 1051 gfx::IntSize size(mSize); 1052 gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat( 1053 gfxPlatform::GetPlatform()->GetOffscreenFormat()); 1054 gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size); 1055 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION || 1056 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) { 1057 NS_ERROR("Illegal image dest width or height"); 1058 return nullptr; 1059 } 1060 1061 RefPtr<gfx::DataSourceSurface> surface = 1062 gfx::Factory::CreateDataSourceSurface(size, format); 1063 if (NS_WARN_IF(!surface)) { 1064 return nullptr; 1065 } 1066 1067 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE); 1068 if (NS_WARN_IF(!mapping.IsMapped())) { 1069 return nullptr; 1070 } 1071 1072 if (NS_WARN_IF(NS_FAILED(gfx::ConvertYCbCrToRGB( 1073 aData, format, size, mapping.GetData(), mapping.GetStride())))) { 1074 MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data"); 1075 return nullptr; 1076 } 1077 1078 mSourceSurface = surface; 1079 1080 return surface.forget(); 1081 } 1082 1083 nsresult NVImage::BuildSurfaceDescriptorBuffer( 1084 SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags, 1085 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) { 1086 // Convert the current NV12 or NV21 data to YUV420P so that we can follow the 1087 // logics in PlanarYCbCrImage::GetAsSourceSurface(). 1088 auto ySize = mData.YDataSize(); 1089 auto cbcrSize = mData.CbCrDataSize(); 1090 1091 Data aData = mData; 1092 aData.mCbCrStride = cbcrSize.width; 1093 aData.mCbSkip = 0; 1094 aData.mCrSkip = 0; 1095 aData.mCbChannel = aData.mYChannel + ySize.height * aData.mYStride; 1096 aData.mCrChannel = aData.mCbChannel + cbcrSize.height * aData.mCbCrStride; 1097 1098 UniquePtr<uint8_t[]> buffer; 1099 1100 if (!mSourceSurface) { 1101 const int bufferLength = 1102 ySize.height * mData.mYStride + cbcrSize.height * cbcrSize.width * 2; 1103 buffer = MakeUnique<uint8_t[]>(bufferLength); 1104 aData.mYChannel = buffer.get(); 1105 1106 if (mData.mCbChannel < mData.mCrChannel) { // NV12 1107 libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel, 1108 mData.mCbCrStride, aData.mYChannel, aData.mYStride, 1109 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel, 1110 aData.mCbCrStride, ySize.width, ySize.height); 1111 } else { // NV21 1112 libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel, 1113 mData.mCbCrStride, aData.mYChannel, aData.mYStride, 1114 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel, 1115 aData.mCbCrStride, ySize.width, ySize.height); 1116 } 1117 } 1118 1119 // The logics in PlanarYCbCrImage::GetAsSourceSurface(). 1120 gfx::IntSize size(mSize); 1121 gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat( 1122 gfxPlatform::GetPlatform()->GetOffscreenFormat()); 1123 gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size); 1124 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION || 1125 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) { 1126 NS_ERROR("Illegal image dest width or height"); 1127 return NS_ERROR_FAILURE; 1128 } 1129 1130 if (mSourceSurface && mSourceSurface->GetSize() != size) { 1131 return NS_ERROR_NOT_IMPLEMENTED; 1132 } 1133 1134 uint8_t* output = nullptr; 1135 int32_t stride = 0; 1136 nsresult rv = AllocateSurfaceDescriptorBufferRgb( 1137 size, format, output, aSdBuffer, stride, aAllocate); 1138 if (NS_WARN_IF(NS_FAILED(rv))) { 1139 return rv; 1140 } 1141 1142 if (!mSourceSurface) { 1143 rv = gfx::ConvertYCbCrToRGB(aData, format, size, output, stride); 1144 if (NS_WARN_IF(NS_FAILED(rv))) { 1145 MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data"); 1146 return rv; 1147 } 1148 return NS_OK; 1149 } 1150 1151 DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::WRITE); 1152 if (NS_WARN_IF(!map.IsMapped())) { 1153 return NS_ERROR_FAILURE; 1154 } 1155 1156 if (!SwizzleData(map.GetData(), map.GetStride(), mSourceSurface->GetFormat(), 1157 output, stride, format, size)) { 1158 return NS_ERROR_FAILURE; 1159 } 1160 1161 return NS_OK; 1162 } 1163 1164 bool NVImage::IsValid() const { return !!mBufferSize; } 1165 1166 uint32_t NVImage::GetBufferSize() const { return mBufferSize; } 1167 1168 NVImage* NVImage::AsNVImage() { return this; }; 1169 1170 nsresult NVImage::SetData(const Data& aData) { 1171 MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1); 1172 MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1); 1173 1174 // Calculate buffer size 1175 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize 1176 const auto checkedSize = 1177 CheckedInt<uint32_t>(aData.YDataSize().height) * aData.mYStride + 1178 CheckedInt<uint32_t>(aData.CbCrDataSize().height) * aData.mCbCrStride; 1179 1180 if (!checkedSize.isValid()) { 1181 return NS_ERROR_INVALID_ARG; 1182 } 1183 1184 const auto size = checkedSize.value(); 1185 1186 // Allocate a new buffer. 1187 mBuffer = AllocateBuffer(size); 1188 if (!mBuffer) { 1189 return NS_ERROR_OUT_OF_MEMORY; 1190 } 1191 1192 // Update mBufferSize. 1193 mBufferSize = size; 1194 1195 // Update mData. 1196 mData = aData; 1197 mData.mYChannel = mBuffer.get(); 1198 mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel); 1199 mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel); 1200 1201 // Update mSize. 1202 mSize = aData.mPictureRect.Size(); 1203 1204 // Copy the input data into mBuffer. 1205 // This copies the y-channel and the interleaving CbCr-channel. 1206 memcpy(mData.mYChannel, aData.mYChannel, mBufferSize); 1207 1208 return NS_OK; 1209 } 1210 1211 const NVImage::Data* NVImage::GetData() const { return &mData; } 1212 1213 UniquePtr<uint8_t[]> NVImage::AllocateBuffer(uint32_t aSize) { 1214 UniquePtr<uint8_t[]> buffer(new uint8_t[aSize]); 1215 return buffer; 1216 } 1217 1218 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, 1219 gfx::SourceSurface* aSourceSurface) 1220 : Image(nullptr, ImageFormat::MOZ2D_SURFACE), 1221 mSize(aSize), 1222 mSourceSurface(aSourceSurface), 1223 mTextureFlags(TextureFlags::DEFAULT) {} 1224 1225 SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface) 1226 : Image(nullptr, ImageFormat::MOZ2D_SURFACE), 1227 mSize(aSourceSurface->GetSize()), 1228 mSourceSurface(aSourceSurface), 1229 mTextureFlags(TextureFlags::DEFAULT) {} 1230 1231 SourceSurfaceImage::~SourceSurfaceImage() { 1232 NS_ReleaseOnMainThread("SourceSurfaceImage::mSourceSurface", 1233 mSourceSurface.forget()); 1234 } 1235 1236 TextureClient* SourceSurfaceImage::GetTextureClient( 1237 KnowsCompositor* aKnowsCompositor) { 1238 if (!aKnowsCompositor) { 1239 return nullptr; 1240 } 1241 1242 return mTextureClients.WithEntryHandle( 1243 aKnowsCompositor->GetSerial(), [&](auto&& entry) -> TextureClient* { 1244 if (entry) { 1245 return entry->get(); 1246 } 1247 1248 RefPtr<TextureClient> textureClient; 1249 RefPtr<SourceSurface> surface = GetAsSourceSurface(); 1250 MOZ_ASSERT(surface); 1251 if (surface) { 1252 // gfx::BackendType::NONE means default to content backend 1253 textureClient = TextureClient::CreateFromSurface( 1254 aKnowsCompositor, surface, BackendSelector::Content, 1255 mTextureFlags, ALLOC_DEFAULT); 1256 } 1257 if (textureClient) { 1258 textureClient->SyncWithObject(aKnowsCompositor->GetSyncObject()); 1259 return entry.Insert(std::move(textureClient)).get(); 1260 } 1261 1262 return nullptr; 1263 }); 1264 } 1265 1266 ImageContainer::ProducerID ImageContainer::AllocateProducerID() { 1267 // Callable on all threads. 1268 static Atomic<ImageContainer::ProducerID> sProducerID(0u); 1269 return ++sProducerID; 1270 } 1271 1272 } // namespace mozilla::layers