Bug 1077651 Measure frame uniformity by synthesizing native events. r=kats,mrbkap

This commit is contained in:
Mason Chang 2015-06-08 09:53:41 -07:00
parent 12f2a36dce
commit 89a5b63555
17 changed files with 467 additions and 13 deletions

View File

@ -70,7 +70,6 @@
#endif
#include "Layers.h"
#include "mozilla/layers/ShadowLayers.h"
#include "gfxPrefs.h"
#include "mozilla/dom/Element.h"
@ -82,6 +81,8 @@
#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"
@ -3716,6 +3717,27 @@ nsDOMWindowUtils::SetChromeMargin(int32_t aTop,
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<LayerManager> 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)
{

View File

@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest;
interface nsIObserver;
[scriptable, uuid(098d9f0d-7809-4d3c-8fc6-e5b3fb71835b)]
[scriptable, uuid(ec176f3b-2886-4090-938e-dded103c5f1c)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -1814,6 +1814,14 @@ interface nsIDOMWindowUtils : nsISupports {
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();
/*
* Increase the chaos mode activation level. An equivalent number of
* calls to leaveChaosMode must be made in order to restore the original
* chaos mode state. If the activation level is nonzero all chaos mode

View File

@ -34,4 +34,14 @@ dictionary APZBucket {
dictionary APZTestData {
sequence<APZBucket> paints;
sequence<APZBucket> repaintRequests;
};
};
// A frame uniformity measurement for every scrollable layer
dictionary FrameUniformity {
unsigned long layerAddress;
float frameUniformity;
};
dictionary FrameUniformityResults {
sequence<FrameUniformity> layerUniformities;
};

View File

@ -92,6 +92,7 @@ class ShadowLayerForwarder;
class LayerManagerComposite;
class SpecificLayerAttributes;
class Compositor;
class FrameUniformityData;
namespace layerscope {
class LayersPacket;
@ -643,6 +644,7 @@ public:
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) { }

View File

@ -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

View File

@ -0,0 +1,83 @@
<html>
<head>
<title>Test Frame Uniformity While Scrolling</title>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<style>
#content {
height: 5000px;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
</style>
<script type="text/javascript">
var scrollEvents = 100;
var i = 0;
var testPref = "gfx.vsync.collect-scroll-transforms";
// Scroll points
var x = 100;
var y = 150;
SimpleTest.waitForExplicitFinish();
var utils = _getDOMWindowUtils(window);
function sendScrollEvent(aRafTimestamp) {
var scrollDiv = document.getElementById("content");
if (i < scrollEvents) {
i++;
// Scroll diff
var dx = 0;
var dy = -10; // Negative to scroll down
synthesizeNativeWheelAndWaitForEvent(scrollDiv, x, y, dx, dy);
window.requestAnimationFrame(sendScrollEvent);
} else {
// Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5
// w/o silk + e10s + apz, I get up to 7. Lower is better.
// Windows, I get ~3. Values are not valid w/o hardware vsync
var uniformities = _getDOMWindowUtils().getFrameUniformityTestData();
for (var j = 0; j < uniformities.layerUniformities.length; j++) {
var layerResult = uniformities.layerUniformities[j];
var layerAddr = layerResult.layerAddress;
var uniformity = layerResult.frameUniformity;
var msg = "Layer: " + layerAddr.toString(16) + " Uniformity: " + uniformity;
SimpleTest.ok((uniformity >= 0) && (uniformity < 4.0), msg);
}
SimpleTest.finish();
}
}
function startTest() {
window.requestAnimationFrame(sendScrollEvent);
}
window.onload = function() {
var apzEnabled = SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled");
if (!apzEnabled) {
SimpleTest.ok(true, "APZ not enabled, skipping test");
SimpleTest.finish();
}
var hwVsyncEnabled = SpecialPowers.getBoolPref("gfx.vsync.hw-vsync.enabled");
if (!hwVsyncEnabled) {
SimpleTest.ok(true, "Hardware vsync not enabled, skipping test");
SimpleTest.finish();
}
SpecialPowers.pushPrefEnv({
"set" : [
[testPref, true]
]
}, startTest);
}
</script>
</head>
<body>
<div id="content">
</div>
</body>
</html>

View File

@ -14,6 +14,7 @@
#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
@ -426,6 +427,20 @@ ClientLayerManager::StartNewRepaintRequest(SequenceNumber 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)
{

View File

@ -34,6 +34,7 @@ class ClientPaintedLayer;
class CompositorChild;
class ImageLayer;
class PLayerChild;
class FrameUniformityData;
class TextureClientPool;
class ClientLayerManager final : public LayerManager
@ -200,6 +201,7 @@ public:
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;

View File

@ -37,6 +37,7 @@
# include "AndroidBridge.h"
#endif
#include "GeckoProfiler.h"
#include "FrameUniformityData.h"
struct nsCSSValueSharedList;
@ -94,6 +95,18 @@ WalkTheTree(Layer* aLayer,
}
}
AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager)
: mLayerManager(aManager)
, mIsFirstPaint(true)
, mLayersUpdated(false)
, mReadyForCompose(true)
{
}
AsyncCompositionManager::~AsyncCompositionManager()
{
}
void
AsyncCompositionManager::ResolveRefLayers()
{
@ -545,6 +558,36 @@ SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp 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)
{
@ -1051,6 +1094,13 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
aLayer->GetLocalTransform(), fixedLayerMargins);
}
void
AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
mLayerTransformRecorder.EndTest(aOutData);
}
bool
AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
TransformsToSkip aSkip)
@ -1103,6 +1153,9 @@ AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
trans *= gfx::Matrix4x4::From2D(mWorldTransform);
rootComposite->SetShadowTransform(trans);
if (gfxPrefs::CollectScrollTransforms()) {
RecordShadowTransforms(root);
}
return wantNextFrame;
}

View File

@ -14,6 +14,7 @@
#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
@ -70,19 +71,12 @@ struct ViewTransform {
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
@ -123,6 +117,10 @@ public:
// 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
@ -190,6 +188,9 @@ private:
*/
void DetachRefLayers();
// Records the shadow transforms for the tree of layers rooted at the given layer
void RecordShadowTransforms(Layer* aLayer);
TargetConfig mTargetConfig;
CSSRect mContentRect;
@ -208,6 +209,7 @@ private:
bool mReadyForCompose;
gfx::Matrix mWorldTransform;
LayerTransformRecorder mLayerTransformRecorder;
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AsyncCompositionManager::TransformsToSkip)

View File

@ -0,0 +1,151 @@
/* -*- 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 <map>
#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)
{
for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
uintptr_t layer = iter->first;
float uniformity = CalculateFrameUniformity(layer);
std::pair<uintptr_t,float> result(layer, uniformity);
aOutData->mUniformities.insert(result);
}
Reset();
}
LayerTransforms*
LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer)
{
if (!mFrameTransforms.count(aLayer)) {
LayerTransforms* newTransform = new LayerTransforms();
std::pair<uintptr_t, LayerTransforms*> newLayer(aLayer, newTransform);
mFrameTransforms.insert(newLayer);
}
return mFrameTransforms.find(aLayer)->second;
}
void
LayerTransformRecorder::Reset()
{
for (auto 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<dom::FrameUniformity>& layers = results.mLayerUniformities.Construct();
for (auto 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;
}
return dom::ToJSValue(aContext, results, aOutValue);
}
}
}

View File

@ -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<FrameUniformityData>;
public:
bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext);
// Contains the calculated frame uniformities
std::map<uintptr_t,float> mUniformities;
};
struct LayerTransforms {
LayerTransforms() {}
gfx::Point GetAverage();
gfx::Point GetStdDev();
// 60 fps * 5 seconds worth of data
nsAutoTArray<gfx::Point, 300> 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<uintptr_t,LayerTransforms*> mFrameTransforms;
};
} // mozilla
} // layers
namespace IPC {
template<>
struct ParamTraits<mozilla::layers::FrameUniformityData>
{
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<std::map<uintptr_t,float>>::Read(aMsg, aIter, &aResult->mUniformities);
}
};
}// ipc
#endif // mozilla_layers_FrameUniformityData_h_

View File

@ -37,6 +37,7 @@
#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"
@ -1324,6 +1325,13 @@ CompositorParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
}
}
bool
CompositorParent::RecvGetFrameUniformity(FrameUniformityData* aOutData)
{
mCompositionManager->GetFrameUniformity(aOutData);
return true;
}
bool
CompositorParent::RecvRequestOverfill()
{
@ -1713,6 +1721,13 @@ public:
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.

View File

@ -229,6 +229,7 @@ public:
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;

View File

@ -19,6 +19,7 @@ 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 {
@ -80,6 +81,9 @@ 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();

View File

@ -125,6 +125,7 @@ EXPORTS.mozilla.layers += [
'composite/ColorLayerComposite.h',
'composite/ContainerLayerComposite.h',
'composite/ContentHost.h',
'composite/FrameUniformityData.h',
'composite/ImageHost.h',
'composite/ImageLayerComposite.h',
'composite/LayerManagerComposite.h',
@ -277,6 +278,7 @@ UNIFIED_SOURCES += [
'composite/ContainerLayerComposite.cpp',
'composite/ContentHost.cpp',
'composite/FPSCounter.cpp',
'composite/FrameUniformityData.cpp',
'composite/ImageHost.cpp',
'composite/ImageLayerComposite.cpp',
'composite/LayerManagerComposite.cpp',
@ -391,6 +393,7 @@ CXXFLAGS += [
]
MOCHITEST_MANIFESTS += ['apz/test/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['apz/test/chrome.ini']
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CXXFLAGS += CONFIG['TK_CFLAGS']

View File

@ -240,6 +240,7 @@ private:
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.