display_item_list.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "cc/paint/display_item_list.h"
  5. #include <stddef.h>
  6. #include <map>
  7. #include <string>
  8. #include "base/trace_event/trace_event.h"
  9. #include "base/trace_event/traced_value.h"
  10. #include "cc/base/math_util.h"
  11. #include "cc/debug/picture_debug_util.h"
  12. #include "cc/paint/solid_color_analyzer.h"
  13. #include "third_party/skia/include/core/SkCanvas.h"
  14. #include "third_party/skia/include/core/SkPictureRecorder.h"
  15. #include "ui/gfx/geometry/rect.h"
  16. #include "ui/gfx/geometry/rect_conversions.h"
  17. #include "ui/gfx/geometry/rect_f.h"
  18. #include "ui/gfx/geometry/skia_conversions.h"
  19. namespace cc {
  20. namespace {
  21. bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) {
  22. SkRect canvas_clip_bounds;
  23. if (!canvas->getLocalClipBounds(&canvas_clip_bounds))
  24. return false;
  25. *clip_bounds = ToEnclosingRect(gfx::SkRectToRectF(canvas_clip_bounds));
  26. return true;
  27. }
  28. template <typename Function>
  29. void IterateTextContent(const PaintOpBuffer& buffer,
  30. const Function& yield,
  31. const gfx::Rect& rect) {
  32. if (!buffer.has_draw_text_ops())
  33. return;
  34. for (auto* op : PaintOpBuffer::Iterator(&buffer)) {
  35. if (op->GetType() == PaintOpType::DrawTextBlob) {
  36. yield(static_cast<DrawTextBlobOp*>(op), rect);
  37. } else if (op->GetType() == PaintOpType::DrawRecord) {
  38. IterateTextContent(*static_cast<DrawRecordOp*>(op)->record.get(), yield,
  39. rect);
  40. }
  41. }
  42. }
  43. template <typename Function>
  44. void IterateTextContentByOffsets(const PaintOpBuffer& buffer,
  45. const std::vector<size_t>& offsets,
  46. const std::vector<gfx::Rect>& rects,
  47. const Function& yield) {
  48. DCHECK(buffer.has_draw_text_ops());
  49. DCHECK_EQ(rects.size(), offsets.size());
  50. size_t index = 0;
  51. for (auto* op : PaintOpBuffer::OffsetIterator(&buffer, &offsets)) {
  52. if (op->GetType() == PaintOpType::DrawTextBlob) {
  53. yield(static_cast<DrawTextBlobOp*>(op), rects[index]);
  54. } else if (op->GetType() == PaintOpType::DrawRecord) {
  55. IterateTextContent(*static_cast<DrawRecordOp*>(op)->record.get(), yield,
  56. rects[index]);
  57. }
  58. ++index;
  59. }
  60. }
  61. } // namespace
  62. DisplayItemList::DisplayItemList(UsageHint usage_hint)
  63. : usage_hint_(usage_hint) {
  64. if (usage_hint_ == kTopLevelDisplayItemList) {
  65. visual_rects_.reserve(1024);
  66. offsets_.reserve(1024);
  67. paired_begin_stack_.reserve(32);
  68. }
  69. }
  70. DisplayItemList::~DisplayItemList() = default;
  71. void DisplayItemList::Raster(SkCanvas* canvas,
  72. ImageProvider* image_provider) const {
  73. DCHECK(usage_hint_ == kTopLevelDisplayItemList);
  74. gfx::Rect canvas_playback_rect;
  75. if (!GetCanvasClipBounds(canvas, &canvas_playback_rect))
  76. return;
  77. std::vector<size_t> offsets;
  78. rtree_.Search(canvas_playback_rect, &offsets);
  79. paint_op_buffer_.Playback(canvas, PlaybackParams(image_provider), &offsets);
  80. }
  81. void DisplayItemList::CaptureContent(const gfx::Rect& rect,
  82. std::vector<NodeInfo>* content) const {
  83. if (!paint_op_buffer_.has_draw_text_ops())
  84. return;
  85. std::vector<size_t> offsets;
  86. std::vector<gfx::Rect> rects;
  87. rtree_.Search(rect, &offsets, &rects);
  88. IterateTextContentByOffsets(
  89. paint_op_buffer_, offsets, rects,
  90. [content](const DrawTextBlobOp* op, const gfx::Rect& rect) {
  91. // Only union the rect if the current is the same as the last one.
  92. if (!content->empty() && content->back().node_id == op->node_id)
  93. content->back().visual_rect.Union(rect);
  94. else
  95. content->emplace_back(op->node_id, rect);
  96. });
  97. }
  98. double DisplayItemList::AreaOfDrawText(const gfx::Rect& rect) const {
  99. if (!paint_op_buffer_.has_draw_text_ops())
  100. return 0;
  101. std::vector<size_t> offsets;
  102. std::vector<gfx::Rect> rects;
  103. rtree_.Search(rect, &offsets, &rects);
  104. DCHECK_EQ(offsets.size(), rects.size());
  105. double area = 0;
  106. size_t index = 0;
  107. for (auto* op : PaintOpBuffer::OffsetIterator(&paint_op_buffer_, &offsets)) {
  108. if (op->GetType() == PaintOpType::DrawTextBlob ||
  109. // Don't walk into the record because the visual rect is already the
  110. // bounding box of the sub paint operations. This works for most paint
  111. // results for text generated by blink.
  112. (op->GetType() == PaintOpType::DrawRecord &&
  113. static_cast<DrawRecordOp*>(op)->record->has_draw_text_ops())) {
  114. area += static_cast<double>(rects[index].width()) * rects[index].height();
  115. }
  116. ++index;
  117. }
  118. return area;
  119. }
  120. void DisplayItemList::EndPaintOfPairedEnd() {
  121. #if DCHECK_IS_ON()
  122. DCHECK(IsPainting());
  123. DCHECK_LT(current_range_start_, paint_op_buffer_.size());
  124. current_range_start_ = kNotPainting;
  125. #endif
  126. if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
  127. return;
  128. DCHECK(paired_begin_stack_.size());
  129. size_t last_begin_index = paired_begin_stack_.back().first_index;
  130. size_t last_begin_count = paired_begin_stack_.back().count;
  131. DCHECK_GT(last_begin_count, 0u);
  132. // Copy the visual rect at |last_begin_index| to all indices that constitute
  133. // the begin item. Note that because we possibly reallocate the
  134. // |visual_rects_| buffer below, we need an actual copy instead of a const
  135. // reference which can become dangling.
  136. auto visual_rect = visual_rects_[last_begin_index];
  137. for (size_t i = 1; i < last_begin_count; ++i)
  138. visual_rects_[i + last_begin_index] = visual_rect;
  139. paired_begin_stack_.pop_back();
  140. // Copy the visual rect of the matching begin item to the end item(s).
  141. visual_rects_.resize(paint_op_buffer_.size(), visual_rect);
  142. // The block that ended needs to be included in the bounds of the enclosing
  143. // block.
  144. GrowCurrentBeginItemVisualRect(visual_rect);
  145. }
  146. void DisplayItemList::Finalize() {
  147. TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
  148. "DisplayItemList::Finalize");
  149. #if DCHECK_IS_ON()
  150. // If this fails a call to StartPaint() was not ended.
  151. DCHECK(!IsPainting());
  152. // If this fails we had more calls to EndPaintOfPairedBegin() than
  153. // to EndPaintOfPairedEnd().
  154. DCHECK(paired_begin_stack_.empty());
  155. DCHECK_EQ(visual_rects_.size(), offsets_.size());
  156. #endif
  157. if (usage_hint_ == kTopLevelDisplayItemList) {
  158. rtree_.Build(visual_rects_,
  159. [](const std::vector<gfx::Rect>& rects, size_t index) {
  160. return rects[index];
  161. },
  162. [this](const std::vector<gfx::Rect>& rects, size_t index) {
  163. // Ignore the given rects, since the payload comes from
  164. // offsets. However, the indices match, so we can just index
  165. // into offsets.
  166. return offsets_[index];
  167. });
  168. }
  169. paint_op_buffer_.ShrinkToFit();
  170. visual_rects_.clear();
  171. visual_rects_.shrink_to_fit();
  172. offsets_.clear();
  173. offsets_.shrink_to_fit();
  174. paired_begin_stack_.shrink_to_fit();
  175. }
  176. void DisplayItemList::EmitTraceSnapshot() const {
  177. bool include_items;
  178. TRACE_EVENT_CATEGORY_GROUP_ENABLED(
  179. TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"), &include_items);
  180. TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
  181. TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items") ","
  182. TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
  183. TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
  184. "cc::DisplayItemList", TRACE_ID_LOCAL(this),
  185. CreateTracedValue(include_items));
  186. }
  187. std::string DisplayItemList::ToString() const {
  188. base::trace_event::TracedValueJSON value;
  189. AddToValue(&value, true);
  190. return value.ToFormattedJSON();
  191. }
  192. std::unique_ptr<base::trace_event::TracedValue>
  193. DisplayItemList::CreateTracedValue(bool include_items) const {
  194. auto state = std::make_unique<base::trace_event::TracedValue>();
  195. AddToValue(state.get(), include_items);
  196. return state;
  197. }
  198. void DisplayItemList::AddToValue(base::trace_event::TracedValue* state,
  199. bool include_items) const {
  200. state->BeginDictionary("params");
  201. gfx::Rect bounds;
  202. if (rtree_.has_valid_bounds()) {
  203. bounds = rtree_.GetBoundsOrDie();
  204. } else {
  205. // For tracing code, just use the entire positive quadrant if the |rtree_|
  206. // has invalid bounds.
  207. bounds = gfx::Rect(INT_MAX, INT_MAX);
  208. }
  209. if (include_items) {
  210. state->BeginArray("items");
  211. PlaybackParams params(nullptr, SkM44());
  212. std::map<size_t, gfx::Rect> visual_rects = rtree_.GetAllBoundsForTracing();
  213. for (const PaintOp* op : PaintOpBuffer::Iterator(&paint_op_buffer_)) {
  214. state->BeginDictionary();
  215. state->SetString("name", PaintOpTypeToString(op->GetType()));
  216. MathUtil::AddToTracedValue(
  217. "visual_rect",
  218. visual_rects[paint_op_buffer_.GetOpOffsetForTracing(op)], state);
  219. SkPictureRecorder recorder;
  220. SkCanvas* canvas = recorder.beginRecording(gfx::RectToSkRect(bounds));
  221. op->Raster(canvas, params);
  222. sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
  223. if (picture->approximateOpCount()) {
  224. std::string b64_picture;
  225. PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture);
  226. state->SetString("skp64", b64_picture);
  227. }
  228. state->EndDictionary();
  229. }
  230. state->EndArray(); // "items".
  231. }
  232. MathUtil::AddToTracedValue("layer_rect", bounds, state);
  233. state->EndDictionary(); // "params".
  234. {
  235. SkPictureRecorder recorder;
  236. SkCanvas* canvas = recorder.beginRecording(gfx::RectToSkRect(bounds));
  237. canvas->translate(-bounds.x(), -bounds.y());
  238. canvas->clipRect(gfx::RectToSkRect(bounds));
  239. Raster(canvas);
  240. sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
  241. std::string b64_picture;
  242. PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture);
  243. state->SetString("skp64", b64_picture);
  244. }
  245. }
  246. void DisplayItemList::GenerateDiscardableImagesMetadata() {
  247. DCHECK(usage_hint_ == kTopLevelDisplayItemList);
  248. gfx::Rect bounds;
  249. if (rtree_.has_valid_bounds()) {
  250. bounds = rtree_.GetBoundsOrDie();
  251. } else {
  252. // Bounds are only used to size an SkNoDrawCanvas, pass INT_MAX.
  253. bounds = gfx::Rect(INT_MAX, INT_MAX);
  254. }
  255. image_map_.Generate(&paint_op_buffer_, bounds);
  256. }
  257. void DisplayItemList::Reset() {
  258. #if DCHECK_IS_ON()
  259. DCHECK(!IsPainting());
  260. DCHECK(paired_begin_stack_.empty());
  261. #endif
  262. rtree_.Reset();
  263. image_map_.Reset();
  264. paint_op_buffer_.Reset();
  265. visual_rects_.clear();
  266. visual_rects_.shrink_to_fit();
  267. offsets_.clear();
  268. offsets_.shrink_to_fit();
  269. paired_begin_stack_.clear();
  270. paired_begin_stack_.shrink_to_fit();
  271. }
  272. sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() {
  273. sk_sp<PaintRecord> record =
  274. sk_make_sp<PaintOpBuffer>(std::move(paint_op_buffer_));
  275. Reset();
  276. return record;
  277. }
  278. bool DisplayItemList::GetColorIfSolidInRect(const gfx::Rect& rect,
  279. SkColor4f* color,
  280. int max_ops_to_analyze) {
  281. DCHECK(usage_hint_ == kTopLevelDisplayItemList);
  282. std::vector<size_t>* offsets_to_use = nullptr;
  283. std::vector<size_t> offsets;
  284. if (rtree_.has_valid_bounds() && !rect.Contains(rtree_.GetBoundsOrDie())) {
  285. rtree_.Search(rect, &offsets);
  286. offsets_to_use = &offsets;
  287. }
  288. absl::optional<SkColor4f> solid_color =
  289. SolidColorAnalyzer::DetermineIfSolidColor(
  290. &paint_op_buffer_, rect, max_ops_to_analyze, offsets_to_use);
  291. if (solid_color) {
  292. *color = *solid_color;
  293. return true;
  294. }
  295. return false;
  296. }
  297. namespace {
  298. absl::optional<DisplayItemList::DirectlyCompositedImageResult>
  299. DirectlyCompositedImageResultForPaintOpBuffer(const PaintOpBuffer& op_buffer) {
  300. // A PaintOpBuffer for an image may have 1 (a DrawImageRect or a DrawRecord
  301. // that recursively contains a PaintOpBuffer for an image) or 4 paint
  302. // operations:
  303. // (1) Save
  304. // (2) Translate which applies an offset of the image in the layer
  305. // or Concat with a transformation rotating the image by +/-90 degrees for
  306. // image orientation
  307. // (3) DrawImageRect or DrawRecord (see the 1 operation case above)
  308. // (4) Restore
  309. // The following algorithm also supports Translate and Concat in the same
  310. // PaintOpBuffer (i.e. 5 operations).
  311. constexpr size_t kMaxDrawImageOps = 5;
  312. if (op_buffer.size() > kMaxDrawImageOps)
  313. return absl::nullopt;
  314. bool transpose_image_size = false;
  315. absl::optional<DisplayItemList::DirectlyCompositedImageResult> result;
  316. for (auto* op : PaintOpBuffer::Iterator(&op_buffer)) {
  317. switch (op->GetType()) {
  318. case PaintOpType::Save:
  319. case PaintOpType::Restore:
  320. case PaintOpType::Translate:
  321. break;
  322. case PaintOpType::Concat: {
  323. // We only expect a single transformation. If we see another one, then
  324. // this image won't be eligible for directly compositing.
  325. if (transpose_image_size)
  326. return absl::nullopt;
  327. // The transformation must be before the DrawImageRect operation.
  328. if (result)
  329. return absl::nullopt;
  330. const ConcatOp* concat_op = static_cast<const ConcatOp*>(op);
  331. if (!MathUtil::SkM44Preserves2DAxisAlignment(concat_op->matrix))
  332. return absl::nullopt;
  333. // If the image has been rotated +/-90 degrees we'll need to transpose
  334. // the width and height dimensions to account for the same transform
  335. // applying when the layer bounds were calculated. Since we already
  336. // know that the transformation preserves axis alignment, we only
  337. // need to confirm that this is not a scaling operation.
  338. transpose_image_size = (concat_op->matrix.rc(0, 0) == 0);
  339. break;
  340. }
  341. case PaintOpType::DrawImageRect: {
  342. if (result)
  343. return absl::nullopt;
  344. const auto* draw_image_rect_op =
  345. static_cast<const DrawImageRectOp*>(op);
  346. const SkRect& src = draw_image_rect_op->src;
  347. const SkRect& dst = draw_image_rect_op->dst;
  348. if (src.isEmpty() || dst.isEmpty())
  349. return absl::nullopt;
  350. result.emplace();
  351. result->default_raster_scale = gfx::Vector2dF(
  352. src.width() / dst.width(), src.height() / dst.height());
  353. // Ensure the layer will use nearest neighbor when drawn by the display
  354. // compositor, if required.
  355. result->nearest_neighbor =
  356. draw_image_rect_op->flags.getFilterQuality() ==
  357. PaintFlags::FilterQuality::kNone;
  358. break;
  359. }
  360. case PaintOpType::DrawRecord:
  361. if (result)
  362. return absl::nullopt;
  363. result = DirectlyCompositedImageResultForPaintOpBuffer(
  364. *static_cast<const DrawRecordOp*>(op)->record);
  365. if (!result)
  366. return absl::nullopt;
  367. break;
  368. default:
  369. // Disqualify the layer as a directly composited image if any other
  370. // paint op is detected.
  371. return absl::nullopt;
  372. }
  373. }
  374. if (result && transpose_image_size)
  375. result->default_raster_scale.Transpose();
  376. return result;
  377. }
  378. } // anonymous namespace
  379. absl::optional<DisplayItemList::DirectlyCompositedImageResult>
  380. DisplayItemList::GetDirectlyCompositedImageResult() const {
  381. return DirectlyCompositedImageResultForPaintOpBuffer(paint_op_buffer_);
  382. }
  383. } // namespace cc