Bumping manifests a=b2g-bump
[gecko.git] / gfx / layers / ImageContainer.cpp
blob8f6e54451b33bb397269f0e31af8bddb101e04c1
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/. */
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 "mozilla/RefPtr.h" // for TemporaryRef
14 #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
15 #include "mozilla/layers/CompositorTypes.h"
16 #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
17 #include "mozilla/layers/ImageClient.h" // for ImageClient
18 #include "nsISupportsUtils.h" // for NS_IF_ADDREF
19 #include "YCbCrUtils.h" // for YCbCr conversions
20 #ifdef MOZ_WIDGET_GONK
21 #include "GrallocImages.h"
22 #endif
23 #include "gfx2DGlue.h"
24 #include "mozilla/gfx/2D.h"
26 #ifdef XP_MACOSX
27 #include "mozilla/gfx/QuartzSupport.h"
28 #include "MacIOSurfaceImage.h"
29 #endif
31 #ifdef XP_WIN
32 #include "gfxD2DSurface.h"
33 #include "gfxWindowsPlatform.h"
34 #include <d3d10_1.h>
35 #include "d3d10/ImageLayerD3D10.h"
36 #include "D3D9SurfaceImage.h"
37 #endif
39 namespace mozilla {
40 namespace layers {
42 using namespace mozilla::ipc;
43 using namespace android;
44 using namespace mozilla::gfx;
46 Atomic<int32_t> Image::sSerialCounter(0);
48 already_AddRefed<Image>
49 ImageFactory::CreateImage(ImageFormat aFormat,
50 const gfx::IntSize &,
51 BufferRecycleBin *aRecycleBin)
53 nsRefPtr<Image> img;
54 #ifdef MOZ_WIDGET_GONK
55 if (aFormat == ImageFormat::GRALLOC_PLANAR_YCBCR) {
56 img = new GrallocImage();
57 return img.forget();
59 if (aFormat == ImageFormat::OVERLAY_IMAGE) {
60 img = new OverlayImage();
61 return img.forget();
63 #endif
64 if (aFormat == ImageFormat::PLANAR_YCBCR) {
65 img = new PlanarYCbCrImage(aRecycleBin);
66 return img.forget();
68 if (aFormat == ImageFormat::CAIRO_SURFACE) {
69 img = new CairoImage();
70 return img.forget();
72 #ifdef MOZ_WIDGET_ANDROID
73 if (aFormat == ImageFormat::SURFACE_TEXTURE) {
74 img = new SurfaceTextureImage();
75 return img.forget();
77 #endif
78 if (aFormat == ImageFormat::EGLIMAGE) {
79 img = new EGLImageImage();
80 return img.forget();
82 #ifdef XP_MACOSX
83 if (aFormat == ImageFormat::MAC_IOSURFACE) {
84 img = new MacIOSurfaceImage();
85 return img.forget();
87 #endif
88 #ifdef XP_WIN
89 if (aFormat == ImageFormat::D3D9_RGB32_TEXTURE) {
90 img = new D3D9SurfaceImage();
91 return img.forget();
93 #endif
94 return nullptr;
97 BufferRecycleBin::BufferRecycleBin()
98 : mLock("mozilla.layers.BufferRecycleBin.mLock")
102 void
103 BufferRecycleBin::RecycleBuffer(uint8_t* aBuffer, uint32_t aSize)
105 MutexAutoLock lock(mLock);
107 if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
108 mRecycledBuffers.Clear();
110 mRecycledBufferSize = aSize;
111 mRecycledBuffers.AppendElement(aBuffer);
114 uint8_t*
115 BufferRecycleBin::GetBuffer(uint32_t aSize)
117 MutexAutoLock lock(mLock);
119 if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
120 return new uint8_t[aSize];
122 uint32_t last = mRecycledBuffers.Length() - 1;
123 uint8_t* result = mRecycledBuffers[last].forget();
124 mRecycledBuffers.RemoveElementAt(last);
125 return result;
128 ImageContainer::ImageContainer(int flag)
129 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
130 mPaintCount(0),
131 mPreviousImagePainted(false),
132 mImageFactory(new ImageFactory()),
133 mRecycleBin(new BufferRecycleBin()),
134 mRemoteData(nullptr),
135 mRemoteDataMutex(nullptr),
136 mCompositionNotifySink(nullptr),
137 mImageClient(nullptr)
139 if (flag == ENABLE_ASYNC && ImageBridgeChild::IsCreated()) {
140 // the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
141 // of this class must be done on the ImageBridge thread.
142 mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::BUFFER_IMAGE_SINGLE).drop();
143 MOZ_ASSERT(mImageClient);
147 ImageContainer::~ImageContainer()
149 if (IsAsync()) {
150 ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
154 already_AddRefed<Image>
155 ImageContainer::CreateImage(ImageFormat aFormat)
157 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
159 #ifdef MOZ_WIDGET_GONK
160 if (aFormat == ImageFormat::OVERLAY_IMAGE) {
161 if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
162 // If this ImageContainer is async but the image type mismatch, fix it here
163 if (ImageBridgeChild::IsCreated()) {
164 ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
165 mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY).drop();
169 #endif
170 if (mImageClient) {
171 nsRefPtr<Image> img = mImageClient->CreateImage(aFormat);
172 if (img) {
173 return img.forget();
176 return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
179 void
180 ImageContainer::SetCurrentImageInternal(Image *aImage)
182 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
184 if (mRemoteData) {
185 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
186 mRemoteDataMutex->Lock();
187 // This is important since it ensures we won't change the active image
188 // when we currently have a locked image that depends on mRemoteData.
191 mActiveImage = aImage;
192 CurrentImageChanged();
194 if (mRemoteData) {
195 mRemoteDataMutex->Unlock();
199 void
200 ImageContainer::ClearCurrentImage()
202 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
203 SetCurrentImageInternal(nullptr);
206 void
207 ImageContainer::SetCurrentImage(Image *aImage)
209 if (!aImage) {
210 ClearAllImages();
211 return;
214 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
215 if (IsAsync()) {
216 ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
218 SetCurrentImageInternal(aImage);
221 void
222 ImageContainer::ClearAllImages()
224 if (IsAsync()) {
225 // Let ImageClient release all TextureClients.
226 ImageBridgeChild::FlushAllImages(mImageClient, this, false);
227 return;
230 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
231 SetCurrentImageInternal(nullptr);
234 void
235 ImageContainer::ClearAllImagesExceptFront()
237 if (IsAsync()) {
238 // Let ImageClient release all TextureClients except front one.
239 ImageBridgeChild::FlushAllImages(mImageClient, this, true);
243 void
244 ImageContainer::SetCurrentImageInTransaction(Image *aImage)
246 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
247 NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
249 SetCurrentImageInternal(aImage);
252 bool ImageContainer::IsAsync() const {
253 return mImageClient != nullptr;
256 uint64_t ImageContainer::GetAsyncContainerID() const
258 NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
259 if (IsAsync()) {
260 return mImageClient->GetAsyncID();
261 } else {
262 return 0; // zero is always an invalid AsyncID
266 bool
267 ImageContainer::HasCurrentImage()
269 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
271 if (mRemoteData) {
272 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
274 EnsureActiveImage();
276 return !!mActiveImage.get();
279 return !!mActiveImage.get();
282 already_AddRefed<Image>
283 ImageContainer::LockCurrentImage()
285 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
287 if (mRemoteData) {
288 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
289 mRemoteDataMutex->Lock();
292 EnsureActiveImage();
294 nsRefPtr<Image> retval = mActiveImage;
295 return retval.forget();
298 TemporaryRef<gfx::SourceSurface>
299 ImageContainer::LockCurrentAsSourceSurface(gfx::IntSize *aSize, Image** aCurrentImage)
301 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
303 if (mRemoteData) {
304 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
305 mRemoteDataMutex->Lock();
307 EnsureActiveImage();
309 if (aCurrentImage) {
310 NS_IF_ADDREF(mActiveImage);
311 *aCurrentImage = mActiveImage.get();
314 if (!mActiveImage) {
315 return nullptr;
318 if (mActiveImage->GetFormat() == ImageFormat::REMOTE_IMAGE_BITMAP) {
319 gfxImageFormat fmt = mRemoteData->mFormat == RemoteImageData::BGRX32
320 ? gfxImageFormat::ARGB32
321 : gfxImageFormat::RGB24;
323 RefPtr<gfx::DataSourceSurface> newSurf
324 = gfx::Factory::CreateWrappingDataSourceSurface(mRemoteData->mBitmap.mData,
325 mRemoteData->mBitmap.mStride,
326 mRemoteData->mSize,
327 gfx::ImageFormatToSurfaceFormat(fmt));
328 *aSize = newSurf->GetSize();
330 return newSurf;
333 *aSize = mActiveImage->GetSize();
334 return mActiveImage->GetAsSourceSurface();
337 if (aCurrentImage) {
338 NS_IF_ADDREF(mActiveImage);
339 *aCurrentImage = mActiveImage.get();
342 if (!mActiveImage) {
343 return nullptr;
346 *aSize = mActiveImage->GetSize();
347 return mActiveImage->GetAsSourceSurface();
350 void
351 ImageContainer::UnlockCurrentImage()
353 if (mRemoteData) {
354 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
355 mRemoteDataMutex->Unlock();
359 TemporaryRef<gfx::SourceSurface>
360 ImageContainer::GetCurrentAsSourceSurface(gfx::IntSize *aSize)
362 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
364 if (mRemoteData) {
365 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
366 EnsureActiveImage();
368 if (!mActiveImage)
369 return nullptr;
370 *aSize = mRemoteData->mSize;
371 } else {
372 if (!mActiveImage)
373 return nullptr;
374 *aSize = mActiveImage->GetSize();
376 return mActiveImage->GetAsSourceSurface();
379 gfx::IntSize
380 ImageContainer::GetCurrentSize()
382 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
384 if (mRemoteData) {
385 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
387 // We don't need to ensure we have an active image here, as we need to
388 // be in the mutex anyway, and this is easiest to return from there.
389 return mRemoteData->mSize;
392 if (!mActiveImage) {
393 return gfx::IntSize(0, 0);
396 return mActiveImage->GetSize();
399 void
400 ImageContainer::SetRemoteImageData(RemoteImageData *aData, CrossProcessMutex *aMutex)
402 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
404 NS_ASSERTION(!mActiveImage || !aData, "No active image expected when SetRemoteImageData is called with non-NULL aData.");
405 NS_ASSERTION(!mRemoteData || !aData, "No remote data expected when SetRemoteImageData is called with non-NULL aData.");
407 mRemoteData = aData;
409 if (aData) {
410 memset(aData, 0, sizeof(RemoteImageData));
411 } else {
412 mActiveImage = nullptr;
415 mRemoteDataMutex = aMutex;
418 void
419 ImageContainer::EnsureActiveImage()
421 if (mRemoteData) {
422 if (mRemoteData->mWasUpdated) {
423 mActiveImage = nullptr;
426 if (mRemoteData->mType == RemoteImageData::RAW_BITMAP &&
427 mRemoteData->mBitmap.mData && !mActiveImage) {
428 nsRefPtr<RemoteBitmapImage> newImg = new RemoteBitmapImage();
430 newImg->mFormat = mRemoteData->mFormat;
431 newImg->mData = mRemoteData->mBitmap.mData;
432 newImg->mSize = mRemoteData->mSize;
433 newImg->mStride = mRemoteData->mBitmap.mStride;
434 mRemoteData->mWasUpdated = false;
436 mActiveImage = newImg;
438 #ifdef XP_WIN
439 else if (mRemoteData->mType == RemoteImageData::DXGI_TEXTURE_HANDLE &&
440 mRemoteData->mTextureHandle && !mActiveImage) {
441 nsRefPtr<RemoteDXGITextureImage> newImg = new RemoteDXGITextureImage();
442 newImg->mSize = mRemoteData->mSize;
443 newImg->mHandle = mRemoteData->mTextureHandle;
444 newImg->mFormat = mRemoteData->mFormat;
445 mRemoteData->mWasUpdated = false;
447 mActiveImage = newImg;
449 #endif
454 PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
455 : Image(nullptr, ImageFormat::PLANAR_YCBCR)
456 , mBufferSize(0)
457 , mOffscreenFormat(gfxImageFormat::Unknown)
458 , mRecycleBin(aRecycleBin)
462 PlanarYCbCrImage::~PlanarYCbCrImage()
464 if (mBuffer) {
465 mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
469 size_t
470 PlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
472 // Ignoring:
473 // - mData - just wraps mBuffer
474 // - Surfaces should be reported under gfx-surfaces-*:
475 // - mSourceSurface
476 // - Base class:
477 // - mImplData is not used
478 // Not owned:
479 // - mRecycleBin
480 size_t size = mBuffer.SizeOfExcludingThis(aMallocSizeOf);
482 // Could add in the future:
483 // - mBackendData (from base class)
485 return size;
488 uint8_t*
489 PlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
491 return mRecycleBin->GetBuffer(aSize);
494 static void
495 CopyPlane(uint8_t *aDst, const uint8_t *aSrc,
496 const gfx::IntSize &aSize, int32_t aStride, int32_t aSkip)
498 if (!aSkip) {
499 // Fast path: planar input.
500 memcpy(aDst, aSrc, aSize.height * aStride);
501 } else {
502 int32_t height = aSize.height;
503 int32_t width = aSize.width;
504 for (int y = 0; y < height; ++y) {
505 const uint8_t *src = aSrc;
506 uint8_t *dst = aDst;
507 // Slow path
508 for (int x = 0; x < width; ++x) {
509 *dst++ = *src++;
510 src += aSkip;
512 aSrc += aStride;
513 aDst += aStride;
518 void
519 PlanarYCbCrImage::CopyData(const Data& aData)
521 mData = aData;
523 // update buffer size
524 size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
525 mData.mYStride * mData.mYSize.height;
527 // get new buffer
528 mBuffer = AllocateBuffer(size);
529 if (!mBuffer)
530 return;
532 // update buffer size
533 mBufferSize = size;
535 mData.mYChannel = mBuffer;
536 mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
537 mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
539 CopyPlane(mData.mYChannel, aData.mYChannel,
540 mData.mYSize, mData.mYStride, mData.mYSkip);
541 CopyPlane(mData.mCbChannel, aData.mCbChannel,
542 mData.mCbCrSize, mData.mCbCrStride, mData.mCbSkip);
543 CopyPlane(mData.mCrChannel, aData.mCrChannel,
544 mData.mCbCrSize, mData.mCbCrStride, mData.mCrSkip);
546 mSize = aData.mPicSize;
549 void
550 PlanarYCbCrImage::SetData(const Data &aData)
552 CopyData(aData);
555 gfxImageFormat
556 PlanarYCbCrImage::GetOffscreenFormat()
558 return mOffscreenFormat == gfxImageFormat::Unknown ?
559 gfxPlatform::GetPlatform()->GetOffscreenFormat() :
560 mOffscreenFormat;
563 void
564 PlanarYCbCrImage::SetDataNoCopy(const Data &aData)
566 mData = aData;
567 mSize = aData.mPicSize;
570 uint8_t*
571 PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
573 // get new buffer
574 mBuffer = AllocateBuffer(aSize);
575 if (mBuffer) {
576 // update buffer size
577 mBufferSize = aSize;
579 return mBuffer;
582 TemporaryRef<gfx::SourceSurface>
583 PlanarYCbCrImage::GetAsSourceSurface()
585 if (mSourceSurface) {
586 return mSourceSurface.get();
589 gfx::IntSize size(mSize);
590 gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
591 gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
592 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
593 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
594 NS_ERROR("Illegal image dest width or height");
595 return nullptr;
598 RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
599 if (NS_WARN_IF(!surface)) {
600 return nullptr;
603 gfx::ConvertYCbCrToRGB(mData, format, size, surface->GetData(), surface->Stride());
605 mSourceSurface = surface;
607 return surface.forget();
610 TemporaryRef<gfx::SourceSurface>
611 RemoteBitmapImage::GetAsSourceSurface()
613 gfx::SurfaceFormat fmt = mFormat == RemoteImageData::BGRX32
614 ? gfx::SurfaceFormat::B8G8R8X8
615 : gfx::SurfaceFormat::B8G8R8A8;
616 RefPtr<gfx::DataSourceSurface> newSurf = gfx::Factory::CreateDataSourceSurface(mSize, fmt);
617 if (NS_WARN_IF(!newSurf)) {
618 return nullptr;
621 for (int y = 0; y < mSize.height; y++) {
622 memcpy(newSurf->GetData() + newSurf->Stride() * y,
623 mData + mStride * y,
624 mSize.width * 4);
627 return newSurf;
630 CairoImage::CairoImage()
631 : Image(nullptr, ImageFormat::CAIRO_SURFACE)
634 CairoImage::~CairoImage()
638 TextureClient*
639 CairoImage::GetTextureClient(CompositableClient *aClient)
641 if (!aClient) {
642 return nullptr;
645 CompositableForwarder* forwarder = aClient->GetForwarder();
646 RefPtr<TextureClient> textureClient = mTextureClients.Get(forwarder->GetSerial());
647 if (textureClient) {
648 return textureClient;
651 RefPtr<SourceSurface> surface = GetAsSourceSurface();
652 MOZ_ASSERT(surface);
653 if (!surface) {
654 return nullptr;
658 // XXX windows' TextureClients do not hold ISurfaceAllocator,
659 // recycler does not work on windows.
660 #ifndef XP_WIN
662 // XXX only gonk ensure when TextureClient is recycled,
663 // TextureHost is not used by CompositableHost.
664 #ifdef MOZ_WIDGET_GONK
665 RefPtr<TextureClientRecycleAllocator> recycler =
666 aClient->GetTextureClientRecycler();
667 if (recycler) {
668 textureClient =
669 recycler->CreateOrRecycleForDrawing(surface->GetFormat(),
670 surface->GetSize(),
671 gfx::BackendType::NONE,
672 aClient->GetTextureFlags());
674 #endif
676 #endif
677 if (!textureClient) {
678 // gfx::BackendType::NONE means default to content backend
679 textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
680 surface->GetSize(),
681 gfx::BackendType::NONE,
682 TextureFlags::DEFAULT);
684 if (!textureClient) {
685 return nullptr;
687 MOZ_ASSERT(textureClient->CanExposeDrawTarget());
688 if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
689 return nullptr;
692 TextureClientAutoUnlock autoUnolck(textureClient);
694 // We must not keep a reference to the DrawTarget after it has been unlocked.
695 DrawTarget* dt = textureClient->BorrowDrawTarget();
696 if (!dt) {
697 return nullptr;
699 dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
702 mTextureClients.Put(forwarder->GetSerial(), textureClient);
703 return textureClient;
706 } // namespace
707 } // namespace