зеркало из 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:
Родитель
8daf5cd7e6
Коммит
fd7a809e96
|
@ -1013,6 +1013,17 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
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);
|
||||
|
||||
// Handle the chain.
|
||||
|
@ -1127,6 +1138,10 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshDriver) {
|
||||
driver->ExitUserInputProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
preVisitor.mEventStatus = postVisitor.mEventStatus;
|
||||
|
|
|
@ -771,6 +771,12 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
|
|||
|
||||
void RunRefreshDrivers(VsyncId aId, TimeStamp 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
|
||||
|
@ -1124,6 +1130,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
|||
mNotifyDOMContentFlushed(false),
|
||||
mNeedToUpdateIntersectionObservations(false),
|
||||
mInNormalTick(false),
|
||||
mAttemptedExtraTickSinceLastVsync(false),
|
||||
mWarningThreshold(REFRESH_WAIT_WARNING) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPresContext,
|
||||
|
@ -1402,6 +1409,36 @@ bool nsRefreshDriver::CanDoCatchUpTick() {
|
|||
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) {
|
||||
// FIXME: Bug 1346065: We should also assert the case where we have
|
||||
// STYLO_THREADS=1.
|
||||
|
@ -1416,7 +1453,31 @@ void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
|
|||
}
|
||||
|
||||
// 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 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
|
||||
// changed while the runnable was pending.
|
||||
if (self->CanDoCatchUpTick()) {
|
||||
LOG("[%p] Doing catch up tick", self.get());
|
||||
self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
|
||||
self->mActiveTimer->MostRecentRefresh());
|
||||
}
|
||||
|
@ -1965,7 +2027,8 @@ static CallState ReduceAnimations(Document& aDocument) {
|
|||
return CallState::Continue;
|
||||
}
|
||||
|
||||
void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
|
||||
void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
|
||||
IsExtraTick aIsExtraTick /* = No */) {
|
||||
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
|
||||
"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
|
||||
// driver from going back in time, just skip this tick and
|
||||
// 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;
|
||||
}
|
||||
auto cleanupInExtraTick = MakeScopeExit([&] { mInNormalTick = false; });
|
||||
mInNormalTick = true;
|
||||
mInNormalTick = aIsExtraTick != IsExtraTick::Yes;
|
||||
|
||||
bool isPresentingInVR = false;
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
|
|
|
@ -146,6 +146,16 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
bool AddImageRequest(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.
|
||||
*/
|
||||
|
@ -408,6 +418,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext);
|
||||
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:
|
||||
typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray;
|
||||
typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
|
||||
|
@ -443,7 +457,13 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||
void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime);
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
void Tick(mozilla::VsyncId aId, mozilla::TimeStamp aNowTime);
|
||||
|
||||
enum class IsExtraTick {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
void Tick(mozilla::VsyncId aId, mozilla::TimeStamp aNowTime,
|
||||
IsExtraTick aIsExtraTick = IsExtraTick::No);
|
||||
|
||||
enum EnsureTimerStartedFlags {
|
||||
eNone = 0,
|
||||
|
@ -478,7 +498,18 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
|
||||
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();
|
||||
/**
|
||||
* 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() {
|
||||
return mPendingTransactions.Length() == 2;
|
||||
|
@ -502,6 +533,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
AutoTArray<TransactionId, 3> mPendingTransactions;
|
||||
|
||||
uint32_t mFreezeCount;
|
||||
uint32_t mUserInputProcessingCount = 0;
|
||||
|
||||
// How long we wait between ticks for throttled (which generally means
|
||||
// non-visible) documents registered with a non-throttled refresh driver.
|
||||
|
@ -554,6 +586,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
// (timer-driven) tick.
|
||||
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
|
||||
// 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.
|
||||
|
|
|
@ -7165,6 +7165,18 @@
|
|||
value: 1
|
||||
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
|
||||
# before completion (and display the partial results) when user events
|
||||
# are pending.
|
||||
|
|
Загрузка…
Ссылка в новой задаче