Bug 580531 - Record first paint time and number of images painted in ImageContainer. r=roc
authorChris Pearce <chris@pearce.org.nz>
Thu, 24 Mar 2011 11:28:57 +1300
changeset 63615 8b6d6b01cbdf181216f448e6465be44d2a26ad0d
parent 63614 48a74f63565e4649230add851d06a5bbc5122a60
child 63616 47ca9ea23fc3e407f9ff7feb43906ff56527ee82
push id19247
push usercpearce@mozilla.com
push dateWed, 23 Mar 2011 22:39:37 +0000
treeherdermozilla-central@4902d72f6072 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs580531
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 580531 - Record first paint time and number of images painted in ImageContainer. r=roc
gfx/layers/ImageLayers.h
gfx/layers/basic/BasicImages.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/d3d10/ImageLayerD3D10.cpp
gfx/layers/d3d9/ImageLayerD3D9.cpp
gfx/layers/opengl/ImageLayerOGL.cpp
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -39,16 +39,17 @@
 #define GFX_IMAGELAYER_H
 
 #include "Layers.h"
 
 #include "gfxPattern.h"
 #include "nsThreadUtils.h"
 #include "nsCoreAnimationSupport.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
 
 namespace mozilla {
 namespace layers {
 
 enum StereoMode {
   STEREO_MODE_MONO,
   STEREO_MODE_LEFT_RIGHT,
   STEREO_MODE_RIGHT_LEFT,
@@ -127,17 +128,22 @@ protected:
  * (because layers can only be used on the main thread) and we want to
  * be able to set the current Image from any thread, to facilitate
  * video playback without involving the main thread, for example.
  */
 class THEBES_API ImageContainer {
   THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
 
 public:
-  ImageContainer() : mMonitor("ImageContainer") {}
+  ImageContainer() :
+    mMonitor("ImageContainer"),
+    mPaintCount(0),
+    mPreviousImagePainted(PR_FALSE)
+  {}
+
   virtual ~ImageContainer() {}
 
   /**
    * Create an Image in one of the given formats.
    * Picks the "best" format from the list and creates an Image of that
    * format.
    * Returns null if this backend does not support any of the formats.
    * Can be called on any thread. This method takes mMonitor when accessing
@@ -158,16 +164,18 @@ public:
 
   /**
    * Get the current Image.
    * This has to add a reference since otherwise there are race conditions
    * where the current image is destroyed before the caller can add
    * 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<Image> GetCurrentImage() = 0;
 
   /**
    * Get the current image as a gfxASurface. This is useful for fallback
    * rendering.
    * This can only be called from the main thread, since cairo objects
    * can only be used from the main thread.
@@ -219,25 +227,93 @@ public:
 
   /**
    * 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;
 
+  /**
+   * 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<Image> 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;
 
   // Monitor to protect thread safe access to the "current image", and any
   // 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;
 };
 
 /**
  * A Layer which renders an Image.
  */
 class THEBES_API ImageLayer : public Layer {
 public:
   /**
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -345,16 +345,17 @@ BasicImageContainer::CreateImage(const I
   return image.forget();
 }
 
 void
 BasicImageContainer::SetCurrentImage(Image* aImage)
 {
   MonitorAutoEnter mon(mMonitor);
   mImage = aImage;
+  CurrentImageChanged();
 }
 
 already_AddRefed<Image>
 BasicImageContainer::GetCurrentImage()
 {
   MonitorAutoEnter mon(mMonitor);
   nsRefPtr<Image> image = mImage;
   return image.forget();
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -745,16 +745,18 @@ BasicImageLayer::Paint(gfxContext* aCont
 
 already_AddRefed<gfxPattern>
 BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext,
                                          float aOpacity)
 {
   if (!mContainer)
     return nsnull;
 
+  nsRefPtr<Image> image = mContainer->GetCurrentImage();
+
   nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&mSize);
   if (!surface) {
     return nsnull;
   }
 
   nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
   if (!pat) {
     return nsnull;
@@ -764,17 +766,20 @@ BasicImageLayer::GetAndPaintCurrentImage
 
   // The visible region can extend outside the image.  If we're not
   // tiling, we don't want to draw into that area, so just draw within
   // the image bounds.
   const nsIntRect* tileSrcRect = GetTileSourceRect();
   PaintContext(pat,
                tileSrcRect ? GetVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
                tileSrcRect,
-               aOpacity, aContext); 
+               aOpacity, aContext);
+
+  GetContainer()->NotifyPaintedImage(image);
+
   return pat.forget();
 }
 
 /*static*/ void
 BasicImageLayer::PaintContext(gfxPattern* aPattern,
                               const nsIntRegion& aVisible,
                               const nsIntRect* aTileSourceRect,
                               float aOpacity,
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp
@@ -119,16 +119,17 @@ ImageContainerD3D10::CreateImage(const I
 }
 
 void
 ImageContainerD3D10::SetCurrentImage(Image *aImage)
 {
   MonitorAutoEnter mon(mMonitor);
 
   mActiveImage = aImage;
+  CurrentImageChanged();
 }
 
 already_AddRefed<Image>
 ImageContainerD3D10::GetCurrentImage()
 {
   MonitorAutoEnter mon(mMonitor);
 
   nsRefPtr<Image> retval = mActiveImage;
@@ -346,16 +347,18 @@ ImageLayerD3D10::RenderLayer()
         (float)0,
         (float)yuvImage->mSize.width,
         (float)yuvImage->mSize.height)
       );
   }
 
   technique->GetPassByIndex(0)->Apply(0);
   device()->Draw(4, 0);
+
+  GetContainer()->NotifyPaintedImage(image);
 }
 
 PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice)
   : PlanarYCbCrImage(static_cast<ImageD3D10*>(this))
   , mDevice(aDevice)
   , mHasData(PR_FALSE)
 {
 }
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp
@@ -154,16 +154,17 @@ ImageContainerD3D9::CreateImage(const Im
 }
 
 void
 ImageContainerD3D9::SetCurrentImage(Image *aImage)
 {
   MonitorAutoEnter mon(mMonitor);
 
   mActiveImage = aImage;
+  CurrentImageChanged();
 }
 
 already_AddRefed<Image>
 ImageContainerD3D9::GetCurrentImage()
 {
   MonitorAutoEnter mon(mMonitor);
 
   nsRefPtr<Image> retval = mActiveImage;
@@ -367,16 +368,18 @@ ImageLayerD3D9::RenderLayer()
     }
     device()->SetTexture(0, cairoImage->GetOrCreateTexture());
     device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
     if (mFilter == gfxPattern::FILTER_NEAREST) {
       device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
       device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
     }
   }
+
+  GetContainer()->NotifyPaintedImage(image);
 }
 
 PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9()
   : PlanarYCbCrImage(static_cast<ImageD3D9*>(this))
   , mHasData(PR_FALSE)
 {
 }
 
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -230,16 +230,17 @@ ImageContainerOGL::SetCurrentImage(Image
 {
   nsRefPtr<Image> oldImage;
 
   {
     MonitorAutoEnter mon(mMonitor);
 
     oldImage = mActiveImage.forget();
     mActiveImage = aImage;
+    CurrentImageChanged();
   }
 
   // Make sure oldImage is released outside the lock, so it can take our
   // lock in RecycleBuffer
 }
 
 already_AddRefed<Image>
 ImageContainerOGL::GetCurrentImage()
@@ -494,16 +495,17 @@ ImageLayerOGL::RenderLayer(int,
      program->SetLayerOpacity(GetEffectiveOpacity());
      program->SetRenderOffset(aOffset);
      program->SetTextureUnit(0);
     
      mOGLManager->BindAndDrawQuad(program);
      gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
 #endif
   }
+  GetContainer()->NotifyPaintedImage(image);
 }
 
 static void
 InitTexture(GLContext* aGL, GLuint aTexture, GLenum aFormat, const gfxIntSize& aSize)
 {
   aGL->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
   aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
   aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);