File: overlay_processor_ozone.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (549 lines) | stat: -rw-r--r-- 23,677 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/viz/service/display/overlay_processor_ozone.h"

#include <algorithm>
#include <memory>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "components/viz/common/buildflags.h"
#include "components/viz/common/features.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "components/viz/service/display/overlay_strategy_fullscreen.h"
#include "components/viz/service/display/overlay_strategy_single_on_top.h"
#include "components/viz/service/display/overlay_strategy_underlay.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "gpu/config/gpu_finch_features.h"
#include "ui/display/display_features.h"
#include "ui/gl/gl_switches.h"
#endif

#if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY)
#include "components/viz/service/display/overlay_strategy_underlay_cast.h"
#endif

namespace viz {

namespace {

gfx::ColorSpace GetColorSpaceForOzone(gfx::BufferFormat format,
                                      const gfx::ColorSpace& orig_color_space) {
#if BUILDFLAG(IS_CHROMEOS)
  // The goal here is to ensure that the hw overlay path and the compositing
  // path produce visually identical results. Otherwise, the user would perceive
  // flickering when a particular resource (like a video) goes in and out of hw
  // overlays repeatedly (for whatever reason).
  //
  // The Vulkan path is able to treat YUV 4:2:0 BT.601 and BT.709 resources
  // correctly (see gpu::CreateVulkanYcbcrConversionInfo()). The GL path
  // currently doesn't and just treats YUV 4:2:0 BT.709 resources as if they
  // were BT.601 (but see the TODO below). Therefore, when Vulkan is enabled,
  // we allow the overlay path to also distinguish between the two YUV
  // encodings. Otherwise, we always use BT.601. This should ensure visual
  // consistency between the compositing and the hw overlays path in both GL
  // and Vulkan ChromeOS devices.
  //
  // TODO(b/233667677): the only reason the GL path treats YUV 4:2:0 BT.709
  // resources as BT.601 is because of https://crrev.com/c/2336347. If we can
  // be confident that hw overlays on all platforms can deal with BT.601 and
  // BT.709 correctly, then we don't need GetColorSpaceForOzone() at all and we
  // can just the pass the color space as-is to Ozone (and we would also need to
  // change the GL path to distinguish between BT.601 and BT.709, see
  // NativePixmapEGLBinding::InitializeFromNativePixmap()).
  if ((format == gfx::BufferFormat::YUV_420_BIPLANAR) &&
      (!base::FeatureList::IsEnabled(features::kVulkan) ||
       !base::FeatureList::IsEnabled(features::kDefaultANGLEVulkan) ||
       !base::FeatureList::IsEnabled(features::kVulkanFromANGLE))) {
    return orig_color_space.GetWithMatrixAndRange(
        gfx::ColorSpace::MatrixID::SMPTE170M, orig_color_space.GetRangeID());
  }
#endif
  return orig_color_space;
}

// TODO(weiliangc): When difference between primary plane and non-primary plane
// can be internalized, merge these two helper functions.
void ConvertToOzoneOverlaySurface(
    const OverlayProcessorInterface::OutputSurfaceOverlayPlane& primary_plane,
    ui::OverlaySurfaceCandidate* ozone_candidate) {
  ozone_candidate->transform = primary_plane.transform;
  ozone_candidate->format =
      SinglePlaneSharedImageFormatToBufferFormat(primary_plane.format);
  ozone_candidate->color_space = GetColorSpaceForOzone(
      ozone_candidate->format, /*orig_color_space=*/primary_plane.color_space);
  ozone_candidate->display_rect = primary_plane.display_rect;
  ozone_candidate->crop_rect = primary_plane.uv_rect;
  ozone_candidate->clip_rect.reset();
  ozone_candidate->is_opaque = !primary_plane.enable_blending;
  ozone_candidate->opacity = primary_plane.opacity;
  ozone_candidate->plane_z_order = 0;
  ozone_candidate->buffer_size = primary_plane.resource_size;
  ozone_candidate->priority_hint = primary_plane.priority_hint;
  ozone_candidate->rounded_corners = primary_plane.rounded_corners;
}

void ConvertToOzoneOverlaySurface(
    const OverlayCandidate& overlay_candidate,
    ui::OverlaySurfaceCandidate* ozone_candidate) {
  ozone_candidate->transform = overlay_candidate.transform;
  ozone_candidate->format = gpu::ToBufferFormat(overlay_candidate.format);
  ozone_candidate->color_space =
      GetColorSpaceForOzone(ozone_candidate->format,
                            /*orig_color_space=*/overlay_candidate.color_space);
  ozone_candidate->display_rect = overlay_candidate.display_rect;
  ozone_candidate->crop_rect = overlay_candidate.uv_rect;
  ozone_candidate->clip_rect = overlay_candidate.clip_rect;
  ozone_candidate->is_opaque = overlay_candidate.is_opaque;
  ozone_candidate->opacity = overlay_candidate.opacity;
  ozone_candidate->plane_z_order = overlay_candidate.plane_z_order;
  ozone_candidate->buffer_size = overlay_candidate.resource_size_in_pixels;
  ozone_candidate->requires_overlay = overlay_candidate.requires_overlay;
  ozone_candidate->priority_hint = overlay_candidate.priority_hint;
  ozone_candidate->rounded_corners = overlay_candidate.rounded_corners;
  ozone_candidate->overlay_type = overlay_candidate.overlay_type;
  // TODO(crbug.com/40219248): OverlaySurfaceCandidate to SkColor4f
  // That can be a solid color quad.
  if (!overlay_candidate.is_solid_color && ozone_candidate->background_color &&
      overlay_candidate.color) {
    ozone_candidate->background_color = overlay_candidate.color->toSkColor();
  }
}

void ConvertToTiledOzoneOverlaySurface(
    const OverlayCandidate& overlay_candidate,
    ui::OverlaySurfaceCandidate* ozone_candidate) {
  ozone_candidate->transform = gfx::OVERLAY_TRANSFORM_NONE;
  ozone_candidate->format = gfx::BufferFormat::RGBA_8888;
  ozone_candidate->color_space =
      GetColorSpaceForOzone(ozone_candidate->format,
                            /*orig_color_space=*/overlay_candidate.color_space);
  ozone_candidate->display_rect = overlay_candidate.display_rect;
  ozone_candidate->crop_rect = gfx::RectF(1.0, 1.0);
  ozone_candidate->clip_rect = std::nullopt;
  ozone_candidate->is_opaque = overlay_candidate.is_opaque;
  ozone_candidate->opacity = overlay_candidate.opacity;
  ozone_candidate->plane_z_order = overlay_candidate.plane_z_order;
  ozone_candidate->buffer_size =
      gfx::Size(static_cast<int>(overlay_candidate.display_rect.width()),
                static_cast<int>(overlay_candidate.display_rect.height()));
  ozone_candidate->requires_overlay = true;
  ozone_candidate->priority_hint = overlay_candidate.priority_hint;
  ozone_candidate->rounded_corners = overlay_candidate.rounded_corners;
  ozone_candidate->native_pixmap = nullptr;
  ozone_candidate->overlay_type = overlay_candidate.overlay_type;
}

#if BUILDFLAG(IS_CHROMEOS)
bool IsYUVColorSpace(const gfx::ColorSpace& color_space) {
  SkYUVColorSpace yuv_color_space;
  return color_space.ToSkYUVColorSpace(&yuv_color_space);
}

bool AllowColorSpaceCombination(
    SharedImageFormat source_format,
    const gfx::ColorSpace& source_color_space,
    const gfx::ColorSpace& destination_color_space) {
  // Allow invalid source color spaces because the assumption is that the
  // compositor won't do a color space conversion in this case anyway, so it
  // should be consistent with the overlay path.
  if (!source_color_space.IsValid())
    return true;

  // Since https://crrev.com/c/2336347, we force BT.601/narrow for the
  // COLOR_ENCODING and COLOR_RANGE DRM/KMS properties. On the other hand, the
  // compositor is able to handle different YUV encodings and ranges. Therefore,
  // in theory, if we don't want to see a difference between overlays and
  // compositing, we should not promote video frames to overlays unless they
  // actually use BT.601/narrow.
  //
  // In practice, however, we expect to see lots of BT.709 video frames, and we
  // don't want to reject all of them for overlays because the visual difference
  // between BT.601/narrow and BT.709/narrow is not expected to be much.
  // Therefore, in being consistent with the values we provide for
  // EGL_YUV_COLOR_SPACE_HINT_EXT/EGL_SAMPLE_RANGE_HINT_EXT, we'll only allow
  // frames that use non-BT.2020 with non-full range. In those cases, the
  // compositor and the display controller are expected to render the frames
  // equally (and decently - with the understanding that the final result may
  // not be fully correct).
  //
  // TODO(b/233667677): Remove this when we've plumbed the YUV encoding and
  // range to DRM/KMS. At that point, we need to ensure that
  // EGL_YUV_COLOR_SPACE_HINT_EXT/EGL_SAMPLE_RANGE_HINT_EXT would also get the
  // same values as DRM/KMS.
  if ((source_format == MultiPlaneFormat::kNV12 ||
       source_format == MultiPlaneFormat::kYV12 ||
       source_format == MultiPlaneFormat::kP010) &&
      (source_color_space.GetMatrixID() ==
           gfx::ColorSpace::MatrixID::BT2020_NCL ||
       source_color_space.GetRangeID() == gfx::ColorSpace::RangeID::FULL)) {
    DCHECK(IsYUVColorSpace(source_color_space));
    return false;
  }

  // Do not promote SDR content to overlay if the screen is in HDR10 mode.
  // TODO(b/367739334): Fix color conversion layer in skia_renderer.cc
  if (base::FeatureList::IsEnabled(
          display::features::kEnableExternalDisplayHDR10Mode) &&
      destination_color_space.GetContentColorUsage() ==
          gfx::ContentColorUsage::kHDR) {
    return source_color_space.GetContentColorUsage() ==
           destination_color_space.GetContentColorUsage();
  }

  // Allow color space mismatches as long as either a) the source color space is
  // SRGB; or b) both the source and destination color spaces have the same
  // color usage. It is possible that case (a) still allows for visible color
  // inconsistency between overlays and composition, but we'll address that case
  // if it comes up.
  return source_color_space.GetContentColorUsage() ==
             gfx::ContentColorUsage::kSRGB ||
         source_color_space.GetContentColorUsage() ==
             destination_color_space.GetContentColorUsage();
}
#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace

// |overlay_candidates| is an object used to answer questions about possible
// overlays configurations.
// |available_strategies| is a list of overlay strategies that should be
// initialized by InitializeStrategies.
OverlayProcessorOzone::OverlayProcessorOzone(
    std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
    std::vector<OverlayStrategy> available_strategies,
    std::unique_ptr<PixmapProvider> pixmap_provider)
    : overlay_candidates_(std::move(overlay_candidates)),
      available_strategies_(std::move(available_strategies)),
      pixmap_provider_(std::move(pixmap_provider)) {
  for (OverlayStrategy strategy : available_strategies_) {
    switch (strategy) {
      case OverlayStrategy::kFullscreen:
        strategies_.push_back(
            std::make_unique<OverlayStrategyFullscreen>(this));
        break;
      case OverlayStrategy::kSingleOnTop:
        strategies_.push_back(
            std::make_unique<OverlayStrategySingleOnTop>(this));
        break;
      case OverlayStrategy::kUnderlay:
        strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this));
        break;
#if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY)
      case OverlayStrategy::kUnderlayCast:
        strategies_.push_back(
            std::make_unique<OverlayStrategyUnderlayCast>(this));
        break;
#endif
      default:
        NOTREACHED();
    }
  }
}

OverlayProcessorOzone::~OverlayProcessorOzone() = default;

bool OverlayProcessorOzone::IsOverlaySupported() const {
  return true;
}

bool OverlayProcessorOzone::NeedsSurfaceDamageRectList() const {
  return true;
}

bool OverlayProcessorOzone::SupportsFlipRotateTransform() const {
  // TODO(petermcneeley): Test and enable for ChromeOS.
#if BUILDFLAG(IS_CASTOS)
  return false;
#else
  return false;
#endif
}

void OverlayProcessorOzone::NotifyOverlayPromotion(
    DisplayResourceProvider* display_resource_provider,
    const OverlayCandidateList& candidate_list,
    const QuadList& quad_list) {
  if (!overlay_candidates_) {
    return;
  }

  std::vector<gfx::OverlayType> promoted_overlay_types;
  promoted_overlay_types.reserve(candidate_list.size());
  for (const auto& candidate : candidate_list) {
    promoted_overlay_types.emplace_back(candidate.overlay_type);
  }

  overlay_candidates_->NotifyOverlayPromotion(
      std::move(promoted_overlay_types));
}

void OverlayProcessorOzone::CheckOverlaySupportImpl(
    const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
    OverlayCandidateList* surfaces) {
  MaybeObserveHardwareCapabilities();

  auto full_size = surfaces->size();
  if (primary_plane)
    full_size += 1;

  ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list(
      full_size);

  // Convert OverlayCandidateList to OzoneSurfaceCandidateList.
  {
    auto ozone_surface_iterator = ozone_surface_list.begin();

    // For ozone-cast, there will not be a primary_plane.
    if (primary_plane) {
      ConvertToOzoneOverlaySurface(*primary_plane, &(*ozone_surface_iterator));
      // TODO(crbug.com/40153057): Fuchsia claims support for presenting primary
      // plane as overlay, but does not provide a mailbox. Handle this case.
#if !BUILDFLAG(IS_FUCHSIA)
      if (pixmap_provider_) {
        bool result = SetNativePixmapForCandidate(&(*ozone_surface_iterator),
                                                  primary_plane->mailbox,
                                                  /*is_primary=*/true);
#if BUILDFLAG(IS_CHROMEOS)
        if (!result) {
          // For ChromeOS HW protected content, there's a race condition that
          // can occur here where the mailbox for the native pixmap isn't
          // registered yet so we will fail to promote to overlay due to this
          // check. Allow us to proceed even w/out the native pixmap in that
          // case as it will still succeed and would otherwise cause black
          // flashing between frames while the race condition is completing.
          // We don't know if we have a required overlay yet, so we need to
          // go through all the candidates to see if one is present.
          for (auto surface_iterator = surfaces->cbegin();
               surface_iterator < surfaces->cend(); surface_iterator++) {
            if (surface_iterator->requires_overlay) {
              DLOG(WARNING)
                  << "Allowing required overlay with missing primary pixmap";
              result = true;
              break;
            }
          }
        }
#endif  // BUILDFLAG(IS_CHROMEOS)
        // We cannot validate an overlay configuration without the buffer for
        // primary plane present.
        if (!result) {
          for (auto& candidate : *surfaces) {
            candidate.overlay_handled = false;
          }
          return;
        }
      }
#endif
      ozone_surface_iterator++;
    }

    auto surface_iterator = surfaces->cbegin();
    for (; ozone_surface_iterator < ozone_surface_list.end() &&
           surface_iterator < surfaces->cend();
         ozone_surface_iterator++, surface_iterator++) {
      if (surface_iterator->needs_detiling) {
        ConvertToTiledOzoneOverlaySurface(*surface_iterator,
                                          &(*ozone_surface_iterator));
        continue;
      }

      ConvertToOzoneOverlaySurface(*surface_iterator,
                                   &(*ozone_surface_iterator));
#if BUILDFLAG(IS_CHROMEOS)
      // On Chrome OS, skip the candidate if we think a color space combination
      // might cause visible color differences between compositing and overlays.
      // The reason is that on Chrome OS, we don't yet have an API to set up
      // color space conversion per plane. Note however that we should only do
      // this if the candidate does not require an overlay (e.g., for protected
      // content, it's better to display it with an incorrect color space than
      // to not display it at all).
      // TODO(b/181974042): plumb the color space all the way to the ozone DRM
      // backend when we get an API for per-plane color management.
      if (!surface_iterator->requires_overlay &&
          !AllowColorSpaceCombination(
              /*source_format=*/surface_iterator->format,
              /*source_color_space=*/surface_iterator->color_space,
              /*destination_color_space=*/primary_plane_color_space_)) {
        *ozone_surface_iterator = ui::OverlaySurfaceCandidate();
        ozone_surface_iterator->plane_z_order = surface_iterator->plane_z_order;
        continue;
      }
#endif  // BUILDFLAG(IS_CHROMEOS)
      if (pixmap_provider_) {
        bool result = SetNativePixmapForCandidate(&(*ozone_surface_iterator),
                                                  surface_iterator->mailbox,
                                                  /*is_primary=*/false);
#if BUILDFLAG(IS_CHROMEOS)
        if (!result && surface_iterator->requires_overlay) {
          // For ChromeOS HW protected content, same condition as above
          // regarding missing pixmaps.
          result = true;
          DLOG(WARNING) << "Allowing required overlay with missing pixmap";
        }
#endif  // BUILDFLAG(IS_CHROMEOS)

        // Skip the candidate if the corresponding NativePixmap is not found.
        if (!result) {
          *ozone_surface_iterator = ui::OverlaySurfaceCandidate();
          ozone_surface_iterator->plane_z_order =
              surface_iterator->plane_z_order;
        }
      }
    }

#if BUILDFLAG(IS_CHROMEOS)
    // Some platforms (e.g. AMD) do not provide a dedicated cursor plane, and
    // the display hardware will need to blit the cursor to the topmost plane.
    // If the topmost plane is scaled/translated, the cursor will then be
    // transformed along with it. Thus, we need to reject the topmost candidate
    // if the buffer size is transformed at all.
    if (!has_independent_cursor_plane_) {
      auto highest_zindex_surface =
          std::max_element(ozone_surface_list.begin(), ozone_surface_list.end(),
                           [](const auto& a, const auto& b) {
                             return a.plane_z_order < b.plane_z_order;
                           });
      if (highest_zindex_surface != ozone_surface_list.end()) {
        gfx::RectF display_rect = highest_zindex_surface->display_rect;
        gfx::Size buffer_size = highest_zindex_surface->buffer_size;
        if (!display_rect.origin().IsOrigin() ||
            buffer_size != gfx::ToFlooredSize(display_rect.size())) {
          int zindex = highest_zindex_surface->plane_z_order;
          *highest_zindex_surface = ui::OverlaySurfaceCandidate();
          highest_zindex_surface->plane_z_order = zindex;
        }
      }
    }
#endif  // BUILDFLAG(IS_CHROMEOS)
  }
  overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);

  // Copy information from OzoneSurfaceCandidatelist back to
  // OverlayCandidateList.
  {
    DCHECK_EQ(full_size, ozone_surface_list.size());
    auto ozone_surface_iterator = ozone_surface_list.cbegin();
    // The primary plane is always handled, and don't need to copy information.
    if (primary_plane)
      ozone_surface_iterator++;

    auto surface_iterator = surfaces->begin();
    for (; surface_iterator < surfaces->end() &&
           ozone_surface_iterator < ozone_surface_list.cend();
         surface_iterator++, ozone_surface_iterator++) {
      surface_iterator->overlay_handled =
          ozone_surface_iterator->overlay_handled;
      surface_iterator->display_rect = ozone_surface_iterator->display_rect;
    }
  }
}

void OverlayProcessorOzone::MaybeObserveHardwareCapabilities() {
  if (tried_observing_hardware_capabilities_) {
    return;
  }
  tried_observing_hardware_capabilities_ = true;

  if (overlay_candidates_) {
    overlay_candidates_->ObserveHardwareCapabilities(
        base::BindRepeating(&OverlayProcessorOzone::ReceiveHardwareCapabilities,
                            weak_ptr_factory_.GetWeakPtr()));
  }
}

void OverlayProcessorOzone::ReceiveHardwareCapabilities(
    ui::HardwareCapabilities hardware_capabilities) {
  if (hardware_capabilities.is_valid) {
    // Subtract 1 because one of these overlay capable planes will be needed for
    // the primary plane.
    int max_overlays_supported =
        hardware_capabilities.num_overlay_capable_planes - 1;
    max_overlays_considered_ =
        std::min(max_overlays_supported, max_overlays_config_);
    has_independent_cursor_plane_ =
        hardware_capabilities.has_independent_cursor_plane;

    UMA_HISTOGRAM_COUNTS_100(
        "Compositing.Display.OverlayProcessorOzone.MaxPlanesSupported",
        hardware_capabilities.num_overlay_capable_planes);

    DCHECK(overlay_candidates_);
    overlay_candidates_->SetSupportedBufferFormats(
        std::move(hardware_capabilities.supported_buffer_formats));
  } else {
    // Default to attempting 1 overlay if we get an invalid response.
    max_overlays_considered_ = 1;
  }

  // Different hardware capabilities may mean a different result for a specific
  // combination of overlays, so clear this cache.
  ClearOverlayCombinationCache();
}

gfx::Rect OverlayProcessorOzone::GetOverlayDamageRectForOutputSurface(
    const OverlayCandidate& overlay) const {
  return ToEnclosedRect(overlay.display_rect);
}

void OverlayProcessorOzone::RegisterOverlayRequirement(bool requires_overlay) {
  // This can be null in unit tests.
  if (overlay_candidates_)
    overlay_candidates_->RegisterOverlayRequirement(requires_overlay);
}

void OverlayProcessorOzone::OnSwapBuffersComplete(gfx::SwapResult swap_result) {
  if (overlay_candidates_) {
    overlay_candidates_->OnSwapBuffersComplete(swap_result);
  }
}

bool OverlayProcessorOzone::SetNativePixmapForCandidate(
    ui::OverlaySurfaceCandidate* candidate,
    const gpu::Mailbox& mailbox,
    bool is_primary) {
  DCHECK(pixmap_provider_);
  scoped_refptr<gfx::NativePixmap> native_pixmap =
      pixmap_provider_->GetNativePixmap(mailbox);

  if (!native_pixmap) {
    // SharedImage creation and destruction happens on a different
    // thread so there is no guarantee that we can always look them up
    // successfully. If a SharedImage doesn't exist, ignore the
    // candidate. We will try again next frame.
    DLOG(ERROR) << "Unable to find the NativePixmap corresponding to the "
                   "overlay candidate";
    return false;
  }

  if (is_primary && (candidate->buffer_size != native_pixmap->GetBufferSize() ||
                     candidate->format != native_pixmap->GetBufferFormat())) {
    // If |mailbox| corresponds to the last submitted primary plane, its
    // parameters may not match those of the current candidate due to a
    // reshape. If the size and format don't match, skip this candidate for
    // now, and try again next frame.
    return false;
  }

  candidate->native_pixmap = std::move(native_pixmap);
  candidate->native_pixmap_unique_id = mailbox.ToU32();
  return true;
}

OverlayProcessorOzone::PixmapProvider::~PixmapProvider() = default;

}  // namespace viz