gecko-dev/xpcom/threads/InputTaskManager.cpp

205 строки
6.8 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "InputTaskManager.h"
#include "InputEventStatistics.h"
#include "VsyncTaskManager.h"
#include "nsRefreshDriver.h"
namespace mozilla {
StaticRefPtr<InputTaskManager> InputTaskManager::gInputTaskManager;
void InputTaskManager::EnableInputEventPrioritization() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInputQueueState == STATE_DISABLED);
mInputQueueState = STATE_ENABLED;
mInputHandlingStartTime = TimeStamp();
}
void InputTaskManager::FlushInputEventPrioritization() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
mInputQueueState == STATE_SUSPEND);
mInputQueueState =
mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND;
}
void InputTaskManager::SuspendInputEventPrioritization() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
mInputQueueState == STATE_FLUSHING);
mInputQueueState = STATE_SUSPEND;
}
void InputTaskManager::ResumeInputEventPrioritization() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
mInputQueueState = STATE_ENABLED;
}
int32_t InputTaskManager::GetPriorityModifierForEventLoopTurn(
const MutexAutoLock& aProofOfLock) {
// When the state is disabled, the input task that we have is
// very likely SuspendInputEventQueue, so here we also use
// normal priority as ResumeInputEventQueue, FlushInputEventQueue and
// SetInputEventQueueEnabled all uses normal priority, to
// ensure the ordering is correct.
if (State() == InputTaskManager::STATE_DISABLED) {
return static_cast<int32_t>(EventQueuePriority::Normal) -
static_cast<int32_t>(EventQueuePriority::InputHigh);
}
if (StaticPrefs::dom_input_events_strict_input_vsync_alignment()) {
return GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment();
}
size_t inputCount = PendingTaskCount();
if (State() == STATE_ENABLED && InputHandlingStartTime().IsNull() &&
inputCount > 0) {
SetInputHandlingStartTime(
InputEventStatistics::Get().GetInputHandlingStartTime(inputCount));
}
if (inputCount > 0 && (State() == InputTaskManager::STATE_FLUSHING ||
(State() == InputTaskManager::STATE_ENABLED &&
!InputHandlingStartTime().IsNull() &&
TimeStamp::Now() > InputHandlingStartTime()))) {
return 0;
}
int32_t modifier = static_cast<int32_t>(EventQueuePriority::InputLow) -
static_cast<int32_t>(EventQueuePriority::MediumHigh);
return modifier;
}
void InputTaskManager::WillRunTask() {
TaskManager::WillRunTask();
mStartTimes.AppendElement(TimeStamp::Now());
if (StaticPrefs::dom_input_events_strict_input_vsync_alignment()) {
mInputPriorityController.WillRunTask();
}
}
void InputTaskManager::DidRunTask() {
TaskManager::DidRunTask();
MOZ_ASSERT(!mStartTimes.IsEmpty());
TimeStamp start = mStartTimes.PopLastElement();
InputEventStatistics::Get().UpdateInputDuration(TimeStamp::Now() - start);
}
int32_t
InputTaskManager::GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment() {
MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
MOZ_ASSERT(!IsSuspended());
size_t inputCount = PendingTaskCount();
if (inputCount > 0 &&
mInputPriorityController.ShouldUseHighestPriority(this)) {
return static_cast<int32_t>(EventQueuePriority::InputHighest) -
static_cast<int32_t>(EventQueuePriority::InputHigh);
}
if (State() == STATE_FLUSHING ||
nsRefreshDriver::GetNextTickHint().isNothing()) {
return 0;
}
return static_cast<int32_t>(EventQueuePriority::InputLow) -
static_cast<int32_t>(EventQueuePriority::InputHigh);
}
InputTaskManager::InputPriorityController::InputPriorityController()
: mIsInitialized(false),
mInputVsyncState(InputVsyncState::NoPendingVsync) {}
bool InputTaskManager::InputPriorityController::ShouldUseHighestPriority(
InputTaskManager* aInputTaskManager) {
if (!mIsInitialized) {
// Have to initialize mMaxInputHandlingDuration lazily because
// Preference may not be ready upon the construction of
// InputTaskManager.
mMaxInputHandlingDuration =
InputEventStatistics::Get().GetMaxInputHandlingDuration();
mIsInitialized = true;
}
if (mInputVsyncState == InputVsyncState::HasPendingVsync) {
return true;
}
if (mInputVsyncState == InputVsyncState::RunVsync) {
return false;
}
if (mInputVsyncState == InputVsyncState::NoPendingVsync &&
VsyncTaskManager::Get()->PendingTaskCount()) {
EnterPendingVsyncState(aInputTaskManager->PendingTaskCount());
return true;
}
return false;
}
void InputTaskManager::InputPriorityController::EnterPendingVsyncState(
uint32_t aNumPendingTasks) {
MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
MOZ_ASSERT(mInputVsyncState == InputVsyncState::NoPendingVsync);
mInputVsyncState = InputVsyncState::HasPendingVsync;
mMaxInputTasksToRun = aNumPendingTasks;
mRunInputStartTime = TimeStamp::Now();
}
void InputTaskManager::InputPriorityController::WillRunVsync() {
MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
if (mInputVsyncState == InputVsyncState::RunVsync ||
mInputVsyncState == InputVsyncState::HasPendingVsync) {
LeavePendingVsyncState(false);
}
}
void InputTaskManager::InputPriorityController::LeavePendingVsyncState(
bool aRunVsync) {
MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
if (aRunVsync) {
MOZ_ASSERT(mInputVsyncState == InputVsyncState::HasPendingVsync);
mInputVsyncState = InputVsyncState::RunVsync;
} else {
mInputVsyncState = InputVsyncState::NoPendingVsync;
}
mMaxInputTasksToRun = 0;
}
void InputTaskManager::InputPriorityController::WillRunTask() {
MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
switch (mInputVsyncState) {
case InputVsyncState::NoPendingVsync:
return;
case InputVsyncState::HasPendingVsync:
MOZ_ASSERT(mMaxInputTasksToRun > 0);
--mMaxInputTasksToRun;
if (!mMaxInputTasksToRun ||
TimeStamp::Now() - mRunInputStartTime >= mMaxInputHandlingDuration) {
LeavePendingVsyncState(true);
}
return;
default:
MOZ_DIAGNOSTIC_ASSERT(
false, "Shouldn't run this input task when we suppose to run vsync");
return;
}
}
// static
void InputTaskManager::Init() { gInputTaskManager = new InputTaskManager(); }
} // namespace mozilla