| // 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 |