Bug 773440 - Remove one video frame copy when using async-video. r=roc

This commit is contained in:
Nicolas Silva 2012-08-23 10:56:36 -04:00
Родитель 0a20ff27b9
Коммит 08383d674f
6 изменённых файлов: 427 добавлений и 69 удалений

Просмотреть файл

@ -139,11 +139,45 @@ ImageContainer::~ImageContainer()
}
}
void ImageContainer::CopyPlane(uint8_t *aDst, uint8_t *aSrc,
const gfxIntSize &aSize,
int32_t aSrcStride, int32_t aDstStride,
int32_t aOffset, int32_t aSkip)
{
if (!aOffset && !aSkip && aSrcStride == aDstStride) {
// Fast path: planar input.
memcpy(aDst, aSrc, aSize.height * aSrcStride);
} else {
int32_t height = aSize.height;
int32_t width = aSize.width;
for (int y = 0; y < height; ++y) {
uint8_t *src = aSrc + aOffset;
uint8_t *dst = aDst;
if (!aSkip) {
// Fast path: offset only, no per-pixel skip.
memcpy(dst, src, width);
} else {
// Slow path
for (int x = 0; x < width; ++x) {
*dst++ = *src++;
src += aSkip;
}
}
aSrc += aSrcStride;
aDst += aDstStride;
}
}
}
already_AddRefed<Image>
ImageContainer::CreateImage(const ImageFormat *aFormats,
uint32_t aNumFormats)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (IsAsync()) {
return mImageContainerChild->CreateImage(aFormats, aNumFormats);
}
return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin);
}
@ -411,71 +445,39 @@ PlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
return mRecycleBin->GetBuffer(aSize);
}
static void
CopyPlane(uint8_t *aDst, uint8_t *aSrc,
const gfxIntSize &aSize, int32_t aStride,
int32_t aOffset, int32_t aSkip)
void PlanarYCbCrImage::Allocate(Data& aData)
{
if (!aOffset && !aSkip) {
// Fast path: planar input.
memcpy(aDst, aSrc, aSize.height * aStride);
} else {
int32_t height = aSize.height;
int32_t width = aSize.width;
for (int y = 0; y < height; ++y) {
uint8_t *src = aSrc + aOffset;
uint8_t *dst = aDst;
if (!aSkip) {
// Fast path: offset only, no per-pixel skip.
memcpy(dst, src, width);
} else {
// Slow path
for (int x = 0; x < width; ++x) {
*dst++ = *src++;
src += aSkip;
}
}
aSrc += aStride;
aDst += aStride;
}
}
}
void
PlanarYCbCrImage::CopyData(const Data& aData)
{
mData = aData;
// update buffer size
mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height;
int bufferSize = aData.mCbCrStride * aData.mCbCrSize.height * 2 +
aData.mYStride * aData.mYSize.height;
// get new buffer
mBuffer = AllocateBuffer(mBufferSize);
if (!mBuffer)
uint8_t* buffer = AllocateBuffer(bufferSize);
if (!buffer)
return;
mData.mYChannel = mBuffer;
mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
CopyPlane(mData.mYChannel, aData.mYChannel,
mData.mYSize, mData.mYStride,
mData.mYOffset, mData.mYSkip);
CopyPlane(mData.mCbChannel, aData.mCbChannel,
mData.mCbCrSize, mData.mCbCrStride,
mData.mCbOffset, mData.mCbSkip);
CopyPlane(mData.mCrChannel, aData.mCrChannel,
mData.mCbCrSize, mData.mCbCrStride,
mData.mCrOffset, mData.mCrSkip);
mSize = aData.mPicSize;
aData.mYChannel = buffer;
aData.mCbChannel = aData.mYChannel + aData.mYStride * aData.mYSize.height;
aData.mCrChannel = aData.mCbChannel + aData.mCbCrStride * aData.mCbCrSize.height;
mBuffer = buffer;
}
void
PlanarYCbCrImage::SetData(const Data &aData)
{
CopyData(aData);
mData = aData;
mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height;
mSize = mData.mPicSize;
// If mBuffer has not been allocated (through Allocate(aData)), allocate it.
// This code path is slower than the one used when Allocate has been called
// since it will trigger a full copy.
if (!mBuffer) {
mBuffer = AllocateBuffer(mBufferSize);
NS_ASSERTION(mBuffer, "Failed to Allocate!");
}
if (aData.mYChannel != mBuffer) {
memcpy(mBuffer.get(), aData.mYChannel, mBufferSize);
}
}
already_AddRefed<gfxASurface>

Просмотреть файл

@ -39,6 +39,7 @@ class Shmem;
namespace layers {
class ImageContainerChild;
class SharedPlanarYCbCrImage;
struct ImageBackendData
{
@ -483,6 +484,14 @@ public:
*/
RemoteImageData *GetRemoteImageData() { return mRemoteData; }
/**
* Helper function to copy an image buffer respecting stride, offset and skip.
*/
static void CopyPlane(uint8_t *aDst, uint8_t *aSrc,
const gfxIntSize &aSize,
int32_t aSrcStride, int32_t aDstStride,
int32_t aOffset, int32_t aSkip);
protected:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
@ -651,9 +660,15 @@ public:
virtual ~PlanarYCbCrImage();
virtual SharedPlanarYCbCrImage* AsSharedPlanarYCbCrImage()
{
return nullptr;
}
/**
* This makes a copy of the data buffers, in order to support functioning
* in all different layer managers.
* If Allocate has been invoked on aData, simply acquire aData's buffers.
* Otherwise make a copy of the data buffers, in order to support
* functioning in all different layer managers.
*/
virtual void SetData(const Data& aData);
@ -669,6 +684,23 @@ public:
*/
virtual const Data* GetData() { return &mData; }
/**
* Allocate image buffer(s) and give aData ownership of the image by filling
* the pointers in aData.
* The information about the size, stride, etc. must be set in aData
* prior to calling this method.
*
* This method is meant to be used with SetData in the following order:
* - The caller first fills the necessary information in aData for the data to
* be allocated properly.
* - The caller invoke Allocate(aData) which will fill the remaining pointers
* in aData with allocated memory.
* - The caller can fill the allocated buffer with actual image data.
* - The caller then invoke SetData(aData) with the same aData that was
* passed in Allocate.
*/
virtual void Allocate(Data& aData);
/**
* Return the number of bytes of heap memory used to store this image.
*/

Просмотреть файл

@ -345,7 +345,7 @@ BasicShadowableImageLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
}
YUVImage yuv(mBackBufferY, mBackBufferU, mBackBufferV,
data->GetPictureRect());
data->GetPictureRect(), 0);
BasicManager()->PaintedImage(BasicManager()->Hold(this),
yuv);

Просмотреть файл

@ -12,6 +12,8 @@
#include "ImageContainer.h"
#include "GonkIOSurfaceImage.h"
#include "GrallocImages.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace layers {
@ -39,6 +41,135 @@ namespace layers {
static const unsigned int POOL_MAX_SHARED_IMAGES = 5;
static const unsigned int MAX_ACTIVE_SHARED_IMAGES = 10;
/**
* A YCbCr image that stores its data in shared memory directly so that it's
* data can be sent to the compositor without being copied.
*/
class SharedPlanarYCbCrImage : public PlanarYCbCrImage
{
friend class mozilla::layers::ImageContainerChild;
public:
SharedPlanarYCbCrImage(ImageContainerChild* aProtocol)
: PlanarYCbCrImage(nullptr), mImageDataAllocator(aProtocol), mSent(false)
{
MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
}
~SharedPlanarYCbCrImage()
{
if (mYSurface) {
SharedImage* sharedImg = new SharedImage();
ToSharedImage(sharedImg);
mImageDataAllocator->RecycleSharedImage(sharedImg);
}
MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
}
/**
* To prevent a SharedPlanarYCbCrImage from being sent several times we
* store a boolean state telling wether the image has already been sent to
* the compositor.
*/
bool IsSent() const
{
return mSent;
}
SharedPlanarYCbCrImage* AsSharedPlanarYCbCrImage() MOZ_OVERRIDE
{
return this;
}
void ToSharedImage(SharedImage* aImage)
{
NS_ABORT_IF_FALSE(aImage, "aImage must be allocated");
*aImage = YUVImage(mYSurface->GetShmem(),
mCbSurface->GetShmem(),
mCrSurface->GetShmem(),
mData.GetPictureRect(),
reinterpret_cast<uintptr_t>(this));
}
/**
* Returns nullptr because in most cases the YCbCrImage has lost ownership of
* its data when this is called.
*/
already_AddRefed<gfxASurface> GetAsSurface()
{
if (!mYSurface) {
return nullptr;
}
return PlanarYCbCrImage::GetAsSurface();
}
/**
* See PlanarYCbCrImage::SeData.
* This proxies a synchronous call to the ImageBridgeChild thread.
*/
virtual void Allocate(Data& aData) MOZ_OVERRIDE
{
// copy aData into mData to have the sizes and strides info
// that will be needed by the ImageContainerChild to allocate
// shared buffers.
mData = aData;
// mImageDataAllocator will allocate the shared buffers and
// place them into 'this'
mImageDataAllocator->AllocateSharedBufferForImage(this);
// give aData the resulting buffers
aData.mYChannel = mYSurface->Data();
aData.mCbChannel = mCbSurface->Data();
aData.mCrChannel = mCrSurface->Data();
}
/**
* See PlanarYCbCrImage::SeData.
*/
virtual void SetData(const Data& aData)
{
// do not set mBuffer like in PlanarYCbCrImage because the later
// will try to manage this memory without knowing it belongs to a
// shmem.
mData = aData;
mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height;
mSize = mData.mPicSize;
// If m*Surface has not been allocated (through Allocate(aData)), allocate it.
// This code path is slower than the one used when Allocate has been called
// since it will trigger a full copy.
if (!mYSurface) {
Data data = aData;
Allocate(data);
}
if (mYSurface->Data() != aData.mYChannel) {
ImageContainer::CopyPlane(mYSurface->Data(), aData.mYChannel,
aData.mYSize,
aData.mYStride, aData.mYSize.width,
0,0);
ImageContainer::CopyPlane(mCbSurface->Data(), aData.mCbChannel,
aData.mCbCrSize,
aData.mCbCrStride, aData.mCbCrSize.width,
0,0);
ImageContainer::CopyPlane(mCrSurface->Data(), aData.mCrChannel,
aData.mCbCrSize,
aData.mCbCrStride, aData.mCbCrSize.width,
0,0);
}
}
void MarkAsSent() {
mSent = true;
}
protected:
nsRefPtr<gfxSharedImageSurface> mYSurface;
nsRefPtr<gfxSharedImageSurface> mCbSurface;
nsRefPtr<gfxSharedImageSurface> mCrSurface;
nsRefPtr<ImageContainerChild> mImageDataAllocator;
bool mSent;
};
ImageContainerChild::ImageContainerChild()
: mImageContainerID(0), mActiveImageCount(0),
mStop(false), mDispatchedDestroy(false)
@ -99,15 +230,23 @@ void ImageContainerChild::StopChild()
bool ImageContainerChild::RecvReturnImage(const SharedImage& aImage)
{
SharedImage* img = new SharedImage(aImage);
// Remove oldest image from the queue.
if (mImageQueue.Length() > 0) {
mImageQueue.RemoveElementAt(0);
}
if (!AddSharedImageToPool(img) || mStop) {
DestroySharedImage(*img);
delete img;
if (aImage.type() == SharedImage::TYUVImage &&
aImage.get_YUVImage().imageID() != 0) {
// if the imageID is non-zero, it means that this image's buffers
// are used in a SharedPlanarYCbCrImage that is maybe used in the
// main thread. In this case we let ref counting take care of
// deciding when to recycle the shared memory.
return true;
}
SharedImage* img = new SharedImage(aImage);
RecycleSharedImageNow(img);
return true;
}
@ -198,7 +337,8 @@ SharedImage* ImageContainerChild::CreateSharedImageFromData(Image* image)
YUVImage(tempBufferY->GetShmem(),
tempBufferU->GetShmem(),
tempBufferV->GetShmem(),
data->GetPictureRect()));
data->GetPictureRect(),
0));
NS_ABORT_IF_FALSE(result->type() == SharedImage::TYUVImage,
"SharedImage type not set correctly");
return result;
@ -321,14 +461,32 @@ SharedImage* ImageContainerChild::ImageToSharedImage(Image* aImage)
if (mStop) {
return nullptr;
}
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
SharedPlanarYCbCrImage* sharedYCbCr =
static_cast<PlanarYCbCrImage*>(aImage)->AsSharedPlanarYCbCrImage();
if (sharedYCbCr) {
if (sharedYCbCr->IsSent()) {
// don't send the same image twice
return nullptr;
}
// the image is already using shared memory, this means that we don't
// need to copy it
SharedImage* result = new SharedImage();
sharedYCbCr->ToSharedImage(result);
sharedYCbCr->MarkAsSent();
return result;
}
}
if (mActiveImageCount > (int)MAX_ACTIVE_SHARED_IMAGES) {
// Too many active shared images, perhaps the compositor is hanging.
// Skipping current image
return nullptr;
}
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
SharedImage *img = GetSharedImageFor(aImage);
if (img) {
CopyDataIntoSharedImage(aImage, img);
@ -394,6 +552,113 @@ void ImageContainerChild::DispatchDestroy()
NewRunnableMethod(this, &ImageContainerChild::DestroyNow));
}
} // namespace
} // namespace
already_AddRefed<Image> ImageContainerChild::CreateImage(const ImageFormat *aFormats,
uint32_t aNumFormats)
{
// TODO: Add more image formats
nsRefPtr<Image> img = new SharedPlanarYCbCrImage(this);
return img.forget();
}
void ImageContainerChild::AllocateSharedBufferForImageNow(Image* aImage)
{
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
NS_ABORT_IF_FALSE(aImage && aImage->GetFormat() == ImageFormat::PLANAR_YCBCR,
"Only YUV images supported now");
SharedPlanarYCbCrImage* sharedYCbCr =
static_cast<PlanarYCbCrImage*>(aImage)->AsSharedPlanarYCbCrImage();
// try to reuse shared images from the pool first...
SharedImage* fromPool = GetSharedImageFor(aImage);
if (fromPool) {
YUVImage yuv = fromPool->get_YUVImage();
nsRefPtr<gfxSharedImageSurface> surfY =
gfxSharedImageSurface::Open(yuv.Ydata());
nsRefPtr<gfxSharedImageSurface> surfU =
gfxSharedImageSurface::Open(yuv.Udata());
nsRefPtr<gfxSharedImageSurface> surfV =
gfxSharedImageSurface::Open(yuv.Vdata());
sharedYCbCr->mYSurface = surfY;
sharedYCbCr->mCbSurface = surfU;
sharedYCbCr->mCrSurface = surfV;
} else {
// ...else Allocate a shared image
nsRefPtr<gfxSharedImageSurface> surfY;
nsRefPtr<gfxSharedImageSurface> surfU;
nsRefPtr<gfxSharedImageSurface> surfV;
const PlanarYCbCrImage::Data* data = sharedYCbCr->GetData();
if (!AllocateSharedBuffer(this, data->mYSize, gfxASurface::CONTENT_ALPHA,
getter_AddRefs(surfY)) ||
!AllocateSharedBuffer(this, data->mCbCrSize, gfxASurface::CONTENT_ALPHA,
getter_AddRefs(surfU)) ||
!AllocateSharedBuffer(this, data->mCbCrSize, gfxASurface::CONTENT_ALPHA,
getter_AddRefs(surfV))) {
NS_RUNTIMEABORT("creating SharedImage failed!");
}
sharedYCbCr->mYSurface = surfY;
sharedYCbCr->mCbSurface = surfU;
sharedYCbCr->mCrSurface = surfV;
}
}
void ImageContainerChild::AllocateSharedBufferForImageSync(ReentrantMonitor* aBarrier,
bool* aDone,
Image* aImage)
{
AllocateSharedBufferForImageNow(aImage);
ReentrantMonitorAutoEnter autoMon(*aBarrier);
*aDone = true;
aBarrier->NotifyAll();
}
void ImageContainerChild::AllocateSharedBufferForImage(Image* aImage)
{
if (InImageBridgeChildThread()) {
AllocateSharedBufferForImageNow(aImage);
return;
}
bool done = false;
ReentrantMonitor barrier("ImageBridgeChild::AllocateSharedBufferForImage");
ReentrantMonitorAutoEnter autoMon(barrier);
GetMessageLoop()->PostTask(FROM_HERE,
NewRunnableMethod(this,
&ImageContainerChild::AllocateSharedBufferForImageSync,
&barrier,
&done,
aImage));
while (!done) {
barrier.Wait();
}
}
void ImageContainerChild::RecycleSharedImageNow(SharedImage* aImage)
{
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),"Must be in the ImageBridgeChild Thread.");
if (mStop || !AddSharedImageToPool(aImage)) {
DestroySharedImage(*aImage);
delete aImage;
}
}
void ImageContainerChild::RecycleSharedImage(SharedImage* aImage)
{
if (InImageBridgeChildThread()) {
RecycleSharedImageNow(aImage);
return;
}
GetMessageLoop()->PostTask(FROM_HERE,
NewRunnableMethod(this,
&ImageContainerChild::RecycleSharedImageNow,
aImage));
}
} // namespace
} // namespace

Просмотреть файл

@ -8,8 +8,12 @@
#include "mozilla/layers/PImageContainerChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "ImageTypes.h"
namespace mozilla {
class ReentrantMonitor;
namespace layers {
class ImageBridgeCopyAndSendTask;
@ -116,6 +120,31 @@ public:
*/
void SetIdleNow();
/**
* Returns an instance of Image that should be of a Shared* subclass of Image
* and that should not need to be copied in order to be sent to the compositor.
*/
already_AddRefed<Image> CreateImage(const ImageFormat *aFormats,
uint32_t aNumFormats);
/**
* Allocate shared buffers for the image.
*
* Can be called from any thread. If not called from the ImageBridge thread,
* it will synchronously proxy the call to the ImageBirdgeThread since shared
* memory can only be allocated in the IPDL thread.
*/
void AllocateSharedBufferForImage(Image* aImage);
/**
* Can be called from any thread.
* Deallocates or places aImage in the pool.
* If this method is not called from the ImageBridgeChild thread,
* a task is dispatched and the recycling is done asynchronously on
* the ImageBridgeChild thread.
*/
void RecycleSharedImage(SharedImage* aImage);
protected:
virtual PGrallocBufferChild*
AllocPGrallocBuffer(const gfxIntSize&, const gfxContentType&,
@ -188,6 +217,28 @@ protected:
* Called by ImageToSharedImage.
*/
SharedImage * CreateSharedImageFromData(Image* aImage);
/**
* Allocate shared buffers for the image.
* Must be called on the ImageBridgeChild thread.
*/
void AllocateSharedBufferForImageNow(Image* aImage);
/**
* Calls AllocateSharedBufferForImageNow and notify the barrier monitor.
* This is meant to be called by AllocateSharedBufferForImage if the
* call must be proxied synchronously to the ImageBridgeChild's thread.
*/
void AllocateSharedBufferForImageSync(ReentrantMonitor* aBarrier,
bool* aDone,
Image* aImage);
/**
* Either deallocate or place aImage in the shared image pool.
* Must be called on the ImageBridgeChild thread.
* aImage should not be used after this method is called.
*/
void RecycleSharedImageNow(SharedImage* aImage);
private:
uint64_t mImageContainerID;

Просмотреть файл

@ -73,6 +73,14 @@ struct YUVImage {
Shmem Udata;
Shmem Vdata;
nsIntRect picture;
/**
* An ID meant to tell if the shared buffers are being reference counted
* on the content side and making it possible to find the reference counted
* object on the content side if needed.
* If this ID is zero, it means the data is not reference counted and can
* be recycled right after being sent back to the content side.
*/
uintptr_t imageID;
};
union SharedImage {