# HG changeset patch # Parent f986e55c4e0b41c6b50bd74d287614b564d7895f Bug 1077651. Measure frame uniformity tests. 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 value indicate 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/2d/BasePoint.h b/gfx/2d/BasePoint.h --- a/gfx/2d/BasePoint.h +++ b/gfx/2d/BasePoint.h @@ -53,16 +53,19 @@ struct BasePoint { return *static_cast(this); } Sub& operator-=(const Sub& aPoint) { x -= aPoint.x; y -= aPoint.y; return *static_cast(this); } + Sub operator*(const Sub& aPoint) const { + return Sub(x * aPoint.x, y * aPoint.y); + } Sub operator*(T aScale) const { return Sub(x * aScale, y * aScale); } Sub operator/(T aScale) const { return Sub(x / aScale, y / aScale); } Sub operator-() const { diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -88,16 +88,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; } @@ -639,16 +640,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,7 @@ +[DEFAULT] +support-files = + apz_test_native_event_utils.js +tags = apz-chrome + +[test_smoothness.html] +skip-if = debug || (os != 'mac' && os != 'win') || !e10s # hardware vsync only on win/mac 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,77 @@ + + + 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,45 @@ 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(); + mFrameUniformityData.RecordTransform(aLayer, translation); + } + } +} + 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 +1089,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()); + mFrameUniformityData.EndTest(aOutData); +} + bool AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame, TransformsToSkip aSkip) { PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree", js::ProfileEntry::Category::GRAPHICS); Layer* root = mLayerManager->GetRoot(); @@ -1099,16 +1148,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,26 +9,28 @@ #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; class Layer; class LayerManagerComposite; +class FrameUniformityData; class AutoResolveRefLayers; // Represents (affine) transforms that are calculated from a content view. struct ViewTransform { explicit ViewTransform(LayerToParentLayerScale aScale = LayerToParentLayerScale(), ParentLayerPoint aTranslation = ParentLayerPoint()) : mScale(aScale) , mTranslation(aTranslation) @@ -65,29 +67,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 +113,23 @@ 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; } + // After enough calls to RecordShadowTransforms, GetFrameUniformity + // will return the frame uniformity for each layer attached to an APZ + void GetFrameUniformity(FrameUniformityData* aFrameUniformityData); + + // Records the shadow transform for a given layer due to a scroll + void RecordShadowTransforms(Layer* aLayer); + 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. @@ -203,16 +205,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; + FrameUniformityData mFrameUniformityData; }; 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,153 @@ +/* -*- 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 "gfxPoint.h" +#include "mozilla/TimeStamp.h" +#include "nsTArray.h" +#include "Units.h" // for ScreenPoint, etc +#include "mozilla/dom/APZTestDataBinding.h" +#include "mozilla/dom/ToJSValue.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; + +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; +} + +gfx::Point +LayerTransforms::GetStdDev() +{ + gfx::Point average = GetAverage(); + gfx::Point stdDev; + gfx::Point current = mTransforms[0]; + + for (size_t i = 1; i < mTransforms.Length(); i++) { + gfx::Point next = mTransforms[i]; + gfx::Point move = next - current; + move.x = fabs(move.x); + move.y = fabs(move.y); + + Point diff = move - average; + diff = diff * diff; + stdDev += diff; + + current = next; + } + + stdDev = stdDev / mTransforms.Length(); + stdDev.x = sqrt(stdDev.x); + stdDev.y = sqrt(stdDev.y); + return stdDev; +} + +FrameUniformityData::~FrameUniformityData() +{ + Reset(); +} + +void +FrameUniformityData::RecordTransform(Layer* aLayer, const Point aTransform) +{ + LayerTransforms* layerTransforms = GetLayerTransforms((uintptr_t) aLayer); + layerTransforms->mTransforms.AppendElement(aTransform); +} + +void +FrameUniformityData::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* +FrameUniformityData::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 +FrameUniformityData::Reset() +{ + std::map::iterator iter; + for (iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) { + LayerTransforms* layerTransforms = iter->second; + delete layerTransforms; + } + + mFrameTransforms.clear(); +} + +float +FrameUniformityData::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,71 @@ +/* -*- 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 "nsRefPtr.h" +#include "ipc/IPCMessageUtils.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace layers { +class Layer; + +struct LayerTransforms { + LayerTransforms() {} + + gfx::Point GetAverage(); + gfx::Point GetStdDev(); + + // 60 fps * 5 seconds worth of data + nsAutoTArray mTransforms; +}; + +class FrameUniformityData { + friend struct IPC::ParamTraits; + +public: + FrameUniformityData() {} + ~FrameUniformityData(); + + void RecordTransform(Layer* aLayer, const gfx::Point aTransform); + void Reset(); + void EndTest(FrameUniformityData* aOutData); + bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext); + +private: + float CalculateFrameUniformity(uintptr_t aLayer); + LayerTransforms* GetLayerTransforms(uintptr_t aLayer); + // Contains a list of all frame transforms + std::map mFrameTransforms; + + // Contains the calculated frame uniformities + std::map mUniformities; +}; + +} // 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,22 @@ 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. + 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 @@ -229,16 +229,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);