1 // Copyright 2012 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/direct_renderer.h"
6
7 #include <limits.h>
8 #include <stddef.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/auto_reset.h"
14 #include "base/containers/circular_deque.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/stl_util.h"
19 #include "base/trace_event/trace_event.h"
20 #include "build/build_config.h"
21 #include "cc/base/math_util.h"
22 #include "cc/paint/filter_operations.h"
23 #include "components/viz/common/display/renderer_settings.h"
24 #include "components/viz/common/frame_sinks/copy_output_request.h"
25 #include "components/viz/common/frame_sinks/copy_output_util.h"
26 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
27 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
28 #include "components/viz/common/quads/draw_quad.h"
29 #include "components/viz/service/display/bsp_tree.h"
30 #include "components/viz/service/display/bsp_walk_action.h"
31 #include "components/viz/service/display/output_surface.h"
32 #include "components/viz/service/display/skia_output_surface.h"
33 #include "ui/gfx/geometry/quad_f.h"
34 #include "ui/gfx/geometry/rect_conversions.h"
35 #include "ui/gfx/transform.h"
36 #include "ui/gfx/transform_util.h"
37
38 namespace {
39
40 // Returns the bounding box that contains the specified rounded corner.
ComputeRoundedCornerBoundingBox(const gfx::RRectF & rrect,const gfx::RRectF::Corner corner)41 gfx::RectF ComputeRoundedCornerBoundingBox(const gfx::RRectF& rrect,
42 const gfx::RRectF::Corner corner) {
43 auto radii = rrect.GetCornerRadii(corner);
44 gfx::RectF bounding_box(radii.x(), radii.y());
45 switch (corner) {
46 case gfx::RRectF::Corner::kUpperLeft:
47 bounding_box.Offset(rrect.rect().x(), rrect.rect().y());
48 break;
49 case gfx::RRectF::Corner::kUpperRight:
50 bounding_box.Offset(rrect.rect().right() - radii.x(), rrect.rect().y());
51 break;
52 case gfx::RRectF::Corner::kLowerRight:
53 bounding_box.Offset(rrect.rect().right() - radii.x(),
54 rrect.rect().bottom() - radii.y());
55 break;
56 case gfx::RRectF::Corner::kLowerLeft:
57 bounding_box.Offset(rrect.rect().x(), rrect.rect().bottom() - radii.y());
58 break;
59 }
60 return bounding_box;
61 }
62
63 } // namespace
64
65 namespace viz {
66
67 DirectRenderer::DrawingFrame::DrawingFrame() = default;
68 DirectRenderer::DrawingFrame::~DrawingFrame() = default;
69
70 DirectRenderer::SwapFrameData::SwapFrameData() = default;
71 DirectRenderer::SwapFrameData::~SwapFrameData() = default;
72 DirectRenderer::SwapFrameData::SwapFrameData(SwapFrameData&&) = default;
73 DirectRenderer::SwapFrameData& DirectRenderer::SwapFrameData::operator=(
74 SwapFrameData&&) = default;
75
DirectRenderer(const RendererSettings * settings,const DebugRendererSettings * debug_settings,OutputSurface * output_surface,DisplayResourceProvider * resource_provider,OverlayProcessorInterface * overlay_processor)76 DirectRenderer::DirectRenderer(const RendererSettings* settings,
77 const DebugRendererSettings* debug_settings,
78 OutputSurface* output_surface,
79 DisplayResourceProvider* resource_provider,
80 OverlayProcessorInterface* overlay_processor)
81 : settings_(settings),
82 debug_settings_(debug_settings),
83 output_surface_(output_surface),
84 resource_provider_(resource_provider),
85 overlay_processor_(overlay_processor) {
86 DCHECK(output_surface_);
87 }
88
89 DirectRenderer::~DirectRenderer() = default;
90
Initialize()91 void DirectRenderer::Initialize() {
92 auto* context_provider = output_surface_->context_provider();
93
94 use_partial_swap_ = settings_->partial_swap_enabled && CanPartialSwap();
95 allow_empty_swap_ = use_partial_swap_;
96 if (context_provider) {
97 if (context_provider->ContextCapabilities().commit_overlay_planes)
98 allow_empty_swap_ = true;
99 #if DCHECK_IS_ON()
100 supports_occlusion_query_ =
101 context_provider->ContextCapabilities().occlusion_query;
102 #endif
103 } else {
104 allow_empty_swap_ |=
105 output_surface_->capabilities().supports_commit_overlay_planes;
106 }
107
108 initialized_ = true;
109 }
110
111 // static
QuadVertexRect()112 gfx::RectF DirectRenderer::QuadVertexRect() {
113 return gfx::RectF(-0.5f, -0.5f, 1.f, 1.f);
114 }
115
116 // static
QuadRectTransform(gfx::Transform * quad_rect_transform,const gfx::Transform & quad_transform,const gfx::RectF & quad_rect)117 void DirectRenderer::QuadRectTransform(gfx::Transform* quad_rect_transform,
118 const gfx::Transform& quad_transform,
119 const gfx::RectF& quad_rect) {
120 *quad_rect_transform = quad_transform;
121 quad_rect_transform->Translate(0.5 * quad_rect.width() + quad_rect.x(),
122 0.5 * quad_rect.height() + quad_rect.y());
123 quad_rect_transform->Scale(quad_rect.width(), quad_rect.height());
124 }
125
InitializeViewport(DrawingFrame * frame,const gfx::Rect & draw_rect,const gfx::Rect & viewport_rect,const gfx::Size & surface_size)126 void DirectRenderer::InitializeViewport(DrawingFrame* frame,
127 const gfx::Rect& draw_rect,
128 const gfx::Rect& viewport_rect,
129 const gfx::Size& surface_size) {
130 DCHECK_GE(viewport_rect.x(), 0);
131 DCHECK_GE(viewport_rect.y(), 0);
132 DCHECK_LE(viewport_rect.right(), surface_size.width());
133 DCHECK_LE(viewport_rect.bottom(), surface_size.height());
134 bool flip_y = FlippedFramebuffer();
135 if (flip_y) {
136 frame->projection_matrix = gfx::OrthoProjectionMatrix(
137 draw_rect.x(), draw_rect.right(), draw_rect.bottom(), draw_rect.y());
138 } else {
139 frame->projection_matrix = gfx::OrthoProjectionMatrix(
140 draw_rect.x(), draw_rect.right(), draw_rect.y(), draw_rect.bottom());
141 }
142
143 gfx::Rect window_rect = viewport_rect;
144 if (flip_y)
145 window_rect.set_y(surface_size.height() - viewport_rect.bottom());
146 frame->window_matrix =
147 gfx::WindowMatrix(window_rect.x(), window_rect.y(), window_rect.width(),
148 window_rect.height());
149 current_draw_rect_ = draw_rect;
150 current_viewport_rect_ = viewport_rect;
151 current_surface_size_ = surface_size;
152 current_window_space_viewport_ = window_rect;
153 }
154
MoveFromDrawToWindowSpace(const gfx::Rect & draw_rect) const155 gfx::Rect DirectRenderer::MoveFromDrawToWindowSpace(
156 const gfx::Rect& draw_rect) const {
157 gfx::Rect window_rect = draw_rect;
158 window_rect -= current_draw_rect_.OffsetFromOrigin();
159 window_rect += current_viewport_rect_.OffsetFromOrigin();
160 if (FlippedFramebuffer())
161 window_rect.set_y(current_surface_size_.height() - window_rect.bottom());
162 return window_rect;
163 }
164
CanPassBeDrawnDirectly(const AggregatedRenderPass * pass)165 const DrawQuad* DirectRenderer::CanPassBeDrawnDirectly(
166 const AggregatedRenderPass* pass) {
167 return nullptr;
168 }
169
SetVisible(bool visible)170 void DirectRenderer::SetVisible(bool visible) {
171 DCHECK(initialized_);
172 if (visible_ == visible)
173 return;
174 visible_ = visible;
175 DidChangeVisibility();
176 }
177
DecideRenderPassAllocationsForFrame(const AggregatedRenderPassList & render_passes_in_draw_order)178 void DirectRenderer::DecideRenderPassAllocationsForFrame(
179 const AggregatedRenderPassList& render_passes_in_draw_order) {
180 DCHECK(render_pass_bypass_quads_.empty());
181
182 auto& root_render_pass = render_passes_in_draw_order.back();
183
184 base::flat_map<AggregatedRenderPassId, RenderPassRequirements>
185 render_passes_in_frame;
186 for (const auto& pass : render_passes_in_draw_order) {
187 // If there's a copy request, we need an explicit renderpass backing so
188 // only try to draw directly if there are no copy requests.
189 if (pass != root_render_pass && pass->copy_requests.empty()) {
190 if (const DrawQuad* quad = CanPassBeDrawnDirectly(pass.get())) {
191 // If the render pass is drawn directly, it will not be drawn from as
192 // a render pass so it's not added to the map.
193 render_pass_bypass_quads_[pass->id] = quad;
194 continue;
195 }
196 }
197 render_passes_in_frame[pass->id] = {
198 CalculateTextureSizeForRenderPass(pass.get()), pass->generate_mipmap};
199 }
200 UMA_HISTOGRAM_COUNTS_1000(
201 "Compositing.Display.FlattenedRenderPassCount",
202 base::saturated_cast<int>(render_passes_in_draw_order.size() -
203 render_pass_bypass_quads_.size()));
204 UpdateRenderPassTextures(render_passes_in_draw_order, render_passes_in_frame);
205 }
206
DrawFrame(AggregatedRenderPassList * render_passes_in_draw_order,float device_scale_factor,const gfx::Size & device_viewport_size,const gfx::DisplayColorSpaces & display_color_spaces,SurfaceDamageRectList * surface_damage_rect_list)207 void DirectRenderer::DrawFrame(
208 AggregatedRenderPassList* render_passes_in_draw_order,
209 float device_scale_factor,
210 const gfx::Size& device_viewport_size,
211 const gfx::DisplayColorSpaces& display_color_spaces,
212 SurfaceDamageRectList* surface_damage_rect_list) {
213 DCHECK(visible_);
214 TRACE_EVENT0("viz,benchmark", "DirectRenderer::DrawFrame");
215 UMA_HISTOGRAM_COUNTS_1M(
216 "Renderer4.renderPassCount",
217 base::saturated_cast<int>(render_passes_in_draw_order->size()));
218
219 auto* root_render_pass = render_passes_in_draw_order->back().get();
220 DCHECK(root_render_pass);
221
222 #if DCHECK_IS_ON()
223 bool overdraw_tracing_enabled;
224 TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"),
225 &overdraw_tracing_enabled);
226 DLOG_IF(WARNING, !overdraw_tracing_support_missing_logged_once_ &&
227 overdraw_tracing_enabled && !supports_occlusion_query_)
228 << "Overdraw tracing enabled on platform without support.";
229 overdraw_tracing_support_missing_logged_once_ = true;
230 #endif
231
232 bool overdraw_feedback = debug_settings_->show_overdraw_feedback;
233 if (overdraw_feedback && !output_surface_->capabilities().supports_stencil) {
234 #if DCHECK_IS_ON()
235 DLOG_IF(WARNING, !overdraw_feedback_support_missing_logged_once_)
236 << "Overdraw feedback enabled on platform without support.";
237 overdraw_feedback_support_missing_logged_once_ = true;
238 #endif
239 overdraw_feedback = false;
240 }
241 base::AutoReset<bool> auto_reset_overdraw_feedback(&overdraw_feedback_,
242 overdraw_feedback);
243
244 current_frame_valid_ = true;
245 current_frame_ = DrawingFrame();
246 current_frame()->render_passes_in_draw_order = render_passes_in_draw_order;
247 current_frame()->root_render_pass = root_render_pass;
248 current_frame()->root_damage_rect = root_render_pass->damage_rect;
249 if (overlay_processor_) {
250 current_frame()->root_damage_rect.Union(
251 overlay_processor_->GetAndResetOverlayDamage());
252 }
253 if (DelegatedInkPointRendererBase* ink_renderer =
254 GetDelegatedInkPointRenderer()) {
255 // The path must be finalized before GetDamageRect() can return an accurate
256 // rect that will allow the old trail to be removed and the new trail to
257 // be drawn at the same time.
258 ink_renderer->FinalizePathForDraw();
259 gfx::Rect delegated_ink_damage_rect = ink_renderer->GetDamageRect();
260
261 // The viewport could have changed size since the presentation area was
262 // created and propagated, such as if is window was resized. Intersect the
263 // viewport here to ensure the damage rect doesn't extend beyond the current
264 // viewport.
265 delegated_ink_damage_rect.Intersect(gfx::Rect(device_viewport_size));
266 current_frame()->root_damage_rect.Union(delegated_ink_damage_rect);
267 }
268 current_frame()->root_damage_rect.Intersect(gfx::Rect(device_viewport_size));
269 current_frame()->device_viewport_size = device_viewport_size;
270 current_frame()->display_color_spaces = display_color_spaces;
271
272 output_surface_->SetNeedsMeasureNextDrawLatency();
273 BeginDrawingFrame();
274
275 // RenderPass owns filters, backdrop_filters, etc., and will outlive this
276 // function call. So it is safe to store pointers in these maps.
277 for (const auto& pass : *render_passes_in_draw_order) {
278 if (!pass->filters.IsEmpty())
279 render_pass_filters_[pass->id] = &pass->filters;
280 if (!pass->backdrop_filters.IsEmpty()) {
281 render_pass_backdrop_filters_[pass->id] = &pass->backdrop_filters;
282 render_pass_backdrop_filter_bounds_[pass->id] =
283 pass->backdrop_filter_bounds;
284 if (pass->backdrop_filters.HasFilterThatMovesPixels()) {
285 backdrop_filter_output_rects_[pass->id] =
286 cc::MathUtil::MapEnclosingClippedRect(
287 pass->transform_to_root_target, pass->output_rect);
288 }
289 }
290 }
291
292 bool frame_has_alpha =
293 current_frame()->root_render_pass->has_transparent_background;
294 gfx::ColorSpace frame_color_space = RootRenderPassColorSpace();
295 gfx::BufferFormat frame_buffer_format =
296 current_frame()->display_color_spaces.GetOutputBufferFormat(
297 current_frame()->root_render_pass->content_color_usage,
298 frame_has_alpha);
299 if (overlay_processor_) {
300 // Display transform and viewport size are needed for overlay validator on
301 // Android SurfaceControl, and viewport size is need on Windows. These need
302 // to be called before ProcessForOverlays.
303 overlay_processor_->SetDisplayTransformHint(
304 output_surface_->GetDisplayTransform());
305 overlay_processor_->SetViewportSize(device_viewport_size);
306
307 // Before ProcessForOverlay calls into the hardware to ask about whether the
308 // overlay setup can be handled, we need to set up the primary plane.
309 OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane =
310 nullptr;
311 if (output_surface_->IsDisplayedAsOverlayPlane()) {
312 // OutputSurface::GetOverlayMailbox() returns the mailbox for the last
313 // used buffer, which is most likely different from the one being used
314 // this frame. However, for the purpose of testing the overlay
315 // configuration, the mailbox for ANY buffer from BufferQueue is good
316 // enough because they're all created with identical properties.
317 current_frame()->output_surface_plane =
318 overlay_processor_->ProcessOutputSurfaceAsOverlay(
319 device_viewport_size, frame_buffer_format, frame_color_space,
320 frame_has_alpha, output_surface_->GetOverlayMailbox());
321 primary_plane = &(current_frame()->output_surface_plane.value());
322 }
323
324 // Attempt to replace some or all of the quads of the root render pass with
325 // overlays.
326 overlay_processor_->ProcessForOverlays(
327 resource_provider_, render_passes_in_draw_order,
328 output_surface_->color_matrix(), render_pass_filters_,
329 render_pass_backdrop_filters_, surface_damage_rect_list, primary_plane,
330 ¤t_frame()->overlay_list, ¤t_frame()->root_damage_rect,
331 ¤t_frame()->root_content_bounds);
332
333 // If we promote any quad to an underlay then the main plane must support
334 // alpha.
335 // TODO(ccameron): We should update |frame_color_space|, and
336 // |frame_buffer_format| based on the change in |frame_has_alpha|.
337 if (current_frame()->output_surface_plane) {
338 frame_has_alpha |= current_frame()->output_surface_plane->enable_blending;
339 root_render_pass->has_transparent_background = frame_has_alpha;
340 }
341
342 overlay_processor_->AdjustOutputSurfaceOverlay(
343 &(current_frame()->output_surface_plane));
344 }
345
346 // Only reshape when we know we are going to draw. Otherwise, the reshape
347 // can leave the window at the wrong size if we never draw and the proper
348 // viewport size is never set.
349 bool use_stencil = overdraw_feedback_;
350 bool needs_full_frame_redraw = false;
351 if (device_viewport_size != reshape_surface_size_ ||
352 device_scale_factor != reshape_device_scale_factor_ ||
353 frame_color_space != reshape_color_space_ ||
354 frame_buffer_format != reshape_buffer_format_ ||
355 use_stencil != reshape_use_stencil_) {
356 reshape_surface_size_ = device_viewport_size;
357 reshape_device_scale_factor_ = device_scale_factor;
358 reshape_color_space_ = frame_color_space;
359 reshape_buffer_format_ = frame_buffer_format;
360 reshape_use_stencil_ = overdraw_feedback_;
361 output_surface_->Reshape(reshape_surface_size_,
362 reshape_device_scale_factor_, reshape_color_space_,
363 *reshape_buffer_format_, reshape_use_stencil_);
364 #if defined(OS_APPLE)
365 // For Mac, all render passes will be promoted to CALayer, the redraw full
366 // frame is for the main surface only.
367 // TODO(penghuang): verify this logic with SkiaRenderer.
368 if (!output_surface_->capabilities().supports_surfaceless)
369 needs_full_frame_redraw = true;
370 #else
371 // The entire surface has to be redrawn if reshape is requested.
372 needs_full_frame_redraw = true;
373 #endif
374 }
375
376 // Draw all non-root render passes except for the root render pass.
377 for (const auto& pass : *render_passes_in_draw_order) {
378 if (pass.get() == root_render_pass)
379 break;
380 DrawRenderPassAndExecuteCopyRequests(pass.get());
381 }
382
383 bool skip_drawing_root_render_pass =
384 current_frame()->root_damage_rect.IsEmpty() && allow_empty_swap_ &&
385 !needs_full_frame_redraw;
386
387 // If partial swap is not used, and the frame can not be skipped, the whole
388 // frame has to be redrawn.
389 if (!use_partial_swap_ && !skip_drawing_root_render_pass)
390 needs_full_frame_redraw = true;
391
392 // If we need to redraw the frame, the whole output should be considered
393 // damaged.
394 if (needs_full_frame_redraw)
395 current_frame()->root_damage_rect = gfx::Rect(device_viewport_size);
396
397 if (!skip_drawing_root_render_pass)
398 DrawRenderPassAndExecuteCopyRequests(root_render_pass);
399
400 // Use a fence to synchronize display of the main fb used by the output
401 // surface. Note that gpu_fence_id may have the special value 0 ("no fence")
402 // if fences are not supported. In that case synchronization will happen
403 // through other means on the service side.
404 // TODO(afrantzis): Consider using per-overlay fences instead of the one
405 // associated with the output surface when possible.
406 if (current_frame()->output_surface_plane)
407 current_frame()->output_surface_plane->gpu_fence_id =
408 output_surface_->UpdateGpuFence();
409
410 if (overlay_processor_)
411 overlay_processor_->TakeOverlayCandidates(¤t_frame()->overlay_list);
412
413 FinishDrawingFrame();
414
415 if (overlay_processor_)
416 overlay_processor_->ScheduleOverlays(resource_provider_);
417
418 render_passes_in_draw_order->clear();
419 render_pass_filters_.clear();
420 render_pass_backdrop_filters_.clear();
421 render_pass_backdrop_filter_bounds_.clear();
422 render_pass_bypass_quads_.clear();
423 backdrop_filter_output_rects_.clear();
424
425 current_frame_valid_ = false;
426 }
427
GetTargetDamageBoundingRect() const428 gfx::Rect DirectRenderer::GetTargetDamageBoundingRect() const {
429 gfx::Rect bounding_rect = output_surface_->GetCurrentFramebufferDamage();
430 if (overlay_processor_) {
431 bounding_rect.Union(
432 overlay_processor_->GetPreviousFrameOverlaysBoundingRect());
433 }
434 return bounding_rect;
435 }
436
DeviceViewportRectInDrawSpace() const437 gfx::Rect DirectRenderer::DeviceViewportRectInDrawSpace() const {
438 gfx::Rect device_viewport_rect(current_frame()->device_viewport_size);
439 device_viewport_rect -= current_viewport_rect_.OffsetFromOrigin();
440 device_viewport_rect += current_draw_rect_.OffsetFromOrigin();
441 return device_viewport_rect;
442 }
443
OutputSurfaceRectInDrawSpace() const444 gfx::Rect DirectRenderer::OutputSurfaceRectInDrawSpace() const {
445 if (current_frame()->current_render_pass ==
446 current_frame()->root_render_pass) {
447 gfx::Rect output_surface_rect(current_frame()->device_viewport_size);
448 output_surface_rect -= current_viewport_rect_.OffsetFromOrigin();
449 output_surface_rect += current_draw_rect_.OffsetFromOrigin();
450 return output_surface_rect;
451 } else {
452 return current_frame()->current_render_pass->output_rect;
453 }
454 }
455
ShouldSkipQuad(const DrawQuad & quad,const gfx::Rect & render_pass_scissor)456 bool DirectRenderer::ShouldSkipQuad(const DrawQuad& quad,
457 const gfx::Rect& render_pass_scissor) {
458 if (render_pass_scissor.IsEmpty())
459 return true;
460
461 gfx::Rect target_rect = cc::MathUtil::MapEnclosingClippedRect(
462 quad.shared_quad_state->quad_to_target_transform, quad.visible_rect);
463 if (quad.shared_quad_state->is_clipped)
464 target_rect.Intersect(quad.shared_quad_state->clip_rect);
465
466 target_rect.Intersect(render_pass_scissor);
467 return target_rect.IsEmpty();
468 }
469
SetScissorStateForQuad(const DrawQuad & quad,const gfx::Rect & render_pass_scissor,bool use_render_pass_scissor)470 void DirectRenderer::SetScissorStateForQuad(
471 const DrawQuad& quad,
472 const gfx::Rect& render_pass_scissor,
473 bool use_render_pass_scissor) {
474 if (use_render_pass_scissor) {
475 gfx::Rect quad_scissor_rect = render_pass_scissor;
476 if (quad.shared_quad_state->is_clipped)
477 quad_scissor_rect.Intersect(quad.shared_quad_state->clip_rect);
478 SetScissorTestRectInDrawSpace(quad_scissor_rect);
479 return;
480 } else if (quad.shared_quad_state->is_clipped) {
481 SetScissorTestRectInDrawSpace(quad.shared_quad_state->clip_rect);
482 return;
483 }
484
485 EnsureScissorTestDisabled();
486 }
487
SetScissorTestRectInDrawSpace(const gfx::Rect & draw_space_rect)488 void DirectRenderer::SetScissorTestRectInDrawSpace(
489 const gfx::Rect& draw_space_rect) {
490 gfx::Rect window_space_rect = MoveFromDrawToWindowSpace(draw_space_rect);
491 SetScissorTestRect(window_space_rect);
492 }
493
DoDrawPolygon(const DrawPolygon & poly,const gfx::Rect & render_pass_scissor,bool use_render_pass_scissor)494 void DirectRenderer::DoDrawPolygon(const DrawPolygon& poly,
495 const gfx::Rect& render_pass_scissor,
496 bool use_render_pass_scissor) {
497 SetScissorStateForQuad(*poly.original_ref(), render_pass_scissor,
498 use_render_pass_scissor);
499
500 // If the poly has not been split, then it is just a normal DrawQuad,
501 // and we should save any extra processing that would have to be done.
502 if (!poly.is_split()) {
503 DoDrawQuad(poly.original_ref(), nullptr);
504 return;
505 }
506
507 std::vector<gfx::QuadF> quads;
508 poly.ToQuads2D(&quads);
509 for (size_t i = 0; i < quads.size(); ++i) {
510 DoDrawQuad(poly.original_ref(), &quads[i]);
511 }
512 }
513
FiltersForPass(AggregatedRenderPassId render_pass_id) const514 const cc::FilterOperations* DirectRenderer::FiltersForPass(
515 AggregatedRenderPassId render_pass_id) const {
516 auto it = render_pass_filters_.find(render_pass_id);
517 return it == render_pass_filters_.end() ? nullptr : it->second;
518 }
519
BackdropFiltersForPass(AggregatedRenderPassId render_pass_id) const520 const cc::FilterOperations* DirectRenderer::BackdropFiltersForPass(
521 AggregatedRenderPassId render_pass_id) const {
522 auto it = render_pass_backdrop_filters_.find(render_pass_id);
523 return it == render_pass_backdrop_filters_.end() ? nullptr : it->second;
524 }
525
BackdropFilterBoundsForPass(AggregatedRenderPassId render_pass_id) const526 const base::Optional<gfx::RRectF> DirectRenderer::BackdropFilterBoundsForPass(
527 AggregatedRenderPassId render_pass_id) const {
528 auto it = render_pass_backdrop_filter_bounds_.find(render_pass_id);
529 return it == render_pass_backdrop_filter_bounds_.end()
530 ? base::Optional<gfx::RRectF>()
531 : it->second;
532 }
533
FlushPolygons(base::circular_deque<std::unique_ptr<DrawPolygon>> * poly_list,const gfx::Rect & render_pass_scissor,bool use_render_pass_scissor)534 void DirectRenderer::FlushPolygons(
535 base::circular_deque<std::unique_ptr<DrawPolygon>>* poly_list,
536 const gfx::Rect& render_pass_scissor,
537 bool use_render_pass_scissor) {
538 if (poly_list->empty()) {
539 return;
540 }
541
542 BspTree bsp_tree(poly_list);
543 BspWalkActionDrawPolygon action_handler(this, render_pass_scissor,
544 use_render_pass_scissor);
545 bsp_tree.TraverseWithActionHandler(&action_handler);
546 DCHECK(poly_list->empty());
547 }
548
DrawRenderPassAndExecuteCopyRequests(AggregatedRenderPass * render_pass)549 void DirectRenderer::DrawRenderPassAndExecuteCopyRequests(
550 AggregatedRenderPass* render_pass) {
551 if (render_pass_bypass_quads_.find(render_pass->id) !=
552 render_pass_bypass_quads_.end()) {
553 return;
554 }
555
556 // Repeated draw to simulate a slower device for the evaluation of performance
557 // improvements in UI effects.
558 for (int i = 0; i < settings_->slow_down_compositing_scale_factor; ++i)
559 DrawRenderPass(render_pass);
560
561 for (auto& request : render_pass->copy_requests) {
562 // Finalize the source subrect (output_rect, result_bounds,
563 // sampling_bounds), as the entirety of the RenderPass's output optionally
564 // clamped to the requested copy area. Then, compute the result rect
565 // (result_selection), which is the selection clamped to the maximum
566 // possible result bounds. If there will be zero pixels of output or the
567 // scaling ratio was not reasonable, do not proceed.
568 gfx::Rect output_rect = render_pass->output_rect;
569 if (request->has_area())
570 output_rect.Intersect(request->area());
571
572 copy_output::RenderPassGeometry geometry;
573 geometry.result_bounds =
574 request->is_scaled() ? copy_output::ComputeResultRect(
575 gfx::Rect(output_rect.size()),
576 request->scale_from(), request->scale_to())
577 : gfx::Rect(output_rect.size());
578
579 geometry.result_selection = geometry.result_bounds;
580 if (request->has_result_selection())
581 geometry.result_selection.Intersect(request->result_selection());
582 if (geometry.result_selection.IsEmpty())
583 continue;
584
585 geometry.sampling_bounds = MoveFromDrawToWindowSpace(output_rect);
586
587 geometry.readback_offset =
588 MoveFromDrawToWindowSpace(geometry.result_selection +
589 output_rect.OffsetFromOrigin())
590 .OffsetFromOrigin();
591 CopyDrawnRenderPass(geometry, std::move(request));
592 }
593 }
594
DrawRenderPass(const AggregatedRenderPass * render_pass)595 void DirectRenderer::DrawRenderPass(const AggregatedRenderPass* render_pass) {
596 TRACE_EVENT0("viz", "DirectRenderer::DrawRenderPass");
597 if (CanSkipRenderPass(render_pass))
598 return;
599 UseRenderPass(render_pass);
600
601 // TODO(crbug.com/582554): This change applies only when Vulkan is enabled and
602 // it will be removed once SkiaRenderer has complete support for Vulkan.
603 if (current_frame()->current_render_pass !=
604 current_frame()->root_render_pass &&
605 !IsRenderPassResourceAllocated(render_pass->id))
606 return;
607
608 const gfx::Rect surface_rect_in_draw_space = OutputSurfaceRectInDrawSpace();
609 gfx::Rect render_pass_scissor_in_draw_space = surface_rect_in_draw_space;
610
611 bool is_root_render_pass =
612 current_frame()->current_render_pass == current_frame()->root_render_pass;
613 if (is_root_render_pass) {
614 render_pass_scissor_in_draw_space.Intersect(
615 DeviceViewportRectInDrawSpace());
616 }
617
618 if (use_partial_swap_) {
619 render_pass_scissor_in_draw_space.Intersect(
620 ComputeScissorRectForRenderPass(current_frame()->current_render_pass));
621 }
622
623 const bool render_pass_is_clipped =
624 !render_pass_scissor_in_draw_space.Contains(surface_rect_in_draw_space);
625
626 // The SetDrawRectangleCHROMIUM spec requires that the scissor bit is always
627 // set on the root framebuffer or else the rendering may modify something
628 // outside the damage rectangle, even if the damage rectangle is the size of
629 // the full backbuffer.
630 const bool supports_dc_layers =
631 output_surface_->capabilities().supports_dc_layers;
632 const bool render_pass_requires_scissor =
633 render_pass_is_clipped || (supports_dc_layers && is_root_render_pass);
634
635 const bool has_external_stencil_test =
636 is_root_render_pass && output_surface_->HasExternalStencilTest();
637 const bool should_clear_surface =
638 !has_external_stencil_test &&
639 (!is_root_render_pass || settings_->should_clear_root_render_pass);
640
641 // If |has_external_stencil_test| we can't discard or clear. Make sure we
642 // don't need to.
643 DCHECK(!has_external_stencil_test ||
644 !current_frame()->current_render_pass->has_transparent_background);
645
646 SurfaceInitializationMode mode;
647 if (should_clear_surface && render_pass_requires_scissor) {
648 mode = SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR;
649 } else if (should_clear_surface) {
650 mode = SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR;
651 } else {
652 mode = SURFACE_INITIALIZATION_MODE_PRESERVE;
653 }
654
655 PrepareSurfaceForPass(
656 mode, MoveFromDrawToWindowSpace(render_pass_scissor_in_draw_space));
657
658 if (is_root_render_pass)
659 last_root_render_pass_scissor_rect_ = render_pass_scissor_in_draw_space;
660
661 const QuadList& quad_list = render_pass->quad_list;
662 base::circular_deque<std::unique_ptr<DrawPolygon>> poly_list;
663
664 int next_polygon_id = 0;
665 int last_sorting_context_id = 0;
666 for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd();
667 ++it) {
668 const DrawQuad& quad = **it;
669
670 if (render_pass_is_clipped &&
671 ShouldSkipQuad(quad, render_pass_scissor_in_draw_space)) {
672 continue;
673 }
674
675 if (last_sorting_context_id != quad.shared_quad_state->sorting_context_id) {
676 last_sorting_context_id = quad.shared_quad_state->sorting_context_id;
677 FlushPolygons(&poly_list, render_pass_scissor_in_draw_space,
678 render_pass_requires_scissor);
679 }
680
681 // This layer is in a 3D sorting context so we add it to the list of
682 // polygons to go into the BSP tree.
683 if (quad.shared_quad_state->sorting_context_id != 0) {
684 // TODO(danakj): It's sad to do a malloc here to compare. Maybe construct
685 // this on the stack and move it into the list.
686 auto new_polygon = std::make_unique<DrawPolygon>(
687 *it, gfx::RectF(quad.visible_rect),
688 quad.shared_quad_state->quad_to_target_transform, next_polygon_id++);
689 if (new_polygon->points().size() > 2u) {
690 poly_list.push_back(std::move(new_polygon));
691 }
692 continue;
693 }
694
695 // We are not in a 3d sorting context, so we should draw the quad normally.
696 SetScissorStateForQuad(quad, render_pass_scissor_in_draw_space,
697 render_pass_requires_scissor);
698
699 DoDrawQuad(&quad, nullptr);
700 }
701 FlushPolygons(&poly_list, render_pass_scissor_in_draw_space,
702 render_pass_requires_scissor);
703 FinishDrawingQuadList();
704
705 if (is_root_render_pass && overdraw_feedback_)
706 FlushOverdrawFeedback(render_pass_scissor_in_draw_space);
707
708 if (render_pass->generate_mipmap)
709 GenerateMipmap();
710 }
711
CanSkipRenderPass(const AggregatedRenderPass * render_pass) const712 bool DirectRenderer::CanSkipRenderPass(
713 const AggregatedRenderPass* render_pass) const {
714 if (render_pass == current_frame()->root_render_pass)
715 return false;
716
717 // TODO(crbug.com/783275): It's possible to skip a child RenderPass if damage
718 // does not overlap it, since that means nothing has changed:
719 // ComputeScissorRectForRenderPass(render_pass).IsEmpty()
720 // However that caused crashes where the RenderPass' texture was not present
721 // (never seen the RenderPass before, or the texture was deleted when not used
722 // for a frame). It could avoid skipping if there is no texture present, which
723 // is what was done for a while, but this seems to papering over a missing
724 // damage problem, or we're failing to understand the system wholey.
725 // If attempted again this should probably CHECK() that the texture exists,
726 // and attempt to figure out where the new RenderPass texture without damage
727 // is coming from.
728
729 // If the RenderPass wants to be cached, then we only draw it if we need to.
730 // When damage is present, then we can't skip the RenderPass. Or if the
731 // texture does not exist (first frame, or was deleted) then we can't skip
732 // the RenderPass.
733 if (render_pass->cache_render_pass) {
734 if (render_pass->has_damage_from_contributing_content)
735 return false;
736 return IsRenderPassResourceAllocated(render_pass->id);
737 }
738
739 return false;
740 }
741
UseRenderPass(const AggregatedRenderPass * render_pass)742 void DirectRenderer::UseRenderPass(const AggregatedRenderPass* render_pass) {
743 current_frame()->current_render_pass = render_pass;
744 if (render_pass == current_frame()->root_render_pass) {
745 BindFramebufferToOutputSurface();
746 if (output_surface_->capabilities().supports_dc_layers)
747 output_surface_->SetDrawRectangle(current_frame()->root_damage_rect);
748 InitializeViewport(current_frame(), render_pass->output_rect,
749 gfx::Rect(current_frame()->device_viewport_size),
750 current_frame()->device_viewport_size);
751 return;
752 }
753
754 gfx::Size enlarged_size = CalculateTextureSizeForRenderPass(render_pass);
755 enlarged_size.Enlarge(enlarge_pass_texture_amount_.width(),
756 enlarge_pass_texture_amount_.height());
757
758 AllocateRenderPassResourceIfNeeded(
759 render_pass->id, {enlarged_size, render_pass->generate_mipmap});
760
761 // TODO(crbug.com/582554): This change applies only when Vulkan is enabled and
762 // it will be removed once SkiaRenderer has complete support for Vulkan.
763 if (!IsRenderPassResourceAllocated(render_pass->id))
764 return;
765
766 BindFramebufferToTexture(render_pass->id);
767 InitializeViewport(current_frame(), render_pass->output_rect,
768 gfx::Rect(render_pass->output_rect.size()),
769 // If the render pass backing is cached, we might have
770 // bigger size comparing to the size that was generated.
771 GetRenderPassBackingPixelSize(render_pass->id));
772 }
773
ComputeScissorRectForRenderPass(const AggregatedRenderPass * render_pass) const774 gfx::Rect DirectRenderer::ComputeScissorRectForRenderPass(
775 const AggregatedRenderPass* render_pass) const {
776 const AggregatedRenderPass* root_render_pass =
777 current_frame()->root_render_pass;
778 gfx::Rect root_damage_rect = current_frame()->root_damage_rect;
779
780 if (render_pass == root_render_pass) {
781 base::CheckedNumeric<int> display_area =
782 current_frame()->device_viewport_size.GetCheckedArea();
783 gfx::Rect frame_buffer_damage =
784 output_surface_->GetCurrentFramebufferDamage();
785 base::CheckedNumeric<int> root_damage_area =
786 root_damage_rect.size().GetCheckedArea();
787 if (display_area.IsValid() && root_damage_area.IsValid()) {
788 DCHECK_GT(static_cast<int>(display_area.ValueOrDie()), 0);
789 {
790 base::CheckedNumeric<int> frame_buffer_damage_area =
791 frame_buffer_damage.size().GetCheckedArea();
792 int ratio =
793 (frame_buffer_damage_area / display_area).ValueOrDefault(INT_MAX);
794 UMA_HISTOGRAM_PERCENTAGE(
795 "Compositing.DirectRenderer.PartialSwap.FrameBufferDamage",
796 100ull * ratio);
797 }
798 {
799 int ratio = (root_damage_area / display_area).ValueOrDie();
800 UMA_HISTOGRAM_PERCENTAGE(
801 "Compositing.DirectRenderer.PartialSwap.RootDamage",
802 100ull * ratio);
803 }
804
805 root_damage_rect.Union(frame_buffer_damage);
806
807 // If the root damage rect intersects any child render pass that has a
808 // pixel-moving backdrop-filter, expand the damage to include the entire
809 // child pass. See crbug.com/986206 for context.
810 if (!backdrop_filter_output_rects_.empty() &&
811 !root_damage_rect.IsEmpty()) {
812 for (auto* quad : render_pass->quad_list) {
813 // Sanity check: we should not have a Compositor
814 // CompositorRenderPassDrawQuad here.
815 DCHECK_NE(quad->material, DrawQuad::Material::kCompositorRenderPass);
816 if (quad->material == DrawQuad::Material::kAggregatedRenderPass) {
817 auto iter = backdrop_filter_output_rects_.find(
818 AggregatedRenderPassDrawQuad::MaterialCast(quad)
819 ->render_pass_id);
820 if (iter != backdrop_filter_output_rects_.end()) {
821 gfx::Rect this_output_rect = iter->second;
822 if (root_damage_rect.Intersects(this_output_rect))
823 root_damage_rect.Union(this_output_rect);
824 }
825 }
826 }
827 }
828
829 // Total damage after all adjustments.
830 base::CheckedNumeric<int> total_damage_area =
831 root_damage_rect.size().GetCheckedArea();
832 {
833 int ratio = (total_damage_area / display_area).ValueOrDefault(INT_MAX);
834 UMA_HISTOGRAM_PERCENTAGE(
835 "Compositing.DirectRenderer.PartialSwap.TotalDamage",
836 100ull * ratio);
837 }
838 {
839 int ratio = ((total_damage_area - root_damage_area) / display_area)
840 .ValueOrDefault(INT_MAX);
841 UMA_HISTOGRAM_PERCENTAGE(
842 "Compositing.DirectRenderer.PartialSwap.ExtraDamage",
843 100ull * ratio);
844 }
845 }
846
847 return root_damage_rect;
848 }
849
850 // If the root damage rect has been expanded due to overlays, all the other
851 // damage rect calculations are incorrect.
852 if (!root_render_pass->damage_rect.Contains(root_damage_rect))
853 return render_pass->output_rect;
854
855 DCHECK(render_pass->copy_requests.empty() ||
856 (render_pass->damage_rect == render_pass->output_rect));
857 return render_pass->damage_rect;
858 }
859
CalculateTextureSizeForRenderPass(const AggregatedRenderPass * render_pass)860 gfx::Size DirectRenderer::CalculateTextureSizeForRenderPass(
861 const AggregatedRenderPass* render_pass) {
862 // Round the size of the render pass backings to a multiple of 64 pixels. This
863 // reduces memory fragmentation. https://crbug.com/146070. This also allows
864 // backings to be more easily reused during a resize operation.
865 int width = render_pass->output_rect.width();
866 int height = render_pass->output_rect.height();
867 if (!settings_->dont_round_texture_sizes_for_pixel_tests) {
868 int multiple = 64;
869 width = cc::MathUtil::CheckedRoundUp(width, multiple);
870 height = cc::MathUtil::CheckedRoundUp(height, multiple);
871 }
872 return gfx::Size(width, height);
873 }
874
SetCurrentFrameForTesting(const DrawingFrame & frame)875 void DirectRenderer::SetCurrentFrameForTesting(const DrawingFrame& frame) {
876 current_frame_valid_ = true;
877 current_frame_ = frame;
878 }
879
HasAllocatedResourcesForTesting(const AggregatedRenderPassId & render_pass_id) const880 bool DirectRenderer::HasAllocatedResourcesForTesting(
881 const AggregatedRenderPassId& render_pass_id) const {
882 return IsRenderPassResourceAllocated(render_pass_id);
883 }
884
ShouldApplyRoundedCorner(const DrawQuad * quad) const885 bool DirectRenderer::ShouldApplyRoundedCorner(const DrawQuad* quad) const {
886 const SharedQuadState* sqs = quad->shared_quad_state;
887 const gfx::MaskFilterInfo& mask_filter_info = sqs->mask_filter_info;
888
889 // There is no rounded corner set.
890 if (!mask_filter_info.HasRoundedCorners())
891 return false;
892
893 const gfx::RRectF& rounded_corner_bounds =
894 mask_filter_info.rounded_corner_bounds();
895
896 const gfx::RectF target_quad = cc::MathUtil::MapClippedRect(
897 sqs->quad_to_target_transform, gfx::RectF(quad->visible_rect));
898
899 const gfx::RRectF::Corner corners[] = {
900 gfx::RRectF::Corner::kUpperLeft, gfx::RRectF::Corner::kUpperRight,
901 gfx::RRectF::Corner::kLowerRight, gfx::RRectF::Corner::kLowerLeft};
902 for (auto c : corners) {
903 if (ComputeRoundedCornerBoundingBox(rounded_corner_bounds, c)
904 .Intersects(target_quad)) {
905 return true;
906 }
907 }
908 return false;
909 }
910
RootRenderPassColorSpace() const911 gfx::ColorSpace DirectRenderer::RootRenderPassColorSpace() const {
912 return current_frame()->display_color_spaces.GetOutputColorSpace(
913 current_frame()->root_render_pass->content_color_usage,
914 current_frame()->root_render_pass->has_transparent_background);
915 }
916
CurrentRenderPassColorSpace() const917 gfx::ColorSpace DirectRenderer::CurrentRenderPassColorSpace() const {
918 if (current_frame()->current_render_pass ==
919 current_frame()->root_render_pass) {
920 return RootRenderPassColorSpace();
921 }
922 return current_frame()->display_color_spaces.GetCompositingColorSpace(
923 current_frame()->current_render_pass->has_transparent_background,
924 current_frame()->current_render_pass->content_color_usage);
925 }
926
CreateDelegatedInkPointRenderer()927 bool DirectRenderer::CreateDelegatedInkPointRenderer() {
928 return false;
929 }
930
GetDelegatedInkPointRenderer()931 DelegatedInkPointRendererBase* DirectRenderer::GetDelegatedInkPointRenderer() {
932 return nullptr;
933 }
934
SetDelegatedInkMetadata(std::unique_ptr<DelegatedInkMetadata> metadata)935 void DirectRenderer::SetDelegatedInkMetadata(
936 std::unique_ptr<DelegatedInkMetadata> metadata) {
937 if (!GetDelegatedInkPointRenderer() && !CreateDelegatedInkPointRenderer())
938 return;
939
940 GetDelegatedInkPointRenderer()->SetDelegatedInkMetadata(std::move(metadata));
941 }
942
DrawDelegatedInkTrail()943 void DirectRenderer::DrawDelegatedInkTrail() {
944 NOTREACHED();
945 }
946
CompositeTimeTracingEnabled()947 bool DirectRenderer::CompositeTimeTracingEnabled() {
948 return false;
949 }
950
AddCompositeTimeTraces(base::TimeTicks ready_timestamp)951 void DirectRenderer::AddCompositeTimeTraces(base::TimeTicks ready_timestamp) {}
952
GetDelegatedInkTrailDamageRect()953 gfx::Rect DirectRenderer::GetDelegatedInkTrailDamageRect() {
954 if (!GetDelegatedInkPointRenderer())
955 return gfx::Rect();
956
957 return GetDelegatedInkPointRenderer()->GetDamageRect();
958 }
959
960 } // namespace viz
961