зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1708325 - Allow doing an extra refresh driver tick for user input events. r=mstange,bas
Differential Revision: https://phabricator.services.mozilla.com/D113737
This commit is contained in:
Родитель
e76f264866
Коммит
728f5a24cf
|
@ -1013,6 +1013,17 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||||
chain[i].PreHandleEvent(preVisitor);
|
chain[i].PreHandleEvent(preVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<nsRefreshDriver> refreshDriver;
|
||||||
|
if (aPresContext && aPresContext->GetRootPresContext() &&
|
||||||
|
aEvent->IsTrusted() &&
|
||||||
|
(aEvent->mMessage == eKeyPress ||
|
||||||
|
aEvent->mMessage == eMouseClick)) {
|
||||||
|
refreshDriver = aPresContext->GetRootPresContext()->RefreshDriver();
|
||||||
|
if (refreshDriver) {
|
||||||
|
refreshDriver->EnterUserInputProcessing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clearTargets = ShouldClearTargets(aEvent);
|
clearTargets = ShouldClearTargets(aEvent);
|
||||||
|
|
||||||
// Handle the chain.
|
// Handle the chain.
|
||||||
|
@ -1127,6 +1138,10 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (refreshDriver) {
|
||||||
|
driver->ExitUserInputProcessing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preVisitor.mEventStatus = postVisitor.mEventStatus;
|
preVisitor.mEventStatus = postVisitor.mEventStatus;
|
||||||
|
|
|
@ -771,6 +771,12 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
|
||||||
|
|
||||||
void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
|
void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
|
||||||
Tick(aId, aTimeStamp);
|
Tick(aId, aTimeStamp);
|
||||||
|
for (auto& driver : mContentRefreshDrivers) {
|
||||||
|
driver->FinishedVsyncTick();
|
||||||
|
}
|
||||||
|
for (auto& driver : mRootRefreshDrivers) {
|
||||||
|
driver->FinishedVsyncTick();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When using local vsync source, we keep a strong ref to it here to ensure
|
// When using local vsync source, we keep a strong ref to it here to ensure
|
||||||
|
@ -1124,6 +1130,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
||||||
mNotifyDOMContentFlushed(false),
|
mNotifyDOMContentFlushed(false),
|
||||||
mNeedToUpdateIntersectionObservations(false),
|
mNeedToUpdateIntersectionObservations(false),
|
||||||
mInNormalTick(false),
|
mInNormalTick(false),
|
||||||
|
mAttemptedExtraTickSinceLastVsync(false),
|
||||||
mWarningThreshold(REFRESH_WAIT_WARNING) {
|
mWarningThreshold(REFRESH_WAIT_WARNING) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(mPresContext,
|
MOZ_ASSERT(mPresContext,
|
||||||
|
@ -1402,6 +1409,36 @@ bool nsRefreshDriver::CanDoCatchUpTick() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nsRefreshDriver::CanDoExtraTick() {
|
||||||
|
// Only allow one extra tick per normal vsync tick.
|
||||||
|
if (mAttemptedExtraTickSinceLastVsync) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a timer, or we didn't tick on the timer's
|
||||||
|
// refresh then we can't do an 'extra' tick (but we may still
|
||||||
|
// do a catch up tick).
|
||||||
|
if (!mActiveTimer ||
|
||||||
|
mActiveTimer->MostRecentRefresh() != mMostRecentRefresh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the current timestamp before checking the tick hint to be sure
|
||||||
|
// sure that it's equal or smaller than the value used within checking
|
||||||
|
// the tick hint.
|
||||||
|
TimeStamp now = TimeStamp::Now();
|
||||||
|
Maybe<TimeStamp> nextTick = mActiveTimer->GetNextTickHint();
|
||||||
|
int32_t minimumRequiredTime = StaticPrefs::layout_extra_tick_minimum_ms();
|
||||||
|
// If there's less than 4 milliseconds until the next tick, it's probably
|
||||||
|
// not worth trying to catch up.
|
||||||
|
if (minimumRequiredTime < 0 || !nextTick ||
|
||||||
|
(*nextTick - now) < TimeDuration::FromMilliseconds(minimumRequiredTime)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
|
void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
|
||||||
// FIXME: Bug 1346065: We should also assert the case where we have
|
// FIXME: Bug 1346065: We should also assert the case where we have
|
||||||
// STYLO_THREADS=1.
|
// STYLO_THREADS=1.
|
||||||
|
@ -1416,7 +1453,31 @@ void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// will it already fire, and no other changes needed?
|
// will it already fire, and no other changes needed?
|
||||||
if (mActiveTimer && !(aFlags & eForceAdjustTimer)) return;
|
if (mActiveTimer && !(aFlags & eForceAdjustTimer)) {
|
||||||
|
// If we're being called from within a user input handler, and we think
|
||||||
|
// there's time to rush an extra tick immediately, then schedule a runnable
|
||||||
|
// to run the extra tick.
|
||||||
|
if (mUserInputProcessingCount && CanDoExtraTick()) {
|
||||||
|
RefPtr<nsRefreshDriver> self = this;
|
||||||
|
NS_DispatchToCurrentThreadQueue(
|
||||||
|
NS_NewRunnableFunction(
|
||||||
|
"RefreshDriver::EnsureTimerStarted::extra",
|
||||||
|
[self]() -> void {
|
||||||
|
// Re-check if we can still do an extra tick, in case anything
|
||||||
|
// changed while the runnable was pending.
|
||||||
|
if (self->CanDoExtraTick()) {
|
||||||
|
PROFILER_MARKER_UNTYPED("ExtraRefreshDriverTick", GRAPHICS);
|
||||||
|
LOG("[%p] Doing extra tick for user input", self.get());
|
||||||
|
self->mAttemptedExtraTickSinceLastVsync = true;
|
||||||
|
self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
|
||||||
|
self->mActiveTimer->MostRecentRefresh(),
|
||||||
|
IsExtraTick::Yes);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
EventQueuePriority::Vsync);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsFrozen() || !mPresContext) {
|
if (IsFrozen() || !mPresContext) {
|
||||||
// If we don't want to start it now, or we've been disconnected.
|
// If we don't want to start it now, or we've been disconnected.
|
||||||
|
@ -1456,6 +1517,7 @@ void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
|
||||||
// Re-check if we can still do a catch-up, in case anything
|
// Re-check if we can still do a catch-up, in case anything
|
||||||
// changed while the runnable was pending.
|
// changed while the runnable was pending.
|
||||||
if (self->CanDoCatchUpTick()) {
|
if (self->CanDoCatchUpTick()) {
|
||||||
|
LOG("[%p] Doing catch up tick", self.get());
|
||||||
self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
|
self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
|
||||||
self->mActiveTimer->MostRecentRefresh());
|
self->mActiveTimer->MostRecentRefresh());
|
||||||
}
|
}
|
||||||
|
@ -1965,7 +2027,8 @@ static CallState ReduceAnimations(Document& aDocument) {
|
||||||
return CallState::Continue;
|
return CallState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
|
void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
|
||||||
|
IsExtraTick aIsExtraTick /* = No */) {
|
||||||
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
|
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
|
||||||
"Shouldn't have a JSContext on the stack");
|
"Shouldn't have a JSContext on the stack");
|
||||||
|
|
||||||
|
@ -1982,11 +2045,14 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
|
||||||
// go forward in time, not backwards. To prevent the refresh
|
// go forward in time, not backwards. To prevent the refresh
|
||||||
// driver from going back in time, just skip this tick and
|
// driver from going back in time, just skip this tick and
|
||||||
// wait until the next tick.
|
// wait until the next tick.
|
||||||
if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
|
// If this is an 'extra' tick, then we expect it to be using the same
|
||||||
|
// vsync id and timestamp as the original tick, so also allow those.
|
||||||
|
if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes &&
|
||||||
|
aIsExtraTick == IsExtraTick::No) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto cleanupInExtraTick = MakeScopeExit([&] { mInNormalTick = false; });
|
auto cleanupInExtraTick = MakeScopeExit([&] { mInNormalTick = false; });
|
||||||
mInNormalTick = true;
|
mInNormalTick = aIsExtraTick != IsExtraTick::Yes;
|
||||||
|
|
||||||
bool isPresentingInVR = false;
|
bool isPresentingInVR = false;
|
||||||
#if defined(MOZ_WIDGET_ANDROID)
|
#if defined(MOZ_WIDGET_ANDROID)
|
||||||
|
|
|
@ -146,6 +146,16 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
bool AddImageRequest(imgIRequest* aRequest);
|
bool AddImageRequest(imgIRequest* aRequest);
|
||||||
void RemoveImageRequest(imgIRequest* aRequest);
|
void RemoveImageRequest(imgIRequest* aRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks that we're currently in the middle of processing user input.
|
||||||
|
* Called by EventDispatcher when it's handling an input event.
|
||||||
|
*/
|
||||||
|
void EnterUserInputProcessing() { mUserInputProcessingCount++; }
|
||||||
|
void ExitUserInputProcessing() {
|
||||||
|
MOZ_ASSERT(mUserInputProcessingCount > 0);
|
||||||
|
mUserInputProcessingCount--;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add / remove presshells which have pending resize event.
|
* Add / remove presshells which have pending resize event.
|
||||||
*/
|
*/
|
||||||
|
@ -408,6 +418,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext);
|
void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext);
|
||||||
void FlushForceNotifyContentfulPaintPresContext();
|
void FlushForceNotifyContentfulPaintPresContext();
|
||||||
|
|
||||||
|
// Mark that we've just run a tick from vsync, used to throttle 'extra'
|
||||||
|
// paints to one per vsync (see CanDoExtraTick).
|
||||||
|
void FinishedVsyncTick() { mAttemptedExtraTickSinceLastVsync = false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray;
|
typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray;
|
||||||
typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
|
typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
|
||||||
|
@ -442,8 +456,14 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
MOZ_CAN_RUN_SCRIPT
|
MOZ_CAN_RUN_SCRIPT
|
||||||
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||||
void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime);
|
void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime);
|
||||||
|
|
||||||
|
enum class IsExtraTick {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
};
|
||||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||||
void Tick(mozilla::VsyncId aId, mozilla::TimeStamp aNowTime);
|
void Tick(mozilla::VsyncId aId, mozilla::TimeStamp aNowTime,
|
||||||
|
IsExtraTick aIsExtraTick = IsExtraTick::No);
|
||||||
|
|
||||||
enum EnsureTimerStartedFlags {
|
enum EnsureTimerStartedFlags {
|
||||||
eNone = 0,
|
eNone = 0,
|
||||||
|
@ -478,7 +498,18 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
|
|
||||||
void FinishedWaitingForTransaction();
|
void FinishedWaitingForTransaction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if we didn't tick on the most recent vsync, but we think
|
||||||
|
* we could run one now instead in order to reduce latency.
|
||||||
|
*/
|
||||||
bool CanDoCatchUpTick();
|
bool CanDoCatchUpTick();
|
||||||
|
/**
|
||||||
|
* Returns true if we think it's possible to run an repeat tick (between
|
||||||
|
* vsyncs) to hopefully replace the original tick's paint on the compositor.
|
||||||
|
* We allow this sometimes for tick requests coming for user input handling
|
||||||
|
* to reduce latency.
|
||||||
|
*/
|
||||||
|
bool CanDoExtraTick();
|
||||||
|
|
||||||
bool AtPendingTransactionLimit() {
|
bool AtPendingTransactionLimit() {
|
||||||
return mPendingTransactions.Length() == 2;
|
return mPendingTransactions.Length() == 2;
|
||||||
|
@ -502,6 +533,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
AutoTArray<TransactionId, 3> mPendingTransactions;
|
AutoTArray<TransactionId, 3> mPendingTransactions;
|
||||||
|
|
||||||
uint32_t mFreezeCount;
|
uint32_t mFreezeCount;
|
||||||
|
uint32_t mUserInputProcessingCount = 0;
|
||||||
|
|
||||||
// How long we wait between ticks for throttled (which generally means
|
// How long we wait between ticks for throttled (which generally means
|
||||||
// non-visible) documents registered with a non-throttled refresh driver.
|
// non-visible) documents registered with a non-throttled refresh driver.
|
||||||
|
@ -554,6 +586,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||||
// (timer-driven) tick.
|
// (timer-driven) tick.
|
||||||
bool mInNormalTick : 1;
|
bool mInNormalTick : 1;
|
||||||
|
|
||||||
|
// True if we attempted an extra tick (see CanDoExtraTick) since the last
|
||||||
|
// vsync and thus shouldn't allow another.
|
||||||
|
bool mAttemptedExtraTickSinceLastVsync : 1;
|
||||||
|
|
||||||
// Number of seconds that the refresh driver is blocked waiting for a
|
// Number of seconds that the refresh driver is blocked waiting for a
|
||||||
// compositor transaction to be completed before we append a note to the gfx
|
// compositor transaction to be completed before we append a note to the gfx
|
||||||
// critical log. The number is doubled every time the threshold is hit.
|
// critical log. The number is doubled every time the threshold is hit.
|
||||||
|
|
|
@ -7165,6 +7165,18 @@
|
||||||
value: 1
|
value: 1
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# The minimum amount of time (milliseconds) required to be remaining
|
||||||
|
# in the current vsync interval for us to attempt an extra tick, or
|
||||||
|
# <0 to disable extra ticks entirely.
|
||||||
|
- name: layout.extra-tick.minimum-ms
|
||||||
|
type: int32_t
|
||||||
|
#if defined(NIGHTLY_BUILD)
|
||||||
|
value: 4
|
||||||
|
#else
|
||||||
|
value: -1
|
||||||
|
#endif
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# Enable/disable interruptible reflow, which allows reflows to stop
|
# Enable/disable interruptible reflow, which allows reflows to stop
|
||||||
# before completion (and display the partial results) when user events
|
# before completion (and display the partial results) when user events
|
||||||
# are pending.
|
# are pending.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче