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 "components/viz/service/display/display.h"
6
7 #include <stddef.h>
8 #include <limits>
9
10 #include "base/debug/dump_without_crashing.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/optional.h"
13 #include "base/stl_util.h"
14 #include "base/timer/elapsed_timer.h"
15 #include "base/trace_event/trace_event.h"
16 #include "build/build_config.h"
17 #include "cc/base/region.h"
18 #include "cc/base/simple_enclosed_region.h"
19 #include "cc/benchmarks/benchmark_instrumentation.h"
20 #include "components/viz/common/display/renderer_settings.h"
21 #include "components/viz/common/features.h"
22 #include "components/viz/common/frame_sinks/begin_frame_source.h"
23 #include "components/viz/common/quads/compositor_frame.h"
24 #include "components/viz/common/quads/draw_quad.h"
25 #include "components/viz/common/quads/shared_quad_state.h"
26 #include "components/viz/service/display/damage_frame_annotator.h"
27 #include "components/viz/service/display/direct_renderer.h"
28 #include "components/viz/service/display/display_client.h"
29 #include "components/viz/service/display/display_scheduler.h"
30 #include "components/viz/service/display/gl_renderer.h"
31 #include "components/viz/service/display/output_surface.h"
32 #include "components/viz/service/display/renderer_utils.h"
33 #include "components/viz/service/display/skia_output_surface.h"
34 #include "components/viz/service/display/skia_renderer.h"
35 #include "components/viz/service/display/software_renderer.h"
36 #include "components/viz/service/display/surface_aggregator.h"
37 #include "components/viz/service/surfaces/surface.h"
38 #include "components/viz/service/surfaces/surface_manager.h"
39 #include "gpu/command_buffer/client/gles2_interface.h"
40 #include "gpu/ipc/scheduler_sequence.h"
41 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
42 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h"
43 #include "ui/gfx/buffer_types.h"
44 #include "ui/gfx/geometry/rect_conversions.h"
45 #include "ui/gfx/overlay_transform_utils.h"
46 #include "ui/gfx/presentation_feedback.h"
47 #include "ui/gfx/swap_result.h"
48
49 namespace viz {
50
51 namespace {
52
53 const DrawQuad::Material kNonSplittableMaterials[] = {
54 // Exclude debug quads from quad splitting
55 DrawQuad::Material::kDebugBorder,
56 // Exclude possible overlay candidates from quad splitting
57 // See OverlayCandidate::FromDrawQuad
58 DrawQuad::Material::kStreamVideoContent,
59 DrawQuad::Material::kTextureContent,
60 DrawQuad::Material::kVideoHole,
61 // See DCLayerOverlayProcessor::ProcessRenderPass
62 DrawQuad::Material::kYuvVideoContent,
63 };
64
65 constexpr base::TimeDelta kAllowedDeltaFromFuture =
66 base::TimeDelta::FromMilliseconds(16);
67
68 // Assign each Display instance a starting value for the the display-trace id,
69 // so that multiple Displays all don't start at 0, because that makes it
70 // difficult to associate the trace-events with the particular displays.
GetStartingTraceId()71 int64_t GetStartingTraceId() {
72 static int64_t client = 0;
73 // https://crbug.com/956695
74 return ((++client & 0xffff) << 16);
75 }
76
SanitizePresentationFeedback(const gfx::PresentationFeedback & feedback,base::TimeTicks draw_time)77 gfx::PresentationFeedback SanitizePresentationFeedback(
78 const gfx::PresentationFeedback& feedback,
79 base::TimeTicks draw_time) {
80 // Temporary to investigate large presentation times.
81 // https://crbug.com/894440
82 DCHECK(!draw_time.is_null());
83 if (feedback.timestamp.is_null())
84 return feedback;
85
86 // If the presentation-timestamp is from the future, or from the past (i.e.
87 // before swap-time), then invalidate the feedback. Also report how far into
88 // the future (or from the past) the timestamps are.
89 // https://crbug.com/894440
90 const auto now = base::TimeTicks::Now();
91 // The timestamp for the presentation feedback may have a different source and
92 // therefore the timestamp can be slightly in the future in comparison with
93 // base::TimeTicks::Now(). Such presentation feedbacks should not be rejected.
94 // See https://crbug.com/1040178
95 const auto allowed_delta_from_future =
96 ((feedback.flags & gfx::PresentationFeedback::kHWClock) != 0)
97 ? kAllowedDeltaFromFuture
98 : base::TimeDelta();
99 if (feedback.timestamp > now + allowed_delta_from_future) {
100 const auto diff = feedback.timestamp - now;
101 UMA_HISTOGRAM_MEDIUM_TIMES(
102 "Graphics.PresentationTimestamp.InvalidFromFuture", diff);
103 return gfx::PresentationFeedback::Failure();
104 }
105
106 if (feedback.timestamp < draw_time) {
107 const auto diff = draw_time - feedback.timestamp;
108 UMA_HISTOGRAM_MEDIUM_TIMES(
109 "Graphics.PresentationTimestamp.InvalidBeforeSwap", diff);
110 return gfx::PresentationFeedback::Failure();
111 }
112
113 #ifndef TOOLKIT_QT
114 const auto difference = feedback.timestamp - draw_time;
115 if (difference.InMinutes() > 3) {
116 UMA_HISTOGRAM_CUSTOM_TIMES(
117 "Graphics.PresentationTimestamp.LargePresentationDelta", difference,
118 base::TimeDelta::FromMinutes(3), base::TimeDelta::FromHours(1), 50);
119 }
120 #endif
121 return feedback;
122 }
123
124 // Returns the bounds for the largest rect that can be inscribed in a rounded
125 // rect.
GetOccludingRectForRRectF(const gfx::RRectF & bounds)126 gfx::RectF GetOccludingRectForRRectF(const gfx::RRectF& bounds) {
127 if (bounds.IsEmpty())
128 return gfx::RectF();
129 if (bounds.GetType() == gfx::RRectF::Type::kRect)
130 return bounds.rect();
131 gfx::RectF occluding_rect = bounds.rect();
132
133 // Compute the radius for each corner
134 float top_left = bounds.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).x();
135 float top_right = bounds.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).x();
136 float lower_right =
137 bounds.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).x();
138 float lower_left = bounds.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).x();
139
140 // Get a bounding rect that does not intersect with the rounding clip.
141 // When a rect has rounded corner with radius r, then the largest rect that
142 // can be inscribed inside it has an inset of |((2 - sqrt(2)) / 2) * radius|.
143 occluding_rect.Inset(std::max(top_left, lower_left) * 0.3f,
144 std::max(top_left, top_right) * 0.3f,
145 std::max(top_right, lower_right) * 0.3f,
146 std::max(lower_right, lower_left) * 0.3f);
147 return occluding_rect;
148 }
149
150 // SkRegion uses INT_MAX as a sentinel. Reduce gfx::Rect values when they are
151 // equal to INT_MAX to prevent conversion to an empty region.
SafeConvertRectForRegion(const gfx::Rect & r)152 gfx::Rect SafeConvertRectForRegion(const gfx::Rect& r) {
153 gfx::Rect safe_rect(r);
154 if (safe_rect.x() == INT_MAX)
155 safe_rect.set_x(INT_MAX - 1);
156 if (safe_rect.y() == INT_MAX)
157 safe_rect.set_y(INT_MAX - 1);
158 if (safe_rect.width() == INT_MAX)
159 safe_rect.set_width(INT_MAX - 1);
160 if (safe_rect.height() == INT_MAX)
161 safe_rect.set_height(INT_MAX - 1);
162 return safe_rect;
163 }
164
165 // Computes the accumulated area of all the rectangles in the list of |rects|.
ComputeArea(const std::vector<gfx::Rect> & rects)166 int ComputeArea(const std::vector<gfx::Rect>& rects) {
167 int area = 0;
168 for (const auto& r : rects)
169 area += r.size().GetArea();
170 return area;
171 }
172
173 // Decides whether or not a DrawQuad should be split into a more complex visible
174 // region in order to avoid overdraw.
CanSplitQuad(const DrawQuad::Material m,const int visible_region_area,const int visible_region_bounding_area,const int minimum_fragments_reduced,const float device_scale_factor)175 bool CanSplitQuad(const DrawQuad::Material m,
176 const int visible_region_area,
177 const int visible_region_bounding_area,
178 const int minimum_fragments_reduced,
179 const float device_scale_factor) {
180 return !base::Contains(kNonSplittableMaterials, m) &&
181 (visible_region_bounding_area - visible_region_area) *
182 device_scale_factor * device_scale_factor >
183 minimum_fragments_reduced;
184 }
185
186 // Attempts to consolidate rectangles that were only split because of the
187 // nature of base::Region and transforms the region into a list of visible
188 // rectangles. Returns true upon successful reduction of the region to under
189 // |complexity_limit|, false otherwise.
ReduceComplexity(const cc::Region & region,size_t complexity_limit,std::vector<gfx::Rect> * reduced_region)190 bool ReduceComplexity(const cc::Region& region,
191 size_t complexity_limit,
192 std::vector<gfx::Rect>* reduced_region) {
193 DCHECK(reduced_region);
194
195 reduced_region->clear();
196 for (const gfx::Rect& r : region) {
197 auto it =
198 std::find_if(reduced_region->begin(), reduced_region->end(),
199 [&r](const gfx::Rect& a) { return a.SharesEdgeWith(r); });
200 if (it != reduced_region->end()) {
201 it->Union(r);
202 continue;
203 }
204 reduced_region->push_back(r);
205
206 if (reduced_region->size() >= complexity_limit)
207 return false;
208 }
209 return true;
210 }
211
212 } // namespace
213
214 constexpr base::TimeDelta Display::kDrawToSwapMin;
215 constexpr base::TimeDelta Display::kDrawToSwapMax;
216
217 Display::PresentationGroupTiming::PresentationGroupTiming() = default;
218
219 Display::PresentationGroupTiming::PresentationGroupTiming(
220 Display::PresentationGroupTiming&& other) = default;
221
222 Display::PresentationGroupTiming::~PresentationGroupTiming() = default;
223
AddPresentationHelper(std::unique_ptr<Surface::PresentationHelper> helper)224 void Display::PresentationGroupTiming::AddPresentationHelper(
225 std::unique_ptr<Surface::PresentationHelper> helper) {
226 presentation_helpers_.push_back(std::move(helper));
227 }
228
OnDraw(base::TimeTicks draw_start_timestamp)229 void Display::PresentationGroupTiming::OnDraw(
230 base::TimeTicks draw_start_timestamp) {
231 draw_start_timestamp_ = draw_start_timestamp;
232 }
233
OnSwap(gfx::SwapTimings timings)234 void Display::PresentationGroupTiming::OnSwap(gfx::SwapTimings timings) {
235 swap_timings_ = timings;
236 }
237
OnPresent(const gfx::PresentationFeedback & feedback)238 void Display::PresentationGroupTiming::OnPresent(
239 const gfx::PresentationFeedback& feedback) {
240 for (auto& presentation_helper : presentation_helpers_) {
241 presentation_helper->DidPresent(draw_start_timestamp_, swap_timings_,
242 feedback);
243 }
244 }
245
Display(SharedBitmapManager * bitmap_manager,const RendererSettings & settings,const FrameSinkId & frame_sink_id,std::unique_ptr<OutputSurface> output_surface,std::unique_ptr<OverlayProcessorInterface> overlay_processor,std::unique_ptr<DisplaySchedulerBase> scheduler,scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)246 Display::Display(
247 SharedBitmapManager* bitmap_manager,
248 const RendererSettings& settings,
249 const FrameSinkId& frame_sink_id,
250 std::unique_ptr<OutputSurface> output_surface,
251 std::unique_ptr<OverlayProcessorInterface> overlay_processor,
252 std::unique_ptr<DisplaySchedulerBase> scheduler,
253 scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
254 : bitmap_manager_(bitmap_manager),
255 settings_(settings),
256 frame_sink_id_(frame_sink_id),
257 output_surface_(std::move(output_surface)),
258 skia_output_surface_(output_surface_->AsSkiaOutputSurface()),
259 scheduler_(std::move(scheduler)),
260 current_task_runner_(std::move(current_task_runner)),
261 overlay_processor_(std::move(overlay_processor)),
262 swapped_trace_id_(GetStartingTraceId()),
263 last_swap_ack_trace_id_(swapped_trace_id_),
264 last_presented_trace_id_(swapped_trace_id_) {
265 DCHECK(output_surface_);
266 DCHECK(frame_sink_id_.is_valid());
267 if (scheduler_)
268 scheduler_->SetClient(this);
269 enable_quad_splitting_ = features::ShouldSplitPartiallyOccludedQuads() &&
270 !overlay_processor_->DisableSplittingQuads();
271 }
272
~Display()273 Display::~Display() {
274 #if DCHECK_IS_ON()
275 allow_schedule_gpu_task_during_destruction_.reset(
276 new gpu::ScopedAllowScheduleGpuTask);
277 #endif
278 #if defined(OS_ANDROID)
279 // In certain cases, drivers hang when tearing down the display. Finishing
280 // before teardown appears to address this. As we're during display teardown,
281 // an additional finish should have minimal impact.
282 // TODO(ericrk): Add a more robust workaround. crbug.com/899705
283 if (auto* context = output_surface_->context_provider()) {
284 context->ContextGL()->Finish();
285 }
286 #endif
287
288 if (no_pending_swaps_callback_)
289 std::move(no_pending_swaps_callback_).Run();
290
291 for (auto& observer : observers_)
292 observer.OnDisplayDestroyed();
293 observers_.Clear();
294
295 // Send gfx::PresentationFeedback::Failure() to any surfaces expecting
296 // feedback.
297 pending_presentation_group_timings_.clear();
298
299 // Only do this if Initialize() happened.
300 if (client_) {
301 if (auto* context = output_surface_->context_provider())
302 context->RemoveObserver(this);
303 if (skia_output_surface_)
304 skia_output_surface_->RemoveContextLostObserver(this);
305 }
306
307 // Un-register as DisplaySchedulerClient to prevent us from being called in a
308 // partially destructed state.
309 if (scheduler_)
310 scheduler_->SetClient(nullptr);
311
312 if (damage_tracker_)
313 damage_tracker_->RunDrawCallbacks();
314 }
315
Initialize(DisplayClient * client,SurfaceManager * surface_manager,bool enable_shared_images,bool using_synthetic_bfs)316 void Display::Initialize(DisplayClient* client,
317 SurfaceManager* surface_manager,
318 bool enable_shared_images,
319 bool using_synthetic_bfs) {
320 DCHECK(client);
321 DCHECK(surface_manager);
322 gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
323 client_ = client;
324 surface_manager_ = surface_manager;
325
326 output_surface_->BindToClient(this);
327 if (output_surface_->software_device())
328 output_surface_->software_device()->BindToClient(this);
329
330 frame_rate_decider_ = std::make_unique<FrameRateDecider>(
331 surface_manager_, this, using_synthetic_bfs);
332
333 InitializeRenderer(enable_shared_images);
334
335 damage_tracker_ = std::make_unique<DisplayDamageTracker>(surface_manager_,
336 aggregator_.get());
337 if (scheduler_)
338 scheduler_->SetDamageTracker(damage_tracker_.get());
339
340 // This depends on assumptions that Display::Initialize will happen on the
341 // same callstack as the ContextProvider being created/initialized or else
342 // it could miss a callback before setting this.
343 if (auto* context = output_surface_->context_provider())
344 context->AddObserver(this);
345
346 if (skia_output_surface_)
347 skia_output_surface_->AddContextLostObserver(this);
348 }
349
AddObserver(DisplayObserver * observer)350 void Display::AddObserver(DisplayObserver* observer) {
351 observers_.AddObserver(observer);
352 }
353
RemoveObserver(DisplayObserver * observer)354 void Display::RemoveObserver(DisplayObserver* observer) {
355 observers_.RemoveObserver(observer);
356 }
357
SetLocalSurfaceId(const LocalSurfaceId & id,float device_scale_factor)358 void Display::SetLocalSurfaceId(const LocalSurfaceId& id,
359 float device_scale_factor) {
360 if (current_surface_id_.local_surface_id() == id &&
361 device_scale_factor_ == device_scale_factor) {
362 return;
363 }
364
365 TRACE_EVENT0("viz", "Display::SetSurfaceId");
366 current_surface_id_ = SurfaceId(frame_sink_id_, id);
367 device_scale_factor_ = device_scale_factor;
368
369 damage_tracker_->SetNewRootSurface(current_surface_id_);
370 }
371
SetVisible(bool visible)372 void Display::SetVisible(bool visible) {
373 TRACE_EVENT1("viz", "Display::SetVisible", "visible", visible);
374 if (renderer_)
375 renderer_->SetVisible(visible);
376 if (scheduler_)
377 scheduler_->SetVisible(visible);
378 visible_ = visible;
379
380 if (!visible) {
381 // Damage tracker needs a full reset as renderer resources are dropped when
382 // not visible.
383 if (aggregator_ && current_surface_id_.is_valid())
384 aggregator_->SetFullDamageForSurface(current_surface_id_);
385 }
386 }
387
Resize(const gfx::Size & size)388 void Display::Resize(const gfx::Size& size) {
389 disable_swap_until_resize_ = false;
390
391 if (size == current_surface_size_)
392 return;
393
394 // This DCHECK should probably go at the top of the function, but mac
395 // sometimes calls Resize() with 0x0 before it sets a real size. This will
396 // early out before the DCHECK fails.
397 DCHECK(!size.IsEmpty());
398 TRACE_EVENT0("viz", "Display::Resize");
399
400 swapped_since_resize_ = false;
401 current_surface_size_ = size;
402
403 damage_tracker_->DisplayResized();
404 }
405
DisableSwapUntilResize(base::OnceClosure no_pending_swaps_callback)406 void Display::DisableSwapUntilResize(
407 base::OnceClosure no_pending_swaps_callback) {
408 TRACE_EVENT0("viz", "Display::DisableSwapUntilResize");
409 DCHECK(no_pending_swaps_callback_.is_null());
410
411 if (!disable_swap_until_resize_) {
412 DCHECK(scheduler_);
413
414 if (!swapped_since_resize_)
415 scheduler_->ForceImmediateSwapIfPossible();
416
417 if (no_pending_swaps_callback && pending_swaps_ > 0 &&
418 (output_surface_->context_provider() ||
419 output_surface_->AsSkiaOutputSurface())) {
420 no_pending_swaps_callback_ = std::move(no_pending_swaps_callback);
421 }
422
423 disable_swap_until_resize_ = true;
424 }
425
426 // There are no pending swaps for current size so immediately run callback.
427 if (no_pending_swaps_callback)
428 std::move(no_pending_swaps_callback).Run();
429 }
430
SetColorMatrix(const SkMatrix44 & matrix)431 void Display::SetColorMatrix(const SkMatrix44& matrix) {
432 if (output_surface_)
433 output_surface_->set_color_matrix(matrix);
434
435 // Force a redraw.
436 if (aggregator_) {
437 if (current_surface_id_.is_valid())
438 aggregator_->SetFullDamageForSurface(current_surface_id_);
439 }
440
441 damage_tracker_->SetRootSurfaceDamaged();
442 }
443
SetDisplayColorSpaces(const gfx::DisplayColorSpaces & display_color_spaces)444 void Display::SetDisplayColorSpaces(
445 const gfx::DisplayColorSpaces& display_color_spaces) {
446 display_color_spaces_ = display_color_spaces;
447 if (aggregator_)
448 aggregator_->SetDisplayColorSpaces(display_color_spaces_);
449 }
450
SetOutputIsSecure(bool secure)451 void Display::SetOutputIsSecure(bool secure) {
452 if (secure == output_is_secure_)
453 return;
454 output_is_secure_ = secure;
455
456 if (aggregator_) {
457 aggregator_->set_output_is_secure(secure);
458 // Force a redraw.
459 if (current_surface_id_.is_valid())
460 aggregator_->SetFullDamageForSurface(current_surface_id_);
461 }
462 }
463
InitializeRenderer(bool enable_shared_images)464 void Display::InitializeRenderer(bool enable_shared_images) {
465 auto mode = output_surface_->context_provider() || skia_output_surface_
466 ? DisplayResourceProvider::kGpu
467 : DisplayResourceProvider::kSoftware;
468 resource_provider_ = std::make_unique<DisplayResourceProvider>(
469 mode, output_surface_->context_provider(), bitmap_manager_,
470 enable_shared_images);
471 if (settings_.use_skia_renderer && mode == DisplayResourceProvider::kGpu) {
472 // Default to use DDL if skia_output_surface is not null.
473 if (skia_output_surface_) {
474 renderer_ = std::make_unique<SkiaRenderer>(
475 &settings_, output_surface_.get(), resource_provider_.get(),
476 overlay_processor_.get(), skia_output_surface_,
477 SkiaRenderer::DrawMode::DDL);
478 } else {
479 // GPU compositing with GL to an SKP.
480 DCHECK(output_surface_);
481 DCHECK(output_surface_->context_provider());
482 DCHECK(settings_.record_sk_picture);
483 DCHECK(!overlay_processor_->IsOverlaySupported());
484 renderer_ = std::make_unique<SkiaRenderer>(
485 &settings_, output_surface_.get(), resource_provider_.get(),
486 overlay_processor_.get(), nullptr /* skia_output_surface */,
487 SkiaRenderer::DrawMode::SKPRECORD);
488 }
489 } else if (output_surface_->context_provider()) {
490 renderer_ = std::make_unique<GLRenderer>(
491 &settings_, output_surface_.get(), resource_provider_.get(),
492 overlay_processor_.get(), current_task_runner_);
493 } else {
494 DCHECK(!overlay_processor_->IsOverlaySupported());
495 auto renderer = std::make_unique<SoftwareRenderer>(
496 &settings_, output_surface_.get(), resource_provider_.get(),
497 overlay_processor_.get());
498 software_renderer_ = renderer.get();
499 renderer_ = std::move(renderer);
500 }
501
502 renderer_->Initialize();
503 renderer_->SetVisible(visible_);
504
505 // Outputting a partial list of quads might not work in cases where contents
506 // outside the damage rect might be needed by the renderer.
507 bool output_partial_list =
508 output_surface_->capabilities().only_invalidates_damage_rect &&
509 renderer_->use_partial_swap() &&
510 !overlay_processor_->IsOverlaySupported();
511
512 aggregator_ = std::make_unique<SurfaceAggregator>(
513 surface_manager_, resource_provider_.get(), output_partial_list,
514 overlay_processor_->NeedsSurfaceOccludingDamageRect());
515 if (settings_.show_aggregated_damage)
516 aggregator_->SetFrameAnnotator(std::make_unique<DamageFrameAnnotator>());
517
518 aggregator_->set_output_is_secure(output_is_secure_);
519 aggregator_->SetDisplayColorSpaces(display_color_spaces_);
520 // Consider adding a softare limit as well.
521 aggregator_->SetMaximumTextureSize(
522 (output_surface_ && output_surface_->context_provider())
523 ? output_surface_->context_provider()
524 ->ContextCapabilities()
525 .max_texture_size
526 : 0);
527 }
528
IsRootFrameMissing() const529 bool Display::IsRootFrameMissing() const {
530 return damage_tracker_->root_frame_missing();
531 }
532
HasPendingSurfaces(const BeginFrameArgs & args) const533 bool Display::HasPendingSurfaces(const BeginFrameArgs& args) const {
534 return damage_tracker_->HasPendingSurfaces(args);
535 }
536
OnContextLost()537 void Display::OnContextLost() {
538 if (scheduler_)
539 scheduler_->OutputSurfaceLost();
540 // WARNING: The client may delete the Display in this method call. Do not
541 // make any additional references to members after this call.
542 client_->DisplayOutputSurfaceLost();
543 }
544
DrawAndSwap(base::TimeTicks expected_display_time)545 bool Display::DrawAndSwap(base::TimeTicks expected_display_time) {
546 TRACE_EVENT0("viz", "Display::DrawAndSwap");
547 gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
548
549 if (!current_surface_id_.is_valid()) {
550 TRACE_EVENT_INSTANT0("viz", "No root surface.", TRACE_EVENT_SCOPE_THREAD);
551 return false;
552 }
553
554 if (!output_surface_) {
555 TRACE_EVENT_INSTANT0("viz", "No output surface", TRACE_EVENT_SCOPE_THREAD);
556 return false;
557 }
558
559 if (output_surface_->capabilities().skips_draw) {
560 TRACE_EVENT_INSTANT0("viz", "Skip draw", TRACE_EVENT_SCOPE_THREAD);
561 return true;
562 }
563
564 gfx::OverlayTransform current_display_transform = gfx::OVERLAY_TRANSFORM_NONE;
565 Surface* surface = surface_manager_->GetSurfaceForId(current_surface_id_);
566 if (surface->HasActiveFrame()) {
567 current_display_transform =
568 surface->GetActiveFrame().metadata.display_transform_hint;
569 if (current_display_transform != output_surface_->GetDisplayTransform()) {
570 output_surface_->SetDisplayTransformHint(current_display_transform);
571
572 // Gets the transform from |output_surface_| back so that if it ignores
573 // the hint, the rest of the code ignores the hint too.
574 current_display_transform = output_surface_->GetDisplayTransform();
575 }
576 }
577
578 // During aggregation, SurfaceAggregator marks all resources used for a draw
579 // in the resource provider. This has the side effect of deleting unused
580 // resources and their textures, generating sync tokens, and returning the
581 // resources to the client. This involves GL work which is issued before
582 // drawing commands, and gets prioritized by GPU scheduler because sync token
583 // dependencies aren't issued until the draw.
584 //
585 // Batch and defer returning resources in resource provider. This defers the
586 // GL commands for deleting resources to after the draw, and prevents context
587 // switching because the scheduler knows sync token dependencies at that time.
588 DisplayResourceProvider::ScopedBatchReturnResources returner(
589 resource_provider_.get());
590 base::ElapsedTimer aggregate_timer;
591 aggregate_timer.Begin();
592 CompositorFrame frame;
593 {
594 FrameRateDecider::ScopedAggregate scoped_aggregate(
595 frame_rate_decider_.get());
596 frame =
597 aggregator_->Aggregate(current_surface_id_, expected_display_time,
598 current_display_transform, ++swapped_trace_id_);
599 }
600
601 UMA_HISTOGRAM_COUNTS_1M("Compositing.SurfaceAggregator.AggregateUs",
602 aggregate_timer.Elapsed().InMicroseconds());
603
604 if (frame.render_pass_list.empty()) {
605 TRACE_EVENT_INSTANT0("viz", "Empty aggregated frame.",
606 TRACE_EVENT_SCOPE_THREAD);
607 return false;
608 }
609
610 TRACE_EVENT_ASYNC_BEGIN0("viz,benchmark", "Graphics.Pipeline.DrawAndSwap",
611 swapped_trace_id_);
612
613 // Run callbacks early to allow pipelining and collect presented callbacks.
614 damage_tracker_->RunDrawCallbacks();
615
616 frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
617 stored_latency_info_.begin(),
618 stored_latency_info_.end());
619 stored_latency_info_.clear();
620 bool have_copy_requests = false;
621 for (const auto& pass : frame.render_pass_list)
622 have_copy_requests |= !pass->copy_requests.empty();
623
624 gfx::Size surface_size;
625 bool have_damage = false;
626 auto& last_render_pass = *frame.render_pass_list.back();
627
628 // The CompositorFrame provided by the SurfaceAggregator includes the display
629 // transform while |current_surface_size_| is the pre-transform size received
630 // from the client.
631 const gfx::Transform display_transform = gfx::OverlayTransformToTransform(
632 current_display_transform, gfx::SizeF(current_surface_size_));
633 const gfx::Size current_surface_size =
634 cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
635 display_transform, gfx::Rect(current_surface_size_))
636 .size();
637 if (settings_.auto_resize_output_surface &&
638 last_render_pass.output_rect.size() != current_surface_size &&
639 last_render_pass.damage_rect == last_render_pass.output_rect &&
640 !current_surface_size.IsEmpty()) {
641 // Resize the output rect to the current surface size so that we won't
642 // skip the draw and so that the GL swap won't stretch the output.
643 last_render_pass.output_rect.set_size(current_surface_size);
644 last_render_pass.damage_rect = last_render_pass.output_rect;
645 }
646 surface_size = last_render_pass.output_rect.size();
647 have_damage = !last_render_pass.damage_rect.size().IsEmpty();
648
649 bool size_matches = surface_size == current_surface_size;
650 if (!size_matches)
651 TRACE_EVENT_INSTANT0("viz", "Size mismatch.", TRACE_EVENT_SCOPE_THREAD);
652
653 bool should_draw = have_copy_requests || (have_damage && size_matches);
654 client_->DisplayWillDrawAndSwap(should_draw, &frame.render_pass_list);
655
656 base::Optional<base::ElapsedTimer> draw_timer;
657 if (should_draw) {
658 TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark",
659 "Graphics.Pipeline.DrawAndSwap",
660 swapped_trace_id_, "Draw");
661 base::ElapsedTimer draw_occlusion_timer;
662 RemoveOverdrawQuads(&frame);
663 UMA_HISTOGRAM_COUNTS_1000(
664 "Compositing.Display.Draw.Occlusion.Calculation.Time",
665 draw_occlusion_timer.Elapsed().InMicroseconds());
666
667 bool disable_image_filtering =
668 frame.metadata.is_resourceless_software_draw_with_scroll_or_animation;
669 if (software_renderer_) {
670 software_renderer_->SetDisablePictureQuadImageFiltering(
671 disable_image_filtering);
672 } else {
673 // This should only be set for software draws in synchronous compositor.
674 DCHECK(!disable_image_filtering);
675 }
676
677 draw_timer.emplace();
678 renderer_->DecideRenderPassAllocationsForFrame(frame.render_pass_list);
679 renderer_->DrawFrame(&frame.render_pass_list, device_scale_factor_,
680 current_surface_size, display_color_spaces_);
681 switch (output_surface_->type()) {
682 case OutputSurface::Type::kSoftware:
683 UMA_HISTOGRAM_COUNTS_1M(
684 "Compositing.DirectRenderer.Software.DrawFrameUs",
685 draw_timer->Elapsed().InMicroseconds());
686 break;
687 case OutputSurface::Type::kOpenGL:
688 UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.GL.DrawFrameUs",
689 draw_timer->Elapsed().InMicroseconds());
690 break;
691 case OutputSurface::Type::kVulkan:
692 UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.VK.DrawFrameUs",
693 draw_timer->Elapsed().InMicroseconds());
694 break;
695 }
696 } else {
697 TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD);
698 }
699
700 bool should_swap = !disable_swap_until_resize_ && should_draw && size_matches;
701 if (should_swap) {
702 PresentationGroupTiming presentation_group_timing;
703 presentation_group_timing.OnDraw(draw_timer->Begin());
704
705 for (const auto& id_entry : aggregator_->previous_contained_surfaces()) {
706 Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first);
707 if (surface) {
708 std::unique_ptr<Surface::PresentationHelper> helper =
709 surface->TakePresentationHelperForPresentNotification();
710 if (helper) {
711 presentation_group_timing.AddPresentationHelper(std::move(helper));
712 }
713 }
714 }
715 pending_presentation_group_timings_.emplace_back(
716 std::move(presentation_group_timing));
717
718 TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark",
719 "Graphics.Pipeline.DrawAndSwap",
720 swapped_trace_id_, "WaitForSwap");
721 swapped_since_resize_ = true;
722
723 ui::LatencyInfo::TraceIntermediateFlowEvents(
724 frame.metadata.latency_info,
725 perfetto::protos::pbzero::ChromeLatencyInfo::STEP_DRAW_AND_SWAP);
726
727 cc::benchmark_instrumentation::IssueDisplayRenderingStatsEvent();
728 DirectRenderer::SwapFrameData swap_frame_data;
729 swap_frame_data.latency_info = std::move(frame.metadata.latency_info);
730 if (frame.metadata.top_controls_visible_height.has_value()) {
731 swap_frame_data.top_controls_visible_height_changed =
732 last_top_controls_visible_height_ !=
733 *frame.metadata.top_controls_visible_height;
734 last_top_controls_visible_height_ =
735 *frame.metadata.top_controls_visible_height;
736 }
737
738 // We must notify scheduler and increase |pending_swaps_| before calling
739 // SwapBuffers() as it can call DidReceiveSwapBuffersAck synchronously.
740 if (scheduler_)
741 scheduler_->DidSwapBuffers();
742 pending_swaps_++;
743
744 renderer_->SwapBuffers(std::move(swap_frame_data));
745 } else {
746 TRACE_EVENT_INSTANT0("viz", "Swap skipped.", TRACE_EVENT_SCOPE_THREAD);
747
748 if (have_damage && !size_matches)
749 aggregator_->SetFullDamageForSurface(current_surface_id_);
750
751 if (have_damage) {
752 // Do not store more than the allowed size.
753 if (ui::LatencyInfo::Verify(frame.metadata.latency_info,
754 "Display::DrawAndSwap")) {
755 stored_latency_info_.swap(frame.metadata.latency_info);
756 }
757 } else {
758 // There was no damage. Terminate the latency info objects.
759 while (!frame.metadata.latency_info.empty()) {
760 auto& latency = frame.metadata.latency_info.back();
761 latency.Terminate();
762 frame.metadata.latency_info.pop_back();
763 }
764 }
765
766 renderer_->SwapBuffersSkipped();
767
768 TRACE_EVENT_ASYNC_END1("viz,benchmark", "Graphics.Pipeline.DrawAndSwap",
769 swapped_trace_id_, "status", "canceled");
770 --swapped_trace_id_;
771 if (scheduler_) {
772 scheduler_->DidSwapBuffers();
773 scheduler_->DidReceiveSwapBuffersAck();
774 }
775 }
776
777 client_->DisplayDidDrawAndSwap();
778
779 // Garbage collection can lead to sync IPCs to the GPU service to verify sync
780 // tokens. We defer garbage collection until the end of DrawAndSwap to avoid
781 // stalling the critical path for compositing.
782 surface_manager_->GarbageCollectSurfaces();
783
784 return true;
785 }
786
DidReceiveSwapBuffersAck(const gfx::SwapTimings & timings)787 void Display::DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings) {
788 // Adding to |pending_presentation_group_timings_| must
789 // have been done in DrawAndSwap(), and should not be popped until
790 // DidReceiveSwapBuffersAck.
791 DCHECK(!pending_presentation_group_timings_.empty());
792
793 ++last_swap_ack_trace_id_;
794 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
795 "viz,benchmark", "Graphics.Pipeline.DrawAndSwap", last_swap_ack_trace_id_,
796 "Swap", timings.swap_start);
797 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
798 "viz,benchmark", "Graphics.Pipeline.DrawAndSwap", last_swap_ack_trace_id_,
799 "WaitForPresentation", timings.swap_end);
800
801 DCHECK_GT(pending_swaps_, 0);
802 pending_swaps_--;
803 if (scheduler_) {
804 scheduler_->DidReceiveSwapBuffersAck();
805 }
806
807 if (no_pending_swaps_callback_ && pending_swaps_ == 0)
808 std::move(no_pending_swaps_callback_).Run();
809
810 if (overlay_processor_)
811 overlay_processor_->OverlayPresentationComplete();
812 if (renderer_)
813 renderer_->SwapBuffersComplete();
814
815 // It's possible to receive multiple calls to DidReceiveSwapBuffersAck()
816 // before DidReceivePresentationFeedback(). Ensure that we're not setting
817 // |swap_timings_| for the same PresentationGroupTiming multiple times.
818 base::TimeTicks draw_start_timestamp;
819 for (auto& group_timing : pending_presentation_group_timings_) {
820 if (!group_timing.HasSwapped()) {
821 group_timing.OnSwap(timings);
822 draw_start_timestamp = group_timing.draw_start_timestamp();
823 break;
824 }
825 }
826
827 // We should have at least one group that hasn't received a SwapBuffersAck
828 DCHECK(!draw_start_timestamp.is_null());
829
830 // Check that the swap timings correspond with the timestamp from when
831 // the swap was triggered. Note that not all output surfaces provide timing
832 // information, hence the check for a valid swap_start.
833 if (!timings.swap_start.is_null()) {
834 DCHECK_LE(draw_start_timestamp, timings.swap_start);
835 base::TimeDelta delta = timings.swap_start - draw_start_timestamp;
836 UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
837 "Compositing.Display.DrawToSwapUs", delta, kDrawToSwapMin,
838 kDrawToSwapMax, kDrawToSwapUsBuckets);
839 }
840 }
841
DidReceiveTextureInUseResponses(const gpu::TextureInUseResponses & responses)842 void Display::DidReceiveTextureInUseResponses(
843 const gpu::TextureInUseResponses& responses) {
844 if (renderer_)
845 renderer_->DidReceiveTextureInUseResponses(responses);
846 }
847
DidReceiveCALayerParams(const gfx::CALayerParams & ca_layer_params)848 void Display::DidReceiveCALayerParams(
849 const gfx::CALayerParams& ca_layer_params) {
850 if (client_)
851 client_->DisplayDidReceiveCALayerParams(ca_layer_params);
852 }
853
DidSwapWithSize(const gfx::Size & pixel_size)854 void Display::DidSwapWithSize(const gfx::Size& pixel_size) {
855 if (client_)
856 client_->DisplayDidCompleteSwapWithSize(pixel_size);
857 }
858
DidReceivePresentationFeedback(const gfx::PresentationFeedback & feedback)859 void Display::DidReceivePresentationFeedback(
860 const gfx::PresentationFeedback& feedback) {
861 if (pending_presentation_group_timings_.empty()) {
862 DLOG(ERROR) << "Received unexpected PresentationFeedback";
863 return;
864 }
865 ++last_presented_trace_id_;
866 TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
867 "viz,benchmark", "Graphics.Pipeline.DrawAndSwap",
868 last_presented_trace_id_, feedback.timestamp);
869 auto& presentation_group_timing = pending_presentation_group_timings_.front();
870 auto copy_feedback = SanitizePresentationFeedback(
871 feedback, presentation_group_timing.draw_start_timestamp());
872 TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
873 "benchmark,viz", "Display::FrameDisplayed", TRACE_EVENT_SCOPE_THREAD,
874 copy_feedback.timestamp);
875 presentation_group_timing.OnPresent(copy_feedback);
876 pending_presentation_group_timings_.pop_front();
877 }
878
SetNeedsRedrawRect(const gfx::Rect & damage_rect)879 void Display::SetNeedsRedrawRect(const gfx::Rect& damage_rect) {
880 aggregator_->SetFullDamageForSurface(current_surface_id_);
881 damage_tracker_->SetRootSurfaceDamaged();
882 }
883
DidFinishFrame(const BeginFrameAck & ack)884 void Display::DidFinishFrame(const BeginFrameAck& ack) {
885 for (auto& observer : observers_)
886 observer.OnDisplayDidFinishFrame(ack);
887
888 // Only used with experimental de-jelly effect. Forces us to produce a new
889 // un-skewed frame if the last one had a de-jelly skew applied. This prevents
890 // de-jelly skew from staying on screen for more than one frame.
891 if (aggregator_->last_frame_had_jelly()) {
892 scheduler_->SetNeedsOneBeginFrame(true);
893 }
894 }
895
CurrentSurfaceId()896 const SurfaceId& Display::CurrentSurfaceId() {
897 return current_surface_id_;
898 }
899
GetSurfaceAtAggregation(const FrameSinkId & frame_sink_id) const900 LocalSurfaceId Display::GetSurfaceAtAggregation(
901 const FrameSinkId& frame_sink_id) const {
902 if (!aggregator_)
903 return LocalSurfaceId();
904 auto it = aggregator_->previous_contained_frame_sinks().find(frame_sink_id);
905 if (it == aggregator_->previous_contained_frame_sinks().end())
906 return LocalSurfaceId();
907 return it->second;
908 }
909
SoftwareDeviceUpdatedCALayerParams(const gfx::CALayerParams & ca_layer_params)910 void Display::SoftwareDeviceUpdatedCALayerParams(
911 const gfx::CALayerParams& ca_layer_params) {
912 if (client_)
913 client_->DisplayDidReceiveCALayerParams(ca_layer_params);
914 }
915
ForceImmediateDrawAndSwapIfPossible()916 void Display::ForceImmediateDrawAndSwapIfPossible() {
917 if (scheduler_)
918 scheduler_->ForceImmediateSwapIfPossible();
919 }
920
SetNeedsOneBeginFrame()921 void Display::SetNeedsOneBeginFrame() {
922 if (scheduler_)
923 scheduler_->SetNeedsOneBeginFrame(false);
924 }
925
RemoveOverdrawQuads(CompositorFrame * frame)926 void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
927 if (frame->render_pass_list.empty())
928 return;
929
930 const SharedQuadState* last_sqs = nullptr;
931 cc::Region occlusion_in_target_space;
932 cc::Region backdrop_filters_in_target_space;
933 bool current_sqs_intersects_occlusion = false;
934
935 base::flat_map<RenderPassId, gfx::Rect> backdrop_filter_rects;
936 for (const auto& pass : frame->render_pass_list) {
937 if (!pass->backdrop_filters.IsEmpty() &&
938 pass->backdrop_filters.HasFilterThatMovesPixels()) {
939 backdrop_filter_rects[pass->id] = cc::MathUtil::MapEnclosingClippedRect(
940 pass->transform_to_root_target, pass->output_rect);
941 }
942 }
943
944 const auto& pass = frame->render_pass_list.back();
945 // TODO(yiyix): Add filter effects to draw occlusion calculation and perform
946 // draw occlusion on render pass.
947 if (!pass->filters.IsEmpty() || !pass->backdrop_filters.IsEmpty())
948 return;
949
950 auto quad_list_end = pass->quad_list.end();
951 cc::Region occlusion_in_quad_content_space;
952 gfx::Rect render_pass_quads_in_content_space;
953 for (auto quad = pass->quad_list.begin(); quad != quad_list_end;) {
954 // Skip quad if it is a RenderPassDrawQuad because RenderPassDrawQuad is a
955 // special type of DrawQuad where the visible_rect of shared quad state is
956 // not entirely covered by draw quads in it.
957 if (quad->material == ContentDrawQuadBase::Material::kRenderPass) {
958 // A RenderPass with backdrop filters may apply to a quad underlying
959 // RenderPassQuad. These regions should be tracked so that correctly
960 // handle splitting and occlusion of the underlying quad.
961 auto it = backdrop_filter_rects.find(
962 RenderPassDrawQuad::MaterialCast(*quad)->render_pass_id);
963 if (it != backdrop_filter_rects.end()) {
964 backdrop_filters_in_target_space.Union(it->second);
965 }
966 ++quad;
967 continue;
968 }
969 // Also skip quad if the DrawQuad size is smaller than the
970 // kMinimumDrawOcclusionSize; or the DrawQuad is inside a 3d object.
971 if (quad->shared_quad_state->sorting_context_id != 0) {
972 ++quad;
973 continue;
974 }
975
976 if (!last_sqs)
977 last_sqs = quad->shared_quad_state;
978
979 gfx::Transform transform =
980 quad->shared_quad_state->quad_to_target_transform;
981
982 // TODO(yiyix): Find a rect interior to each transformed quad.
983 if (last_sqs != quad->shared_quad_state) {
984 if (last_sqs->opacity == 1 && last_sqs->are_contents_opaque &&
985 last_sqs->quad_to_target_transform.Preserves2dAxisAlignment()) {
986 gfx::Rect sqs_rect_in_target =
987 cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
988 last_sqs->quad_to_target_transform,
989 last_sqs->visible_quad_layer_rect);
990
991 // If a rounded corner is being applied then the visible rect for the
992 // sqs is actually even smaller. Reduce the rect size to get a
993 // rounded corner adjusted occluding region.
994 if (!last_sqs->rounded_corner_bounds.IsEmpty()) {
995 sqs_rect_in_target.Intersect(gfx::ToEnclosedRect(
996 GetOccludingRectForRRectF(last_sqs->rounded_corner_bounds)));
997 }
998
999 if (last_sqs->is_clipped)
1000 sqs_rect_in_target.Intersect(last_sqs->clip_rect);
1001
1002 // If region complexity is above our threshold, remove the smallest
1003 // rects from occlusion region.
1004 occlusion_in_target_space.Union(sqs_rect_in_target);
1005 while (occlusion_in_target_space.GetRegionComplexity() >
1006 settings_.kMaximumOccluderComplexity) {
1007 gfx::Rect smallest_rect = *occlusion_in_target_space.begin();
1008 for (const auto& occluding_rect : occlusion_in_target_space) {
1009 if (occluding_rect.size().GetArea() <
1010 smallest_rect.size().GetArea())
1011 smallest_rect = occluding_rect;
1012 }
1013 occlusion_in_target_space.Subtract(smallest_rect);
1014 }
1015 }
1016 // If the visible_rect of the current shared quad state does not
1017 // intersect with the occlusion rect, we can skip draw occlusion checks
1018 // for quads in the current SharedQuadState.
1019 last_sqs = quad->shared_quad_state;
1020 occlusion_in_quad_content_space.Clear();
1021 render_pass_quads_in_content_space = gfx::Rect();
1022 const auto current_sqs_in_target_space =
1023 cc::MathUtil::MapEnclosingClippedRect(
1024 transform, last_sqs->visible_quad_layer_rect);
1025 current_sqs_intersects_occlusion =
1026 occlusion_in_target_space.Intersects(current_sqs_in_target_space);
1027
1028 // Compute the occlusion region in the quad content space for scale and
1029 // translation transforms. Note that 0 scale transform will fail the
1030 // positive scale check.
1031 if (current_sqs_intersects_occlusion &&
1032 transform.IsPositiveScaleOrTranslation()) {
1033 gfx::Transform reverse_transform;
1034 bool is_invertible = transform.GetInverse(&reverse_transform);
1035 // Scale transform can be inverted by multiplying 1/scale (given
1036 // scale > 0) and translation transform can be inverted by applying
1037 // the reversed directional translation. Therefore, |transform| is
1038 // always invertible.
1039 DCHECK(is_invertible);
1040 DCHECK_LE(occlusion_in_target_space.GetRegionComplexity(),
1041 settings_.kMaximumOccluderComplexity);
1042
1043 // Since transform can only be a scale or a translation matrix, it is
1044 // safe to use function MapEnclosedRectWith2dAxisAlignedTransform to
1045 // define occluded region in the quad content space with inverted
1046 // transform.
1047 for (const gfx::Rect& rect_in_target_space :
1048 occlusion_in_target_space) {
1049 if (current_sqs_in_target_space.Intersects(rect_in_target_space)) {
1050 auto rect_in_content =
1051 cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
1052 reverse_transform, rect_in_target_space);
1053 occlusion_in_quad_content_space.Union(
1054 SafeConvertRectForRegion(rect_in_content));
1055 }
1056 }
1057
1058 // A render pass quad may apply some filter or transform to an
1059 // underlying quad. Do not split quads when they intersect with a render
1060 // pass quad.
1061 if (current_sqs_in_target_space.Intersects(
1062 backdrop_filters_in_target_space.bounds())) {
1063 for (const auto& rect_in_target_space :
1064 backdrop_filters_in_target_space) {
1065 auto rect_in_content =
1066 cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
1067 reverse_transform, rect_in_target_space);
1068 render_pass_quads_in_content_space.Union(rect_in_content);
1069 }
1070 }
1071 }
1072 }
1073
1074 if (!current_sqs_intersects_occlusion) {
1075 ++quad;
1076 continue;
1077 }
1078
1079 if (occlusion_in_quad_content_space.Contains(quad->visible_rect)) {
1080 // Case 1: for simple transforms (scale or translation), define the
1081 // occlusion region in the quad content space. If |quad| is not
1082 // shown on the screen, then set its rect and visible_rect to be empty.
1083 quad->visible_rect.set_size(gfx::Size());
1084 } else if (occlusion_in_quad_content_space.Intersects(quad->visible_rect)) {
1085 // Case 2: for simple transforms, if the quad is partially shown on
1086 // screen and the region formed by (occlusion region - visible_rect) is
1087 // a rect, then update visible_rect to the resulting rect.
1088 cc::Region visible_region = quad->visible_rect;
1089 visible_region.Subtract(occlusion_in_quad_content_space);
1090 quad->visible_rect = visible_region.bounds();
1091
1092 // Split quad into multiple draw quads when area can be reduce by
1093 // more than X fragments.
1094 const bool should_split_quads =
1095 enable_quad_splitting_ &&
1096 !visible_region.Intersects(render_pass_quads_in_content_space) &&
1097 ReduceComplexity(visible_region, settings_.quad_split_limit,
1098 &cached_visible_region_) &&
1099 CanSplitQuad(quad->material, ComputeArea(cached_visible_region_),
1100 visible_region.bounds().size().GetArea(),
1101 settings_.minimum_fragments_reduced,
1102 device_scale_factor_);
1103 if (should_split_quads) {
1104 auto new_quad = pass->quad_list.InsertCopyBeforeDrawQuad(
1105 quad, cached_visible_region_.size() - 1);
1106 for (const auto& visible_rect : cached_visible_region_) {
1107 new_quad->visible_rect = visible_rect;
1108 ++new_quad;
1109 }
1110 quad = new_quad;
1111 continue;
1112 }
1113 } else if (occlusion_in_quad_content_space.IsEmpty() &&
1114 occlusion_in_target_space.Contains(
1115 cc::MathUtil::MapEnclosingClippedRect(transform,
1116 quad->visible_rect))) {
1117 // Case 3: for non simple transforms, define the occlusion region in
1118 // target space. If |quad| is not shown on the screen, then set its
1119 // rect and visible_rect to be empty.
1120 quad->visible_rect.set_size(gfx::Size());
1121 }
1122 ++quad;
1123 }
1124 }
1125
SetPreferredFrameInterval(base::TimeDelta interval)1126 void Display::SetPreferredFrameInterval(base::TimeDelta interval) {
1127 client_->SetPreferredFrameInterval(interval);
1128 }
1129
GetPreferredFrameIntervalForFrameSinkId(const FrameSinkId & id)1130 base::TimeDelta Display::GetPreferredFrameIntervalForFrameSinkId(
1131 const FrameSinkId& id) {
1132 return client_->GetPreferredFrameIntervalForFrameSinkId(id);
1133 }
1134
SetSupportedFrameIntervals(std::vector<base::TimeDelta> intervals)1135 void Display::SetSupportedFrameIntervals(
1136 std::vector<base::TimeDelta> intervals) {
1137 frame_rate_decider_->SetSupportedFrameIntervals(std::move(intervals));
1138 }
1139
GetCacheBackBufferCb()1140 base::ScopedClosureRunner Display::GetCacheBackBufferCb() {
1141 return output_surface_->GetCacheBackBufferCb();
1142 }
1143
1144 } // namespace viz
1145