Bug 715785: Make ImageContainers independent of LayerManagers. r=roc

This commit is contained in:
Bas Schouten
2012-02-01 03:18:30 +01:00
parent 4762ea78a3
commit 33c8caf378
41 changed files with 1268 additions and 2097 deletions

View File

@@ -46,8 +46,12 @@
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Mutex.h"
#include "gfxPlatform.h"
class nsIOSurface;
#ifdef XP_MACOSX
#include "nsIOSurface.h"
#endif
namespace mozilla {
namespace layers {
@@ -60,6 +64,14 @@ enum StereoMode {
STEREO_MODE_TOP_BOTTOM
};
struct ImageBackendData
{
virtual ~ImageBackendData() {}
protected:
ImageBackendData() {}
};
/**
* A class representing a buffer of pixel data. The data can be in one
* of various formats including YCbCr.
@@ -104,8 +116,7 @@ public:
CAIRO_SURFACE,
/**
* The MAC_IO_SURFACE format creates a MacIOSurfaceImage. This
* is only supported on Mac with OpenGL layers.
* The MAC_IO_SURFACE format creates a MacIOSurfaceImage.
*
* It wraps an IOSurface object and binds it directly to a GL texture.
*/
@@ -115,16 +126,106 @@ public:
Format GetFormat() { return mFormat; }
void* GetImplData() { return mImplData; }
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
virtual gfxIntSize GetSize() = 0;
ImageBackendData* GetBackendData(LayerManager::LayersBackend aBackend)
{ return mBackendData[aBackend]; }
void SetBackendData(LayerManager::LayersBackend aBackend, ImageBackendData* aData)
{ mBackendData[aBackend] = aData; }
protected:
Image(void* aImplData, Format aFormat) :
mImplData(aImplData),
mFormat(aFormat)
{}
nsAutoPtr<ImageBackendData> mBackendData[LayerManager::LAYERS_LAST];
void* mImplData;
Format mFormat;
};
/**
* A RecycleBin is owned by an ImageContainer. We store buffers in it that we
* want to recycle from one image to the next.It's a separate object from
* ImageContainer because images need to store a strong ref to their RecycleBin
* and we must avoid creating a reference loop between an ImageContainer and
* its active image.
*/
class BufferRecycleBin {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin)
typedef mozilla::gl::GLContext GLContext;
public:
BufferRecycleBin();
void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize);
// Returns a recycled buffer of the right size, or allocates a new buffer.
PRUint8* GetBuffer(PRUint32 aSize);
private:
typedef mozilla::Mutex Mutex;
// This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
// and mRecycledTextureSizes
Mutex mLock;
// We should probably do something to prune this list on a timer so we don't
// eat excess memory while video is paused...
nsTArray<nsAutoArrayPtr<PRUint8> > mRecycledBuffers;
// This is only valid if mRecycledBuffers is non-empty
PRUint32 mRecycledBufferSize;
};
/**
* Returns true if aFormat is in the given format array.
*/
static inline bool
FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
Image::Format aFormat)
{
for (PRUint32 i = 0; i < aNumFormats; ++i) {
if (aFormats[i] == aFormat) {
return true;
}
}
return false;
}
/**
* A class that manages Image creation for a LayerManager. The only reason
* we need a separate class here is that LayerMananers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to create images from any thread, to facilitate video playback
* without involving the main thread, for example.
* Different layer managers can implement child classes of this making it
* possible to create layer manager specific images.
* This class is not meant to be used directly but rather can be set on an
* image container. This is usually done by the layer system internally and
* not explicitly by users. For PlanarYCbCr or Cairo images the default
* implementation will creates images whose data lives in system memory, for
* MacIOSurfaces the default implementation will be a simple nsIOSurface
* wrapper.
*/
class THEBES_API ImageFactory
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
protected:
friend class ImageContainer;
ImageFactory() {}
virtual ~ImageFactory() {}
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats,
const gfxIntSize &aScaleHint,
BufferRecycleBin *aRecycleBin);
};
/**
* A class that manages Images for an ImageLayer. The only reason
* we need a separate class here is that ImageLayers aren't threadsafe
@@ -139,10 +240,12 @@ public:
ImageContainer() :
mReentrantMonitor("ImageContainer.mReentrantMonitor"),
mPaintCount(0),
mPreviousImagePainted(false)
mPreviousImagePainted(false),
mImageFactory(new ImageFactory()),
mRecycleBin(new BufferRecycleBin())
{}
virtual ~ImageContainer() {}
~ImageContainer();
/**
* Create an Image in one of the given formats.
@@ -152,8 +255,8 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats) = 0;
already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats);
/**
* Set an Image as the current image to display. The Image must have
@@ -163,13 +266,7 @@ public:
*
* The Image data must not be modified after this method is called!
*/
virtual void SetCurrentImage(Image* aImage) = 0;
/**
* Ask any PlanarYCbCr images created by this container to delay
* YUV -> RGB conversion until draw time. See PlanarYCbCrImage::SetDelayedConversion.
*/
virtual void SetDelayedConversion(bool aDelayed) {}
void SetCurrentImage(Image* aImage);
/**
* Get the current Image.
@@ -181,7 +278,13 @@ public:
* Implementations must call CurrentImageChanged() while holding
* mReentrantMonitor.
*/
virtual already_AddRefed<Image> GetCurrentImage() = 0;
already_AddRefed<Image> GetCurrentImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<Image> retval = mActiveImage;
return retval.forget();
}
/**
* Get the current image as a gfxASurface. This is useful for fallback
@@ -198,32 +301,14 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult) = 0;
/**
* Returns the layer manager for this container. This can only
* be used on the main thread, since layer managers should only be
* accessed on the main thread.
*/
LayerManager* Manager()
{
NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread");
return mManager;
}
already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult);
/**
* Returns the size of the image in pixels.
* Can be called on any thread. This method takes mReentrantMonitor when accessing
* thread-shared state.
*/
virtual gfxIntSize GetCurrentSize() = 0;
/**
* Set a new layer manager for this image container. It must be
* either of the same type as the container's current layer manager,
* or null. TRUE is returned on success. Main thread only.
*/
virtual bool SetLayerManager(LayerManager *aManager) = 0;
gfxIntSize GetCurrentSize();
/**
* Sets a size that the image is expected to be rendered at.
@@ -232,14 +317,14 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { }
void SetScaleHint(const gfxIntSize& aScaleHint)
{ mScaleHint = aScaleHint; }
/**
* Get the layer manager type this image container was created with,
* presumably its users might want to do something special if types do not
* match. Can be called on any thread.
*/
virtual LayerManager::LayersBackend GetBackendType() = 0;
void SetImageFactory(ImageFactory *aFactory)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mImageFactory = aFactory ? aFactory : new ImageFactory();
}
/**
* Returns the time at which the currently contained image was first
@@ -285,19 +370,11 @@ public:
protected:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
LayerManager* mManager;
// ReentrantMonitor to protect thread safe access to the "current
// image", and any other state which is shared between threads.
ReentrantMonitor mReentrantMonitor;
ImageContainer(LayerManager* aManager) :
mManager(aManager),
mReentrantMonitor("ImageContainer.mReentrantMonitor"),
mPaintCount(0),
mPreviousImagePainted(false)
{}
// Performs necessary housekeeping to ensure the painted frame statistics
// are accurate. Must be called by SetCurrentImage() implementations with
// mReentrantMonitor held.
@@ -307,6 +384,8 @@ protected:
mPaintTime = TimeStamp();
}
nsRefPtr<Image> mActiveImage;
// Number of contained images that have been painted at least once. It's up
// to the ImageContainer implementation to ensure accesses to this are
// threadsafe.
@@ -318,6 +397,15 @@ protected:
// Denotes whether the previous image was painted.
bool mPreviousImagePainted;
// This is the image factory used by this container, layer managers using
// this container can set an alternative image factory that will be used to
// create images for this container.
nsRefPtr<ImageFactory> mImageFactory;
gfxIntSize mScaleHint;
nsRefPtr<BufferRecycleBin> mRecycleBin;
};
/**
@@ -332,8 +420,6 @@ public:
*/
void SetContainer(ImageContainer* aContainer)
{
NS_ASSERTION(!aContainer->Manager() || aContainer->Manager() == Manager(),
"ImageContainer must have the same manager as the ImageLayer");
mContainer = aContainer;
}
/**
@@ -422,13 +508,13 @@ public:
MAX_DIMENSION = 16384
};
~PlanarYCbCrImage();
/**
* This makes a copy of the data buffers.
* XXX Eventually we will change this to not make a copy of the data,
* Right now it doesn't matter because the BasicLayer implementation
* does YCbCr conversion here anyway.
* This makes a copy of the data buffers, in order to support functioning
* in all different layer managers.
*/
virtual void SetData(const Data& aData) = 0;
virtual void SetData(const Data& aData);
/**
* Ask this Image to not convert YUV to RGB during SetData, and make
@@ -440,19 +526,14 @@ public:
/**
* Grab the original YUV data. This is optional.
*/
virtual const Data* GetData() { return nsnull; }
virtual const Data* GetData() { return &mData; }
/**
* Make a copy of the YCbCr data.
* Make a copy of the YCbCr data into local storage.
*
* @param aDest Data object to store the plane data in.
* @param aDestSize Size of the Y plane that was copied.
* @param aDestBufferSize Number of bytes allocated for storage.
* @param aData Input image data.
* @return Raw data pointer for the planes or nsnull on failure.
*/
PRUint8 *CopyData(Data& aDest, gfxIntSize& aDestSize,
PRUint32& aDestBufferSize, const Data& aData);
void CopyData(const Data& aData);
/**
* Return a buffer to store image data in.
@@ -464,15 +545,31 @@ public:
/**
* Return the number of bytes of heap memory used to store this image.
*/
virtual PRUint32 GetDataSize() = 0;
virtual PRUint32 GetDataSize() { return mBufferSize; }
protected:
PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {}
already_AddRefed<gfxASurface> GetAsSurface();
virtual gfxIntSize GetSize() { return mSize; }
void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; }
// XXX - not easy to protect these sadly.
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferSize;
Data mData;
gfxIntSize mSize;
gfxImageFormat mOffscreenFormat;
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
nsRefPtr<BufferRecycleBin> mRecycleBin;
PlanarYCbCrImage(BufferRecycleBin *aRecycleBin);
};
/**
* Currently, the data in a CairoImage surface is treated as being in the
* device output color space.
* device output color space. This class is very simple as all backends
* have to know about how to deal with drawing a cairo image.
*/
class THEBES_API CairoImage : public Image {
public:
@@ -486,10 +583,26 @@ public:
* to the surface (which will eventually be released on the main thread).
* The surface must not be modified after this call!!!
*/
virtual void SetData(const Data& aData) = 0;
void SetData(const Data& aData)
{
mSurface = aData.mSurface;
mSize = aData.mSize;
}
protected:
CairoImage(void* aImplData) : Image(aImplData, CAIRO_SURFACE) {}
virtual already_AddRefed<gfxASurface> GetAsSurface()
{
NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
nsRefPtr<gfxASurface> surface = mSurface.get();
return surface.forget();
}
gfxIntSize GetSize() { return mSize; }
CairoImage() : Image(NULL, CAIRO_SURFACE) {}
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
gfxIntSize mSize;
};
#ifdef XP_MACOSX
@@ -499,12 +612,27 @@ public:
nsIOSurface* mIOSurface;
};
MacIOSurfaceImage()
: Image(NULL, MAC_IO_SURFACE)
, mSize(0, 0)
, mPluginInstanceOwner(NULL)
, mUpdateCallback(NULL)
, mDestroyCallback(NULL)
{}
virtual ~MacIOSurfaceImage()
{
if (mDestroyCallback) {
mDestroyCallback(mPluginInstanceOwner);
}
}
/**
* This can only be called on the main thread. It may add a reference
* to the surface (which will eventually be released on the main thread).
* The surface must not be modified after this call!!!
*/
virtual void SetData(const Data& aData) = 0;
virtual void SetData(const Data& aData);
/**
* Temporary hacks to force plugin drawing during an empty transaction.
@@ -512,12 +640,38 @@ public:
* when async plugin rendering is complete.
*/
typedef void (*UpdateSurfaceCallback)(ImageContainer* aContainer, void* aInstanceOwner);
virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner) = 0;
typedef void (*DestroyCallback)(void* aInstanceOwner);
virtual void SetDestroyCallback(DestroyCallback aCallback) = 0;
virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner)
{
mUpdateCallback = aCallback;
mPluginInstanceOwner = aInstanceOwner;
}
protected:
MacIOSurfaceImage(void* aImplData) : Image(aImplData, MAC_IO_SURFACE) {}
typedef void (*DestroyCallback)(void* aInstanceOwner);
virtual void SetDestroyCallback(DestroyCallback aCallback)
{
mDestroyCallback = aCallback;
}
virtual gfxIntSize GetSize()
{
return mSize;
}
nsIOSurface* GetIOSurface()
{
return mIOSurface;
}
void Update(ImageContainer* aContainer);
virtual already_AddRefed<gfxASurface> GetAsSurface();
private:
gfxIntSize mSize;
nsRefPtr<nsIOSurface> mIOSurface;
void* mPluginInstanceOwner;
UpdateSurfaceCallback mUpdateCallback;
DestroyCallback mDestroyCallback;
};
#endif