chromium/ash/wm/overview/overview_window_occlusion_calculator.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wm/overview/overview_window_occlusion_calculator.h"

#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/overview/overview_controller.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"

namespace ash {

OverviewWindowOcclusionCalculator::OverviewWindowOcclusionCalculator(
    OverviewController* overview_controller) {
  overview_controller_observation_.Observe(overview_controller);
}

OverviewWindowOcclusionCalculator::~OverviewWindowOcclusionCalculator() =
    default;

base::WeakPtr<WindowOcclusionCalculator>
OverviewWindowOcclusionCalculator::GetCalculator() {
  return calculator_ ? calculator_->AsWeakPtr() : nullptr;
}

void OverviewWindowOcclusionCalculator::OnOverviewModeStarting() {
  if (!features::IsDeskBarWindowOcclusionOptimizationEnabled() ||
      !desks_util::ShouldRenderDeskBarWithMiniViews()) {
    return;
  }
  TRACE_EVENT0("ui",
               "OverviewWindowOcclusionCalculator::OnOverviewModeWillStart");
  base::ScopedUmaHistogramTimer timer(
      "Ash.Overview.WindowOcclusionCalculator.EnterLatency");
  calculator_.emplace();
  // Compute initial occlusion state of all desk's windows before occlusion
  // calculations are paused at the end of this method. Without this, the
  // occlusion state will be unavailable when the desk's `DeskPreviewView`
  // is built between now and the enter-overview animation's completion.
  ComputeOcclusionStateForAllDesks();
  aura::Window::Windows active_desk_containers;
  for (const auto& root_window : Shell::GetAllRootWindows()) {
    active_desk_containers.push_back(
        DesksController::Get()->active_desk()->GetDeskContainerForRoot(
            root_window));
  }
  // Previewing the active desk in overview mode is a special case. When the
  // active desk's windows get transformed to their new positions in the
  // overview grid shortly after entering overview, a bunch of window occlusion
  // changes get triggered because the windows are all technically visible at
  // that point. Since `DeskPreviewView` should reflect the state of the desk's
  // windows before they're transformed, it's important to snapshot their
  // occlusion states here before the transformations begin.
  calculator_->SnapshotOcclusionStateForWindows(active_desk_containers);
  // Entering overview causes lots of occlusion computations that aren't needed
  // and costs ~10 milliseconds of latency on low-end devices. Occlusion
  // calculations can resume after the animation is complete.
  enter_overview_pause_ = calculator_->Pause();
}

void OverviewWindowOcclusionCalculator::OnOverviewModeStartingAnimationComplete(
    bool canceled) {
  TRACE_EVENT0("ui",
               "OverviewWindowOcclusionCalculator::"
               "OnOverviewModeStartingAnimationComplete");
  enter_overview_pause_.reset();
}

void OverviewWindowOcclusionCalculator::OnOverviewModeEnding(
    OverviewSession* overview_session) {
  // Restoring windows to their original position on overview exit causes lots
  // of occlusion calculations and changes. These are unnecessary since the desk
  // bar is going to be destroyed imminently, and they slow down overview exit
  // so the calculator is destroyed early here.
  if (calculator_) {
    TRACE_EVENT0("ui",
                 "OverviewWindowOcclusionCalculator::OnOverviewModeEnding");
    base::ScopedUmaHistogramTimer timer(
        "Ash.Overview.WindowOcclusionCalculator.ExitLatency");
    calculator_->RemoveObserver(this);
    calculator_.reset();
  }
}

void OverviewWindowOcclusionCalculator::ComputeOcclusionStateForAllDesks() {
  aura::Window::Windows all_desk_containers;
  for (const auto& root_window : Shell::GetAllRootWindows()) {
    for (const auto& desk : DesksController::Get()->desks()) {
      all_desk_containers.push_back(desk->GetDeskContainerForRoot(root_window));
    }
  }
  CHECK(calculator_);
  // `AddObserver()` is just a way of getting the the `calculator_` to do an
  // initial round of occlusion calculations for all desks (while forcing
  // inactive desks to be visible internally) and caching the result for future
  // calls to `GetOcclusionState()`. This class does not actually care about
  // future changes, so `OnWindowOcclusionChanged()` is intentionally a no-op.
  calculator_->AddObserver(all_desk_containers, this);
}

}  // namespace ash