Bug 696398. Add an api to analyse frame rate. r=ajuma
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 05 Jan 2012 17:40:35 -0500
changeset 86347 90e2471b7a422fedafb4e31ede0cc1b766a2cd6a
parent 86346 a48083197b68f2f5b97cda2f82449866bd96aa8e
child 86348 1111830907257ac74f8735ad1b8df50ef5a0b347
push idunknown
push userunknown
push dateunknown
reviewersajuma
bugs696398
milestone12.0a1
Bug 696398. Add an api to analyse frame rate. r=ajuma This adds a startFrameTimeRecording() and stopFrameTimeRecording() to DOMWindowUtils. stopFrameTimeRecording() returns an array of frame times in milliseconds.
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/d3d9/LayerManagerD3D9.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1614,16 +1614,65 @@ nsDOMWindowUtils::GetLayerManagerType(ns
   if (!mgr)
     return NS_ERROR_FAILURE;
 
   mgr->GetBackendName(aType);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::StartFrameTimeRecording()
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  LayerManager *mgr = widget->GetLayerManager();
+  if (!mgr)
+    return NS_ERROR_FAILURE;
+
+  mgr->StartFrameTimeRecording();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::StopFrameTimeRecording(PRUint32 *frameCount NS_OUTPARAM, float **frames NS_OUTPARAM)
+{
+  NS_ENSURE_ARG_POINTER(frameCount);
+  NS_ENSURE_ARG_POINTER(frames);
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  LayerManager *mgr = widget->GetLayerManager();
+  if (!mgr)
+    return NS_ERROR_FAILURE;
+
+  nsTArray<float> frameTimes = mgr->StopFrameTimeRecording();
+
+  *frames = nsnull;
+  *frameCount = frameTimes.Length();
+
+  if (*frameCount != 0) {
+    *frames = (float*)nsMemory::Alloc(*frameCount * sizeof(float*));
+    if (!*frames)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    /* copy over the frame times into the array we just allocated */
+    for (PRUint32 i = 0; i < *frameCount; i++) {
+      (*frames)[i] = frameTimes[i];
+    }
+  }
+
+  return NS_OK;
+}
+
 static bool
 ComputeAnimationValue(nsCSSProperty aProperty,
                       Element* aElement,
                       const nsAString& aInput,
                       nsStyleAnimation::Value& aOutput)
 {
 
   if (!nsStyleAnimation::ComputeValue(aProperty, aElement, aInput,
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -64,17 +64,17 @@ interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 
-[scriptable, uuid(c1fa9c82-acf2-4b27-8ca7-7d1864e606af)]
+[scriptable, uuid(15fcceb0-37ea-11e1-b86c-0800200c9a66)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -833,16 +833,19 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * What type of layer manager the widget associated with this window is
    * using. "Basic" is unaccelerated; other types are accelerated. Throws an
    * error if there is no widget associated with this window.
    */
   readonly attribute AString layerManagerType;
 
+  void startFrameTimeRecording();
+  void stopFrameTimeRecording([optional] out unsigned long frameCount,
+                              [retval, array, size_is(frameCount)] out float frameTime);
   /**
    * The DPI of the display
    */
   readonly attribute float displayDPI;
 
   /**
    * Return the outer window with the given ID, if any.  Can return null.
    */
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -556,16 +556,41 @@ PlanarYCbCrImage::CopyData(Data& aDest, 
            aData.mCrChannel + i * aData.mCbCrStride,
            aDest.mCbCrStride);
   }
 
   aDestSize = aData.mPicSize;
   return buffer;
 }
                          
+void
+LayerManager::StartFrameTimeRecording()
+{
+  mLastFrameTime = TimeStamp::Now();
+}
+
+void
+LayerManager::PostPresent()
+{
+  if (!mLastFrameTime.IsNull()) {
+    TimeStamp now = TimeStamp::Now();
+    mFrameTimes.AppendElement((now - mLastFrameTime).ToMilliseconds());
+    mLastFrameTime = now;
+  }
+}
+
+nsTArray<float>
+LayerManager::StopFrameTimeRecording()
+{
+  mLastFrameTime = TimeStamp();
+  nsTArray<float> result = mFrameTimes;
+  mFrameTimes.Clear();
+  return result;
+}
+
 
 
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer);
 
 void
 Layer::Dump(FILE* aFile, const char* aPrefix)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -46,16 +46,17 @@
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h"
 
 #if defined(DEBUG) || defined(PR_LOGGING)
 #  include <stdio.h>            // FILE
 #  include "prlog.h"
 #  define MOZ_LAYERS_HAVE_LOG
 #  define MOZ_LAYERS_LOG(_args)                             \
   PR_LOG(LayerManager::GetLog(), PR_LOG_DEBUG, _args)
 #else
@@ -507,16 +508,21 @@ public:
    */
   void Log(const char* aPrefix="");
   /**
    * Log information about just this layer manager itself to the NSPR
    * log (if enabled for "Layers").
    */
   void LogSelf(const char* aPrefix="");
 
+  void StartFrameTimeRecording();
+  nsTArray<float> StopFrameTimeRecording();
+
+  void PostPresent();
+
   static bool IsLogEnabled();
   static PRLogModuleInfo* GetLog() { return sLog; }
 
   bool IsCompositingCheap(LayerManager::LayersBackend aBackend)
   { return LAYERS_BASIC != aBackend; }
 
   virtual bool IsCompositingCheap() { return true; }
 
@@ -527,16 +533,19 @@ protected:
   bool mSnapEffectiveTransforms;
 
   // Print interesting information about this into aTo.  Internally
   // used to implement Dump*() and Log*().
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   static void InitLog();
   static PRLogModuleInfo* sLog;
+private:
+  TimeStamp mLastFrameTime;
+  nsTArray<float> mFrameTimes;
 };
 
 class ThebesLayer;
 
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -773,16 +773,17 @@ LayerManagerD3D10::Render()
     ShadowLayerForwarder::EndTransaction(&replies);
     // We expect only 1 reply, but might get none if the parent
     // process crashed
 
     swap(mBackBuffer, mRemoteFrontBuffer);
   } else {
     mSwapChain->Present(0, 0);
   }
+  LayerManager::PostPresent();
 }
 
 void
 LayerManagerD3D10::PaintToTarget()
 {
   nsRefPtr<ID3D10Texture2D> backBuf;
   
   mSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)backBuf.StartAssignment());
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -349,16 +349,17 @@ LayerManagerD3D9::Render()
   device()->EndScene();
 
   if (!mTarget) {
     const nsIntRect *r;
     for (nsIntRegionRectIterator iter(mClippingRegion);
          (r = iter.Next()) != nsnull;) {
       mSwapChain->Present(*r);
     }
+    LayerManager::PostPresent();
   } else {
     PaintToTarget();
   }
 }
 
 void
 LayerManagerD3D9::SetupPipeline()
 {
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -814,16 +814,17 @@ LayerManagerOGL::Render()
   }
 
   if (sDrawFPS) {
     mFPS.DrawFPS(mGLContext, GetCopy2DProgram());
   }
 
   if (mGLContext->IsDoubleBuffered()) {
     mGLContext->SwapBuffers();
+    LayerManager::PostPresent();
     mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     return;
   }
 
   mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);