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 #include <string.h> // for memcpy, memset
9 #include "GLImages.h" // for SurfaceTextureImage
10 #include "gfx2DGlue.h"
11 #include "gfxPlatform.h" // for gfxPlatform
12 #include "gfxUtils.h" // for gfxUtils
13 #include "libyuv.h"
14 #include "mozilla/RefPtr.h" // for already_AddRefed
15 #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
16 #include "mozilla/layers/CompositorTypes.h"
17 #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
18 #include "mozilla/layers/ImageClient.h" // for ImageClient
19 #include "mozilla/layers/ImageDataSerializer.h" // for SurfaceDescriptorBuffer
20 #include "mozilla/layers/LayersMessages.h"
21 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
22 #include "mozilla/layers/SharedSurfacesChild.h" // for SharedSurfacesAnimation
23 #include "mozilla/layers/SharedRGBImage.h"
24 #include "mozilla/layers/TextureClientRecycleAllocator.h"
25 #include "mozilla/StaticPrefs_layers.h"
26 #include "mozilla/gfx/gfxVars.h"
27 #include "nsISupportsUtils.h" // for NS_IF_ADDREF
28 #include "YCbCrUtils.h" // for YCbCr conversions
29 #include "gfx2DGlue.h"
30 #include "mozilla/gfx/2D.h"
31 #include "mozilla/CheckedInt.h"
32
33 #ifdef XP_MACOSX
34 # include "mozilla/gfx/QuartzSupport.h"
35 #endif
36
37 #ifdef XP_WIN
38 # include "gfxWindowsPlatform.h"
39 # include <d3d10_1.h>
40 # include "mozilla/gfx/DeviceManagerDx.h"
41 # include "mozilla/layers/D3D11YCbCrImage.h"
42 #endif
43
44 namespace mozilla::layers {
45
46 using namespace mozilla::gfx;
47 using namespace mozilla::ipc;
48
49 Atomic<int32_t> Image::sSerialCounter(0);
50
51 Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
52
53 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
54 const gfx::IntSize& aSize, int32_t aStride,
55 int32_t aSkip);
56
CreatePlanarYCbCrImage(const gfx::IntSize & aScaleHint,BufferRecycleBin * aRecycleBin)57 RefPtr<PlanarYCbCrImage> ImageFactory::CreatePlanarYCbCrImage(
58 const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) {
59 return new RecyclingPlanarYCbCrImage(aRecycleBin);
60 }
61
BufferRecycleBin()62 BufferRecycleBin::BufferRecycleBin()
63 : mLock("mozilla.layers.BufferRecycleBin.mLock")
64 // This member is only valid when the bin is not empty and will be
65 // properly initialized in RecycleBuffer, but initializing it here avoids
66 // static analysis noise.
67 ,
68 mRecycledBufferSize(0) {}
69
RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,uint32_t aSize)70 void BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,
71 uint32_t aSize) {
72 MutexAutoLock lock(mLock);
73
74 if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
75 mRecycledBuffers.Clear();
76 }
77 mRecycledBufferSize = aSize;
78 mRecycledBuffers.AppendElement(std::move(aBuffer));
79 }
80
GetBuffer(uint32_t aSize)81 UniquePtr<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize) {
82 MutexAutoLock lock(mLock);
83
84 if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) {
85 return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]);
86 }
87
88 uint32_t last = mRecycledBuffers.Length() - 1;
89 UniquePtr<uint8_t[]> result = std::move(mRecycledBuffers[last]);
90 mRecycledBuffers.RemoveElementAt(last);
91 return result;
92 }
93
ClearRecycledBuffers()94 void BufferRecycleBin::ClearRecycledBuffers() {
95 MutexAutoLock lock(mLock);
96 if (!mRecycledBuffers.IsEmpty()) {
97 mRecycledBuffers.Clear();
98 }
99 mRecycledBufferSize = 0;
100 }
101
ImageContainerListener(ImageContainer * aImageContainer)102 ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer)
103 : mLock("mozilla.layers.ImageContainerListener.mLock"),
104 mImageContainer(aImageContainer) {}
105
106 ImageContainerListener::~ImageContainerListener() = default;
107
NotifyComposite(const ImageCompositeNotification & aNotification)108 void ImageContainerListener::NotifyComposite(
109 const ImageCompositeNotification& aNotification) {
110 MutexAutoLock lock(mLock);
111 if (mImageContainer) {
112 mImageContainer->NotifyComposite(aNotification);
113 }
114 }
115
NotifyDropped(uint32_t aDropped)116 void ImageContainerListener::NotifyDropped(uint32_t aDropped) {
117 MutexAutoLock lock(mLock);
118 if (mImageContainer) {
119 mImageContainer->NotifyDropped(aDropped);
120 }
121 }
122
ClearImageContainer()123 void ImageContainerListener::ClearImageContainer() {
124 MutexAutoLock lock(mLock);
125 mImageContainer = nullptr;
126 }
127
DropImageClient()128 void ImageContainerListener::DropImageClient() {
129 MutexAutoLock lock(mLock);
130 if (mImageContainer) {
131 mImageContainer->DropImageClient();
132 }
133 }
134
GetImageClient()135 already_AddRefed<ImageClient> ImageContainer::GetImageClient() {
136 RecursiveMutexAutoLock mon(mRecursiveMutex);
137 EnsureImageClient();
138 RefPtr<ImageClient> imageClient = mImageClient;
139 return imageClient.forget();
140 }
141
DropImageClient()142 void ImageContainer::DropImageClient() {
143 RecursiveMutexAutoLock mon(mRecursiveMutex);
144 if (mImageClient) {
145 mImageClient->ClearCachedResources();
146 mImageClient = nullptr;
147 }
148 }
149
EnsureImageClient()150 void ImageContainer::EnsureImageClient() {
151 // If we're not forcing a new ImageClient, then we can skip this if we don't
152 // have an existing ImageClient, or if the existing one belongs to an IPC
153 // actor that is still open.
154 if (!mIsAsync) {
155 return;
156 }
157 if (mImageClient &&
158 mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
159 return;
160 }
161
162 RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
163 if (imageBridge) {
164 mImageClient =
165 imageBridge->CreateImageClient(CompositableType::IMAGE, this);
166 if (mImageClient) {
167 mAsyncContainerHandle = mImageClient->GetAsyncHandle();
168 } else {
169 // It's okay to drop the async container handle since the ImageBridgeChild
170 // is going to die anyway.
171 mAsyncContainerHandle = CompositableHandle();
172 }
173 }
174 }
175
EnsureSharedSurfacesAnimation()176 SharedSurfacesAnimation* ImageContainer::EnsureSharedSurfacesAnimation() {
177 if (!mSharedAnimation) {
178 mSharedAnimation = new SharedSurfacesAnimation();
179 }
180 return mSharedAnimation;
181 }
182
ImageContainer(Mode flag)183 ImageContainer::ImageContainer(Mode flag)
184 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
185 mGenerationCounter(++sGenerationCounter),
186 mPaintCount(0),
187 mDroppedImageCount(0),
188 mImageFactory(new ImageFactory()),
189 mRecycleBin(new BufferRecycleBin()),
190 mIsAsync(flag == ASYNCHRONOUS),
191 mCurrentProducerID(-1) {
192 if (flag == ASYNCHRONOUS) {
193 mNotifyCompositeListener = new ImageContainerListener(this);
194 EnsureImageClient();
195 }
196 }
197
ImageContainer(const CompositableHandle & aHandle)198 ImageContainer::ImageContainer(const CompositableHandle& aHandle)
199 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
200 mGenerationCounter(++sGenerationCounter),
201 mPaintCount(0),
202 mDroppedImageCount(0),
203 mImageFactory(nullptr),
204 mRecycleBin(nullptr),
205 mIsAsync(true),
206 mAsyncContainerHandle(aHandle),
207 mCurrentProducerID(-1) {
208 MOZ_ASSERT(mAsyncContainerHandle);
209 }
210
~ImageContainer()211 ImageContainer::~ImageContainer() {
212 if (mNotifyCompositeListener) {
213 mNotifyCompositeListener->ClearImageContainer();
214 }
215 if (mAsyncContainerHandle) {
216 if (RefPtr<ImageBridgeChild> imageBridge =
217 ImageBridgeChild::GetSingleton()) {
218 imageBridge->ForgetImageContainer(mAsyncContainerHandle);
219 }
220 }
221 if (mSharedAnimation) {
222 mSharedAnimation->Destroy();
223 }
224 }
225
CreatePlanarYCbCrImage()226 RefPtr<PlanarYCbCrImage> ImageContainer::CreatePlanarYCbCrImage() {
227 RecursiveMutexAutoLock lock(mRecursiveMutex);
228 EnsureImageClient();
229 if (mImageClient && mImageClient->AsImageClientSingle()) {
230 return new SharedPlanarYCbCrImage(mImageClient);
231 }
232 if (mRecycleAllocator) {
233 return new SharedPlanarYCbCrImage(mRecycleAllocator);
234 }
235 return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
236 }
237
CreateSharedRGBImage()238 RefPtr<SharedRGBImage> ImageContainer::CreateSharedRGBImage() {
239 RecursiveMutexAutoLock lock(mRecursiveMutex);
240 EnsureImageClient();
241 if (!mImageClient || !mImageClient->AsImageClientSingle()) {
242 return nullptr;
243 }
244 return new SharedRGBImage(mImageClient);
245 }
246
SetCurrentImageInternal(const nsTArray<NonOwningImage> & aImages)247 void ImageContainer::SetCurrentImageInternal(
248 const nsTArray<NonOwningImage>& aImages) {
249 RecursiveMutexAutoLock lock(mRecursiveMutex);
250
251 mGenerationCounter = ++sGenerationCounter;
252
253 if (!aImages.IsEmpty()) {
254 NS_ASSERTION(mCurrentImages.IsEmpty() ||
255 mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
256 mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
257 "frame IDs shouldn't go backwards");
258 if (aImages[0].mProducerID != mCurrentProducerID) {
259 mCurrentProducerID = aImages[0].mProducerID;
260 }
261 }
262
263 nsTArray<OwningImage> newImages;
264
265 for (uint32_t i = 0; i < aImages.Length(); ++i) {
266 NS_ASSERTION(aImages[i].mImage, "image can't be null");
267 NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
268 "Multiple images require timestamps");
269 if (i > 0) {
270 NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
271 "Timestamps must not decrease");
272 NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
273 "FrameIDs must increase");
274 NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
275 "ProducerIDs must be the same");
276 }
277 OwningImage* img = newImages.AppendElement();
278 img->mImage = aImages[i].mImage;
279 img->mTimeStamp = aImages[i].mTimeStamp;
280 img->mFrameID = aImages[i].mFrameID;
281 img->mProducerID = aImages[i].mProducerID;
282 for (const auto& oldImg : mCurrentImages) {
283 if (oldImg.mFrameID == img->mFrameID &&
284 oldImg.mProducerID == img->mProducerID) {
285 img->mComposited = oldImg.mComposited;
286 break;
287 }
288 }
289 }
290
291 mCurrentImages.SwapElements(newImages);
292 }
293
ClearImagesFromImageBridge()294 void ImageContainer::ClearImagesFromImageBridge() {
295 RecursiveMutexAutoLock lock(mRecursiveMutex);
296 SetCurrentImageInternal(nsTArray<NonOwningImage>());
297 }
298
SetCurrentImages(const nsTArray<NonOwningImage> & aImages)299 void ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages) {
300 AUTO_PROFILER_LABEL("ImageContainer::SetCurrentImages", GRAPHICS);
301 MOZ_ASSERT(!aImages.IsEmpty());
302 RecursiveMutexAutoLock lock(mRecursiveMutex);
303 if (mIsAsync) {
304 if (RefPtr<ImageBridgeChild> imageBridge =
305 ImageBridgeChild::GetSingleton()) {
306 imageBridge->UpdateImageClient(this);
307 }
308 }
309 SetCurrentImageInternal(aImages);
310 }
311
ClearAllImages()312 void ImageContainer::ClearAllImages() {
313 if (mImageClient) {
314 // Let ImageClient release all TextureClients. This doesn't return
315 // until ImageBridge has called ClearCurrentImageFromImageBridge.
316 if (RefPtr<ImageBridgeChild> imageBridge =
317 ImageBridgeChild::GetSingleton()) {
318 imageBridge->FlushAllImages(mImageClient, this);
319 }
320 return;
321 }
322
323 RecursiveMutexAutoLock lock(mRecursiveMutex);
324 SetCurrentImageInternal(nsTArray<NonOwningImage>());
325 }
326
ClearCachedResources()327 void ImageContainer::ClearCachedResources() {
328 RecursiveMutexAutoLock lock(mRecursiveMutex);
329 if (mImageClient && mImageClient->AsImageClientSingle()) {
330 if (!mImageClient->HasTextureClientRecycler()) {
331 return;
332 }
333 mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
334 return;
335 }
336 return mRecycleBin->ClearRecycledBuffers();
337 }
338
SetCurrentImageInTransaction(Image * aImage)339 void ImageContainer::SetCurrentImageInTransaction(Image* aImage) {
340 AutoTArray<NonOwningImage, 1> images;
341 images.AppendElement(NonOwningImage(aImage));
342 SetCurrentImagesInTransaction(images);
343 }
344
SetCurrentImagesInTransaction(const nsTArray<NonOwningImage> & aImages)345 void ImageContainer::SetCurrentImagesInTransaction(
346 const nsTArray<NonOwningImage>& aImages) {
347 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
348 NS_ASSERTION(!mImageClient,
349 "Should use async image transfer with ImageBridge.");
350
351 SetCurrentImageInternal(aImages);
352 }
353
IsAsync() const354 bool ImageContainer::IsAsync() const { return mIsAsync; }
355
GetAsyncContainerHandle()356 CompositableHandle ImageContainer::GetAsyncContainerHandle() {
357 NS_ASSERTION(IsAsync(),
358 "Shared image ID is only relevant to async ImageContainers");
359 NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
360 RecursiveMutexAutoLock mon(mRecursiveMutex);
361 EnsureImageClient();
362 return mAsyncContainerHandle;
363 }
364
HasCurrentImage()365 bool ImageContainer::HasCurrentImage() {
366 RecursiveMutexAutoLock lock(mRecursiveMutex);
367
368 return !mCurrentImages.IsEmpty();
369 }
370
GetCurrentImages(nsTArray<OwningImage> * aImages,uint32_t * aGenerationCounter)371 void ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
372 uint32_t* aGenerationCounter) {
373 RecursiveMutexAutoLock lock(mRecursiveMutex);
374
375 *aImages = mCurrentImages.Clone();
376 if (aGenerationCounter) {
377 *aGenerationCounter = mGenerationCounter;
378 }
379 }
380
GetCurrentSize()381 gfx::IntSize ImageContainer::GetCurrentSize() {
382 RecursiveMutexAutoLock lock(mRecursiveMutex);
383
384 if (mCurrentImages.IsEmpty()) {
385 return gfx::IntSize(0, 0);
386 }
387
388 return mCurrentImages[0].mImage->GetSize();
389 }
390
NotifyComposite(const ImageCompositeNotification & aNotification)391 void ImageContainer::NotifyComposite(
392 const ImageCompositeNotification& aNotification) {
393 RecursiveMutexAutoLock lock(mRecursiveMutex);
394
395 // An image composition notification is sent the first time a particular
396 // image is composited by an ImageHost. Thus, every time we receive such
397 // a notification, a new image has been painted.
398 ++mPaintCount;
399
400 if (aNotification.producerID() == mCurrentProducerID) {
401 for (auto& img : mCurrentImages) {
402 if (img.mFrameID == aNotification.frameID()) {
403 img.mComposited = true;
404 }
405 }
406 }
407
408 if (!aNotification.imageTimeStamp().IsNull()) {
409 mPaintDelay = aNotification.firstCompositeTimeStamp() -
410 aNotification.imageTimeStamp();
411 }
412 }
413
NotifyDropped(uint32_t aDropped)414 void ImageContainer::NotifyDropped(uint32_t aDropped) {
415 mDroppedImageCount += aDropped;
416 }
417
EnsureRecycleAllocatorForRDD(KnowsCompositor * aKnowsCompositor)418 void ImageContainer::EnsureRecycleAllocatorForRDD(
419 KnowsCompositor* aKnowsCompositor) {
420 MOZ_ASSERT(!mIsAsync);
421 MOZ_ASSERT(!mImageClient);
422 MOZ_ASSERT(XRE_IsRDDProcess());
423
424 if (mRecycleAllocator &&
425 aKnowsCompositor == mRecycleAllocator->GetKnowsCompositor()) {
426 return;
427 }
428
429 bool useRecycleAllocator =
430 StaticPrefs::layers_recycle_allocator_rdd_AtStartup();
431 #ifdef XP_MACOSX
432 // Disable RecycleAllocator for RDD on MacOS without WebRender.
433 // Recycling caused rendering artifact on a MacOS PC with OpenGL compositor.
434 if (!gfxVars::UseWebRender()) {
435 useRecycleAllocator = false;
436 }
437 #endif
438 if (!useRecycleAllocator) {
439 return;
440 }
441
442 static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
443
444 mRecycleAllocator =
445 new layers::TextureClientRecycleAllocator(aKnowsCompositor);
446 mRecycleAllocator->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
447 }
448
449 #ifdef XP_WIN
GetD3D11YCbCrRecycleAllocator(KnowsCompositor * aKnowsCompositor)450 D3D11YCbCrRecycleAllocator* ImageContainer::GetD3D11YCbCrRecycleAllocator(
451 KnowsCompositor* aKnowsCompositor) {
452 if (mD3D11YCbCrRecycleAllocator &&
453 aKnowsCompositor == mD3D11YCbCrRecycleAllocator->GetKnowsCompositor()) {
454 return mD3D11YCbCrRecycleAllocator;
455 }
456
457 if (!aKnowsCompositor->SupportsD3D11() ||
458 !gfx::DeviceManagerDx::Get()->GetImageDevice()) {
459 return nullptr;
460 }
461
462 mD3D11YCbCrRecycleAllocator =
463 new D3D11YCbCrRecycleAllocator(aKnowsCompositor);
464 return mD3D11YCbCrRecycleAllocator;
465 }
466 #endif
467
PlanarYCbCrImage()468 PlanarYCbCrImage::PlanarYCbCrImage()
469 : Image(nullptr, ImageFormat::PLANAR_YCBCR),
470 mOffscreenFormat(SurfaceFormat::UNKNOWN),
471 mBufferSize(0) {}
472
BuildSurfaceDescriptorBuffer(SurfaceDescriptorBuffer & aSdBuffer)473 nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
474 SurfaceDescriptorBuffer& aSdBuffer) {
475 const PlanarYCbCrData* pdata = GetData();
476 MOZ_ASSERT(pdata, "must have PlanarYCbCrData");
477 MOZ_ASSERT(pdata->mYSkip == 0 && pdata->mCbSkip == 0 && pdata->mCrSkip == 0,
478 "YCbCrDescriptor doesn't hold skip values");
479 MOZ_ASSERT(pdata->mPicX == 0 && pdata->mPicY == 0,
480 "YCbCrDescriptor doesn't hold picx or picy");
481
482 uint32_t yOffset;
483 uint32_t cbOffset;
484 uint32_t crOffset;
485 ImageDataSerializer::ComputeYCbCrOffsets(
486 pdata->mYStride, pdata->mYSize.height, pdata->mCbCrStride,
487 pdata->mCbCrSize.height, yOffset, cbOffset, crOffset);
488
489 aSdBuffer.desc() = YCbCrDescriptor(
490 pdata->mYSize, pdata->mYStride, pdata->mCbCrSize, pdata->mCbCrStride,
491 yOffset, cbOffset, crOffset, pdata->mStereoMode, pdata->mColorDepth,
492 pdata->mYUVColorSpace, pdata->mColorRange,
493 /*hasIntermediateBuffer*/ false);
494
495 uint8_t* buffer = nullptr;
496 const MemoryOrShmem& memOrShmem = aSdBuffer.data();
497 switch (memOrShmem.type()) {
498 case MemoryOrShmem::Tuintptr_t:
499 buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
500 break;
501 case MemoryOrShmem::TShmem:
502 buffer = memOrShmem.get_Shmem().get<uint8_t>();
503 break;
504 default:
505 MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
506 }
507 MOZ_ASSERT(buffer, "no valid buffer available to copy image data");
508 if (!buffer) {
509 return NS_ERROR_INVALID_ARG;
510 }
511
512 CopyPlane(buffer + yOffset, pdata->mYChannel, pdata->mYSize, pdata->mYStride,
513 pdata->mYSkip);
514 CopyPlane(buffer + cbOffset, pdata->mCbChannel, pdata->mCbCrSize,
515 pdata->mCbCrStride, pdata->mCbSkip);
516 CopyPlane(buffer + crOffset, pdata->mCrChannel, pdata->mCbCrSize,
517 pdata->mCbCrStride, pdata->mCrSkip);
518 return NS_OK;
519 }
520
~RecyclingPlanarYCbCrImage()521 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
522 if (mBuffer) {
523 mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize);
524 }
525 }
526
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const527 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
528 MallocSizeOf aMallocSizeOf) const {
529 // Ignoring:
530 // - mData - just wraps mBuffer
531 // - Surfaces should be reported under gfx-surfaces-*:
532 // - mSourceSurface
533 // - Base class:
534 // - mImplData is not used
535 // Not owned:
536 // - mRecycleBin
537 size_t size = aMallocSizeOf(mBuffer.get());
538
539 // Could add in the future:
540 // - mBackendData (from base class)
541
542 return size;
543 }
544
AllocateBuffer(uint32_t aSize)545 UniquePtr<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize) {
546 return mRecycleBin->GetBuffer(aSize);
547 }
548
CopyPlane(uint8_t * aDst,const uint8_t * aSrc,const gfx::IntSize & aSize,int32_t aStride,int32_t aSkip)549 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
550 const gfx::IntSize& aSize, int32_t aStride,
551 int32_t aSkip) {
552 int32_t height = aSize.height;
553 int32_t width = aSize.width;
554
555 MOZ_RELEASE_ASSERT(width <= aStride);
556
557 if (!aSkip) {
558 // Fast path: planar input.
559 memcpy(aDst, aSrc, height * aStride);
560 } else {
561 for (int y = 0; y < height; ++y) {
562 const uint8_t* src = aSrc;
563 uint8_t* dst = aDst;
564 // Slow path
565 for (int x = 0; x < width; ++x) {
566 *dst++ = *src++;
567 src += aSkip;
568 }
569 aSrc += aStride;
570 aDst += aStride;
571 }
572 }
573 }
574
CopyData(const Data & aData)575 bool RecyclingPlanarYCbCrImage::CopyData(const Data& aData) {
576 // update buffer size
577 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
578 const auto checkedSize =
579 CheckedInt<uint32_t>(aData.mCbCrStride) * aData.mCbCrSize.height * 2 +
580 CheckedInt<uint32_t>(aData.mYStride) * aData.mYSize.height;
581
582 if (!checkedSize.isValid()) return false;
583
584 const auto size = checkedSize.value();
585
586 // get new buffer
587 mBuffer = AllocateBuffer(size);
588 if (!mBuffer) return false;
589
590 // update buffer size
591 mBufferSize = size;
592
593 mData = aData;
594 mData.mYChannel = mBuffer.get();
595 mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
596 mData.mCrChannel =
597 mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
598 mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0;
599
600 CopyPlane(mData.mYChannel, aData.mYChannel, aData.mYSize, aData.mYStride,
601 aData.mYSkip);
602 CopyPlane(mData.mCbChannel, aData.mCbChannel, aData.mCbCrSize,
603 aData.mCbCrStride, aData.mCbSkip);
604 CopyPlane(mData.mCrChannel, aData.mCrChannel, aData.mCbCrSize,
605 aData.mCbCrStride, aData.mCrSkip);
606
607 mSize = aData.mPicSize;
608 mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
609 return true;
610 }
611
GetOffscreenFormat() const612 gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const {
613 return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat()
614 : mOffscreenFormat;
615 }
616
AdoptData(const Data & aData)617 bool PlanarYCbCrImage::AdoptData(const Data& aData) {
618 mData = aData;
619 mSize = aData.mPicSize;
620 mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
621 return true;
622 }
623
GetAsSourceSurface()624 already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
625 if (mSourceSurface) {
626 RefPtr<gfx::SourceSurface> surface(mSourceSurface);
627 return surface.forget();
628 }
629
630 gfx::IntSize size(mSize);
631 gfx::SurfaceFormat format =
632 gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
633 gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
634 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
635 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
636 NS_ERROR("Illegal image dest width or height");
637 return nullptr;
638 }
639
640 RefPtr<gfx::DataSourceSurface> surface =
641 gfx::Factory::CreateDataSourceSurface(size, format);
642 if (NS_WARN_IF(!surface)) {
643 return nullptr;
644 }
645
646 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
647 if (NS_WARN_IF(!mapping.IsMapped())) {
648 return nullptr;
649 }
650
651 gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(),
652 mapping.GetStride());
653
654 mSourceSurface = surface;
655
656 return surface.forget();
657 }
658
NVImage()659 NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {}
660
661 NVImage::~NVImage() = default;
662
GetSize() const663 IntSize NVImage::GetSize() const { return mSize; }
664
GetPictureRect() const665 IntRect NVImage::GetPictureRect() const { return mData.GetPictureRect(); }
666
GetAsSourceSurface()667 already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
668 if (mSourceSurface) {
669 RefPtr<gfx::SourceSurface> surface(mSourceSurface);
670 return surface.forget();
671 }
672
673 // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
674 // logics in PlanarYCbCrImage::GetAsSourceSurface().
675 const int bufferLength = mData.mYSize.height * mData.mYStride +
676 mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
677 auto* buffer = new uint8_t[bufferLength];
678
679 Data aData = mData;
680 aData.mCbCrStride = aData.mCbCrSize.width;
681 aData.mCbSkip = 0;
682 aData.mCrSkip = 0;
683 aData.mYChannel = buffer;
684 aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
685 aData.mCrChannel =
686 aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
687
688 if (mData.mCbChannel < mData.mCrChannel) { // NV12
689 libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel,
690 mData.mCbCrStride, aData.mYChannel, aData.mYStride,
691 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
692 aData.mCbCrStride, aData.mYSize.width,
693 aData.mYSize.height);
694 } else { // NV21
695 libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel,
696 mData.mCbCrStride, aData.mYChannel, aData.mYStride,
697 aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
698 aData.mCbCrStride, aData.mYSize.width,
699 aData.mYSize.height);
700 }
701
702 // The logics in PlanarYCbCrImage::GetAsSourceSurface().
703 gfx::IntSize size(mSize);
704 gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(
705 gfxPlatform::GetPlatform()->GetOffscreenFormat());
706 gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
707 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
708 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
709 NS_ERROR("Illegal image dest width or height");
710 return nullptr;
711 }
712
713 RefPtr<gfx::DataSourceSurface> surface =
714 gfx::Factory::CreateDataSourceSurface(size, format);
715 if (NS_WARN_IF(!surface)) {
716 return nullptr;
717 }
718
719 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
720 if (NS_WARN_IF(!mapping.IsMapped())) {
721 return nullptr;
722 }
723
724 gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(),
725 mapping.GetStride());
726
727 mSourceSurface = surface;
728
729 // Release the temporary buffer.
730 delete[] buffer;
731
732 return surface.forget();
733 }
734
IsValid() const735 bool NVImage::IsValid() const { return !!mBufferSize; }
736
GetBufferSize() const737 uint32_t NVImage::GetBufferSize() const { return mBufferSize; }
738
AsNVImage()739 NVImage* NVImage::AsNVImage() { return this; };
740
SetData(const Data & aData)741 bool NVImage::SetData(const Data& aData) {
742 MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
743 MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
744
745 // Calculate buffer size
746 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
747 const auto checkedSize =
748 CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
749 CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
750
751 if (!checkedSize.isValid()) return false;
752
753 const auto size = checkedSize.value();
754
755 // Allocate a new buffer.
756 mBuffer = AllocateBuffer(size);
757 if (!mBuffer) {
758 return false;
759 }
760
761 // Update mBufferSize.
762 mBufferSize = size;
763
764 // Update mData.
765 mData = aData;
766 mData.mYChannel = mBuffer.get();
767 mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
768 mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
769
770 // Update mSize.
771 mSize = aData.mPicSize;
772
773 // Copy the input data into mBuffer.
774 // This copies the y-channel and the interleaving CbCr-channel.
775 memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
776
777 return true;
778 }
779
GetData() const780 const NVImage::Data* NVImage::GetData() const { return &mData; }
781
AllocateBuffer(uint32_t aSize)782 UniquePtr<uint8_t> NVImage::AllocateBuffer(uint32_t aSize) {
783 UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
784 return buffer;
785 }
786
SourceSurfaceImage(const gfx::IntSize & aSize,gfx::SourceSurface * aSourceSurface)787 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize,
788 gfx::SourceSurface* aSourceSurface)
789 : Image(nullptr, ImageFormat::CAIRO_SURFACE),
790 mSize(aSize),
791 mSourceSurface(aSourceSurface),
792 mTextureFlags(TextureFlags::DEFAULT) {}
793
SourceSurfaceImage(gfx::SourceSurface * aSourceSurface)794 SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
795 : Image(nullptr, ImageFormat::CAIRO_SURFACE),
796 mSize(aSourceSurface->GetSize()),
797 mSourceSurface(aSourceSurface),
798 mTextureFlags(TextureFlags::DEFAULT) {}
799
800 SourceSurfaceImage::~SourceSurfaceImage() = default;
801
GetTextureClient(KnowsCompositor * aKnowsCompositor)802 TextureClient* SourceSurfaceImage::GetTextureClient(
803 KnowsCompositor* aKnowsCompositor) {
804 if (!aKnowsCompositor) {
805 return nullptr;
806 }
807
808 auto entry = mTextureClients.LookupForAdd(aKnowsCompositor->GetSerial());
809 if (entry) {
810 return entry.Data();
811 }
812
813 RefPtr<TextureClient> textureClient;
814 RefPtr<SourceSurface> surface = GetAsSourceSurface();
815 MOZ_ASSERT(surface);
816 if (surface) {
817 // gfx::BackendType::NONE means default to content backend
818 textureClient = TextureClient::CreateFromSurface(
819 aKnowsCompositor, surface, BackendSelector::Content, mTextureFlags,
820 ALLOC_DEFAULT);
821 }
822 if (textureClient) {
823 textureClient->SyncWithObject(aKnowsCompositor->GetSyncObject());
824 entry.OrInsert([&textureClient]() { return textureClient; });
825 return textureClient;
826 }
827
828 // Remove the speculatively added entry.
829 entry.OrRemove();
830 return nullptr;
831 }
832
AllocateProducerID()833 ImageContainer::ProducerID ImageContainer::AllocateProducerID() {
834 // Callable on all threads.
835 static Atomic<ImageContainer::ProducerID> sProducerID(0u);
836 return ++sProducerID;
837 }
838
839 } // namespace mozilla::layers
840