Bug 1219144 - Using the nsRefreshDriver's jank indication for performance monitoring;f?froydnj r=froydnj

This patch (currently WIP) alters the way we determine whether jank is user-visible or not.

Instead of measuring the total time spent doing JS, we now use an
indicator provided by the vsync driver: how long it takes to deliver
the signal from the vsync timer to the main thread. This lets us find
out more accurately if there is user-visible jank. In the future, this
will also let us add an observer to find out whether the process
itself is janky, regardless of JS.

--HG--
extra : rebase_source : a538e3cc9d8904f52d4a0e7bad291189986e4e6d
This commit is contained in:
David Rajchenbach-Teller 2016-01-14 15:07:18 +01:00
Родитель 01d67c795e
Коммит a4607bcbef
5 изменённых файлов: 238 добавлений и 120 удалений

Просмотреть файл

@ -95,6 +95,13 @@ namespace {
// jank-critical mode if and only if at least one vsync driver has // jank-critical mode if and only if at least one vsync driver has
// at least one observer. // at least one observer.
static uint64_t sActiveVsyncTimers = 0; static uint64_t sActiveVsyncTimers = 0;
// The latest value of process-wide jank levels.
//
// For each i, sJankLevels[i] counts the number of times delivery of
// vsync to the main thread has been delayed by at least 2^i ms. Use
// GetJankLevels to grab a copy of this array.
uint64_t sJankLevels[12];
} }
namespace mozilla { namespace mozilla {
@ -446,6 +453,7 @@ private:
sample); sample);
Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
sample); sample);
RecordJank(sample);
} else if (mVsyncRate != TimeDuration::Forever()) { } else if (mVsyncRate != TimeDuration::Forever()) {
TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate; TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
if (contentDelay.ToMilliseconds() < 0 ){ if (contentDelay.ToMilliseconds() < 0 ){
@ -458,6 +466,7 @@ private:
sample); sample);
Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
sample); sample);
RecordJank(sample);
} else { } else {
// Request the vsync rate from the parent process. Might be a few vsyncs // Request the vsync rate from the parent process. Might be a few vsyncs
// until the parent responds. // until the parent responds.
@ -466,6 +475,16 @@ private:
#endif #endif
} }
void RecordJank(uint32_t aJankMS)
{
uint32_t duration = 1 /* ms */;
for (size_t i = 0;
i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
++i, duration *= 2) {
sJankLevels[i]++;
}
}
void TickRefreshDriver(TimeStamp aVsyncTimestamp) void TickRefreshDriver(TimeStamp aVsyncTimestamp)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -821,6 +840,7 @@ CreateVsyncRefreshTimer()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
PodArrayZero(sJankLevels);
// Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
// ready. // ready.
gfxPrefs::GetSingleton(); gfxPrefs::GetSingleton();
@ -2161,4 +2181,10 @@ nsRefreshDriver::IsJankCritical()
return sActiveVsyncTimers > 0; return sActiveVsyncTimers > 0;
} }
/* static */ bool
nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
aJank.clear();
return aJank.append(sJankLevels, ArrayLength(sJankLevels));
}
#undef LOG #undef LOG

Просмотреть файл

@ -13,6 +13,8 @@
#define nsRefreshDriver_h_ #define nsRefreshDriver_h_
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "mozilla/Vector.h"
#include "mozFlushType.h" #include "mozFlushType.h"
#include "nsTObserverArray.h" #include "nsTObserverArray.h"
#include "nsTArray.h" #include "nsTArray.h"
@ -296,6 +298,20 @@ public:
bool IsInRefresh() { return mInRefresh; } bool IsInRefresh() { return mInRefresh; }
/**
* The latest value of process-wide jank levels.
*
* For each i, sJankLevels[i] counts the number of times delivery of
* vsync to the main thread has been delayed by at least 2^i
* ms. This data structure has been designed to make it easy to
* determine how much jank has taken place between two instants in
* time.
*
* Return `false` if `aJank` needs to be grown to accomodate the
* data but we didn't have enough memory.
*/
static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
// mozilla::layers::TransactionIdAllocator // mozilla::layers::TransactionIdAllocator
virtual uint64_t GetTransactionId() override; virtual uint64_t GetTransactionId() override;
void NotifyTransactionCompleted(uint64_t aTransactionId) override; void NotifyTransactionCompleted(uint64_t aTransactionId) override;

Просмотреть файл

@ -126,38 +126,71 @@ interface nsIPerformanceSnapshot: nsISupports {
/** /**
* A performance alert. * A performance alert.
*/ */
[scriptable, builtinclass, uuid(d5379791-3f90-4edc-bcbb-5d4edea03e57)] [scriptable, builtinclass, uuid(a85706ab-d703-4687-8865-78cd771eab93)]
interface nsIPerformanceAlert: nsISupports { interface nsIPerformanceAlert: nsISupports {
// Alert was caused by jank exceeding the threshold. /**
const unsigned long REASON_JANK = 0; * A slowdown was detected.
*
* See REASON_JANK_* for details on whether this slowdown was user-noticeable.
*/
const unsigned long REASON_SLOWDOWN = 1;
/** /**
* The reason for the alert. * This alert was triggered during a jank in animation.
*
* In the current implementation, we consider that there is a jank
* in animation if delivery of the vsync message to the main thread
* has been delayed too much (see
* nsIPerformanceStatsService.animationJankLevelThreshold).
*
* Note that this is a heuristic which may provide false positives,
* so clients of this API are expected to perform post-processing to
* filter out such false positives.
*/
const unsigned long REASON_JANK_IN_ANIMATION = 2;
/**
* This alert was triggered during a jank in user input.
*
* In the current implementation, we consider that there is a jank
* in animation if a user input was received either immediately
* before executing the offending code (see
* nsIPerformanceStatsService.userInputDelayThreshold) or while
* executing the offending code.
*
* Note that this is a heuristic which may provide false positives,
* so clients of this API are expected to perform post-processing to
* filter out such false positives.
*/
const unsigned long REASON_JANK_IN_INPUT = 4;
/**
* The reason for the alert, as a bitwise or of the various REASON_*
* constants.
*/ */
readonly attribute unsigned long reason; readonly attribute unsigned long reason;
/** /**
* Longest interval spent executing code in this group * Longest interval spent executing code in this group
* since the latest alert, in microseconds. * since the latest alert, in microseconds.
*
* Note that the underlying algorithm is probabilistic and may
* provide false positives, so clients of this API are expected to
* perform post-processing to filter out such false positives. In
* particular, a high system load will increase the noise level on
* this measure.
*/ */
readonly attribute unsigned long long highestJank; readonly attribute unsigned long long highestJank;
/** /**
* Longest interval spent executing CPOW in this group * Longest interval spent executing CPOW in this group
* since the latest alert, in microseconds. * since the latest alert, in microseconds.
*
* This measure is reliable and involves no heuristics. However,
* note that the duration of CPOWs is increased by high system
* loads.
*/ */
readonly attribute unsigned long long highestCPOW; readonly attribute unsigned long long highestCPOW;
/**
* `true` if the jank is expected to be noticeable by the user,
* `false` otherwise.
*
* In the current implementation, we consider that jank is noticeable
* if it arrives during an animation, shortly after a user input, or
* if a user input is triggered during the same iteration of the event
* loop.
*/
readonly attribute bool isJankVisible;
}; };
@ -204,7 +237,7 @@ interface nsIPerformanceObservable: nsISupports {
}; };
[scriptable, uuid(5675d1d9-638e-4af0-9392-b04aacfad79a)] [scriptable, uuid(505bc42e-be38-4a53-baba-92cb33690cde)]
interface nsIPerformanceStatsService : nsISupports { interface nsIPerformanceStatsService : nsISupports {
/** /**
* `true` if we should monitor CPOW, `false` otherwise. * `true` if we should monitor CPOW, `false` otherwise.
@ -234,6 +267,26 @@ interface nsIPerformanceStatsService : nsISupports {
*/ */
attribute unsigned long long jankAlertThreshold; attribute unsigned long long jankAlertThreshold;
/**
* If a user is seeing an animation and we spend too long executing
* JS code while blocking refresh, this will be visible to the user.
*
* We assume that any jank during an animation and lasting more than
* 2^animationJankLevelThreshold ms will be visible.
*/
attribute short animationJankLevelThreshold;
/**
* If a user performs an input (e.g. clicking, pressing a key, but
* *NOT* moving the mouse), and we spend too long executing JS code
* before displaying feedback, this will be visible to the user even
* if there is no ongoing animation.
*
* We assume that any jank during `userInputDelayThreshold` us after
* the user input will be visible.
*/
attribute unsigned long long userInputDelayThreshold;
/** /**
* A buffering delay, in milliseconds, used by the service to * A buffering delay, in milliseconds, used by the service to
* regroup performance alerts, before observers are actually * regroup performance alerts, before observers are actually

Просмотреть файл

@ -522,9 +522,6 @@ private:
// in microseconds. // in microseconds.
const uint64_t mHighestJank; const uint64_t mHighestJank;
const uint64_t mHighestCPOW; const uint64_t mHighestCPOW;
// `true` if jank may be noticed by the user.
const bool mIsJankVisible;
}; };
NS_IMPL_ISUPPORTS(PerformanceAlert, nsIPerformanceAlert); NS_IMPL_ISUPPORTS(PerformanceAlert, nsIPerformanceAlert);
@ -533,7 +530,6 @@ PerformanceAlert::PerformanceAlert(const uint32_t reason, nsPerformanceGroup* so
: mReason(reason) : mReason(reason)
, mHighestJank(source->HighestRecentJank()) , mHighestJank(source->HighestRecentJank())
, mHighestCPOW(source->HighestRecentCPOW()) , mHighestCPOW(source->HighestRecentCPOW())
, mIsJankVisible(source->RecentJankVisibility())
{ } { }
NS_IMETHODIMP NS_IMETHODIMP
@ -553,12 +549,6 @@ PerformanceAlert::GetReason(uint32_t* result) {
*result = mReason; *result = mReason;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
PerformanceAlert::GetIsJankVisible(bool* result) {
*result = mIsJankVisible;
return NS_OK;
}
/* ------------------------------------------------------ /* ------------------------------------------------------
* *
* class PendingAlertsCollector * class PendingAlertsCollector
@ -581,6 +571,7 @@ public:
nsresult Start(uint32_t timerDelayMS); nsresult Start(uint32_t timerDelayMS);
nsresult Dispose(); nsresult Dispose();
private: private:
~PendingAlertsCollector() {} ~PendingAlertsCollector() {}
@ -588,6 +579,8 @@ private:
bool mPending; bool mPending;
nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsITimer> mTimer;
mozilla::Vector<uint64_t> mJankLevels;
}; };
NS_IMPL_ISUPPORTS(PendingAlertsCollector, nsITimerCallback); NS_IMPL_ISUPPORTS(PendingAlertsCollector, nsITimerCallback);
@ -595,7 +588,7 @@ NS_IMPL_ISUPPORTS(PendingAlertsCollector, nsITimerCallback);
NS_IMETHODIMP NS_IMETHODIMP
PendingAlertsCollector::Notify(nsITimer*) { PendingAlertsCollector::Notify(nsITimer*) {
mPending = false; mPending = false;
mService->NotifyJankObservers(); mService->NotifyJankObservers(mJankLevels);
return NS_OK; return NS_OK;
} }
@ -616,6 +609,11 @@ PendingAlertsCollector::Start(uint32_t timerDelayMS) {
} }
mPending = true; mPending = true;
{
mozilla::DebugOnly<bool> result = nsRefreshDriver::GetJankLevels(mJankLevels);
MOZ_ASSERT(result);
}
return NS_OK; return NS_OK;
} }
@ -657,13 +655,15 @@ nsPerformanceStatsService::nsPerformanceStatsService()
true, // isSystem true, // isSystem
nsPerformanceGroup::GroupScope::RUNTIME // scope nsPerformanceGroup::GroupScope::RUNTIME // scope
)) ))
, mIsJankCritical(false) , mIsHandlingUserInput(false)
, mProcessStayed(0) , mProcessStayed(0)
, mProcessMoved(0) , mProcessMoved(0)
, mProcessUpdateCounter(0) , mProcessUpdateCounter(0)
, mIsMonitoringPerCompartment(false) , mIsMonitoringPerCompartment(false)
, mJankAlertThreshold(mozilla::MaxValue<uint64_t>::value) // By default, no alerts , mJankAlertThreshold(mozilla::MaxValue<uint64_t>::value) // By default, no alerts
, mJankAlertBufferingDelay(1000 /* ms */) , mJankAlertBufferingDelay(1000 /* ms */)
, mJankLevelVisibilityThreshold(/* 2 ^ */ 8 /* ms */)
, mMaxExpectedDurationOfInteractionUS(150 * 1000)
{ {
mPendingAlertsCollector = new PendingAlertsCollector(mRuntime, this); mPendingAlertsCollector = new PendingAlertsCollector(mRuntime, this);
@ -821,30 +821,15 @@ nsPerformanceStatsService::Observe(nsISupports *aSubject, const char *aTopic,
return NS_OK; return NS_OK;
} }
/*static*/ /*static*/ bool
bool
nsPerformanceStatsService::IsHandlingUserInput() { nsPerformanceStatsService::IsHandlingUserInput() {
if (mozilla::EventStateManager::LatestUserInputStart().IsNull()) { if (mozilla::EventStateManager::LatestUserInputStart().IsNull()) {
return false; return false;
} }
bool result = mozilla::TimeStamp::Now() - mozilla::EventStateManager::LatestUserInputStart() <= mozilla::TimeDuration::FromMilliseconds(MAX_DURATION_OF_INTERACTION_MS); bool result = mozilla::TimeStamp::Now() - mozilla::EventStateManager::LatestUserInputStart() <= mozilla::TimeDuration::FromMicroseconds(mMaxExpectedDurationOfInteractionUS);
return result; return result;
} }
/*static*/ bool
nsPerformanceStatsService::IsJankCritical() {
bool result = IsAnimating() || IsHandlingUserInput();
return result;
}
/*static*/ bool
nsPerformanceStatsService::IsAnimating() {
bool result = nsRefreshDriver::IsJankCritical();
return result;
}
/* [implicit_jscontext] attribute bool isMonitoringCPOW; */ /* [implicit_jscontext] attribute bool isMonitoringCPOW; */
NS_IMETHODIMP NS_IMETHODIMP
nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive) nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
@ -1132,7 +1117,7 @@ bool
nsPerformanceStatsService::StopwatchStart(uint64_t iteration) { nsPerformanceStatsService::StopwatchStart(uint64_t iteration) {
mIteration = iteration; mIteration = iteration;
mIsJankCritical = IsJankCritical(); mIsHandlingUserInput = IsHandlingUserInput();
mUserInputCount = mozilla::EventStateManager::UserInputCount(); mUserInputCount = mozilla::EventStateManager::UserInputCount();
nsresult rv = GetResources(&mUserTimeStart, &mSystemTimeStart); nsresult rv = GetResources(&mUserTimeStart, &mSystemTimeStart);
@ -1174,13 +1159,13 @@ nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& re
MOZ_ASSERT(mTopGroup->isUsedInThisIteration()); MOZ_ASSERT(mTopGroup->isUsedInThisIteration());
const uint64_t totalRecentCycles = mTopGroup->recentCycles(iteration); const uint64_t totalRecentCycles = mTopGroup->recentCycles(iteration);
const bool isJankVisible = mIsJankCritical || mozilla::EventStateManager::UserInputCount() > mUserInputCount; const bool isHandlingUserInput = mIsHandlingUserInput || mozilla::EventStateManager::UserInputCount() > mUserInputCount;
// We should only reach this stage if `group` has had some activity. // We should only reach this stage if `group` has had some activity.
MOZ_ASSERT(mTopGroup->recentTicks(iteration) > 0); MOZ_ASSERT(mTopGroup->recentTicks(iteration) > 0);
for (auto iter = recentGroups.begin(), end = recentGroups.end(); iter != end; ++iter) { for (auto iter = recentGroups.begin(), end = recentGroups.end(); iter != end; ++iter) {
RefPtr<nsPerformanceGroup> group = nsPerformanceGroup::Get(*iter); RefPtr<nsPerformanceGroup> group = nsPerformanceGroup::Get(*iter);
CommitGroup(iteration, userTimeDelta, systemTimeDelta, totalRecentCycles, isJankVisible, group); CommitGroup(iteration, userTimeDelta, systemTimeDelta, totalRecentCycles, isHandlingUserInput, group);
} }
// Make sure that `group` was treated along with the other items of `recentGroups`. // Make sure that `group` was treated along with the other items of `recentGroups`.
@ -1198,7 +1183,7 @@ void
nsPerformanceStatsService::CommitGroup(uint64_t iteration, nsPerformanceStatsService::CommitGroup(uint64_t iteration,
uint64_t totalUserTimeDelta, uint64_t totalSystemTimeDelta, uint64_t totalUserTimeDelta, uint64_t totalSystemTimeDelta,
uint64_t totalCyclesDelta, uint64_t totalCyclesDelta,
bool isJankVisible, bool isHandlingUserInput,
nsPerformanceGroup* group) { nsPerformanceGroup* group) {
MOZ_ASSERT(group->isUsedInThisIteration()); MOZ_ASSERT(group->isUsedInThisIteration());
@ -1247,7 +1232,9 @@ nsPerformanceStatsService::CommitGroup(uint64_t iteration,
group->RecordJank(totalTimeDelta); group->RecordJank(totalTimeDelta);
group->RecordCPOW(cpowTimeDelta); group->RecordCPOW(cpowTimeDelta);
group->RecordJankVisibility(); if (isHandlingUserInput) {
group->RecordUserInput();
}
if (totalTimeDelta >= mJankAlertThreshold) { if (totalTimeDelta >= mJankAlertThreshold) {
if (!group->HasPendingAlert()) { if (!group->HasPendingAlert()) {
@ -1340,7 +1327,7 @@ nsPerformanceStatsService::GetResources(uint64_t* userTime,
} }
void void
nsPerformanceStatsService::NotifyJankObservers() { nsPerformanceStatsService::NotifyJankObservers(const mozilla::Vector<uint64_t>& aPreviousJankLevels) {
GroupVector alerts; GroupVector alerts;
mPendingAlerts.swap(alerts); mPendingAlerts.swap(alerts);
if (!mPendingAlertsCollector) { if (!mPendingAlertsCollector) {
@ -1348,10 +1335,32 @@ nsPerformanceStatsService::NotifyJankObservers() {
return; return;
} }
// Find out if we have noticed any user-noticeable delay in an
// animation recently (i.e. since the start of the execution of JS
// code that caused this collector to start). If so, we'll mark any
// alert as part of a user-noticeable jank. Note that this doesn't
// mean with any certainty that the alert is the only cause of jank,
// or even the main cause of jank.
mozilla::Vector<uint64_t> latestJankLevels;
{
mozilla::DebugOnly<bool> result = nsRefreshDriver::GetJankLevels(latestJankLevels);
MOZ_ASSERT(result);
}
MOZ_ASSERT(latestJankLevels.length() == aPreviousJankLevels.length());
bool isJankInAnimation = false;
for (size_t i = mJankLevelVisibilityThreshold; i < latestJankLevels.length(); ++i) {
if (latestJankLevels[i] > aPreviousJankLevels[i]) {
isJankInAnimation = true;
break;
}
}
MOZ_ASSERT(!alerts.empty()); MOZ_ASSERT(!alerts.empty());
const bool hasUniversalAddonObservers = mUniversalTargets.mAddons->HasObservers(); const bool hasUniversalAddonObservers = mUniversalTargets.mAddons->HasObservers();
const bool hasUniversalWindowObservers = mUniversalTargets.mWindows->HasObservers(); const bool hasUniversalWindowObservers = mUniversalTargets.mWindows->HasObservers();
for (auto iter = alerts.begin(); iter < alerts.end(); ++iter) { for (auto iter = alerts.begin(); iter < alerts.end(); ++iter) {
MOZ_ASSERT(iter);
RefPtr<nsPerformanceGroup> group = *iter; RefPtr<nsPerformanceGroup> group = *iter;
group->SetHasPendingAlert(false); group->SetHasPendingAlert(false);
@ -1362,19 +1371,24 @@ nsPerformanceStatsService::NotifyJankObservers() {
group->ObservationTarget() group->ObservationTarget()
}; };
bool isJankInInput = group->HasRecentUserInput();
RefPtr<PerformanceAlert> alert; RefPtr<PerformanceAlert> alert;
for (nsPerformanceObservationTarget* target : targets) { for (nsPerformanceObservationTarget* target : targets) {
if (!target) { if (!target) {
continue; continue;
} }
if (!alert) { if (!alert) {
const uint32_t reason = nsIPerformanceAlert::REASON_SLOWDOWN
| (isJankInAnimation ? nsIPerformanceAlert::REASON_JANK_IN_ANIMATION : 0)
| (isJankInInput ? nsIPerformanceAlert::REASON_JANK_IN_INPUT : 0);
// Wait until we are sure we need to allocate before we allocate. // Wait until we are sure we need to allocate before we allocate.
alert = new PerformanceAlert(nsIPerformanceAlert::REASON_JANK, group); alert = new PerformanceAlert(reason, group);
} }
target->NotifyJankObservers(details, alert); target->NotifyJankObservers(details, alert);
} }
group->ResetHighest(); group->ResetRecent();
} }
} }
@ -1403,6 +1417,32 @@ nsPerformanceStatsService::GetObservableWindow(uint64_t windowId,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsPerformanceStatsService::GetAnimationJankLevelThreshold(short* result) {
*result = mJankLevelVisibilityThreshold;
return NS_OK;
}
NS_IMETHODIMP
nsPerformanceStatsService::SetAnimationJankLevelThreshold(short value) {
mJankLevelVisibilityThreshold = value;
return NS_OK;
}
NS_IMETHODIMP
nsPerformanceStatsService::GetUserInputDelayThreshold(uint64_t* result) {
*result = mMaxExpectedDurationOfInteractionUS;
return NS_OK;
}
NS_IMETHODIMP
nsPerformanceStatsService::SetUserInputDelayThreshold(uint64_t value) {
mMaxExpectedDurationOfInteractionUS = value;
return NS_OK;
}
nsPerformanceStatsService::UniversalTargets::UniversalTargets() nsPerformanceStatsService::UniversalTargets::UniversalTargets()
: mAddons(new nsPerformanceObservationTarget()) : mAddons(new nsPerformanceObservationTarget())
, mWindows(new nsPerformanceObservationTarget()) , mWindows(new nsPerformanceObservationTarget())
@ -1442,7 +1482,7 @@ nsPerformanceGroup::nsPerformanceGroup(nsPerformanceStatsService* service,
, mScope(scope) , mScope(scope)
, mHighestJank(0) , mHighestJank(0)
, mHighestCPOW(0) , mHighestCPOW(0)
, mIsJankVisible(false) , mHasRecentUserInput(false)
, mHasPendingAlert(false) , mHasPendingAlert(false)
{ {
mozilla::Unused << mService->mGroups.PutEntry(this); mozilla::Unused << mService->mGroups.PutEntry(this);
@ -1538,11 +1578,6 @@ nsPerformanceGroup::RecordCPOW(uint64_t cpow) {
} }
} }
void
nsPerformanceGroup::RecordJankVisibility() {
mIsJankVisible = true;
}
uint64_t uint64_t
nsPerformanceGroup::HighestRecentJank() { nsPerformanceGroup::HighestRecentJank() {
return mHighestJank; return mHighestJank;
@ -1554,14 +1589,18 @@ nsPerformanceGroup::HighestRecentCPOW() {
} }
bool bool
nsPerformanceGroup::RecentJankVisibility() { nsPerformanceGroup::HasRecentUserInput() {
return mIsJankVisible; return mHasRecentUserInput;
} }
void void
nsPerformanceGroup::ResetHighest() { nsPerformanceGroup::RecordUserInput() {
mHighestJank = 0; mHasRecentUserInput = true;
mHighestCPOW = 0;
mIsJankVisible = false;
} }
void
nsPerformanceGroup::ResetRecent() {
mHighestJank = 0;
mHighestCPOW = 0;
mHasRecentUserInput = false;
}

Просмотреть файл

@ -129,17 +129,6 @@ public:
nsPerformanceStatsService(); nsPerformanceStatsService();
nsresult Init(); nsresult Init();
/**
* `true` if we are currently in a jank-critical section, i.e. if a
* slowdown can cause user-visible jank.
*
* In the current implementation, we consider that the code is in a
* jank-critical section if either we are handling user input (see
* `IsHandlingUserInput()`) or we are running an animation (see
* `IsAnimating()`).
*/
static bool IsJankCritical();
private: private:
nsresult InitInternal(); nsresult InitInternal();
void Dispose(); void Dispose();
@ -285,12 +274,7 @@ protected:
uint64_t mUserTimeStart; uint64_t mUserTimeStart;
uint64_t mSystemTimeStart; uint64_t mSystemTimeStart;
/** bool mIsHandlingUserInput;
* `true` if the iteration currently being examined is jank
* critical, i.e. if we expect that any slowdown during this
* iteration will be noticed by the user.
*/
bool mIsJankCritical;
/** /**
* The number of user inputs since the start of the process. Used to * The number of user inputs since the start of the process. Used to
@ -406,18 +390,6 @@ protected:
* Determining whether jank is user-visible. * Determining whether jank is user-visible.
*/ */
/**
* `true` if we believe that any slowdown can cause an animation to
* jank, `false` otherwise.
*
* In the current implementation, we return `true` iff the vsync
* driver of the current process has at least one client. This is a
* pessimistic approximation, insofar as the vsync driver is also
* used by off-main thread animations, which would not jank in case
* of slowdown.
*/
static bool IsAnimating();
/** /**
* `true` if we believe that any slowdown can cause a noticeable * `true` if we believe that any slowdown can cause a noticeable
* delay in handling user-input. * delay in handling user-input.
@ -427,7 +399,7 @@ protected:
* includes all inputs (mouse, keyboard, other devices), with the * includes all inputs (mouse, keyboard, other devices), with the
* exception of mousemove. * exception of mousemove.
*/ */
static bool IsHandlingUserInput(); bool IsHandlingUserInput();
public: public:
@ -449,7 +421,7 @@ public:
* Clear the set of pending alerts and dispatch the pending alerts * Clear the set of pending alerts and dispatch the pending alerts
* to observers. * to observers.
*/ */
void NotifyJankObservers(); void NotifyJankObservers(const mozilla::Vector<uint64_t>& previousJankLevels);
private: private:
/** /**
@ -500,6 +472,27 @@ private:
* performance. * performance.
*/ */
uint32_t mJankAlertBufferingDelay; uint32_t mJankAlertBufferingDelay;
/**
* The threshold above which jank, as reported by the refresh drivers,
* is considered user-visible.
*
* A value of n means that any jank above 2^n ms will be considered
* user visible.
*/
short mJankLevelVisibilityThreshold;
/**
* The number of microseconds during which we assume that a
* user-interaction can keep the code jank-critical. Any user
* interaction that lasts longer than this duration is expected to
* either have already caused jank or have caused a nested event
* loop.
*
* In either case, we consider that monitoring
* jank-during-interaction after this duration is useless.
*/
uint64_t mMaxExpectedDurationOfInteractionUS;
}; };
@ -768,16 +761,18 @@ public:
uint64_t HighestRecentCPOW(); uint64_t HighestRecentCPOW();
/** /**
* Record that any slowdown will be noticed by the user and may * Record that this group has recently been involved in handling
* therefore need to trigger a slowdown alert. * user input. Note that heuristics are involved here, so the
* result is not 100% accurate.
*/ */
void RecordJankVisibility(); void RecordUserInput();
bool RecentJankVisibility(); bool HasRecentUserInput();
/** /**
* Reset highest recent CPOW/jank to 0. * Reset recent values (recent highest CPOW and jank, involvement in
* user input).
*/ */
void ResetHighest(); void ResetRecent();
private: private:
/** /**
* The target used by observers to register for watching slow * The target used by observers to register for watching slow
@ -800,11 +795,14 @@ private:
uint64_t mHighestCPOW; uint64_t mHighestCPOW;
/** /**
* `true` if the iteration currently being examined is jank * `true` if this group has been involved in handling user input,
* critical, i.e. if we expect that any slowdown during this * `false` otherwise.
* iteration will be noticed by the user. *
* Note that we use heuristics to determine whether a group is
* involved in handling user input, so this value is not 100%
* accurate.
*/ */
bool mIsJankVisible; bool mHasRecentUserInput;
/** /**
* `true` if this group has caused a performance alert and this alert * `true` if this group has caused a performance alert and this alert
@ -818,18 +816,4 @@ private:
bool mHasPendingAlert; bool mHasPendingAlert;
}; };
namespace {
/**
* The number of milliseconds during which we assume that a
* user-interaction can keep the code jank-critical. Any user
* interaction that lasts longer than this duration is expected to
* either have already caused jank or have caused a nested event
* loop.
*
* In either case, we consider that monitoring
* jank-during-interaction after this duration is useless.
*/
const uint64_t MAX_DURATION_OF_INTERACTION_MS = 150;
}
#endif #endif