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

// Copyright 2018 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/drm_framebuffer.h"

#include <utility>

#include "base/containers/contains.h"
#include "base/logging.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/gfx/linux/gbm_buffer.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/hardware_display_plane_manager.h"

namespace ui {

namespace {

// Some Display Controllers (e.g. Intel Gen 9.5) don't support AR/B30
// framebuffers, only XR/B30; this function indicates if an opaque format should
// be used instead of the non-opaque |buffer_format| for AddFramebuffer2().
bool ForceUsingOpaqueFormatWorkaround(
    const scoped_refptr<DrmDevice>& drm_device,
    uint32_t drm_fourcc) {
  constexpr uint32_t kHighBitDepthARGBFormats[] = {
      DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, DRM_FORMAT_RGBA1010102,
      DRM_FORMAT_BGRA1010102};
  const bool is_high_bit_depth_format_with_alpha =
      base::Contains(kHighBitDepthARGBFormats, drm_fourcc);
  if (!is_high_bit_depth_format_with_alpha)
    return false;

  const std::vector<uint32_t>& supported_formats =
      drm_device->plane_manager()->GetSupportedFormats();
  return !base::Contains(supported_formats, drm_fourcc);
}

}  // namespace

DrmFramebuffer::AddFramebufferParams::AddFramebufferParams() = default;
DrmFramebuffer::AddFramebufferParams::AddFramebufferParams(
    const AddFramebufferParams& other) = default;
DrmFramebuffer::AddFramebufferParams::~AddFramebufferParams() = default;

// static
scoped_refptr<DrmFramebuffer> DrmFramebuffer::AddFramebuffer(
    scoped_refptr<DrmDevice> drm_device,
    DrmFramebuffer::AddFramebufferParams params) {
  uint64_t modifiers[4] = {0};
  if (params.modifier != DRM_FORMAT_MOD_INVALID) {
    for (size_t i = 0; i < params.num_planes; ++i)
      modifiers[i] = params.modifier;
  }

  const auto buffer_format = GetBufferFormatFromFourCCFormat(params.format);
  const uint32_t opaque_format =
      GetFourCCFormatForOpaqueFramebuffer(buffer_format);
  const auto drm_format =
      ForceUsingOpaqueFormatWorkaround(drm_device, params.format)
          ? opaque_format
          : params.format;

  uint32_t framebuffer_id = 0;
  if (!drm_device->AddFramebuffer2(params.width, params.height, drm_format,
                                   params.handles, params.strides,
                                   params.offsets, modifiers, &framebuffer_id,
                                   params.flags)) {
    VLOG(4) << "AddFramebuffer2:" << "size=" << params.width << "x"
            << params.height << " drm_format=" << DrmFormatToString(drm_format)
            << " fb_id=" << framebuffer_id << " flags=" << params.flags;
    return nullptr;
  }

  uint32_t opaque_framebuffer_id = 0;
  if (opaque_format != drm_format &&
      !drm_device->AddFramebuffer2(params.width, params.height, opaque_format,
                                   params.handles, params.strides,
                                   params.offsets, modifiers,
                                   &opaque_framebuffer_id, params.flags)) {
    VLOG(4) << "AddFramebuffer2:" << "size=" << params.width << "x"
            << params.height << " drm_format=" << DrmFormatToString(drm_format)
            << " fb_id=" << opaque_framebuffer_id << " flags=" << params.flags;
    drm_device->RemoveFramebuffer(framebuffer_id);
    return nullptr;
  }

  return base::MakeRefCounted<DrmFramebuffer>(
      std::move(drm_device), framebuffer_id, drm_format, opaque_framebuffer_id,
      opaque_format, params.modifier, params.preferred_modifiers,
      gfx::Size(params.width, params.height), params.is_original_buffer);
}

// static
scoped_refptr<DrmFramebuffer> DrmFramebuffer::AddFramebuffer(
    scoped_refptr<DrmDevice> drm,
    const GbmBuffer* buffer,
    const gfx::Size& framebuffer_size,
    std::vector<uint64_t> preferred_modifiers,
    bool is_original_buffer) {
  DCHECK(gfx::Rect(buffer->GetSize()).Contains(gfx::Rect(framebuffer_size)));
  AddFramebufferParams params;
  params.format = buffer->GetFormat();
  params.modifier = buffer->GetFormatModifier();
  params.width = framebuffer_size.width();
  params.height = framebuffer_size.height();
  params.num_planes = buffer->GetNumPlanes();
  params.is_original_buffer = is_original_buffer;
  params.preferred_modifiers = preferred_modifiers;
  for (size_t i = 0; i < params.num_planes; ++i) {
    params.handles[i] = buffer->GetPlaneHandle(i);
    params.strides[i] = buffer->GetPlaneStride(i);
    params.offsets[i] = buffer->GetPlaneOffset(i);
  }

  // AddFramebuffer2 only considers the modifiers if addfb_flags has
  // DRM_MODE_FB_MODIFIERS set. We only set that when we've created
  // a bo with modifiers, otherwise, we rely on the "no modifiers"
  // behavior doing the right thing.
  params.flags = 0;
  if (IsAddfb2ModifierCapable(*drm) &&
      params.modifier != DRM_FORMAT_MOD_INVALID) {
    params.flags |= DRM_MODE_FB_MODIFIERS;
  }

  return AddFramebuffer(std::move(drm), params);
}

DrmFramebuffer::DrmFramebuffer(scoped_refptr<DrmDevice> drm_device,
                               uint32_t framebuffer_id,
                               uint32_t framebuffer_pixel_format,
                               uint32_t opaque_framebuffer_id,
                               uint32_t opaque_framebuffer_pixel_format,
                               uint64_t format_modifier,
                               std::vector<uint64_t> modifiers,
                               const gfx::Size& size,
                               bool is_original_buffer)
    : drm_device_(std::move(drm_device)),
      framebuffer_id_(framebuffer_id),
      framebuffer_pixel_format_(framebuffer_pixel_format),
      opaque_framebuffer_id_(opaque_framebuffer_id),
      opaque_framebuffer_pixel_format_(opaque_framebuffer_pixel_format),
      format_modifier_(format_modifier),
      is_original_buffer_(is_original_buffer),
      preferred_modifiers_(modifiers),
      size_(size),
      modeset_sequence_id_at_allocation_(drm_device_->modeset_sequence_id()) {}

DrmFramebuffer::~DrmFramebuffer() {
  if (!drm_device_->RemoveFramebuffer(framebuffer_id_)) {
    VLOG(4) << "RemoveFramebuffer";
  }

  if (opaque_framebuffer_id_ &&
      !drm_device_->RemoveFramebuffer(opaque_framebuffer_id_)) {
    VLOG(4) << "RemoveFramebuffer";
  }
}

}  // namespace ui