1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156

ash / wm / raster_scale / raster_scale_controller.h [blame]

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

#ifndef ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_
#define ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_

#include <memory>
#include <vector>

#include "ash/ash_export.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"

namespace ash {

// ScopedPauseRasterScaleUpdates pauses any updates to all windows raster scales
// until it is destructed. Once it is destructed and there are no other
// ScopedPauseRasterScaleUpdates objects, windows that still exist will have
// their raster scales updated if they have changed.
class ASH_EXPORT ScopedPauseRasterScaleUpdates {
 public:
  ScopedPauseRasterScaleUpdates();

  ScopedPauseRasterScaleUpdates(const ScopedPauseRasterScaleUpdates&) = delete;
  ScopedPauseRasterScaleUpdates& operator=(
      const ScopedPauseRasterScaleUpdates&) = delete;
  ~ScopedPauseRasterScaleUpdates();
};

// ScopedSetRasterScale keeps the raster scale property of the given window
// at or above the given value while in scope. This is necessary because,
// for example, if a window is shown both in overview mode and virtual desks
// preview, we don't want to reduce its raster scale to the tiny preview
// for the virtual desks preview. Instead, we want to use the raster scale
// for the largest preview of the window currently visible.
class ASH_EXPORT ScopedSetRasterScale : public aura::WindowObserver {
 public:
  ScopedSetRasterScale(aura::Window* window, float raster_scale);

  ScopedSetRasterScale(const ScopedSetRasterScale&) = delete;
  ScopedSetRasterScale& operator=(const ScopedSetRasterScale&) = delete;
  ~ScopedSetRasterScale() override;

  float raster_scale() const { return raster_scale_; }

  void UpdateScale(float raster_scale);

  static void SetOrUpdateRasterScale(aura::Window* window,
                                     float raster_scale,
                                     std::unique_ptr<ScopedSetRasterScale>* p);

 private:
  void Shutdown();

  // aura::WindowObserver:
  void OnWindowDestroying(aura::Window* window) override;

  raw_ptr<aura::Window> window_;
  float raster_scale_;
};

// RasterScaleController keeps track of a list of raster scales for each window.
// It is necessary to do this, because we always want to raster each window at
// the largest scale it needs to be displayed at. For example, if a window is
// displayed in overview mode and previewed in a virtual desk, both of those may
// want different scale factors. But, since the window is displayed larger in
// overview mode, we want to make sure to raster it at the higher scale to avoid
// blurriness.
class ASH_EXPORT RasterScaleController : public aura::WindowObserver {
 public:
  RasterScaleController();

  RasterScaleController(const RasterScaleController&) = delete;
  RasterScaleController& operator=(const RasterScaleController&) = delete;
  ~RasterScaleController() override;

  // With raster slop (see comment on `raster_scale_slop_`), there is degenerate
  // exponential updating behaviour as raster scale tends towards 0. The
  // `kMinimumRasterScale` value denotes a minimum value for raster scale. This
  // is set such that the largest windows we expect (e.g. on 4k displays) can
  // still have their raster scales reduced down so the width and height of
  // their buffers is on the order of a few hundred pixels at most.
  static inline constexpr float kMinimumRasterScale = 0.05;

  // Computes the appropriate raster scale given a transform. Normally we expect
  // x and y scaling to be the same, but in case they are not, this takes the
  // larger of the two as the raster scale, to make sure that the scale along
  // that axis is rasterised at a high enough scale.
  static float RasterScaleFromTransform(const gfx::Transform& transform);

  // Adds a raster scale to be tracked for a window. The kRasterScale property
  // for this window will be set to the largest raster scale currently active,
  // or 1.0 if there are no raster scales active.
  //
  // N.B. This relies on float equality. Currently, this is okay because all
  // usages of this are from ScopedSetRasterScale which uses the same float
  // value for push and pop. We will need to change the design if raster_scale
  // is being recomputed between the push and pop.
  void PushRasterScale(aura::Window* window, float raster_scale);

  void PopRasterScale(aura::Window* window, float raster_scale);

  float ComputeRasterScaleForWindow(aura::Window* window);

  float raster_scale_slop_proportion() const {
    return raster_scale_slop_proportion_;
  }

  void set_raster_scale_slop_proportion_for_testing(
      float raster_scale_slop_proportion) {
    raster_scale_slop_proportion_ = raster_scale_slop_proportion;
  }

 private:
  friend class ScopedPauseRasterScaleUpdates;

  void Pause();
  void Unpause();

  void MaybeSetRasterScale(aura::Window* window);

  // aura::WindowObserver:
  void OnWindowDestroying(aura::Window* window) override;

  int pause_count_ = 0;

  base::flat_map<aura::Window*, std::vector<float>> window_scales_;

  // Holds a set of windows that have had their raster scales change while
  // RasterScaleController is paused.
  base::flat_set<raw_ptr<aura::Window, CtnExperimental>> pending_windows_;

  // Raster scale won't be updated for a window unless the currently requested
  // raster scale is more than `raster_scale_slop_proportion_` different by
  // proportion to the currently set (via the raster scale window property)
  // value. As a special case, requesting the raster scale to 1.0 will always
  // update the raster scale window property. This is to prevent windows from
  // getting stuck in non-1.0f raster scales when all `ScopedSetRasterScale`s
  // are released. This value was determined by eyeballing the sharpness at a
  // reduced raster scale. Once the difference in raster scale proportion starts
  // exceeding around 15%, it starts becoming noticeable. Set this to 10% as a
  // safe value.
  float raster_scale_slop_proportion_ = 0.1;

  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
      windows_observation_{this};
};

}  // namespace ash

#endif  // ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_