Source code

Revision control

Other Tools

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