Bug 565875. Part 3: Recycle temporary buffer used by PlanarYCbCrImageOGL to avoid reallocation/recommit costs (especially on Mac). r=bas

This commit is contained in:
Robert O'Callahan 2010-05-17 11:08:41 +12:00
parent a36e98ca1e
commit e3d9ccde5f
2 changed files with 101 additions and 11 deletions

View File

@ -116,8 +116,40 @@ GLTexture::Release()
mTexture = 0;
}
RecycleBin::RecycleBin()
: mLock("mozilla.layers.RecycleBin.mLock")
{
}
void
RecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize)
{
MutexAutoLock lock(mLock);
if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
mRecycledBuffers.Clear();
}
mRecycledBufferSize = aSize;
mRecycledBuffers.AppendElement(aBuffer);
}
PRUint8*
RecycleBin::TakeBuffer(PRUint32 aSize)
{
MutexAutoLock lock(mLock);
if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
return new PRUint8[aSize];
PRUint32 last = mRecycledBuffers.Length() - 1;
PRUint8* result = mRecycledBuffers[last].forget();
mRecycledBuffers.RemoveElementAt(last);
return result;
}
ImageContainerOGL::ImageContainerOGL(LayerManagerOGL *aManager)
: ImageContainer(aManager)
, mRecycleBin(new RecycleBin())
, mActiveImageLock("mozilla.layers.ImageContainerOGL.mActiveImageLock")
{
}
@ -131,7 +163,7 @@ ImageContainerOGL::CreateImage(const Image::Format *aFormats,
}
nsRefPtr<Image> img;
if (aFormats[0] == Image::PLANAR_YCBCR) {
img = new PlanarYCbCrImageOGL();
img = new PlanarYCbCrImageOGL(mRecycleBin);
} else if (aFormats[0] == Image::CAIRO_SURFACE) {
img = new CairoImageOGL(static_cast<LayerManagerOGL*>(mManager));
}
@ -141,9 +173,17 @@ ImageContainerOGL::CreateImage(const Image::Format *aFormats,
void
ImageContainerOGL::SetCurrentImage(Image *aImage)
{
MutexAutoLock lock(mActiveImageLock);
nsRefPtr<Image> oldImage;
mActiveImage = aImage;
{
MutexAutoLock lock(mActiveImageLock);
oldImage = mActiveImage.forget();
mActiveImage = aImage;
}
// Make sure oldImage is released outside the lock, so it can take our
// lock in RecycleBuffer
}
already_AddRefed<Image>
@ -278,11 +318,18 @@ ImageLayerOGL::RenderLayer(int)
}
}
PlanarYCbCrImageOGL::PlanarYCbCrImageOGL()
: PlanarYCbCrImage(nsnull), mHasData(PR_FALSE)
PlanarYCbCrImageOGL::PlanarYCbCrImageOGL(RecycleBin *aRecycleBin)
: PlanarYCbCrImage(nsnull), mRecycleBin(aRecycleBin), mHasData(PR_FALSE)
{
}
PlanarYCbCrImageOGL::~PlanarYCbCrImageOGL()
{
if (mBuffer) {
mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
}
}
void
PlanarYCbCrImageOGL::SetData(const PlanarYCbCrImage::Data &aData)
{
@ -313,8 +360,13 @@ PlanarYCbCrImageOGL::SetData(const PlanarYCbCrImage::Data &aData)
mData.mCbCrSize.height = aData.mPicSize.height >> height_shift;
mData.mYSize = aData.mPicSize;
mData.mYStride = mData.mYSize.width;
mBuffer = new PRUint8[mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height];
mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height;
mBuffer = mRecycleBin->TakeBuffer(mBufferSize);
if (!mBuffer)
return;
mData.mYChannel = mBuffer;
mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
@ -441,8 +493,10 @@ PlanarYCbCrImageOGL::AllocateTextures(LayerManagerOGL *aManager)
// Reset alignment to default
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
// Free main-memory buffer now that we've got the data in our textures
mBuffer = nsnull;
// Recycle main-memory buffer now that we've got the data in our textures
if (mBuffer) {
mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
}
}
CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager) : CairoImage(nsnull)

View File

@ -85,6 +85,36 @@ private:
GLuint mTexture;
};
/**
* A RecycleBin is owned by an ImageContainerOGL. We store buffers
* and textures in it that we want to recycle from one image to the next.
* It's a separate object from ImageContainerOGL because images need to store
* a strong ref to their RecycleBin and we must avoid creating a
* reference loop between an ImageContainerOGL and its active image.
*/
class RecycleBin {
THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin)
public:
RecycleBin();
void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize);
// Returns a recycled buffer of the right size, or allocates a new buffer.
PRUint8* TakeBuffer(PRUint32 aSize);
private:
typedef mozilla::Mutex Mutex;
// This protects mRecycledBuffers and mRecycledBufferSize
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;
};
class THEBES_API ImageContainerOGL : public ImageContainer
{
public:
@ -105,9 +135,12 @@ public:
private:
typedef mozilla::Mutex Mutex;
nsRefPtr<Image> mActiveImage;
nsRefPtr<RecycleBin> mRecycleBin;
// This protects mActiveImage
Mutex mActiveImageLock;
nsRefPtr<Image> mActiveImage;
};
class THEBES_API ImageLayerOGL : public ImageLayer,
@ -134,7 +167,8 @@ class THEBES_API PlanarYCbCrImageOGL : public PlanarYCbCrImage
typedef mozilla::gl::GLContext GLContext;
public:
PlanarYCbCrImageOGL();
PlanarYCbCrImageOGL(RecycleBin *aRecycleBin);
~PlanarYCbCrImageOGL();
virtual void SetData(const Data &aData);
@ -151,6 +185,8 @@ public:
}
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferSize;
nsRefPtr<RecycleBin> mRecycleBin;
GLTexture mTextures[3];
Data mData;
gfxIntSize mSize;