diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/threads/InputTaskManager.cpp | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/xpcom/threads/InputTaskManager.cpp b/xpcom/threads/InputTaskManager.cpp new file mode 100644 index 0000000000..42b2814290 --- /dev/null +++ b/xpcom/threads/InputTaskManager.cpp @@ -0,0 +1,156 @@ +/* -*- 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 "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; +} + +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); + } + + return GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment(); +} + +void InputTaskManager::WillRunTask() { + TaskManager::WillRunTask(); + mInputPriorityController.WillRunTask(); +} + +int32_t +InputTaskManager::GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment() { + 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() + : mInputVsyncState(InputVsyncState::NoPendingVsync) {} + +bool InputTaskManager::InputPriorityController::ShouldUseHighestPriority( + InputTaskManager* aInputTaskManager) { + 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(mInputVsyncState == InputVsyncState::NoPendingVsync); + + mInputVsyncState = InputVsyncState::HasPendingVsync; + mMaxInputTasksToRun = aNumPendingTasks; + mRunInputStartTime = TimeStamp::Now(); +} + +void InputTaskManager::InputPriorityController::WillRunVsync() { + if (mInputVsyncState == InputVsyncState::RunVsync || + mInputVsyncState == InputVsyncState::HasPendingVsync) { + LeavePendingVsyncState(false); + } +} + +void InputTaskManager::InputPriorityController::LeavePendingVsyncState( + bool aRunVsync) { + if (aRunVsync) { + MOZ_ASSERT(mInputVsyncState == InputVsyncState::HasPendingVsync); + mInputVsyncState = InputVsyncState::RunVsync; + } else { + mInputVsyncState = InputVsyncState::NoPendingVsync; + } + + mMaxInputTasksToRun = 0; +} + +void InputTaskManager::InputPriorityController::WillRunTask() { + switch (mInputVsyncState) { + case InputVsyncState::NoPendingVsync: + return; + case InputVsyncState::HasPendingVsync: + MOZ_ASSERT(mMaxInputTasksToRun > 0); + --mMaxInputTasksToRun; + if (!mMaxInputTasksToRun || + TimeStamp::Now() - mRunInputStartTime >= + TimeDuration::FromMilliseconds( + StaticPrefs::dom_input_event_queue_duration_max())) { + 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 |