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