blob: 0c40c3e9fe38e0ac814115ac99e76e2b20620848 [file] [log] [blame]
// Copyright 2015 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_thread.h"
#include <gbm.h>
#include <utility>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/ozone/common/linux/drm_util_linux.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
#include "ui/ozone/platform/drm/gpu/drm_window.h"
#include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
#include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
#include "ui/ozone/platform/drm/gpu/gbm_device.h"
#include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
#include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"
#include "ui/ozone/public/ozone_switches.h"
namespace ui {
namespace {
class GbmBufferGenerator : public ScanoutBufferGenerator {
public:
GbmBufferGenerator() {}
~GbmBufferGenerator() override {}
// ScanoutBufferGenerator:
scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
uint32_t format,
const std::vector<uint64_t>& modifiers,
const gfx::Size& size) override {
scoped_refptr<GbmDevice> gbm(static_cast<GbmDevice*>(drm.get()));
if (modifiers.size() > 0) {
return GbmBuffer::CreateBufferWithModifiers(
gbm, format, size, GBM_BO_USE_SCANOUT, modifiers);
} else {
return GbmBuffer::CreateBuffer(gbm, format, size, GBM_BO_USE_SCANOUT);
}
}
protected:
DISALLOW_COPY_AND_ASSIGN(GbmBufferGenerator);
};
class GbmDeviceGenerator : public DrmDeviceGenerator {
public:
GbmDeviceGenerator() {}
~GbmDeviceGenerator() override {}
// DrmDeviceGenerator:
scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
base::File file,
bool is_primary_device) override {
scoped_refptr<DrmDevice> drm =
new GbmDevice(path, std::move(file), is_primary_device);
if (drm->Initialize())
return drm;
return nullptr;
}
private:
DISALLOW_COPY_AND_ASSIGN(GbmDeviceGenerator);
};
} // namespace
DrmThread::DrmThread() : base::Thread("DrmThread"), weak_ptr_factory_(this) {}
DrmThread::~DrmThread() {
Stop();
}
void DrmThread::Start(base::OnceClosure binding_completer) {
complete_early_binding_requests_ = std::move(binding_completer);
base::Thread::Options thread_options;
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
thread_options.priority = base::ThreadPriority::DISPLAY;
if (!StartWithOptions(thread_options))
LOG(FATAL) << "Failed to create DRM thread";
}
void DrmThread::Init() {
device_manager_.reset(
new DrmDeviceManager(std::make_unique<GbmDeviceGenerator>()));
buffer_generator_.reset(new GbmBufferGenerator());
screen_manager_.reset(new ScreenManager(buffer_generator_.get()));
display_manager_.reset(
new DrmGpuDisplayManager(screen_manager_.get(), device_manager_.get()));
DCHECK(task_runner())
<< "DrmThread::Init -- thread doesn't have a task_runner";
// DRM thread is running now so can safely handle binding requests. So drain
// the queue of as-yet unhandled binding requests if there are any.
std::move(complete_early_binding_requests_).Run();
}
void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
uint32_t client_flags,
scoped_refptr<GbmBuffer>* buffer) {
scoped_refptr<GbmDevice> gbm =
static_cast<GbmDevice*>(device_manager_->GetDrmDevice(widget).get());
DCHECK(gbm);
uint32_t flags = 0;
switch (usage) {
case gfx::BufferUsage::GPU_READ:
flags = GBM_BO_USE_TEXTURING;
break;
case gfx::BufferUsage::SCANOUT:
flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING;
break;
case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
flags = GBM_BO_USE_LINEAR | GBM_BO_USE_CAMERA_WRITE | GBM_BO_USE_SCANOUT |
GBM_BO_USE_TEXTURING;
break;
case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
flags =
GBM_BO_USE_LINEAR | GBM_BO_USE_CAMERA_WRITE | GBM_BO_USE_TEXTURING;
break;
case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
flags = GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING;
break;
case gfx::BufferUsage::SCANOUT_VDA_WRITE:
flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING |
GBM_BO_USE_HW_VIDEO_DECODER;
break;
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
flags = GBM_BO_USE_LINEAR | GBM_BO_USE_TEXTURING;
break;
}
DrmWindow* window = screen_manager_->GetWindow(widget);
std::vector<uint64_t> modifiers;
uint32_t fourcc_format = ui::GetFourCCFormatFromBufferFormat(format);
// TODO(hoegsberg): We shouldn't really get here without a window,
// but it happens during init. Need to figure out why.
if (window && window->GetController())
modifiers = window->GetController()->GetFormatModifiers(fourcc_format);
// NOTE: BufferUsage::SCANOUT is used to create buffers that will be
// explicitly set via kms on a CRTC (e.g: BufferQueue buffers), therefore
// allocation should fail if it's not possible to allocate a BO_USE_SCANOUT
// buffer in that case.
bool retry_without_scanout = usage != gfx::BufferUsage::SCANOUT;
do {
if (modifiers.size() > 0 && !(flags & GBM_BO_USE_LINEAR) &&
!(client_flags & GbmBuffer::kFlagNoModifiers))
*buffer = GbmBuffer::CreateBufferWithModifiers(gbm, fourcc_format, size,
flags, modifiers);
else
*buffer = GbmBuffer::CreateBuffer(gbm, fourcc_format, size, flags);
retry_without_scanout =
retry_without_scanout && !*buffer && (flags & GBM_BO_USE_SCANOUT);
flags &= ~GBM_BO_USE_SCANOUT;
} while (retry_without_scanout);
}
void DrmThread::CreateBufferFromFds(
gfx::AcceleratedWidget widget,
const gfx::Size& size,
gfx::BufferFormat format,
std::vector<base::ScopedFD> fds,
const std::vector<gfx::NativePixmapPlane>& planes,
scoped_refptr<GbmBuffer>* buffer) {
scoped_refptr<GbmDevice> gbm =
static_cast<GbmDevice*>(device_manager_->GetDrmDevice(widget).get());
DCHECK(gbm);
*buffer = GbmBuffer::CreateBufferFromFds(
gbm, ui::GetFourCCFormatFromBufferFormat(format), size, std::move(fds),
planes);
}
void DrmThread::GetScanoutFormats(
gfx::AcceleratedWidget widget,
std::vector<gfx::BufferFormat>* scanout_formats) {
display_manager_->GetScanoutFormats(widget, scanout_formats);
}
void DrmThread::SchedulePageFlip(
gfx::AcceleratedWidget widget,
std::vector<DrmOverlayPlane> planes,
SwapCompletionOnceCallback submission_callback,
PresentationOnceCallback presentation_callback) {
scoped_refptr<ui::DrmDevice> drm_device =
device_manager_->GetDrmDevice(widget);
drm_device->plane_manager()->RequestPlanesReadyCallback(
std::move(planes), base::BindOnce(&DrmThread::OnPlanesReadyForPageFlip,
weak_ptr_factory_.GetWeakPtr(), widget,
std::move(submission_callback),
std::move(presentation_callback)));
}
void DrmThread::OnPlanesReadyForPageFlip(
gfx::AcceleratedWidget widget,
SwapCompletionOnceCallback submission_callback,
PresentationOnceCallback presentation_callback,
std::vector<DrmOverlayPlane> planes) {
DrmWindow* window = screen_manager_->GetWindow(widget);
if (window) {
window->SchedulePageFlip(std::move(planes), std::move(submission_callback),
std::move(presentation_callback));
} else {
std::move(submission_callback).Run(gfx::SwapResult::SWAP_ACK, nullptr);
std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
}
}
void DrmThread::GetVSyncParameters(
gfx::AcceleratedWidget widget,
const gfx::VSyncProvider::UpdateVSyncCallback& callback) {
DrmWindow* window = screen_manager_->GetWindow(widget);
// No need to call the callback if there isn't a window since the vsync
// provider doesn't require the callback to be called if there isn't a vsync
// data source.
if (window)
window->GetVSyncParameters(callback);
}
void DrmThread::CreateWindow(gfx::AcceleratedWidget widget) {
std::unique_ptr<DrmWindow> window(
new DrmWindow(widget, device_manager_.get(), screen_manager_.get()));
window->Initialize(buffer_generator_.get());
screen_manager_->AddWindow(widget, std::move(window));
}
void DrmThread::DestroyWindow(gfx::AcceleratedWidget widget) {
std::unique_ptr<DrmWindow> window = screen_manager_->RemoveWindow(widget);
window->Shutdown();
}
void DrmThread::SetWindowBounds(gfx::AcceleratedWidget widget,
const gfx::Rect& bounds) {
screen_manager_->GetWindow(widget)->SetBounds(bounds);
}
void DrmThread::SetCursor(gfx::AcceleratedWidget widget,
const std::vector<SkBitmap>& bitmaps,
const gfx::Point& location,
int32_t frame_delay_ms) {
screen_manager_->GetWindow(widget)
->SetCursor(bitmaps, location, frame_delay_ms);
}
void DrmThread::MoveCursor(gfx::AcceleratedWidget widget,
const gfx::Point& location) {
screen_manager_->GetWindow(widget)->MoveCursor(location);
}
void DrmThread::CheckOverlayCapabilities(
gfx::AcceleratedWidget widget,
const OverlaySurfaceCandidateList& overlays,
base::OnceCallback<void(gfx::AcceleratedWidget,
const OverlaySurfaceCandidateList&,
const OverlayStatusList&)> callback) {
TRACE_EVENT0("drm,hwoverlays", "DrmThread::CheckOverlayCapabilities");
auto params = CreateParamsFromOverlaySurfaceCandidate(overlays);
std::move(callback).Run(
widget, overlays,
CreateOverlayStatusListFrom(
screen_manager_->GetWindow(widget)->TestPageFlip(params)));
}
void DrmThread::RefreshNativeDisplays(
base::OnceCallback<void(MovableDisplaySnapshots)> callback) {
std::move(callback).Run(display_manager_->GetDisplays());
}
void DrmThread::ConfigureNativeDisplay(
int64_t id,
std::unique_ptr<display::DisplayMode> mode,
const gfx::Point& origin,
base::OnceCallback<void(int64_t, bool)> callback) {
std::move(callback).Run(
id, display_manager_->ConfigureDisplay(id, *mode, origin));
}
void DrmThread::DisableNativeDisplay(
int64_t id,
base::OnceCallback<void(int64_t, bool)> callback) {
std::move(callback).Run(id, display_manager_->DisableDisplay(id));
}
void DrmThread::TakeDisplayControl(base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(display_manager_->TakeDisplayControl());
}
void DrmThread::RelinquishDisplayControl(
base::OnceCallback<void(bool)> callback) {
display_manager_->RelinquishDisplayControl();
std::move(callback).Run(true);
}
void DrmThread::AddGraphicsDevice(const base::FilePath& path, base::File file) {
device_manager_->AddDrmDevice(path, std::move(file));
}
void DrmThread::RemoveGraphicsDevice(const base::FilePath& path) {
device_manager_->RemoveDrmDevice(path);
}
void DrmThread::GetHDCPState(
int64_t display_id,
base::OnceCallback<void(int64_t, bool, display::HDCPState)> callback) {
display::HDCPState state = display::HDCP_STATE_UNDESIRED;
bool success = display_manager_->GetHDCPState(display_id, &state);
std::move(callback).Run(display_id, success, state);
}
void DrmThread::SetHDCPState(int64_t display_id,
display::HDCPState state,
base::OnceCallback<void(int64_t, bool)> callback) {
std::move(callback).Run(display_id,
display_manager_->SetHDCPState(display_id, state));
}
void DrmThread::SetColorMatrix(int64_t display_id,
const std::vector<float>& color_matrix) {
display_manager_->SetColorMatrix(display_id, color_matrix);
}
void DrmThread::SetGammaCorrection(
int64_t display_id,
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
display_manager_->SetGammaCorrection(display_id, degamma_lut, gamma_lut);
}
void DrmThread::StartDrmDevice(StartDrmDeviceCallback callback) {
// We currently assume that |Init| always succeeds so return true to indicate
// when the DRM thread has completed launching. In particular, the invocation
// of the callback in the client triggers the invocation of DRM thread
// readiness observers.
std::move(callback).Run(true);
}
// DrmThread requires a BindingSet instead of a simple Binding because it will
// be used from multiple threads in multiple processes.
void DrmThread::AddBindingCursorDevice(
ozone::mojom::DeviceCursorRequest request) {
cursor_bindings_.AddBinding(this, std::move(request));
}
void DrmThread::AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request) {
TRACE_EVENT0("drm", "DrmThread::AddBindingDrmDevice");
drm_bindings_.AddBinding(this, std::move(request));
}
} // namespace ui