diff --git a/gfx/layers/ImageLayers.h b/gfx/layers/ImageLayers.h index 51632743142d..e0507602798b 100644 --- a/gfx/layers/ImageLayers.h +++ b/gfx/layers/ImageLayers.h @@ -44,6 +44,7 @@ #include "nsThreadUtils.h" #include "nsCoreAnimationSupport.h" #include "mozilla/Monitor.h" +#include "mozilla/TimeStamp.h" namespace mozilla { namespace layers { @@ -132,7 +133,12 @@ class THEBES_API ImageContainer { THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer) public: - ImageContainer() : mMonitor("ImageContainer") {} + ImageContainer() : + mMonitor("ImageContainer"), + mPaintCount(0), + mPreviousImagePainted(PR_FALSE) + {} + virtual ~ImageContainer() {} /** @@ -163,6 +169,8 @@ public: * a reference. * Can be called on any thread. This method takes mMonitor when accessing * thread-shared state. + * Implementations must call CurrentImageChanged() while holding mMonitor. + * */ virtual already_AddRefed GetCurrentImage() = 0; @@ -224,6 +232,48 @@ public: */ virtual LayerManager::LayersBackend GetBackendType() = 0; + /** + * Returns the time at which the currently contained image was first + * painted. This is reset every time a new image is set as the current + * image. Note this may return a null timestamp if the current image + * has not yet been painted. Can be called from any thread. + */ + TimeStamp GetPaintTime() { + MonitorAutoEnter mon(mMonitor); + return mPaintTime; + } + + /** + * Returns the number of images which have been contained in this container + * and painted at least once. Can be called from any thread. + */ + PRUint32 GetPaintCount() { + MonitorAutoEnter mon(mMonitor); + return mPaintCount; + } + + /** + * Increments mPaintCount if this is the first time aPainted has been + * painted, and sets mPaintTime if the painted image is the current image. + * current image. Can be called from any thread. + */ + void NotifyPaintedImage(Image* aPainted) { + MonitorAutoEnter mon(mMonitor); + nsRefPtr current = GetCurrentImage(); + if (aPainted == current) { + if (mPaintTime.IsNull()) { + mPaintTime = TimeStamp::Now(); + mPaintCount++; + } + } else if (!mPreviousImagePainted) { + // While we were painting this image, the current image changed. We + // still must count it as painted, but can't set mPaintTime, since we're + // no longer the current image. + mPaintCount++; + mPreviousImagePainted = PR_TRUE; + } + } + protected: typedef mozilla::Monitor Monitor; LayerManager* mManager; @@ -232,7 +282,33 @@ protected: // other state which is shared between threads. Monitor mMonitor; - ImageContainer(LayerManager* aManager) : mManager(aManager), mMonitor("ImageContainer") {} + ImageContainer(LayerManager* aManager) : + mManager(aManager), + mMonitor("ImageContainer"), + mPaintCount(0), + mPreviousImagePainted(PR_FALSE) + {} + + // Performs necessary housekeeping to ensure the painted frame statistics + // are accurate. Must be called by SetCurrentImage() implementations with + // mMonitor held. + void CurrentImageChanged() { + mMonitor.AssertCurrentThreadIn(); + mPreviousImagePainted = !mPaintTime.IsNull(); + mPaintTime = TimeStamp(); + } + + // 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. + PRUint32 mPaintCount; + + // Time stamp at which the current image was first painted. It's up to the + // ImageContainer implementation to ensure accesses to this are threadsafe. + TimeStamp mPaintTime; + + // Denotes whether the previous image was painted. + PRPackedBool mPreviousImagePainted; }; /** diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp index b6be688ecfd4..49d956e15fe6 100644 --- a/gfx/layers/basic/BasicImages.cpp +++ b/gfx/layers/basic/BasicImages.cpp @@ -350,6 +350,7 @@ BasicImageContainer::SetCurrentImage(Image* aImage) { MonitorAutoEnter mon(mMonitor); mImage = aImage; + CurrentImageChanged(); } already_AddRefed diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 6f7f31936aa2..86bfa762e677 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -750,6 +750,8 @@ BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext, if (!mContainer) return nsnull; + nsRefPtr image = mContainer->GetCurrentImage(); + nsRefPtr surface = mContainer->GetCurrentAsSurface(&mSize); if (!surface) { return nsnull; @@ -769,7 +771,10 @@ BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext, PaintContext(pat, tileSrcRect ? GetVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)), tileSrcRect, - aOpacity, aContext); + aOpacity, aContext); + + GetContainer()->NotifyPaintedImage(image); + return pat.forget(); } diff --git a/gfx/layers/d3d10/ImageLayerD3D10.cpp b/gfx/layers/d3d10/ImageLayerD3D10.cpp index 508c29d1c6a3..fa971f61700a 100644 --- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -124,6 +124,7 @@ ImageContainerD3D10::SetCurrentImage(Image *aImage) MonitorAutoEnter mon(mMonitor); mActiveImage = aImage; + CurrentImageChanged(); } already_AddRefed @@ -351,6 +352,8 @@ ImageLayerD3D10::RenderLayer() technique->GetPassByIndex(0)->Apply(0); device()->Draw(4, 0); + + GetContainer()->NotifyPaintedImage(image); } PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice) diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index 5d51c32504c4..e80272ac4560 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -159,6 +159,7 @@ ImageContainerD3D9::SetCurrentImage(Image *aImage) MonitorAutoEnter mon(mMonitor); mActiveImage = aImage; + CurrentImageChanged(); } already_AddRefed @@ -372,6 +373,8 @@ ImageLayerD3D9::RenderLayer() device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); } } + + GetContainer()->NotifyPaintedImage(image); } PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9() diff --git a/gfx/layers/opengl/ImageLayerOGL.cpp b/gfx/layers/opengl/ImageLayerOGL.cpp index 8829f8d8b3f0..122a2dcc2a4f 100644 --- a/gfx/layers/opengl/ImageLayerOGL.cpp +++ b/gfx/layers/opengl/ImageLayerOGL.cpp @@ -235,6 +235,7 @@ ImageContainerOGL::SetCurrentImage(Image *aImage) oldImage = mActiveImage.forget(); mActiveImage = aImage; + CurrentImageChanged(); } // Make sure oldImage is released outside the lock, so it can take our @@ -499,6 +500,7 @@ ImageLayerOGL::RenderLayer(int, gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0); #endif } + GetContainer()->NotifyPaintedImage(image); } static void