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"
23 #include "gfx2DGlue.h"
24 #include "mozilla/gfx/2D.h"
27 #include "mozilla/gfx/QuartzSupport.h"
28 #include "MacIOSurfaceImage.h"
32 #include "gfxD2DSurface.h"
33 #include "gfxWindowsPlatform.h"
35 #include "d3d10/ImageLayerD3D10.h"
36 #include "D3D9SurfaceImage.h"
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
,
51 BufferRecycleBin
*aRecycleBin
)
54 #ifdef MOZ_WIDGET_GONK
55 if (aFormat
== ImageFormat::GRALLOC_PLANAR_YCBCR
) {
56 img
= new GrallocImage();
59 if (aFormat
== ImageFormat::OVERLAY_IMAGE
) {
60 img
= new OverlayImage();
64 if (aFormat
== ImageFormat::PLANAR_YCBCR
) {
65 img
= new PlanarYCbCrImage(aRecycleBin
);
68 if (aFormat
== ImageFormat::CAIRO_SURFACE
) {
69 img
= new CairoImage();
72 #ifdef MOZ_WIDGET_ANDROID
73 if (aFormat
== ImageFormat::SURFACE_TEXTURE
) {
74 img
= new SurfaceTextureImage();
78 if (aFormat
== ImageFormat::EGLIMAGE
) {
79 img
= new EGLImageImage();
83 if (aFormat
== ImageFormat::MAC_IOSURFACE
) {
84 img
= new MacIOSurfaceImage();
89 if (aFormat
== ImageFormat::D3D9_RGB32_TEXTURE
) {
90 img
= new D3D9SurfaceImage();
97 BufferRecycleBin::BufferRecycleBin()
98 : mLock("mozilla.layers.BufferRecycleBin.mLock")
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
);
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
);
128 ImageContainer::ImageContainer(int flag
)
129 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
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()
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();
171 nsRefPtr
<Image
> img
= mImageClient
->CreateImage(aFormat
);
176 return mImageFactory
->CreateImage(aFormat
, mScaleHint
, mRecycleBin
);
180 ImageContainer::SetCurrentImageInternal(Image
*aImage
)
182 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
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();
195 mRemoteDataMutex
->Unlock();
200 ImageContainer::ClearCurrentImage()
202 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
203 SetCurrentImageInternal(nullptr);
207 ImageContainer::SetCurrentImage(Image
*aImage
)
214 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
216 ImageBridgeChild::DispatchImageClientUpdate(mImageClient
, this);
218 SetCurrentImageInternal(aImage
);
222 ImageContainer::ClearAllImages()
225 // Let ImageClient release all TextureClients.
226 ImageBridgeChild::FlushAllImages(mImageClient
, this, false);
230 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
231 SetCurrentImageInternal(nullptr);
235 ImageContainer::ClearAllImagesExceptFront()
238 // Let ImageClient release all TextureClients except front one.
239 ImageBridgeChild::FlushAllImages(mImageClient
, this, true);
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");
260 return mImageClient
->GetAsyncID();
262 return 0; // zero is always an invalid AsyncID
267 ImageContainer::HasCurrentImage()
269 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
272 CrossProcessMutexAutoLock
autoLock(*mRemoteDataMutex
);
276 return !!mActiveImage
.get();
279 return !!mActiveImage
.get();
282 already_AddRefed
<Image
>
283 ImageContainer::LockCurrentImage()
285 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
288 NS_ASSERTION(mRemoteDataMutex
, "Should have remote data mutex when having remote data!");
289 mRemoteDataMutex
->Lock();
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
);
304 NS_ASSERTION(mRemoteDataMutex
, "Should have remote data mutex when having remote data!");
305 mRemoteDataMutex
->Lock();
310 NS_IF_ADDREF(mActiveImage
);
311 *aCurrentImage
= mActiveImage
.get();
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
,
327 gfx::ImageFormatToSurfaceFormat(fmt
));
328 *aSize
= newSurf
->GetSize();
333 *aSize
= mActiveImage
->GetSize();
334 return mActiveImage
->GetAsSourceSurface();
338 NS_IF_ADDREF(mActiveImage
);
339 *aCurrentImage
= mActiveImage
.get();
346 *aSize
= mActiveImage
->GetSize();
347 return mActiveImage
->GetAsSourceSurface();
351 ImageContainer::UnlockCurrentImage()
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
);
365 CrossProcessMutexAutoLock
autoLock(*mRemoteDataMutex
);
370 *aSize
= mRemoteData
->mSize
;
374 *aSize
= mActiveImage
->GetSize();
376 return mActiveImage
->GetAsSourceSurface();
380 ImageContainer::GetCurrentSize()
382 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
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
;
393 return gfx::IntSize(0, 0);
396 return mActiveImage
->GetSize();
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.");
410 memset(aData
, 0, sizeof(RemoteImageData
));
412 mActiveImage
= nullptr;
415 mRemoteDataMutex
= aMutex
;
419 ImageContainer::EnsureActiveImage()
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
;
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
;
454 PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin
*aRecycleBin
)
455 : Image(nullptr, ImageFormat::PLANAR_YCBCR
)
457 , mOffscreenFormat(gfxImageFormat::Unknown
)
458 , mRecycleBin(aRecycleBin
)
462 PlanarYCbCrImage::~PlanarYCbCrImage()
465 mRecycleBin
->RecycleBuffer(mBuffer
.forget(), mBufferSize
);
470 PlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
473 // - mData - just wraps mBuffer
474 // - Surfaces should be reported under gfx-surfaces-*:
477 // - mImplData is not used
480 size_t size
= mBuffer
.SizeOfExcludingThis(aMallocSizeOf
);
482 // Could add in the future:
483 // - mBackendData (from base class)
489 PlanarYCbCrImage::AllocateBuffer(uint32_t aSize
)
491 return mRecycleBin
->GetBuffer(aSize
);
495 CopyPlane(uint8_t *aDst
, const uint8_t *aSrc
,
496 const gfx::IntSize
&aSize
, int32_t aStride
, int32_t aSkip
)
499 // Fast path: planar input.
500 memcpy(aDst
, aSrc
, aSize
.height
* aStride
);
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
;
508 for (int x
= 0; x
< width
; ++x
) {
519 PlanarYCbCrImage::CopyData(const Data
& aData
)
523 // update buffer size
524 size_t size
= mData
.mCbCrStride
* mData
.mCbCrSize
.height
* 2 +
525 mData
.mYStride
* mData
.mYSize
.height
;
528 mBuffer
= AllocateBuffer(size
);
532 // update buffer 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
;
550 PlanarYCbCrImage::SetData(const Data
&aData
)
556 PlanarYCbCrImage::GetOffscreenFormat()
558 return mOffscreenFormat
== gfxImageFormat::Unknown
?
559 gfxPlatform::GetPlatform()->GetOffscreenFormat() :
564 PlanarYCbCrImage::SetDataNoCopy(const Data
&aData
)
567 mSize
= aData
.mPicSize
;
571 PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize
)
574 mBuffer
= AllocateBuffer(aSize
);
576 // update buffer size
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");
598 RefPtr
<gfx::DataSourceSurface
> surface
= gfx::Factory::CreateDataSourceSurface(size
, format
);
599 if (NS_WARN_IF(!surface
)) {
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
)) {
621 for (int y
= 0; y
< mSize
.height
; y
++) {
622 memcpy(newSurf
->GetData() + newSurf
->Stride() * y
,
630 CairoImage::CairoImage()
631 : Image(nullptr, ImageFormat::CAIRO_SURFACE
)
634 CairoImage::~CairoImage()
639 CairoImage::GetTextureClient(CompositableClient
*aClient
)
645 CompositableForwarder
* forwarder
= aClient
->GetForwarder();
646 RefPtr
<TextureClient
> textureClient
= mTextureClients
.Get(forwarder
->GetSerial());
648 return textureClient
;
651 RefPtr
<SourceSurface
> surface
= GetAsSourceSurface();
658 // XXX windows' TextureClients do not hold ISurfaceAllocator,
659 // recycler does not work on windows.
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();
669 recycler
->CreateOrRecycleForDrawing(surface
->GetFormat(),
671 gfx::BackendType::NONE
,
672 aClient
->GetTextureFlags());
677 if (!textureClient
) {
678 // gfx::BackendType::NONE means default to content backend
679 textureClient
= aClient
->CreateTextureClientForDrawing(surface
->GetFormat(),
681 gfx::BackendType::NONE
,
682 TextureFlags::DEFAULT
);
684 if (!textureClient
) {
687 MOZ_ASSERT(textureClient
->CanExposeDrawTarget());
688 if (!textureClient
->Lock(OpenMode::OPEN_WRITE_ONLY
)) {
692 TextureClientAutoUnlock
autoUnolck(textureClient
);
694 // We must not keep a reference to the DrawTarget after it has been unlocked.
695 DrawTarget
* dt
= textureClient
->BorrowDrawTarget();
699 dt
->CopySurface(surface
, IntRect(IntPoint(), surface
->GetSize()), IntPoint());
702 mTextureClients
.Put(forwarder
->GetSerial(), textureClient
);
703 return textureClient
;