OLD | NEW |
| (Empty) |
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 | |
5 #include "cc/playback/display_item_list.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <string> | |
10 | |
11 #include "base/memory/ptr_util.h" | |
12 #include "base/numerics/safe_conversions.h" | |
13 #include "base/strings/stringprintf.h" | |
14 #include "base/trace_event/trace_event.h" | |
15 #include "base/trace_event/trace_event_argument.h" | |
16 #include "cc/base/math_util.h" | |
17 #include "cc/debug/picture_debug_util.h" | |
18 #include "cc/output/render_surface_filters.h" | |
19 #include "cc/playback/clip_display_item.h" | |
20 #include "cc/playback/clip_path_display_item.h" | |
21 #include "cc/playback/compositing_display_item.h" | |
22 #include "cc/playback/drawing_display_item.h" | |
23 #include "cc/playback/filter_display_item.h" | |
24 #include "cc/playback/float_clip_display_item.h" | |
25 #include "cc/playback/largest_display_item.h" | |
26 #include "cc/playback/transform_display_item.h" | |
27 #include "third_party/skia/include/core/SkCanvas.h" | |
28 #include "third_party/skia/include/core/SkImageFilter.h" | |
29 #include "third_party/skia/include/core/SkPaint.h" | |
30 #include "third_party/skia/include/core/SkPictureRecorder.h" | |
31 #include "ui/gfx/geometry/rect.h" | |
32 #include "ui/gfx/geometry/rect_conversions.h" | |
33 #include "ui/gfx/skia_util.h" | |
34 | |
35 namespace cc { | |
36 | |
37 namespace { | |
38 | |
39 // We don't perform per-layer solid color analysis when there are too many skia | |
40 // operations. | |
41 const int kOpCountThatIsOkToAnalyze = 10; | |
42 | |
43 bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) { | |
44 SkRect canvas_clip_bounds; | |
45 if (!canvas->getLocalClipBounds(&canvas_clip_bounds)) | |
46 return false; | |
47 *clip_bounds = ToEnclosingRect(gfx::SkRectToRectF(canvas_clip_bounds)); | |
48 return true; | |
49 } | |
50 | |
51 const int kDefaultNumDisplayItemsToReserve = 100; | |
52 | |
53 NOINLINE DISABLE_CFI_PERF void RasterItem(const DisplayItem& base_item, | |
54 SkCanvas* canvas, | |
55 SkPicture::AbortCallback* callback) { | |
56 switch (base_item.type) { | |
57 case DisplayItem::CLIP: { | |
58 const auto& item = static_cast<const ClipDisplayItem&>(base_item); | |
59 canvas->save(); | |
60 canvas->clipRect(gfx::RectToSkRect(item.clip_rect), item.antialias); | |
61 for (const auto& rrect : item.rounded_clip_rects) { | |
62 if (rrect.isRect()) { | |
63 canvas->clipRect(rrect.rect(), item.antialias); | |
64 } else { | |
65 canvas->clipRRect(rrect, item.antialias); | |
66 } | |
67 } | |
68 break; | |
69 } | |
70 case DisplayItem::END_CLIP: | |
71 canvas->restore(); | |
72 break; | |
73 case DisplayItem::CLIP_PATH: { | |
74 const auto& item = static_cast<const ClipPathDisplayItem&>(base_item); | |
75 canvas->save(); | |
76 canvas->clipPath(item.clip_path, item.antialias); | |
77 break; | |
78 } | |
79 case DisplayItem::END_CLIP_PATH: | |
80 canvas->restore(); | |
81 break; | |
82 case DisplayItem::COMPOSITING: { | |
83 const auto& item = static_cast<const CompositingDisplayItem&>(base_item); | |
84 SkPaint paint; | |
85 paint.setBlendMode(item.xfermode); | |
86 paint.setAlpha(item.alpha); | |
87 paint.setColorFilter(item.color_filter); | |
88 const SkRect* bounds = item.has_bounds ? &item.bounds : nullptr; | |
89 if (item.lcd_text_requires_opaque_layer) | |
90 canvas->saveLayer(bounds, &paint); | |
91 else | |
92 canvas->saveLayerPreserveLCDTextRequests(bounds, &paint); | |
93 break; | |
94 } | |
95 case DisplayItem::END_COMPOSITING: | |
96 canvas->restore(); | |
97 break; | |
98 case DisplayItem::DRAWING: { | |
99 const auto& item = static_cast<const DrawingDisplayItem&>(base_item); | |
100 if (canvas->quickReject(item.picture->cullRect())) | |
101 break; | |
102 | |
103 // SkPicture always does a wrapping save/restore on the canvas, so it is | |
104 // not necessary here. | |
105 if (callback) { | |
106 item.picture->playback(canvas, callback); | |
107 } else { | |
108 // TODO(enne): switch this to playback once PaintRecord is real. | |
109 canvas->drawPicture(ToSkPicture(item.picture.get())); | |
110 } | |
111 break; | |
112 } | |
113 case DisplayItem::FLOAT_CLIP: { | |
114 const auto& item = static_cast<const FloatClipDisplayItem&>(base_item); | |
115 canvas->save(); | |
116 canvas->clipRect(gfx::RectFToSkRect(item.clip_rect)); | |
117 break; | |
118 } | |
119 case DisplayItem::END_FLOAT_CLIP: | |
120 canvas->restore(); | |
121 break; | |
122 case DisplayItem::FILTER: { | |
123 const auto& item = static_cast<const FilterDisplayItem&>(base_item); | |
124 canvas->save(); | |
125 canvas->translate(item.origin.x(), item.origin.y()); | |
126 | |
127 sk_sp<SkImageFilter> image_filter = | |
128 RenderSurfaceFilters::BuildImageFilter(item.filters, | |
129 item.bounds.size()); | |
130 SkRect boundaries = RectFToSkRect(item.bounds); | |
131 boundaries.offset(-item.origin.x(), -item.origin.y()); | |
132 | |
133 SkPaint paint; | |
134 paint.setBlendMode(SkBlendMode::kSrcOver); | |
135 paint.setImageFilter(std::move(image_filter)); | |
136 canvas->saveLayer(&boundaries, &paint); | |
137 | |
138 canvas->translate(-item.origin.x(), -item.origin.y()); | |
139 break; | |
140 } | |
141 case DisplayItem::END_FILTER: | |
142 canvas->restore(); | |
143 canvas->restore(); | |
144 break; | |
145 case DisplayItem::TRANSFORM: { | |
146 const auto& item = static_cast<const TransformDisplayItem&>(base_item); | |
147 canvas->save(); | |
148 if (!item.transform.IsIdentity()) | |
149 canvas->concat(item.transform.matrix()); | |
150 break; | |
151 } | |
152 case DisplayItem::END_TRANSFORM: | |
153 canvas->restore(); | |
154 break; | |
155 } | |
156 } | |
157 | |
158 } // namespace | |
159 | |
160 DisplayItemList::DisplayItemList() | |
161 : items_(LargestDisplayItemSize(), | |
162 LargestDisplayItemSize() * kDefaultNumDisplayItemsToReserve) {} | |
163 | |
164 DisplayItemList::~DisplayItemList() = default; | |
165 | |
166 void DisplayItemList::Raster(SkCanvas* canvas, | |
167 SkPicture::AbortCallback* callback, | |
168 const gfx::Rect& canvas_target_playback_rect, | |
169 float contents_scale) const { | |
170 canvas->save(); | |
171 if (!canvas_target_playback_rect.IsEmpty()) { | |
172 // canvas_target_playback_rect is specified in device space. We can't | |
173 // use clipRect because canvas CTM will be applied on it. Use clipRegion | |
174 // instead because it ignores canvas CTM. | |
175 SkRegion device_clip; | |
176 device_clip.setRect(gfx::RectToSkIRect(canvas_target_playback_rect)); | |
177 canvas->clipRegion(device_clip); | |
178 } | |
179 canvas->scale(contents_scale, contents_scale); | |
180 Raster(canvas, callback); | |
181 canvas->restore(); | |
182 } | |
183 | |
184 void DisplayItemList::Raster(SkCanvas* canvas, | |
185 SkPicture::AbortCallback* callback) const { | |
186 gfx::Rect canvas_playback_rect; | |
187 if (!GetCanvasClipBounds(canvas, &canvas_playback_rect)) | |
188 return; | |
189 | |
190 std::vector<size_t> indices; | |
191 rtree_.Search(canvas_playback_rect, &indices); | |
192 for (size_t index : indices) { | |
193 RasterItem(items_[index], canvas, callback); | |
194 | |
195 // We use a callback during solid color analysis on the compositor thread to | |
196 // break out early. Since we're handling a sequence of pictures via rtree | |
197 // query results ourselves, we have to respect the callback and early out. | |
198 if (callback && callback->abort()) | |
199 break; | |
200 } | |
201 } | |
202 | |
203 void DisplayItemList::GrowCurrentBeginItemVisualRect( | |
204 const gfx::Rect& visual_rect) { | |
205 if (!begin_item_indices_.empty()) | |
206 visual_rects_[begin_item_indices_.back()].Union(visual_rect); | |
207 } | |
208 | |
209 void DisplayItemList::Finalize() { | |
210 TRACE_EVENT0("cc", "DisplayItemList::Finalize"); | |
211 DCHECK(items_.size() == visual_rects_.size()) | |
212 << "items.size() " << items_.size() << " visual_rects.size() " | |
213 << visual_rects_.size(); | |
214 rtree_.Build(visual_rects_); | |
215 | |
216 if (!retain_visual_rects_) | |
217 // This clears both the vector and the vector's capacity, since | |
218 // visual_rects won't be used anymore. | |
219 std::vector<gfx::Rect>().swap(visual_rects_); | |
220 } | |
221 | |
222 bool DisplayItemList::IsSuitableForGpuRasterization() const { | |
223 // TODO(wkorman): This is more permissive than Picture's implementation, since | |
224 // none of the items might individually trigger a veto even though they | |
225 // collectively have enough "bad" operations that a corresponding Picture | |
226 // would get vetoed. See crbug.com/513016. | |
227 return all_items_are_suitable_for_gpu_rasterization_; | |
228 } | |
229 | |
230 int DisplayItemList::ApproximateOpCount() const { | |
231 return approximate_op_count_; | |
232 } | |
233 | |
234 size_t DisplayItemList::ApproximateMemoryUsage() const { | |
235 size_t memory_usage = sizeof(*this); | |
236 | |
237 size_t external_memory_usage = 0; | |
238 for (const auto& item : items_) { | |
239 size_t bytes = 0; | |
240 switch (item.type) { | |
241 case DisplayItem::CLIP: | |
242 bytes = static_cast<const ClipDisplayItem&>(item).ExternalMemoryUsage(); | |
243 break; | |
244 case DisplayItem::CLIP_PATH: | |
245 bytes = | |
246 static_cast<const ClipPathDisplayItem&>(item).ExternalMemoryUsage(); | |
247 break; | |
248 case DisplayItem::COMPOSITING: | |
249 bytes = static_cast<const CompositingDisplayItem&>(item) | |
250 .ExternalMemoryUsage(); | |
251 break; | |
252 case DisplayItem::DRAWING: | |
253 bytes = | |
254 static_cast<const DrawingDisplayItem&>(item).ExternalMemoryUsage(); | |
255 break; | |
256 case DisplayItem::FLOAT_CLIP: | |
257 bytes = static_cast<const FloatClipDisplayItem&>(item) | |
258 .ExternalMemoryUsage(); | |
259 break; | |
260 case DisplayItem::FILTER: | |
261 bytes = | |
262 static_cast<const FilterDisplayItem&>(item).ExternalMemoryUsage(); | |
263 break; | |
264 case DisplayItem::TRANSFORM: | |
265 bytes = static_cast<const TransformDisplayItem&>(item) | |
266 .ExternalMemoryUsage(); | |
267 break; | |
268 case DisplayItem::END_CLIP: | |
269 case DisplayItem::END_CLIP_PATH: | |
270 case DisplayItem::END_COMPOSITING: | |
271 case DisplayItem::END_FLOAT_CLIP: | |
272 case DisplayItem::END_FILTER: | |
273 case DisplayItem::END_TRANSFORM: | |
274 break; | |
275 } | |
276 external_memory_usage += bytes; | |
277 } | |
278 | |
279 // Memory outside this class due to |items_|. | |
280 memory_usage += items_.GetCapacityInBytes() + external_memory_usage; | |
281 | |
282 // TODO(jbroman): Does anything else owned by this class substantially | |
283 // contribute to memory usage? | |
284 // TODO(vmpstr): Probably DiscardableImageMap is worth counting here. | |
285 | |
286 return memory_usage; | |
287 } | |
288 | |
289 bool DisplayItemList::ShouldBeAnalyzedForSolidColor() const { | |
290 return ApproximateOpCount() <= kOpCountThatIsOkToAnalyze; | |
291 } | |
292 | |
293 void DisplayItemList::EmitTraceSnapshot() const { | |
294 bool include_items; | |
295 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | |
296 TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"), &include_items); | |
297 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | |
298 TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items") "," | |
299 TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") "," | |
300 TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"), | |
301 "cc::DisplayItemList", this, CreateTracedValue(include_items)); | |
302 } | |
303 | |
304 std::unique_ptr<base::trace_event::TracedValue> | |
305 DisplayItemList::CreateTracedValue(bool include_items) const { | |
306 auto state = base::MakeUnique<base::trace_event::TracedValue>(); | |
307 state->BeginDictionary("params"); | |
308 | |
309 if (include_items) { | |
310 state->BeginArray("items"); | |
311 | |
312 auto visual_rects_it = visual_rects_.begin(); | |
313 for (const DisplayItem& base_item : items_) { | |
314 gfx::Rect visual_rect; | |
315 if (visual_rects_it != visual_rects_.end()) { | |
316 visual_rect = *visual_rects_it; | |
317 ++visual_rects_it; | |
318 } | |
319 | |
320 switch (base_item.type) { | |
321 case DisplayItem::CLIP: { | |
322 const auto& item = static_cast<const ClipDisplayItem&>(base_item); | |
323 std::string output = | |
324 base::StringPrintf("ClipDisplayItem rect: [%s] visualRect: [%s]", | |
325 item.clip_rect.ToString().c_str(), | |
326 visual_rect.ToString().c_str()); | |
327 for (const SkRRect& rounded_rect : item.rounded_clip_rects) { | |
328 base::StringAppendF( | |
329 &output, " rounded_rect: [rect: [%s]", | |
330 gfx::SkRectToRectF(rounded_rect.rect()).ToString().c_str()); | |
331 base::StringAppendF(&output, " radii: ["); | |
332 SkVector upper_left_radius = | |
333 rounded_rect.radii(SkRRect::kUpperLeft_Corner); | |
334 base::StringAppendF(&output, "[%f,%f],", upper_left_radius.x(), | |
335 upper_left_radius.y()); | |
336 SkVector upper_right_radius = | |
337 rounded_rect.radii(SkRRect::kUpperRight_Corner); | |
338 base::StringAppendF(&output, " [%f,%f],", upper_right_radius.x(), | |
339 upper_right_radius.y()); | |
340 SkVector lower_right_radius = | |
341 rounded_rect.radii(SkRRect::kLowerRight_Corner); | |
342 base::StringAppendF(&output, " [%f,%f],", lower_right_radius.x(), | |
343 lower_right_radius.y()); | |
344 SkVector lower_left_radius = | |
345 rounded_rect.radii(SkRRect::kLowerLeft_Corner); | |
346 base::StringAppendF(&output, " [%f,%f]]", lower_left_radius.x(), | |
347 lower_left_radius.y()); | |
348 } | |
349 state->AppendString(output); | |
350 break; | |
351 } | |
352 case DisplayItem::END_CLIP: | |
353 state->AppendString( | |
354 base::StringPrintf("EndClipDisplayItem visualRect: [%s]", | |
355 visual_rect.ToString().c_str())); | |
356 break; | |
357 case DisplayItem::CLIP_PATH: { | |
358 const auto& item = static_cast<const ClipPathDisplayItem&>(base_item); | |
359 state->AppendString(base::StringPrintf( | |
360 "ClipPathDisplayItem length: %d visualRect: [%s]", | |
361 item.clip_path.countPoints(), visual_rect.ToString().c_str())); | |
362 break; | |
363 } | |
364 case DisplayItem::END_CLIP_PATH: | |
365 state->AppendString( | |
366 base::StringPrintf("EndClipPathDisplayItem visualRect: [%s]", | |
367 visual_rect.ToString().c_str())); | |
368 break; | |
369 case DisplayItem::COMPOSITING: { | |
370 const auto& item = | |
371 static_cast<const CompositingDisplayItem&>(base_item); | |
372 std::string output = base::StringPrintf( | |
373 "CompositingDisplayItem alpha: %d, xfermode: %d, visualRect: " | |
374 "[%s]", | |
375 item.alpha, static_cast<int>(item.xfermode), | |
376 visual_rect.ToString().c_str()); | |
377 if (item.has_bounds) { | |
378 base::StringAppendF( | |
379 &output, ", bounds: [%s]", | |
380 gfx::SkRectToRectF(item.bounds).ToString().c_str()); | |
381 } | |
382 state->AppendString(output); | |
383 break; | |
384 } | |
385 case DisplayItem::END_COMPOSITING: | |
386 state->AppendString( | |
387 base::StringPrintf("EndCompositingDisplayItem visualRect: [%s]", | |
388 visual_rect.ToString().c_str())); | |
389 break; | |
390 case DisplayItem::DRAWING: { | |
391 const auto& item = static_cast<const DrawingDisplayItem&>(base_item); | |
392 state->BeginDictionary(); | |
393 state->SetString("name", "DrawingDisplayItem"); | |
394 | |
395 state->BeginArray("visualRect"); | |
396 state->AppendInteger(visual_rect.x()); | |
397 state->AppendInteger(visual_rect.y()); | |
398 state->AppendInteger(visual_rect.width()); | |
399 state->AppendInteger(visual_rect.height()); | |
400 state->EndArray(); | |
401 | |
402 state->BeginArray("cullRect"); | |
403 state->AppendInteger(item.picture->cullRect().x()); | |
404 state->AppendInteger(item.picture->cullRect().y()); | |
405 state->AppendInteger(item.picture->cullRect().width()); | |
406 state->AppendInteger(item.picture->cullRect().height()); | |
407 state->EndArray(); | |
408 | |
409 std::string b64_picture; | |
410 PictureDebugUtil::SerializeAsBase64(ToSkPicture(item.picture.get()), | |
411 &b64_picture); | |
412 state->SetString("skp64", b64_picture); | |
413 state->EndDictionary(); | |
414 break; | |
415 } | |
416 case DisplayItem::FILTER: { | |
417 const auto& item = static_cast<const FilterDisplayItem&>(base_item); | |
418 state->AppendString(base::StringPrintf( | |
419 "FilterDisplayItem bounds: [%s] visualRect: [%s]", | |
420 item.bounds.ToString().c_str(), visual_rect.ToString().c_str())); | |
421 break; | |
422 } | |
423 case DisplayItem::END_FILTER: | |
424 state->AppendString( | |
425 base::StringPrintf("EndFilterDisplayItem visualRect: [%s]", | |
426 visual_rect.ToString().c_str())); | |
427 break; | |
428 case DisplayItem::FLOAT_CLIP: { | |
429 const auto& item = | |
430 static_cast<const FloatClipDisplayItem&>(base_item); | |
431 state->AppendString(base::StringPrintf( | |
432 "FloatClipDisplayItem rect: [%s] visualRect: [%s]", | |
433 item.clip_rect.ToString().c_str(), | |
434 visual_rect.ToString().c_str())); | |
435 break; | |
436 } | |
437 case DisplayItem::END_FLOAT_CLIP: | |
438 state->AppendString( | |
439 base::StringPrintf("EndFloatClipDisplayItem visualRect: [%s]", | |
440 visual_rect.ToString().c_str())); | |
441 break; | |
442 case DisplayItem::TRANSFORM: { | |
443 const auto& item = | |
444 static_cast<const TransformDisplayItem&>(base_item); | |
445 state->AppendString(base::StringPrintf( | |
446 "TransformDisplayItem transform: [%s] visualRect: [%s]", | |
447 item.transform.ToString().c_str(), | |
448 visual_rect.ToString().c_str())); | |
449 break; | |
450 } | |
451 case DisplayItem::END_TRANSFORM: | |
452 state->AppendString( | |
453 base::StringPrintf("EndTransformDisplayItem visualRect: [%s]", | |
454 visual_rect.ToString().c_str())); | |
455 break; | |
456 } | |
457 } | |
458 state->EndArray(); // "items". | |
459 } | |
460 | |
461 MathUtil::AddToTracedValue("layer_rect", rtree_.GetBounds(), state.get()); | |
462 state->EndDictionary(); // "params". | |
463 | |
464 { | |
465 SkPictureRecorder recorder; | |
466 gfx::Rect bounds = rtree_.GetBounds(); | |
467 SkCanvas* canvas = recorder.beginRecording(bounds.width(), bounds.height()); | |
468 canvas->translate(-bounds.x(), -bounds.y()); | |
469 canvas->clipRect(gfx::RectToSkRect(bounds)); | |
470 Raster(canvas, nullptr, gfx::Rect(), 1.f); | |
471 sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); | |
472 | |
473 std::string b64_picture; | |
474 PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture); | |
475 state->SetString("skp64", b64_picture); | |
476 } | |
477 | |
478 return state; | |
479 } | |
480 | |
481 void DisplayItemList::GenerateDiscardableImagesMetadata() { | |
482 // This should be only called once. | |
483 DCHECK(image_map_.empty()); | |
484 | |
485 gfx::Rect bounds = rtree_.GetBounds(); | |
486 DiscardableImageMap::ScopedMetadataGenerator generator( | |
487 &image_map_, gfx::Size(bounds.right(), bounds.bottom())); | |
488 auto* canvas = generator.canvas(); | |
489 for (const auto& item : items_) | |
490 RasterItem(item, canvas, nullptr); | |
491 } | |
492 | |
493 void DisplayItemList::GetDiscardableImagesInRect( | |
494 const gfx::Rect& rect, | |
495 float contents_scale, | |
496 std::vector<DrawImage>* images) { | |
497 image_map_.GetDiscardableImagesInRect(rect, contents_scale, images); | |
498 } | |
499 | |
500 gfx::Rect DisplayItemList::GetRectForImage(ImageId image_id) const { | |
501 return image_map_.GetRectForImage(image_id); | |
502 } | |
503 | |
504 } // namespace cc | |
OLD | NEW |