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
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288

cc / paint / display_item_list.h [blame]

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

#ifndef CC_PAINT_DISPLAY_ITEM_LIST_H_
#define CC_PAINT_DISPLAY_ITEM_LIST_H_

#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/memory/ref_counted.h"
#include "cc/base/rtree.h"
#include "cc/paint/directly_composited_image_info.h"
#include "cc/paint/discardable_image_map.h"
#include "cc/paint/image_id.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_op.h"
#include "cc/paint/paint_op_buffer.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"

class SkCanvas;

namespace base::trace_event {
class TracedValue;
}  // namespace base::trace_event

namespace cc {

// DisplayItemList is a container of paint operations. One can populate the list
// using StartPaint, followed by push{,_with_data,_with_array} functions
// specialized with ops coming from paint_op_buffer.h. Internally, the
// DisplayItemList contains a PaintOpBuffer and defers op saving to it.
// Additionally, it store some meta information about the paint operations.
// Specifically, it creates an rtree to assist in rasterization: when
// rasterizing a rect, it queries the rtree to extract only the byte offsets of
// the ops required and replays those into a canvas.
class CC_PAINT_EXPORT DisplayItemList
    : public base::RefCountedThreadSafe<DisplayItemList> {
 public:
  DisplayItemList();
  DisplayItemList(const DisplayItemList&) = delete;
  DisplayItemList& operator=(const DisplayItemList&) = delete;

  void Raster(
      SkCanvas* canvas,
      ImageProvider* image_provider = nullptr,
      const ScrollOffsetMap* raster_inducing_scroll_offsets = nullptr) const;
  void Raster(SkCanvas* canvas, const PlaybackParams& params) const;
  std::vector<size_t> OffsetsOfOpsToRaster(SkCanvas* canvas) const;

  // Captures |DrawTextBlobOp|s intersecting |rect| and returns the associated
  // |NodeId|s in |content|.
  void CaptureContent(const gfx::Rect& rect,
                      std::vector<NodeInfo>* content) const;

  // Returns the approximate total area covered by |DrawTextBlobOp|s
  // intersecting |rect|, used for statistics purpose.
  double AreaOfDrawText(const gfx::Rect& rect) const;

  void StartPaint() {
#if DCHECK_IS_ON()
    DCHECK(!IsPainting());
    current_range_start_ = paint_op_buffer_.size();
#endif
  }

  // Push functions construct a new op on the paint op buffer, while maintaining
  // bookkeeping information. Must be called after invoking StartPaint().
  // Returns the id (which is an opaque value) of the operation that can be used
  // in UpdateSaveLayerBounds().
  template <typename T, typename... Args>
  size_t push(Args&&... args) {
#if DCHECK_IS_ON()
    DCHECK(IsPainting());
#endif
    size_t offset = paint_op_buffer_.next_op_offset();
    offsets_.push_back(offset);
    const T& op = paint_op_buffer_.push<T>(std::forward<Args>(args)...);
    DCHECK(op.IsValid());
    return offset;
  }

  void PushDrawScrollingContentsOp(
      ElementId scroll_element_id,
      scoped_refptr<DisplayItemList> display_item_list,
      const gfx::Rect& visual_rect);

  // Called by blink::PaintChunksToCcLayer when an effect ends, to update the
  // bounds of a SaveLayer[Alpha]Op which was emitted when the effect started.
  // This is needed because blink doesn't know the bounds when an effect starts.
  // Don't add other mutation methods like this if there is better alternative.
  void UpdateSaveLayerBounds(size_t id, const SkRect& bounds) {
    paint_op_buffer_.UpdateSaveLayerBounds(id, bounds);
  }

  void EndPaintOfUnpaired(const gfx::Rect& visual_rect) {
#if DCHECK_IS_ON()
    DCHECK(IsPainting());
    current_range_start_ = kNotPainting;
#endif
    visual_rects_.resize(paint_op_buffer_.size(), visual_rect);
    GrowCurrentBeginItemVisualRect(visual_rect);
  }

  void EndPaintOfPairedBegin() {
#if DCHECK_IS_ON()
    DCHECK(IsPainting());
    DCHECK_LT(current_range_start_, paint_op_buffer_.size());
    current_range_start_ = kNotPainting;
#endif
    DCHECK_LT(visual_rects_.size(), paint_op_buffer_.size());
    size_t count = paint_op_buffer_.size() - visual_rects_.size();
    paired_begin_stack_.push_back({visual_rects_.size(), count});
    visual_rects_.resize(paint_op_buffer_.size());
  }

  void EndPaintOfPairedEnd();

  // Called after all items are appended, to process the items.
  void Finalize();

  // For testing only, to examine the painted result.
  PaintRecord FinalizeAndReleaseAsRecordForTesting();

  const PaintOpBuffer& paint_op_buffer() const { return paint_op_buffer_; }

  // Returns the indices in paint_op_buffer of paint ops whose visual rects
  // intersect `query`.
  void SearchOpsByRect(const gfx::Rect& query,
                       std::vector<size_t>* op_indices) const {
    return rtree_.Search(query, op_indices);
  }

  // If this list represents an image that should be directly composited (i.e.
  // rasterized at the intrinsic size of the image), return the intrinsic size
  // of the image and whether or not to use nearest neighbor filtering when
  // scaling the layer.
  std::optional<DirectlyCompositedImageInfo> GetDirectlyCompositedImageInfo()
      const;

  int num_slow_paths_up_to_min_for_MSAA() const {
    return paint_op_buffer_.num_slow_paths_up_to_min_for_MSAA();
  }
  bool has_non_aa_paint() const { return paint_op_buffer_.has_non_aa_paint(); }

  // This gives the total number of PaintOps.
  size_t TotalOpCount() const { return paint_op_buffer_.total_op_count(); }
  size_t BytesUsed() const {
    // TODO(jbroman): Does anything else owned by this class substantially
    // contribute to memory usage?
    // TODO(vmpstr): Probably DiscardableImageMap is worth counting here.
    return sizeof(*this) + paint_op_buffer_.bytes_used();
  }
  size_t OpBytesUsed() const { return paint_op_buffer_.paint_ops_size(); }

  scoped_refptr<DiscardableImageMap> GenerateDiscardableImageMap() const;

  void EmitTraceSnapshot() const;

  gfx::Rect VisualRectForTesting(int index) const {
    return visual_rects_[static_cast<size_t>(index)];
  }

  // If a rectangle is solid color, returns that color. |max_ops_to_analyze|
  // indicates the maximum number of draw ops we consider when determining if a
  // rectangle is solid color.
  bool GetColorIfSolidInRect(const gfx::Rect& rect,
                             SkColor4f* color,
                             int max_ops_to_analyze = 1) const;

  std::string ToString() const;

  bool has_draw_ops() const { return paint_op_buffer_.has_draw_ops(); }
  bool has_draw_text_ops() const {
    return paint_op_buffer_.has_draw_text_ops();
  }
  bool has_save_layer_ops() const {
    return paint_op_buffer_.has_save_layer_ops();
  }
  bool has_save_layer_alpha_ops() const {
    return paint_op_buffer_.has_save_layer_alpha_ops();
  }
  bool has_effects_preventing_lcd_text_for_save_layer_alpha() const {
    return paint_op_buffer_
        .has_effects_preventing_lcd_text_for_save_layer_alpha();
  }
  bool has_discardable_images() const {
    return paint_op_buffer_.has_discardable_images();
  }
  gfx::ContentColorUsage content_color_usage() const {
    return paint_op_buffer_.content_color_usage();
  }

  // Ops with nested paint ops are considered as a single op.
  size_t num_paint_ops() const { return paint_op_buffer_.size(); }

  bool NeedsAdditionalInvalidationForLCDText(
      const DisplayItemList& old_list) const {
    return paint_op_buffer_.NeedsAdditionalInvalidationForLCDText(
        old_list.paint_op_buffer_);
  }

  std::optional<gfx::Rect> bounds() const { return rtree_.bounds(); }

  struct RasterInducingScrollInfo {
    // See PushDrawScrollingContentsOp() for how we handle visual rect of
    // nested DrawScrollingContentsOp.
    gfx::Rect visual_rect;
    bool has_discardable_images;
  };
  // Maps scroll element ids of DrawScrollingContentsOps to info.
  // This is only kept in the top-level DisplayItemList after recording.
  using RasterInducingScrollMap =
      base::flat_map<ElementId, RasterInducingScrollInfo>;
  const RasterInducingScrollMap& raster_inducing_scrolls() const {
    return raster_inducing_scrolls_;
  }

 private:
  friend class DisplayItemListTest;

  ~DisplayItemList();

  std::unique_ptr<base::trace_event::TracedValue> CreateTracedValue(
      bool include_items) const;
  void AddToValue(base::trace_event::TracedValue*, bool include_items) const;

  // If we're currently within a paired display item block, unions the
  // given visual rect with the begin display item's visual rect.
  void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect) {
    if (!paired_begin_stack_.empty())
      visual_rects_[paired_begin_stack_.back().first_index].Union(visual_rect);
  }

  RasterInducingScrollMap raster_inducing_scrolls_;

  // RTree stores offsets into the paint op buffer. It's available after
  // Finalize().
  RTree<size_t> rtree_;

  PaintOpBuffer paint_op_buffer_;

  // The visual rects associated with each of the paint ops in this
  // DisplayItemList. This is used during recording and is cleared in
  // Finalize().
  std::vector<gfx::Rect> visual_rects_;
  // Byte offsets associated with each of the ops. This is used during
  // recording and is cleared in Finalize().
  std::vector<size_t> offsets_;
  // A stack of paired begin sequences that haven't been closed. This is used
  // during recording and should be empty when Finalize() is called.
  struct PairedBeginInfo {
    // Index (into virual_rects_ and offsets_) of the first operation in the
    // paired begin sequence.
    size_t first_index;
    // Number of operations in the paired begin sequence.
    size_t count;
  };
  std::vector<PairedBeginInfo> paired_begin_stack_;

#if DCHECK_IS_ON()
  bool IsPainting() const {
    DCHECK(!IsFinalized());
    return current_range_start_ != kNotPainting;
  }
  // paint_op_buffer_ is not mutable once Finalize() is called.
  bool IsFinalized() const { return current_range_start_ == kFinalized; }
  static constexpr size_t kNotPainting = static_cast<size_t>(-1);
  static constexpr size_t kFinalized = static_cast<size_t>(-2);
  // While recording a range of ops, this is the position in the PaintOpBuffer
  // where the recording started.
  size_t current_range_start_ = kNotPainting;
#endif

  friend class base::RefCountedThreadSafe<DisplayItemList>;
  FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed);
};

}  // namespace cc

#endif  // CC_PAINT_DISPLAY_ITEM_LIST_H_