# HG changeset patch # Parent 9eae3880b132898a96a80d497cba2b9523e049a4 Bug 1077651 Measure frame uniformity by synthesizing native events. r=kats diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -64,28 +64,29 @@ #include #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK) #include #include #endif #include "Layers.h" -#include "mozilla/layers/ShadowLayers.h" #include "gfxPrefs.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/IDBFactoryBinding.h" #include "mozilla/dom/IDBMutableFileBinding.h" #include "mozilla/dom/indexedDB/IDBMutableFile.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/layers/FrameUniformityData.h" +#include "mozilla/layers/ShadowLayers.h" #include "nsPrintfCString.h" #include "nsViewportInfo.h" #include "nsIFormControl.h" #include "nsIScriptError.h" #include "nsIAppShell.h" #include "nsWidgetsCID.h" #include "FrameLayerBuilder.h" #include "nsDisplayList.h" @@ -3708,16 +3709,37 @@ nsDOMWindowUtils::SetChromeMargin(int32_ } } } return NS_OK; } NS_IMETHODIMP +nsDOMWindowUtils::GetFrameUniformityTestData(JSContext* aContext, + JS::MutableHandleValue aOutFrameUniformity) +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + nsIWidget* widget = GetWidget(); + if (!widget) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsRefPtr manager = widget->GetLayerManager(); + if (!manager) { + return NS_ERROR_NOT_AVAILABLE; + } + + FrameUniformityData outData; + manager->GetFrameUniformity(&outData); + outData.ToJS(aOutFrameUniformity, aContext); + return NS_OK; +} + +NS_IMETHODIMP nsDOMWindowUtils::XpconnectArgument(nsIDOMWindowUtils* aThis) { // Do nothing. return NS_OK; } NS_IMETHODIMP nsDOMWindowUtils::AskPermission(nsIContentPermissionRequest* aRequest) diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1807,16 +1807,24 @@ interface nsIDOMWindowUtils : nsISupport in int32_t aRight, in int32_t aBottom, in int32_t aLeft); /** * Enable some service workers testing features. */ attribute boolean serviceWorkersTestingEnabled; + + /** + * Returns a JSObject which contains a list of frame uniformities + * when the pref gfx.vsync.collect-scroll-data is enabled. + * Every result contains a layer address and a frame uniformity for that layer. + * A negative frame uniformity value indicates an invalid frame uniformity and an error has occured. + */ + [implicit_jscontext] jsval getFrameUniformityTestData(); }; [scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)] interface nsITranslationNodeList : nsISupports { readonly attribute unsigned long length; nsIDOMNode item(in unsigned long index); // A translation root is a block element, or an inline element diff --git a/dom/webidl/APZTestData.webidl b/dom/webidl/APZTestData.webidl --- a/dom/webidl/APZTestData.webidl +++ b/dom/webidl/APZTestData.webidl @@ -29,9 +29,19 @@ dictionary APZBucket { unsigned long sequenceNumber; sequence scrollFrames; }; // All the paints and repaint requests. This is the top-level data structure. dictionary APZTestData { sequence paints; sequence repaintRequests; -}; \ No newline at end of file +}; + +// A frame uniformity measurement for every scrollable layer +dictionary FrameUniformity { + unsigned long layerAddress; + float frameUniformity; +}; + +dictionary FrameUniformityResults { + sequence layerUniformities; +}; diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -87,16 +87,17 @@ class ReadbackLayer; class ReadbackProcessor; class RefLayer; class LayerComposite; class ShadowableLayer; class ShadowLayerForwarder; class LayerManagerComposite; class SpecificLayerAttributes; class Compositor; +class FrameUniformityData; namespace layerscope { class LayersPacket; } #define MOZ_LAYER_DECL_NAME(n, e) \ virtual const char* Name() const override { return n; } \ virtual LayerType GetType() const override { return e; } @@ -638,16 +639,17 @@ public: // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to // avoid loading the compositor! return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend; } virtual bool IsCompositingCheap() { return true; } bool IsInTransaction() const { return mInTransaction; } + virtual void GetFrameUniformity(FrameUniformityData* aOutData) { } virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { return true; } virtual void RunOverfillCallback(const uint32_t aOverfill) { } virtual void SetRegionToClear(const nsIntRegion& aRegion) { mRegionToClear = aRegion; } diff --git a/gfx/layers/apz/test/chrome.ini b/gfx/layers/apz/test/chrome.ini new file mode 100644 --- /dev/null +++ b/gfx/layers/apz/test/chrome.ini @@ -0,0 +1,9 @@ +[DEFAULT] +support-files = + apz_test_native_event_utils.js +tags = apz-chrome + +[test_smoothness.html] +# hardware vsync only on win/mac +# e10s only since APZ is only enabled on e10s +skip-if = debug || (os != 'mac' && os != 'win') || !e10s diff --git a/gfx/layers/apz/test/test_smoothness.html b/gfx/layers/apz/test/test_smoothness.html new file mode 100644 --- /dev/null +++ b/gfx/layers/apz/test/test_smoothness.html @@ -0,0 +1,83 @@ + + + Test Frame Uniformity While Scrolling + + + + + + + + + +
+
+ + diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -9,16 +9,17 @@ #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/Hal.h" #include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation #include "mozilla/dom/TabChild.h" // for TabChild #include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration #include "mozilla/layers/CompositableClient.h" #include "mozilla/layers/CompositorChild.h" // for CompositorChild #include "mozilla/layers/ContentClient.h" +#include "mozilla/layers/FrameUniformityData.h" #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/LayersMessages.h" // for EditReply, etc #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor #include "mozilla/layers/PLayerChild.h" // for PLayerChild #include "mozilla/layers/LayerTransactionChild.h" #include "mozilla/layers/TextureClientPool.h" // for TextureClientPool #include "ClientReadbackLayer.h" // for ClientReadbackLayer #include "nsAString.h" @@ -421,16 +422,30 @@ ClientLayerManager::RequestProperty(cons void ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber) { if (gfxPrefs::APZTestLoggingEnabled()) { mApzTestData.StartNewRepaintRequest(aSequenceNumber); } } +void +ClientLayerManager::GetFrameUniformity(FrameUniformityData* aOutData) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Frame Uniformity only supported in parent process"); + + if (HasShadowManager()) { + CompositorChild* child = GetRemoteRenderer(); + child->SendGetFrameUniformity(aOutData); + return; + } + + return LayerManager::GetFrameUniformity(aOutData); +} + bool ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { MOZ_ASSERT(aCallback != nullptr); MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now"); if (HasShadowManager()) { CompositorChild* child = GetRemoteRenderer(); diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h --- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -29,16 +29,17 @@ namespace mozilla { namespace layers { class ClientPaintedLayer; class CompositorChild; class ImageLayer; class PLayerChild; +class FrameUniformityData; class TextureClientPool; class ClientLayerManager final : public LayerManager { typedef nsTArray > LayerRefArray; public: explicit ClientLayerManager(nsIWidget* aWidget); @@ -195,16 +196,17 @@ public: void SetNeedsComposite(bool aNeedsComposite) { mNeedsComposite = aNeedsComposite; } bool NeedsComposite() const { return mNeedsComposite; } virtual void Composite() override; + virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override; virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override; virtual void RunOverfillCallback(const uint32_t aOverfill) override; virtual void DidComposite(uint64_t aTransactionId); virtual bool SupportsMixBlendModes(EnumSet& aMixBlendModes) override { return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes; diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -32,16 +32,17 @@ #include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc #include "nsTArrayForwardDeclare.h" // for InfallibleTArray #include "UnitTransforms.h" // for TransformTo #if defined(MOZ_WIDGET_ANDROID) # include # include "AndroidBridge.h" #endif #include "GeckoProfiler.h" +#include "FrameUniformityData.h" struct nsCSSValueSharedList; namespace mozilla { namespace layers { using namespace mozilla::gfx; @@ -89,16 +90,28 @@ WalkTheTree(Layer* aLayer, } } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { WalkTheTree(child, aReady, aTargetConfig); } } +AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager) + : mLayerManager(aManager) + , mIsFirstPaint(true) + , mLayersUpdated(false) + , mReadyForCompose(true) +{ +} + +AsyncCompositionManager::~AsyncCompositionManager() +{ +} + void AsyncCompositionManager::ResolveRefLayers() { if (!mLayerManager->GetRoot()) { return; } mReadyForCompose = true; @@ -540,16 +553,46 @@ SampleAPZAnimations(const LayerMetricsWr if (AsyncPanZoomController* apzc = aLayer.GetApzc()) { activeAnimations |= apzc->AdvanceAnimations(aSampleTime); } return activeAnimations; } +void +AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer) +{ + MOZ_ASSERT(gfxPrefs::CollectScrollTransforms()); + MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + + for (Layer* child = aLayer->GetFirstChild(); + child; child = child->GetNextSibling()) { + RecordShadowTransforms(child); + } + + for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + AsyncPanZoomController* apzc = aLayer->GetAsyncPanZoomController(i); + if (!apzc) { + continue; + } + gfx::Matrix4x4 shadowTransform = aLayer->AsLayerComposite()->GetShadowTransform(); + if (!shadowTransform.Is2D()) { + continue; + } + + Matrix transform = shadowTransform.As2D(); + if (transform.IsTranslation() && !shadowTransform.IsIdentity()) { + Point translation = transform.GetTranslation(); + mLayerTransformRecorder.RecordTransform(aLayer, translation); + return; + } + } +} + Matrix4x4 AdjustForClip(const Matrix4x4& asyncTransform, Layer* aLayer) { Matrix4x4 result = asyncTransform; // Container layers start at the origin, but they are clipped to where they // actually have content on the screen. The tree transform is meant to apply // to the clipped area. If the tree transform includes a scale component, @@ -1047,16 +1090,23 @@ AsyncCompositionManager::TransformScroll oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1); // Make sure fixed position layers don't move away from their anchor points // when we're asynchronously panning or zooming AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform, aLayer->GetLocalTransform(), fixedLayerMargins); } +void +AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData) +{ + MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + mLayerTransformRecorder.EndTest(aOutData); +} + bool AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame, TransformsToSkip aSkip) { PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree", js::ProfileEntry::Category::GRAPHICS); Layer* root = mLayerManager->GetRoot(); @@ -1099,16 +1149,19 @@ AsyncCompositionManager::TransformShadow } LayerComposite* rootComposite = root->AsLayerComposite(); gfx::Matrix4x4 trans = rootComposite->GetShadowTransform(); trans *= gfx::Matrix4x4::From2D(mWorldTransform); rootComposite->SetShadowTransform(trans); + if (gfxPrefs::CollectScrollTransforms()) { + RecordShadowTransforms(root); + } return wantNextFrame; } void AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect) diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h --- a/gfx/layers/composite/AsyncCompositionManager.h +++ b/gfx/layers/composite/AsyncCompositionManager.h @@ -9,16 +9,17 @@ #include "Units.h" // for ScreenPoint, etc #include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite #include "mozilla/Attributes.h" // for final, etc #include "mozilla/RefPtr.h" // for RefCounted #include "mozilla/TimeStamp.h" // for TimeStamp #include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation #include "mozilla/gfx/BasePoint.h" // for BasePoint #include "mozilla/gfx/Matrix.h" // for Matrix4x4 +#include "mozilla/layers/FrameUniformityData.h" // For FrameUniformityData #include "mozilla/layers/LayersMessages.h" // for TargetConfig #include "nsRefPtr.h" // for nsRefPtr #include "nsISupportsImpl.h" // for LayerManager::AddRef, etc namespace mozilla { namespace layers { class AsyncPanZoomController; @@ -65,29 +66,22 @@ struct ViewTransform { * (LayerManagerComposite) which deals with elements of composition which are * usually dealt with by dom or layout when main thread rendering, but which can * short circuit that stuff to directly affect layers as they are composited, * for example, off-main thread animation, async video, async pan/zoom. */ class AsyncCompositionManager final { friend class AutoResolveRefLayers; - ~AsyncCompositionManager() - { - } + ~AsyncCompositionManager(); + public: NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager) - explicit AsyncCompositionManager(LayerManagerComposite* aManager) - : mLayerManager(aManager) - , mIsFirstPaint(true) - , mLayersUpdated(false) - , mReadyForCompose(true) - { - } + explicit AsyncCompositionManager(LayerManagerComposite* aManager); /** * This forces the is-first-paint flag to true. This is intended to * be called by the widget code when it loses its viewport information * (or for whatever reason wants to refresh the viewport information). * The information refresh happens because the compositor will call * SetFirstPaintViewport on the next frame of composition. */ @@ -118,16 +112,20 @@ public: // True if the underlying layer tree is ready to be composited. bool ReadyForCompose() { return mReadyForCompose; } // Returns true if the next composition will be the first for a // particular document. bool IsFirstPaint() { return mIsFirstPaint; } + // GetFrameUniformity will return the frame uniformity for each layer attached to an APZ + // from the recorded data in RecordShadowTransform + void GetFrameUniformity(FrameUniformityData* aFrameUniformityData); + private: void TransformScrollableLayer(Layer* aLayer); // Return true if an AsyncPanZoomController content transform was // applied for |aLayer|. bool ApplyAsyncContentTransformToTree(Layer* aLayer); /** * Update the shadow transform for aLayer assuming that is a scrollbar, * so that it stays in sync with the content that is being scrolled by APZ. @@ -185,16 +183,19 @@ private: void ResolveRefLayers(); /** * Detaches all referents resolved by ResolveRefLayers. * Assumes that mLayerManager->GetRoot() and mTargetConfig have not changed * since ResolveRefLayers was called. */ void DetachRefLayers(); + // Records the shadow transforms for the tree of layers rooted at the given layer + void RecordShadowTransforms(Layer* aLayer); + TargetConfig mTargetConfig; CSSRect mContentRect; nsRefPtr mLayerManager; // When this flag is set, the next composition will be the first for a // particular document (i.e. the document displayed on the screen will change). // This happens when loading a new page or switching tabs. We notify the // front-end (e.g. Java on Android) about this so that it take the new page @@ -203,16 +204,17 @@ private: // This flag is set during a layers update, so that the first composition // after a layers update has it set. It is cleared after that first composition. bool mLayersUpdated; bool mReadyForCompose; gfx::Matrix mWorldTransform; + LayerTransformRecorder mLayerTransformRecorder; }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AsyncCompositionManager::TransformsToSkip) class MOZ_STACK_CLASS AutoResolveRefLayers { public: explicit AutoResolveRefLayers(AsyncCompositionManager* aManager) : mManager(aManager) { diff --git a/gfx/layers/composite/FrameUniformityData.cpp b/gfx/layers/composite/FrameUniformityData.cpp new file mode 100644 --- /dev/null +++ b/gfx/layers/composite/FrameUniformityData.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FrameUniformityData.h" + +#include + +#include "Units.h" +#include "gfxPoint.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/APZTestDataBinding.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; + +Point +LayerTransforms::GetAverage() +{ + MOZ_ASSERT(!mTransforms.IsEmpty()); + + Point current = mTransforms[0]; + Point average; + size_t length = mTransforms.Length(); + + for (size_t i = 1; i < length; i++) { + Point nextTransform = mTransforms[i]; + Point movement = nextTransform - current; + average += Point(std::fabs(movement.x), std::fabs(movement.y)); + current = nextTransform; + } + + average = average / (float) length; + return average; +} + +Point +LayerTransforms::GetStdDev() +{ + Point average = GetAverage(); + Point stdDev; + Point current = mTransforms[0]; + + for (size_t i = 1; i < mTransforms.Length(); i++) { + Point next = mTransforms[i]; + Point move = next - current; + move.x = fabs(move.x); + move.y = fabs(move.y); + + Point diff = move - average; + diff.x = diff.x * diff.x; + diff.y = diff.y * diff.y; + stdDev += diff; + + current = next; + } + + stdDev = stdDev / mTransforms.Length(); + stdDev.x = sqrt(stdDev.x); + stdDev.y = sqrt(stdDev.y); + return stdDev; +} + +LayerTransformRecorder::~LayerTransformRecorder() +{ + Reset(); +} + +void +LayerTransformRecorder::RecordTransform(Layer* aLayer, const Point& aTransform) +{ + LayerTransforms* layerTransforms = GetLayerTransforms((uintptr_t) aLayer); + layerTransforms->mTransforms.AppendElement(aTransform); +} + +void +LayerTransformRecorder::EndTest(FrameUniformityData* aOutData) +{ + std::map::iterator iter; + for (iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) { + uintptr_t layer = iter->first; + float uniformity = CalculateFrameUniformity(layer); + + std::pair result(layer, uniformity); + aOutData->mUniformities.insert(result); + } + + Reset(); +} + +LayerTransforms* +LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer) +{ + if (!mFrameTransforms.count(aLayer)) { + LayerTransforms* newTransform = new LayerTransforms(); + std::pair newLayer(aLayer, newTransform); + mFrameTransforms.insert(newLayer); + } + + return mFrameTransforms.find(aLayer)->second; +} + +void +LayerTransformRecorder::Reset() +{ + std::map::iterator iter; + for (iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) { + LayerTransforms* layerTransforms = iter->second; + delete layerTransforms; + } + + mFrameTransforms.clear(); +} + +float +LayerTransformRecorder::CalculateFrameUniformity(uintptr_t aLayer) +{ + LayerTransforms* layerTransform = GetLayerTransforms(aLayer); + float yUniformity = -1; + if (!layerTransform->mTransforms.IsEmpty()) { + Point stdDev = layerTransform->GetStdDev(); + yUniformity = stdDev.y; + } + return yUniformity; +} + +bool +FrameUniformityData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) +{ + dom::FrameUniformityResults results; + dom::Sequence& layers = results.mLayerUniformities.Construct(); + + std::map::iterator iter; + for (iter = mUniformities.begin(); iter != mUniformities.end(); ++iter) { + uintptr_t layerAddr = iter->first; + float uniformity = iter->second; + + layers.AppendElement(); + dom::FrameUniformity& entry = layers.LastElement(); + + entry.mLayerAddress.Construct() = layerAddr; + entry.mFrameUniformity.Construct() = uniformity; + } + + bool result = dom::ToJSValue(aContext, results, aOutValue); + return result; +} + +} +} diff --git a/gfx/layers/composite/FrameUniformityData.h b/gfx/layers/composite/FrameUniformityData.h new file mode 100644 --- /dev/null +++ b/gfx/layers/composite/FrameUniformityData.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_FrameUniformityData_h_ +#define mozilla_layers_FrameUniformityData_h_ + +#include "ipc/IPCMessageUtils.h" +#include "js/TypeDecls.h" +#include "nsRefPtr.h" + +namespace mozilla { +namespace layers { +class Layer; + +class FrameUniformityData { + friend struct IPC::ParamTraits; + +public: + bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext); + // Contains the calculated frame uniformities + std::map mUniformities; +}; + +struct LayerTransforms { + LayerTransforms() {} + + gfx::Point GetAverage(); + gfx::Point GetStdDev(); + + // 60 fps * 5 seconds worth of data + nsAutoTArray mTransforms; +}; + +class LayerTransformRecorder { +public: + LayerTransformRecorder() {} + ~LayerTransformRecorder(); + + void RecordTransform(Layer* aLayer, const gfx::Point& aTransform); + void Reset(); + void EndTest(FrameUniformityData* aOutData); + +private: + float CalculateFrameUniformity(uintptr_t aLayer); + LayerTransforms* GetLayerTransforms(uintptr_t aLayer); + std::map mFrameTransforms; +}; + +} // mozilla +} // layers + +namespace IPC { +template<> +struct ParamTraits +{ + typedef mozilla::layers::FrameUniformityData paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mUniformities); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ParamTraitsStd>::Read(aMsg, aIter, &aResult->mUniformities); + } +}; + +}// ipc + +#endif // mozilla_layers_FrameUniformityData_h_ diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -32,16 +32,17 @@ #include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager #include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager #include "mozilla/layers/AsyncCompositionManager.h" #include "mozilla/layers/BasicCompositor.h" // for BasicCompositor #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/CompositorLRU.h" // for CompositorLRU #include "mozilla/layers/CompositorOGL.h" // for CompositorOGL #include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/FrameUniformityData.h" #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/PLayerTransactionParent.h" #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager #include "mozilla/mozalloc.h" // for operator new, etc #include "mozilla/Telemetry.h" #ifdef MOZ_WIDGET_GTK #include "basic/X11BasicCompositor.h" // for X11BasicCompositor @@ -1314,16 +1315,23 @@ CompositorParent::ApplyAsyncProperties(L CancelCurrentCompositeTask(); // Pretend we composited in case someone is waiting for this event. DidComposite(); } } } bool +CompositorParent::RecvGetFrameUniformity(FrameUniformityData* aOutData) +{ + mCompositionManager->GetFrameUniformity(aOutData); + return true; +} + +bool CompositorParent::RecvRequestOverfill() { uint32_t overfillRatio = mCompositor->GetFillRatio(); unused << SendOverfill(overfillRatio); return true; } void @@ -1700,16 +1708,23 @@ public: virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray* intervals) override { return true; } virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override { *aWidth = gfxPlatform::GetPlatform()->GetTileWidth(); *aHeight = gfxPlatform::GetPlatform()->GetTileHeight(); return true; } + virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override + { + // Don't support calculating frame uniformity on the child process and + // this is just a stub for now. + MOZ_ASSERT(false); + return true; + } /** * Tells this CompositorParent to send a message when the compositor has received the transaction. */ virtual bool RecvRequestNotifyAfterRemotePaint() override; virtual PLayerTransactionParent* AllocPLayerTransactionParent(const nsTArray& aBackendHints, diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -224,16 +224,17 @@ public: int aSurfaceWidth = -1, int aSurfaceHeight = -1); // IToplevelProtocol::CloneToplevel() virtual IToplevelProtocol* CloneToplevel(const InfallibleTArray& aFds, base::ProcessHandle aPeerProcess, mozilla::ipc::ProtocolCloneContext* aCtx) override; + virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override; virtual bool RecvRequestOverfill() override; virtual bool RecvWillStop() override; virtual bool RecvStop() override; virtual bool RecvPause() override; virtual bool RecvResume() override; virtual bool RecvNotifyHidden(const uint64_t& id) override { return true; } virtual bool RecvNotifyVisible(const uint64_t& id) override { return true; } virtual bool RecvNotifyChildCreated(const uint64_t& child) override; diff --git a/gfx/layers/ipc/PCompositor.ipdl b/gfx/layers/ipc/PCompositor.ipdl --- a/gfx/layers/ipc/PCompositor.ipdl +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -14,16 +14,17 @@ include "nsRegion.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h"; using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h"; namespace mozilla { namespace layers { /** * The PCompositor protocol is used to manage communication between * the main thread and the compositor thread context. It's primary @@ -75,16 +76,19 @@ child: * side. */ async ClearCachedResources(uint64_t id); parent: // Child sends the parent a request for fill ratio numbers. async RequestOverfill(); + // Child requests frame uniformity measurements + sync GetFrameUniformity() returns (FrameUniformityData data); + // The child is about to be destroyed, so perform any necessary cleanup. sync WillStop(); // Clean up in preparation for own destruction. sync Stop(); // Pause/resume the compositor. These are intended to be used on mobile, when // the compositor needs to pause/resume in lockstep with the application. diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -119,16 +119,17 @@ EXPORTS.mozilla.layers += [ 'client/TextureClientPool.h', 'client/TextureClientRecycleAllocator.h', 'client/TiledContentClient.h', 'composite/AsyncCompositionManager.h', 'composite/CanvasLayerComposite.h', 'composite/ColorLayerComposite.h', 'composite/ContainerLayerComposite.h', 'composite/ContentHost.h', + 'composite/FrameUniformityData.h', 'composite/ImageHost.h', 'composite/ImageLayerComposite.h', 'composite/LayerManagerComposite.h', 'composite/PaintedLayerComposite.h', 'composite/TextureHost.h', 'Compositor.h', 'CompositorTypes.h', 'D3D11ShareHandleImage.h', @@ -270,16 +271,17 @@ UNIFIED_SOURCES += [ 'client/TiledContentClient.cpp', 'composite/AsyncCompositionManager.cpp', 'composite/CanvasLayerComposite.cpp', 'composite/ColorLayerComposite.cpp', 'composite/CompositableHost.cpp', 'composite/ContainerLayerComposite.cpp', 'composite/ContentHost.cpp', 'composite/FPSCounter.cpp', + 'composite/FrameUniformityData.cpp', 'composite/ImageHost.cpp', 'composite/ImageLayerComposite.cpp', 'composite/LayerManagerComposite.cpp', 'composite/PaintedLayerComposite.cpp', 'composite/TextRenderer.cpp', 'composite/TextureHost.cpp', 'composite/TiledContentHost.cpp', 'Compositor.cpp', @@ -384,11 +386,12 @@ CXXFLAGS += [ 'frameworks/base/include/media/stagefright', 'frameworks/base/include/media/stagefright/openmax', 'frameworks/av/include/media/stagefright', 'frameworks/native/include/media/openmax', ] ] MOCHITEST_MANIFESTS += ['apz/test/mochitest.ini'] +MOCHITEST_CHROME_MANIFESTS += ['apz/test/chrome.ini'] CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -235,16 +235,17 @@ private: DECL_GFX_PREF(Once, "gfx.touch.resample", TouchResampling, bool, false); // These times should be in milliseconds DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold", TouchResampleVsyncDelayThreshold, int32_t, 20); DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict", TouchResampleMaxPredict, int32_t, 8); DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17); DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust", TouchVsyncSampleAdjust, int32_t, 5); + DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms", CollectScrollTransforms, bool, false); DECL_GFX_PREF(Once, "gfx.vsync.compositor", VsyncAlignedCompositor, bool, false); // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10. DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count", CompositorUnobserveCount, int32_t, 10); // Use vsync events generated by hardware DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled", HardwareVsyncEnabled, bool, false); DECL_GFX_PREF(Once, "gfx.vsync.refreshdriver", VsyncAlignedRefreshDriver, bool, false); DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true);