Bug 944866 - Part 2: Add layers.scroll-graph. r=gal
authorBenoit Girard <b56girard@gmail.com>
Tue, 03 Dec 2013 10:49:46 -0500
changeset 173292 361907c4a2ce95f67a6f618ecf7cf10a57cbc653
parent 173291 80ae75938371e3ca39f011c612710fda52b5da2c
child 173293 a9aaef3ab91f2f0abbcfb775e5553045b0808cc6
push idunknown
push userunknown
push dateunknown
reviewersgal
bugs944866
milestone28.0a1
Bug 944866 - Part 2: Add layers.scroll-graph. r=gal
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/ipc/CompositorParent.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
modules/libpref/src/init/all.js
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -27,16 +27,17 @@
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_RELEASE
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsAutoTArray
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
+#include <vector>
 
 namespace mozilla {
 namespace layers {
 
 // HasOpaqueAncestorLayer and ContainerRender are shared between RefLayer and ContainerLayer
 static bool
 HasOpaqueAncestorLayer(Layer* aLayer)
 {
@@ -74,16 +75,136 @@ GetOpaqueRect(Layer* aLayer)
   }
   const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
   if (clipRect) {
     result.IntersectRect(result, *clipRect);
   }
   return result;
 }
 
+struct LayerVelocityUserData : public LayerUserData {
+public:
+  LayerVelocityUserData() {
+    MOZ_COUNT_CTOR(LayerVelocityUserData);
+  }
+  ~LayerVelocityUserData() {
+    MOZ_COUNT_DTOR(LayerVelocityUserData);
+  }
+
+  struct VelocityData {
+    VelocityData(TimeStamp frameTime, int scrollX, int scrollY)
+      : mFrameTime(frameTime)
+      , mPoint(scrollX, scrollY)
+    {}
+
+    TimeStamp mFrameTime;
+    Point mPoint;
+  };
+  std::vector<VelocityData> mData;
+};
+
+static void DrawVelGraph(const nsIntRect& aClipRect,
+                         LayerManagerComposite* aManager,
+                         Layer* aLayer) {
+  static char sLayerVelocityUserDataKey;
+  Compositor* compositor = aManager->GetCompositor();
+  gfx::Rect clipRect(aClipRect.x, aClipRect.y,
+                     aClipRect.width, aClipRect.height);
+
+  TimeStamp now = TimeStamp::Now();
+
+  void* key = reinterpret_cast<void*>(&sLayerVelocityUserDataKey);
+  if (!aLayer->HasUserData(key)) {
+    aLayer->SetUserData(key, new LayerVelocityUserData());
+  }
+
+  LayerVelocityUserData* velocityData =
+    static_cast<LayerVelocityUserData*>(aLayer->GetUserData(key));
+
+  if (velocityData->mData.size() >= 1 &&
+    now > velocityData->mData[velocityData->mData.size() - 1].mFrameTime +
+      TimeDuration::FromMilliseconds(200)) {
+    // clear stale data
+    velocityData->mData.clear();
+  }
+
+  nsIntPoint scrollOffset =
+    aLayer->GetEffectiveVisibleRegion().GetBounds().TopLeft();
+  velocityData->mData.push_back(
+    LayerVelocityUserData::VelocityData(now, scrollOffset.x, scrollOffset.y));
+
+
+  // XXX: Uncomment these lines to enable ScrollGraph logging. This is
+  //      useful for HVGA phones or to output the data to accurate
+  //      graphing software.
+  //printf_stderr("ScrollGraph (%p): %i, %i\n",
+  //  aLayer, scrollOffset.x, scrollOffset.y);
+
+  // Keep a circular buffer of 100.
+  size_t circularBufferSize = 100;
+  if (velocityData->mData.size() > circularBufferSize) {
+    velocityData->mData.erase(velocityData->mData.begin());
+  }
+
+  if (velocityData->mData.size() == 1) {
+    return;
+  }
+
+  // Clear and disable the graph when it's flat
+  for (size_t i = 1; i < velocityData->mData.size(); i++) {
+    if (velocityData->mData[i - 1].mPoint != velocityData->mData[i].mPoint) {
+      break;
+    }
+    if (i == velocityData->mData.size() - 1) {
+      velocityData->mData.clear();
+      return;
+    }
+  }
+
+  if (aLayer->GetEffectiveVisibleRegion().GetBounds().width < 300 ||
+      aLayer->GetEffectiveVisibleRegion().GetBounds().height < 300) {
+    // Don't want a graph for smaller layers
+    return;
+  }
+
+  aManager->SetDebugOverlayWantsNextFrame(true);
+
+  gfx::Matrix4x4 transform;
+  ToMatrix4x4(aLayer->GetEffectiveTransform(), transform);
+  nsIntRect bounds = aLayer->GetEffectiveVisibleRegion().GetBounds();
+  gfx::Rect graphBounds = gfx::Rect(bounds.x, bounds.y,
+                                    bounds.width, bounds.height);
+  gfx::Rect graphRect = gfx::Rect(bounds.x, bounds.y, 200, 100);
+
+  float opacity = 1.0;
+  EffectChain effects;
+  effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0.2,0,0,1));
+  compositor->DrawQuad(graphRect,
+                       clipRect,
+                       effects,
+                       opacity,
+                       transform);
+
+  std::vector<gfx::Point> graph;
+  int yScaleFactor = 3;
+  for (int32_t i = (int32_t)velocityData->mData.size() - 2; i >= 0; i--) {
+    const Point& p1 = velocityData->mData[i+1].mPoint;
+    const Point& p2 = velocityData->mData[i].mPoint;
+    int vel = sqrt((p1.x - p2.x) * (p1.x - p2.x) +
+                   (p1.y - p2.y) * (p1.y - p2.y));
+    graph.push_back(
+      gfx::Point(bounds.x + graphRect.width / circularBufferSize * i,
+                 graphBounds.y + graphRect.height - vel/yScaleFactor));
+  }
+
+  compositor->DrawLines(graph, clipRect, gfx::Color(0,1,0,1),
+                        opacity, transform);
+
+}
+
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                 LayerManagerComposite* aManager,
                 const nsIntRect& aClipRect)
 {
   /**
    * Setup our temporary surface for rendering the contents of this container.
    */
@@ -195,16 +316,20 @@ ContainerRender(ContainerT* aContainer,
 
     if (layerToRender->HasLayerBeenComposited()) {
       // Composer2D will compose this layer so skip GPU composition
       // this time & reset composition flag for next composition phase
       layerToRender->SetLayerComposited(false);
     } else {
       layerToRender->RenderLayer(clipRect);
     }
+
+    if (gfxPlatform::GetPrefLayersScrollGraph()) {
+      DrawVelGraph(clipRect, aManager, layerToRender->GetLayer());
+    }
     // invariant: our GL context should be current here, I don't think we can
     // assert it though
   }
 
   if (needsSurface) {
     // Unbind the current surface and rebind the previous one.
 #ifdef MOZ_DUMP_PAINTING
     if (gfxUtils::sDumpPainting) {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -98,16 +98,17 @@ LayerManagerComposite::ClearCachedResour
 
 /**
  * LayerManagerComposite
  */
 LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
 : mCompositor(aCompositor)
 , mInTransaction(false)
 , mIsCompositorReady(false)
+, mDebugOverlayWantsNextFrame(false)
 {
   MOZ_ASSERT(aCompositor);
 }
 
 LayerManagerComposite::~LayerManagerComposite()
 {
   Destroy();
 }
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -236,16 +236,24 @@ public:
 
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
 
+  /**
+   * LayerManagerComposite provides sophisticated debug overlays
+   * that can request a next frame.
+   */
+  bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+  void SetDebugOverlayWantsNextFrame(bool aVal)
+  { mDebugOverlayWantsNextFrame = aVal; }
+
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   nsIntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite *RootLayer() const;
 
@@ -282,16 +290,17 @@ private:
   DrawThebesLayerCallback mThebesLayerCallback;
   void *mThebesLayerCallbackData;
   gfxMatrix mWorldMatrix;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   nsIntRegion mInvalidRegion;
   nsAutoPtr<LayerProperties> mClonedLayerTreeProperties;
+  bool mDebugOverlayWantsNextFrame;
 };
 
 /**
  * Composite layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Basic layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree, all rendering is
  * done by a Compositor (see Compositor.h). As such, composite layers are
  * platform-independent and can be used on any platform for which there is a
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -554,16 +554,17 @@ CompositorParent::Composite()
 void
 CompositorParent::CompositeInTransaction()
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
   PROFILER_LABEL("CompositorParent", "Composite");
   NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(),
                     "Composite can only be called on the compositor thread");
   mCurrentCompositeTask = nullptr;
+  mLayerManager->SetDebugOverlayWantsNextFrame(false);
 
   mLastCompose = TimeStamp::Now();
 
   if (!CanComposite()) {
     return;
   }
 
   AutoResolveRefLayers resolve(mCompositionManager);
@@ -590,16 +591,20 @@ CompositorParent::CompositeInTransaction
   static bool gDumpCompositorTree = false;
   if (gDumpCompositorTree) {
     printf_stderr("Painting --- compositing layer tree:\n");
     mLayerManager->Dump();
   }
 #endif
   mLayerManager->EndEmptyTransaction();
 
+  if (mLayerManager->DebugOverlayWantsNextFrame()) {
+    ScheduleComposition();
+  }
+
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   if (mExpectedComposeTime + TimeDuration::FromMilliseconds(15) < TimeStamp::Now()) {
     printf_stderr("Compositor: Composite took %i ms.\n",
                   15 + (int)(TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
   }
 #endif
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
 }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2032,16 +2032,17 @@ gfxPlatform::GetOrientationSyncMillis() 
 static bool sPrefLayersOffMainThreadCompositionEnabled = false;
 static bool sPrefLayersOffMainThreadCompositionTestingEnabled = false;
 static bool sPrefLayersOffMainThreadCompositionForceEnabled = false;
 static bool sPrefLayersAccelerationForceEnabled = false;
 static bool sPrefLayersAccelerationDisabled = false;
 static bool sPrefLayersPreferOpenGL = false;
 static bool sPrefLayersPreferD3D9 = false;
 static bool sPrefLayersDump = false;
+static bool sPrefLayersScrollGraph = false;
 static bool sLayersSupportsD3D9 = false;
 static int  sPrefLayoutFrameRate = -1;
 static bool sBufferRotationEnabled = false;
 static bool sComponentAlphaEnabled = true;
 static bool sPrefBrowserTabsRemote = false;
 
 static bool sLayersAccelerationPrefsInitialized = false;
 
@@ -2059,16 +2060,17 @@ InitLayersAccelerationPrefs()
     sPrefLayersOffMainThreadCompositionEnabled = Preferences::GetBool("layers.offmainthreadcomposition.enabled", false);
     sPrefLayersOffMainThreadCompositionTestingEnabled = Preferences::GetBool("layers.offmainthreadcomposition.testing.enabled", false);
     sPrefLayersOffMainThreadCompositionForceEnabled = Preferences::GetBool("layers.offmainthreadcomposition.force-enabled", false);
     sPrefLayersAccelerationForceEnabled = Preferences::GetBool("layers.acceleration.force-enabled", false);
     sPrefLayersAccelerationDisabled = Preferences::GetBool("layers.acceleration.disabled", false);
     sPrefLayersPreferOpenGL = Preferences::GetBool("layers.prefer-opengl", false);
     sPrefLayersPreferD3D9 = Preferences::GetBool("layers.prefer-d3d9", false);
     sPrefLayersDump = Preferences::GetBool("layers.dump", false);
+    sPrefLayersScrollGraph = Preferences::GetBool("layers.scroll-graph", false);
     sPrefLayoutFrameRate = Preferences::GetInt("layout.frame_rate", -1);
     sBufferRotationEnabled = Preferences::GetBool("layers.bufferrotation.enabled", true);
     sComponentAlphaEnabled = Preferences::GetBool("layers.componentalpha.enabled", true);
     sPrefBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false);
 
 #ifdef XP_WIN
     if (sPrefLayersAccelerationForceEnabled) {
       sLayersSupportsD3D9 = true;
@@ -2164,16 +2166,25 @@ gfxPlatform::GetPrefLayoutFrameRate()
 bool
 gfxPlatform::GetPrefLayersDump()
 {
   InitLayersAccelerationPrefs();
   return sPrefLayersDump;
 }
 
 bool
+gfxPlatform::GetPrefLayersScrollGraph()
+{
+  // this function is called from the compositor thread, so it is not
+  // safe to init the prefs etc. from here.
+  MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
+  return sPrefLayersScrollGraph;
+}
+
+bool
 gfxPlatform::BufferRotationEnabled()
 {
   MutexAutoLock autoLock(*gGfxPlatformPrefsLock);
 
   InitLayersAccelerationPrefs();
   return sBufferRotationEnabled;
 }
 
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -501,16 +501,17 @@ public:
     static bool GetPrefLayersOffMainThreadCompositionForceEnabled();
     static bool GetPrefLayersAccelerationForceEnabled();
     static bool GetPrefLayersAccelerationDisabled();
     static bool GetPrefLayersPreferOpenGL();
     static bool GetPrefLayersPreferD3D9();
     static bool CanUseDirect3D9();
     static int  GetPrefLayoutFrameRate();
     static bool GetPrefLayersDump();
+    static bool GetPrefLayersScrollGraph();
 
     static bool OffMainThreadCompositionRequired();
 
     /**
      * Is it possible to use buffer rotation
      */
     static bool BufferRotationEnabled();
     static void DisableBufferRotation();
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -4238,16 +4238,20 @@ pref("layers.acceleration.draw-fps", fal
 
 pref("layers.dump", false);
 pref("layers.draw-borders", false);
 pref("layers.draw-tile-borders", false);
 pref("layers.draw-bigimage-borders", false);
 pref("layers.frame-counter", false);
 // Max number of layers per container. See Overwrite in mobile prefs.
 pref("layers.max-active", -1);
+// When a layer is moving it will add a scroll graph to measure the smoothness
+// of the movement. NOTE: This pref triggers composites to refresh
+// the graph.
+pref("layers.scroll-graph", false);
 
 // Set the default values, and then override per-platform as needed
 pref("layers.offmainthreadcomposition.enabled", false);
 // Whether to use the deprecated texture architecture rather than the new one.
 pref("layers.use-deprecated-textures", true);
 // Asynchonous video compositing using the ImageBridge IPDL protocol.
 // requires off-main-thread compositing.
 pref("layers.async-video.enabled",false);