chromium/ui/ozone/platform/drm/gpu/drm_window.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.

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

#include <stddef.h>
#include <stdint.h>
#include <utility>

#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/gpu_fence.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h"
#include "ui/ozone/platform/drm/gpu/drm_overlay_validator.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"

namespace ui {

DrmWindow::DrmWindow(gfx::AcceleratedWidget widget,
                     DrmDeviceManager* device_manager,
                     ScreenManager* screen_manager)
    : widget_(widget),
      device_manager_(device_manager),
      screen_manager_(screen_manager) {}

DrmWindow::~DrmWindow() = default;

void DrmWindow::Initialize() {
  TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_);

  device_manager_->UpdateDrmDevice(widget_, nullptr);
  overlay_validator_ = std::make_unique<DrmOverlayValidator>(this);
}

void DrmWindow::Shutdown() {
  TRACE_EVENT1("drm", "DrmWindow::Shutdown", "widget", widget_);
  device_manager_->RemoveDrmDevice(widget_);
}

gfx::AcceleratedWidget DrmWindow::GetAcceleratedWidget() const {
  return widget_;
}

HardwareDisplayController* DrmWindow::GetController() const {
  return controller_;
}

void DrmWindow::SetBounds(const gfx::Rect& bounds) {
  TRACE_EVENT2("drm", "DrmWindow::SetBounds", "widget", widget_, "bounds",
               bounds.ToString());
  if (bounds_.size() != bounds.size())
    last_submitted_planes_.clear();

  bounds_ = bounds;
  screen_manager_->UpdateControllerToWindowMapping();
}

void DrmWindow::SetCursor(const std::vector<SkBitmap>& bitmaps,
                          const std::optional<gfx::Point>& location,
                          base::TimeDelta frame_delay) {
  cursor_bitmaps_ = bitmaps;
  if (location.has_value()) {
    cursor_location_ = location.value();
  }
  cursor_frame_ = 0;
  cursor_timer_.Stop();

  if (!frame_delay.is_zero()) {
    cursor_timer_.Start(FROM_HERE, frame_delay, this,
                        &DrmWindow::OnCursorAnimationTimeout);
  }

  if (location.has_value()) {
    ResetCursor();
  } else {
    UpdateCursorImage();
  }
}

void DrmWindow::MoveCursor(const gfx::Point& location) {
  cursor_location_ = location;
  UpdateCursorLocation();
}

void DrmWindow::SchedulePageFlip(
    std::vector<DrmOverlayPlane> planes,
    SwapCompletionOnceCallback submission_callback,
    PresentationOnceCallback presentation_callback) {
  if (controller_) {
    const DrmDevice* drm = controller_->GetDrmDevice().get();
    for (const auto& plane : planes) {
      if (plane.buffer && plane.buffer->drm_device() != drm) {
        // Although |force_buffer_reallocation_| is set to true during window
        // bounds update, this may still be needed because of in-flight buffers.
        force_buffer_reallocation_ = true;
        break;
      }
    }
  }

  if (force_buffer_reallocation_) {
    force_buffer_reallocation_ = false;
    std::move(submission_callback)
        .Run(gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS,
             /*release_fence=*/gfx::GpuFenceHandle());
    std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
    return;
  }

  last_submitted_planes_ = DrmOverlayPlane::Clone(planes);

  if (!controller_) {
    std::move(submission_callback)
        .Run(gfx::SwapResult::SWAP_ACK,
             /*release_fence=*/gfx::GpuFenceHandle());
    std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
    return;
  }

  controller_->SchedulePageFlip(std::move(planes),
                                std::move(submission_callback),
                                std::move(presentation_callback));
}

OverlayStatusList DrmWindow::TestPageFlip(
    const OverlaySurfaceCandidateList& overlay_params) {
  return overlay_validator_->TestPageFlip(overlay_params,
                                          last_submitted_planes_);
}

void DrmWindow::UpdateCursorImage() {
  if (!controller_)
    return;
  if (cursor_bitmaps_.size()) {
    controller_->SetCursor(cursor_bitmaps_[cursor_frame_]);
  } else {
    // No cursor set.
    controller_->SetCursor(SkBitmap());
  }
}

void DrmWindow::UpdateCursorLocation() {
  if (!controller_)
    return;
  controller_->MoveCursor(cursor_location_);
}

void DrmWindow::ResetCursor() {
  UpdateCursorLocation();
  UpdateCursorImage();
}

void DrmWindow::OnCursorAnimationTimeout() {
  cursor_frame_++;
  cursor_frame_ %= cursor_bitmaps_.size();

  UpdateCursorImage();
}

void DrmWindow::SetController(HardwareDisplayController* controller) {
  if (controller_ == controller)
    return;

  // Force buffer reallocation since the window moved to a different controller.
  // This is required otherwise the GPU will eventually try to render into the
  // buffer currently showing on the old controller (there is no guarantee that
  // the old controller has been updated in the meantime).
  force_buffer_reallocation_ = true;

  controller_ = controller;
  device_manager_->UpdateDrmDevice(
      widget_, controller ? controller->GetDrmDevice() : nullptr);

  // We changed displays, so we want to update the cursor as well.
  ResetCursor();
}

}  // namespace ui