forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			202 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
	
		
			6.7 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 "VsyncTaskManager.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
 |