blob: 23ce39eb1c98ed55906a79bcd6172e9d4c14cea0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 "base/trace_event/trace_event.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkDevice.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
#include "ui/ozone/platform/drm/gpu/drm_buffer.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/scanout_buffer.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"
namespace ui {
namespace {
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
#ifndef DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_HEIGHT 0x9
#endif
void EmptyFlipCallback(gfx::SwapResult) {
}
void UpdateCursorImage(DrmBuffer* cursor, const SkBitmap& image) {
SkRect damage;
image.getBounds(&damage);
// Clear to transparent in case |image| is smaller than the canvas.
SkCanvas* canvas = cursor->GetCanvas();
canvas->clear(SK_ColorTRANSPARENT);
SkRect clip;
clip.set(0, 0, canvas->getDeviceSize().width(),
canvas->getDeviceSize().height());
canvas->clipRect(clip, SkRegion::kReplace_Op);
canvas->drawBitmapRect(image, damage, damage, nullptr);
}
} // namespace
DrmWindow::DrmWindow(gfx::AcceleratedWidget widget,
DrmDeviceManager* device_manager,
ScreenManager* screen_manager)
: widget_(widget),
device_manager_(device_manager),
screen_manager_(screen_manager) {
}
DrmWindow::~DrmWindow() {
}
void DrmWindow::Initialize() {
TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_);
device_manager_->UpdateDrmDevice(widget_, nullptr);
}
void DrmWindow::Shutdown() {
TRACE_EVENT1("drm", "DrmWindow::Shutdown", "widget", widget_);
device_manager_->RemoveDrmDevice(widget_);
}
gfx::AcceleratedWidget DrmWindow::GetAcceleratedWidget() {
return widget_;
}
HardwareDisplayController* DrmWindow::GetController() {
return controller_;
}
void DrmWindow::OnBoundsChanged(const gfx::Rect& bounds) {
TRACE_EVENT2("drm", "DrmWindow::OnBoundsChanged", "widget", widget_, "bounds",
bounds.ToString());
bounds_ = bounds;
if (bounds_.size() != bounds.size())
last_submitted_planes_.clear();
screen_manager_->UpdateControllerToWindowMapping();
}
void DrmWindow::SetCursor(const std::vector<SkBitmap>& bitmaps,
const gfx::Point& location,
int frame_delay_ms) {
cursor_bitmaps_ = bitmaps;
cursor_location_ = location;
cursor_frame_ = 0;
cursor_frame_delay_ms_ = frame_delay_ms;
cursor_timer_.Stop();
if (cursor_frame_delay_ms_)
cursor_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_),
this, &DrmWindow::OnCursorAnimationTimeout);
ResetCursor(false);
}
void DrmWindow::SetCursorWithoutAnimations(const std::vector<SkBitmap>& bitmaps,
const gfx::Point& location) {
cursor_bitmaps_ = bitmaps;
cursor_location_ = location;
cursor_frame_ = 0;
cursor_frame_delay_ms_ = 0;
ResetCursor(false);
}
void DrmWindow::MoveCursor(const gfx::Point& location) {
cursor_location_ = location;
if (controller_)
controller_->MoveCursor(location);
}
void DrmWindow::QueueOverlayPlane(const OverlayPlane& plane) {
pending_planes_.push_back(plane);
}
bool DrmWindow::SchedulePageFlip(bool is_sync,
const SwapCompletionCallback& callback) {
last_submitted_planes_.clear();
last_submitted_planes_.swap(pending_planes_);
last_swap_sync_ = is_sync;
if (controller_) {
return controller_->SchedulePageFlip(last_submitted_planes_, is_sync, false,
callback);
}
callback.Run(gfx::SwapResult::SWAP_ACK);
return true;
}
bool DrmWindow::TestPageFlip(const std::vector<OverlayCheck_Params>& overlays,
ScanoutBufferGenerator* buffer_generator) {
if (!controller_)
return true;
for (const auto& overlay : overlays) {
// It is possible that the cc rect we get actually falls off the edge of
// the screen. Usually this is prevented via things like status bars
// blocking overlaying or cc clipping it, but in case it wasn't properly
// clipped (since GL will render this situation fine) just ignore it here.
// This should be an extremely rare occurrance.
if (overlay.plane_z_order != 0 && !bounds().Contains(overlay.display_rect))
return false;
}
scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice();
OverlayPlaneList planes;
for (const auto& overlay : overlays) {
gfx::Size size =
(overlay.plane_z_order == 0) ? bounds().size() : overlay.buffer_size;
scoped_refptr<ScanoutBuffer> buffer = buffer_generator->Create(drm, size);
if (!buffer)
return false;
planes.push_back(OverlayPlane(buffer, overlay.plane_z_order,
overlay.transform, overlay.display_rect,
gfx::RectF(gfx::Size(1, 1))));
}
return controller_->SchedulePageFlip(planes, true, true,
base::Bind(&EmptyFlipCallback));
}
const OverlayPlane* DrmWindow::GetLastModesetBuffer() {
return OverlayPlane::GetPrimaryPlane(last_submitted_planes_);
}
void DrmWindow::ResetCursor(bool bitmap_only) {
if (!controller_)
return;
if (cursor_bitmaps_.size()) {
// Draw new cursor into backbuffer.
UpdateCursorImage(cursor_buffers_[cursor_frontbuffer_ ^ 1].get(),
cursor_bitmaps_[cursor_frame_]);
// Reset location & buffer.
if (!bitmap_only)
controller_->MoveCursor(cursor_location_);
controller_->SetCursor(cursor_buffers_[cursor_frontbuffer_ ^ 1]);
cursor_frontbuffer_ ^= 1;
} else {
// No cursor set.
controller_->UnsetCursor();
}
}
void DrmWindow::OnCursorAnimationTimeout() {
cursor_frame_++;
cursor_frame_ %= cursor_bitmaps_.size();
ResetCursor(true);
}
void DrmWindow::SetController(HardwareDisplayController* controller) {
if (controller_ == controller)
return;
controller_ = controller;
device_manager_->UpdateDrmDevice(
widget_, controller ? controller->GetAllocationDrmDevice() : nullptr);
UpdateCursorBuffers();
// We changed displays, so we want to update the cursor as well.
ResetCursor(false /* bitmap_only */);
}
void DrmWindow::UpdateCursorBuffers() {
if (!controller_) {
for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
cursor_buffers_[i] = nullptr;
}
} else {
scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice();
uint64_t cursor_width = 64;
uint64_t cursor_height = 64;
drm->GetCapability(DRM_CAP_CURSOR_WIDTH, &cursor_width);
drm->GetCapability(DRM_CAP_CURSOR_HEIGHT, &cursor_height);
SkImageInfo info = SkImageInfo::MakeN32Premul(cursor_width, cursor_height);
for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
cursor_buffers_[i] = new DrmBuffer(drm);
// Don't register a framebuffer for cursors since they are special (they
// aren't modesetting buffers and drivers may fail to register them due to
// their small sizes).
if (!cursor_buffers_[i]->Initialize(
info, false /* should_register_framebuffer */)) {
LOG(FATAL) << "Failed to initialize cursor buffer";
return;
}
}
}
}
} // namespace ui