chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"

#include <drm_fourcc.h>
#include <drm_mode.h>

#include <string>

#include "base/logging.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/trace_event/traced_value.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"

namespace ui {

namespace {

void ParseSupportedFormatsAndModifiers(
    drmModePropertyBlobPtr blob,
    std::vector<uint32_t>* supported_formats,
    std::vector<drm_format_modifier>* supported_format_modifiers) {
  auto* data = static_cast<const uint8_t*>(blob->data);
  auto* header = reinterpret_cast<const drm_format_modifier_blob*>(data);
  auto* formats =
      reinterpret_cast<const uint32_t*>(data + header->formats_offset);
  auto* modifiers = reinterpret_cast<const drm_format_modifier*>(
      data + header->modifiers_offset);

  for (uint32_t k = 0; k < header->count_formats; k++)
    supported_formats->push_back(formats[k]);

  for (uint32_t k = 0; k < header->count_modifiers; k++)
    supported_format_modifiers->push_back(modifiers[k]);
}

std::vector<gfx::Size> ParseSupportedCursorSizes(drmModePropertyBlobPtr blob) {
  auto* data = static_cast<const uint8_t*>(blob->data);
  auto* size_hints_ptr = reinterpret_cast<const drm_plane_size_hint*>(data);

  int num_of_size_hints = blob->length / sizeof(drm_plane_size_hint);

  std::vector<gfx::Size> supported_cursor_sizes;
  for (int i = 0; i < num_of_size_hints; i++) {
    supported_cursor_sizes.push_back(
        gfx::Size(size_hints_ptr[i].width, size_hints_ptr[i].height));
  }
  return supported_cursor_sizes;
}

std::string IdSetToString(const base::flat_set<uint32_t>& ids) {
  std::vector<std::string> string_ids;
  for (auto id : ids)
    string_ids.push_back(std::to_string(id));
  return "[" + base::JoinString(string_ids, ", ") + "]";
}

}  // namespace

HardwareDisplayPlane::Properties::Properties() = default;
HardwareDisplayPlane::Properties::~Properties() = default;

HardwareDisplayPlane::HardwareDisplayPlane(uint32_t id) : id_(id) {}

HardwareDisplayPlane::~HardwareDisplayPlane() = default;

bool HardwareDisplayPlane::CanUseForCrtcId(uint32_t crtc_id) const {
  return possible_crtc_ids_.contains(crtc_id);
}

void HardwareDisplayPlane::WriteIntoTrace(perfetto::TracedValue context) const {
  auto dict = std::move(context).WriteDictionary();

  dict.Add("plane_id", id_);
  dict.Add("owning_crtc", owning_crtc_);
  dict.Add("in_use", in_use_);

  dict.Add("possible_crtc_ids", possible_crtc_ids_);

  auto type = dict.AddItem("type");
  switch (properties_.type.value) {
    case DRM_PLANE_TYPE_OVERLAY:
      std::move(type).WriteString("DRM_PLANE_TYPE_OVERLAY");
      break;
    case DRM_PLANE_TYPE_PRIMARY:
      std::move(type).WriteString("DRM_PLANE_TYPE_PRIMARY");
      break;
    case DRM_PLANE_TYPE_CURSOR:
      std::move(type).WriteString("DRM_PLANE_TYPE_CURSOR");
      break;
    default:
      NOTREACHED_IN_MIGRATION();
  }
}

bool HardwareDisplayPlane::Initialize(DrmDevice* drm) {
  InitializeProperties(drm);

  ScopedDrmPlanePtr drm_plane(drm->GetPlane(id_));
  DCHECK(drm_plane);

  possible_crtc_ids_ =
      drm->plane_manager()->CrtcMaskToCrtcIds(drm_plane->possible_crtcs);

  if (properties_.in_formats.id) {
    ScopedDrmPropertyBlobPtr blob(
        drm->GetPropertyBlob(properties_.in_formats.value));
    DCHECK(blob) << "No blob found with id=" << properties_.in_formats.value;
    ParseSupportedFormatsAndModifiers(blob.get(), &supported_formats_,
                                      &supported_format_modifiers_);
  }

  if (supported_formats_.empty()) {
    for (uint32_t i = 0; i < drm_plane->count_formats; ++i)
      supported_formats_.push_back(drm_plane->formats[i]);
  }

  if (properties_.type.id)
    type_ = properties_.type.value;

  if (properties_.plane_color_encoding.id) {
    color_encoding_bt601_ = GetEnumValueForName(
        *drm, properties_.plane_color_encoding.id, "ITU-R BT.601 YCbCr");
    color_encoding_bt709_ = GetEnumValueForName(
        *drm, properties_.plane_color_encoding.id, "ITU-R BT.709 YCbCr");
    color_range_limited_ = GetEnumValueForName(
        *drm, properties_.plane_color_range.id, "YCbCr limited range");
  }

  // The SIZE_HINTS is only meaningful for cursor planes.
  if (type_ == DRM_PLANE_TYPE_CURSOR && properties_.size_hints.id) {
    ScopedDrmPropertyBlobPtr size_hints_blob(
        drm->GetPropertyBlob(properties_.size_hints.value));
    if (size_hints_blob) {
      supported_cursor_sizes_ =
          ParseSupportedCursorSizes(size_hints_blob.get());
    }
  }

  VLOG(3) << "Initialized plane=" << id_
          << " possible_crtc_ids=" << IdSetToString(possible_crtc_ids_)
          << " supported_formats_count=" << supported_formats_.size()
          << " supported_modifiers_count=" << supported_format_modifiers_.size()
          << " supported_cursor_sizes_count=" << supported_cursor_sizes_.size();
  return true;
}

bool HardwareDisplayPlane::IsSupportedFormat(uint32_t format) {
  if (!format)
    return false;

  if (last_used_format_ == format)
    return true;

  for (auto& element : supported_formats_) {
    if (element == format) {
      last_used_format_ = format;
      return true;
    }
  }

  last_used_format_ = 0;
  return false;
}

const std::vector<uint32_t>& HardwareDisplayPlane::supported_formats() const {
  return supported_formats_;
}

const std::vector<gfx::Size>& HardwareDisplayPlane::supported_cursor_sizes()
    const {
  return supported_cursor_sizes_;
}

std::vector<uint64_t> HardwareDisplayPlane::ModifiersForFormat(
    uint32_t format) const {
  std::vector<uint64_t> modifiers;

  auto it = base::ranges::find(supported_formats_, format);
  if (it == supported_formats_.end())
    return modifiers;

  uint32_t format_index = it - supported_formats_.begin();
  for (const auto& modifier : supported_format_modifiers_) {
    // modifier.formats is a bitmask of the formats the modifier
    // applies to, starting at format modifier.offset. That is, if bit
    // n is set in modifier.formats, the modifier applies to format
    // modifier.offset + n.  In the expression below, if format_index
    // is lower than modifier.offset or more than 63 higher than
    // modifier.offset, the right hand side of the shift is 64 or
    // above and the result will be 0. That means that the modifier
    // doesn't apply to this format, which is what we want.
    if (modifier.formats & (1ull << (format_index - modifier.offset)))
      modifiers.push_back(modifier.modifier);
  }

  return modifiers;
}

void HardwareDisplayPlane::InitializeProperties(DrmDevice* drm) {
  ScopedDrmObjectPropertyPtr props =
      drm->GetObjectProperties(id_, DRM_MODE_OBJECT_PLANE);
  GetDrmPropertyForName(drm, props.get(), "CRTC_ID", &properties_.crtc_id);
  GetDrmPropertyForName(drm, props.get(), "CRTC_X", &properties_.crtc_x);
  GetDrmPropertyForName(drm, props.get(), "CRTC_Y", &properties_.crtc_y);
  GetDrmPropertyForName(drm, props.get(), "CRTC_W", &properties_.crtc_w);
  GetDrmPropertyForName(drm, props.get(), "CRTC_H", &properties_.crtc_h);
  GetDrmPropertyForName(drm, props.get(), "FB_ID", &properties_.fb_id);
  GetDrmPropertyForName(drm, props.get(), "SRC_X", &properties_.src_x);
  GetDrmPropertyForName(drm, props.get(), "SRC_Y", &properties_.src_y);
  GetDrmPropertyForName(drm, props.get(), "SRC_W", &properties_.src_w);
  GetDrmPropertyForName(drm, props.get(), "SRC_H", &properties_.src_h);
  GetDrmPropertyForName(drm, props.get(), "type", &properties_.type);
  GetDrmPropertyForName(drm, props.get(), "rotation", &properties_.rotation);
  GetDrmPropertyForName(drm, props.get(), "IN_FORMATS",
                        &properties_.in_formats);
  GetDrmPropertyForName(drm, props.get(), "IN_FENCE_FD",
                        &properties_.in_fence_fd);
  GetDrmPropertyForName(drm, props.get(), "COLOR_ENCODING",
                        &properties_.plane_color_encoding);
  GetDrmPropertyForName(drm, props.get(), "COLOR_RANGE",
                        &properties_.plane_color_range);
  if (display::features::IsPanelSelfRefresh2Enabled()) {
    GetDrmPropertyForName(drm, props.get(), "FB_DAMAGE_CLIPS",
                          &properties_.plane_fb_damage_clips);
  }
  GetDrmPropertyForName(drm, props.get(), "SIZE_HINTS",
                        &properties_.size_hints);
}

}  // namespace ui