blob: 50ab78278adcb7ec61e800eb99d08f1fccf13a3e [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/ozone_platform_gbm.h"
#include <gbm.h>
#include <stdlib.h>
#include <xf86drm.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
#include "ui/base/ui_features.h"
#include "ui/events/ozone/device/device_manager.h"
#include "ui/events/ozone/evdev/event_factory_evdev.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/ozone/common/linux/scanout_buffer.h"
#include "ui/ozone/platform/drm/common/drm_util.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_thread_message_proxy.h"
#include "ui/ozone/platform/drm/gpu/drm_thread_proxy.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/screen_manager.h"
#include "ui/ozone/platform/drm/host/drm_cursor.h"
#include "ui/ozone/platform/drm/host/drm_device_connector.h"
#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
#include "ui/ozone/platform/drm/host/drm_window_host.h"
#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
#include "ui/ozone/platform/drm/host/host_drm_device.h"
#include "ui/ozone/public/cursor_factory_ozone.h"
#include "ui/ozone/public/gpu_platform_support_host.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/ozone_switches.h"
#include "ui/platform_window/platform_window_init_properties.h"
#if BUILDFLAG(USE_XKBCOMMON)
#include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h"
#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
#else
#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
#endif
namespace ui {
namespace {
class OzonePlatformGbm : public OzonePlatform {
public:
OzonePlatformGbm()
: using_mojo_(false), single_process_(false), weak_factory_(this) {}
~OzonePlatformGbm() override {}
// OzonePlatform:
ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
return surface_factory_.get();
}
OverlayManagerOzone* GetOverlayManager() override {
return overlay_manager_.get();
}
CursorFactoryOzone* GetCursorFactoryOzone() override {
return cursor_factory_ozone_.get();
}
InputController* GetInputController() override {
return event_factory_ozone_->input_controller();
}
IPC::MessageFilter* GetGpuMessageFilter() override {
if (using_mojo_) {
return nullptr;
} else {
return gpu_message_filter_.get();
}
}
GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
if (using_mojo_) {
return drm_device_connector_.get();
} else {
return gpu_platform_support_host_.get();
}
}
std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() override {
return event_factory_ozone_->CreateSystemInputInjector();
}
// In multi-process mode, this function must be executed in Viz as it sets up
// the callbacks needed for Mojo bindings. In single process mode, it may be
// called on any thread. It must follow one of |InitializeUI| or
// |InitializeGPU|. Invocations of this method when not using mojo will be
// ignored. While the caller may choose to invoke this method before entering
// the sandbox, the actual interface adding has to happen on the DRM Device
// thread and so will be deferred until the DRM thread is running.
void AddInterfaces(
service_manager::BinderRegistryWithArgs<
const service_manager::BindSourceInfo&>* registry) override {
if (!using_mojo_)
return;
registry->AddInterface<ozone::mojom::DeviceCursor>(
base::Bind(&OzonePlatformGbm::CreateDeviceCursorBinding,
weak_factory_.GetWeakPtr()),
base::ThreadTaskRunnerHandle::Get());
registry->AddInterface<ozone::mojom::DrmDevice>(
base::Bind(&OzonePlatformGbm::CreateDrmDeviceBinding,
weak_factory_.GetWeakPtr()),
base::ThreadTaskRunnerHandle::Get());
}
// Runs on the thread where AddInterfaces was invoked. But the endpoint is
// always bound on the DRM thread.
void CreateDeviceCursorBinding(
ozone::mojom::DeviceCursorRequest request,
const service_manager::BindSourceInfo& source_info) {
if (drm_thread_started_)
drm_thread_proxy_->AddBindingCursorDevice(std::move(request));
else
pending_cursor_requests_.push_back(std::move(request));
}
// Runs on the thread where AddInterfaces was invoked. But the endpoint is
// always bound on the DRM thread.
// service_manager::InterfaceFactory<ozone::mojom::DrmDevice>:
void CreateDrmDeviceBinding(
ozone::mojom::DrmDeviceRequest request,
const service_manager::BindSourceInfo& source_info) {
if (drm_thread_started_)
drm_thread_proxy_->AddBindingDrmDevice(std::move(request));
else
pending_gpu_adapter_requests_.push_back(std::move(request));
}
// Runs on the thread that invoked |AddInterfaces| to drain the queue of
// binding requests that could not be satisfied until the DRM thread is
// available (i.e. if waiting until the sandbox has been entered.)
void DrainBindingRequests() {
for (auto& request : pending_cursor_requests_)
drm_thread_proxy_->AddBindingCursorDevice(std::move(request));
pending_cursor_requests_.clear();
for (auto& request : pending_gpu_adapter_requests_)
drm_thread_proxy_->AddBindingDrmDevice(std::move(request));
pending_gpu_adapter_requests_.clear();
drm_thread_started_ = true;
}
std::unique_ptr<PlatformWindow> CreatePlatformWindow(
PlatformWindowDelegate* delegate,
const PlatformWindowInitProperties& properties) override {
GpuThreadAdapter* adapter = gpu_platform_support_host_.get();
if (using_mojo_) {
adapter = host_drm_device_.get();
}
std::unique_ptr<DrmWindowHost> platform_window(new DrmWindowHost(
delegate, properties.bounds, adapter, event_factory_ozone_.get(),
cursor_.get(), window_manager_.get(), display_manager_.get(),
overlay_manager_.get()));
platform_window->Initialize();
return std::move(platform_window);
}
std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate()
override {
return std::make_unique<DrmNativeDisplayDelegate>(display_manager_.get());
}
void InitializeUI(const InitParams& args) override {
// Ozone drm can operate in four modes configured at
// runtime. Three process modes:
// 1. legacy mode where host and viz components communicate
// via param traits IPC.
// 2. single-process mode where host and viz components
// communicate via in-process mojo. Single-process mode can be single
// or multi-threaded.
// 3. multi-process mode where host and viz components communicate
// via mojo IPC.
//
// and 2 connection modes
// a. Viz is launched via content::GpuProcessHost and it notifies the
// ozone host when Viz becomes available. b. The ozone host uses a service
// manager to launch and connect to Viz.
//
// Combinations 1a, 2b, and 3a, and 3b are supported and expected to work.
// Combination 1a will hopefully be deprecated and replaced with 3a.
// Combination 2b adds undesirable code-debt and the intent is to remove it.
single_process_ = args.single_process;
using_mojo_ = args.using_mojo || args.connector != nullptr;
host_thread_ = base::PlatformThread::CurrentRef();
device_manager_ = CreateDeviceManager();
window_manager_.reset(new DrmWindowHostManager());
cursor_.reset(new DrmCursor(window_manager_.get()));
#if BUILDFLAG(USE_XKBCOMMON)
KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
std::make_unique<XkbKeyboardLayoutEngine>(xkb_evdev_code_converter_));
#else
KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
std::make_unique<StubKeyboardLayoutEngine>());
#endif
event_factory_ozone_.reset(new EventFactoryEvdev(
cursor_.get(), device_manager_.get(),
KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()));
GpuThreadAdapter* adapter;
if (using_mojo_) {
host_drm_device_ = base::MakeRefCounted<HostDrmDevice>(cursor_.get());
drm_device_connector_ = std::make_unique<DrmDeviceConnector>(
args.connector, host_drm_device_);
adapter = host_drm_device_.get();
} else {
gpu_platform_support_host_.reset(
new DrmGpuPlatformSupportHost(cursor_.get()));
adapter = gpu_platform_support_host_.get();
}
overlay_manager_.reset(
new DrmOverlayManager(adapter, window_manager_.get()));
display_manager_.reset(new DrmDisplayHostManager(
adapter, device_manager_.get(), overlay_manager_.get(),
event_factory_ozone_->input_controller()));
cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
if (using_mojo_) {
host_drm_device_->ProvideManagers(display_manager_.get(),
overlay_manager_.get());
host_drm_device_->AsyncStartDrmDevice(*drm_device_connector_);
}
}
void InitializeGPU(const InitParams& args) override {
using_mojo_ = args.using_mojo;
gpu_task_runner_ = base::ThreadTaskRunnerHandle::Get();
InterThreadMessagingProxy* itmp;
if (!using_mojo_) {
scoped_refptr<DrmThreadMessageProxy> message_proxy(
new DrmThreadMessageProxy());
itmp = message_proxy.get();
gpu_message_filter_ = std::move(message_proxy);
}
// NOTE: Can't start the thread here since this is called before sandbox
// initialization in multi-process Chrome.
drm_thread_proxy_.reset(new DrmThreadProxy());
surface_factory_.reset(new GbmSurfaceFactory(drm_thread_proxy_.get()));
if (!using_mojo_) {
drm_thread_proxy_->BindThreadIntoMessagingProxy(itmp);
}
// If InitializeGPU and InitializeUI are invoked on the same thread, startup
// sequencing is complicated because tasks are queued on the unbound mojo
// pipe connecting the UI (the host) to the DRM thread before the DRM thread
// is launched above. Special case this sequence via the
// BlockingStartDrmDevice API.
// TODO(rjkroege): In a future when we have completed splitting Viz, it will
// be possible to simplify this logic.
if (using_mojo_ && single_process_) {
CHECK(host_drm_device_)
<< "Mojo single-process mode requires a HostDrmDevice.";
// Wait here if host and gpu are one and the same thread.
if (host_thread_ == base::PlatformThread::CurrentRef()) {
// One-thread exection does not permit use of the sandbox.
AfterSandboxEntry();
host_drm_device_->BlockingStartDrmDevice();
}
}
}
// The DRM thread needs to be started late because we need to wait for the
// sandbox to start. This entry point in the Ozne API gives platforms
// flexibility in handing this requirement.
void AfterSandboxEntry() override {
CHECK(drm_thread_proxy_) << "AfterSandboxEntry before InitializeForGPU is "
"invalid startup order.\n";
// Defer the actual startup of the DRM thread to here.
auto safe_binding_resquest_drainer = CreateSafeOnceCallback(base::BindOnce(
&OzonePlatformGbm::DrainBindingRequests, weak_factory_.GetWeakPtr()));
drm_thread_proxy_->StartDrmThread(std::move(safe_binding_resquest_drainer));
}
private:
bool using_mojo_;
bool single_process_;
// Objects in the GPU process.
std::unique_ptr<DrmThreadProxy> drm_thread_proxy_;
std::unique_ptr<GbmSurfaceFactory> surface_factory_;
scoped_refptr<IPC::MessageFilter> gpu_message_filter_;
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
// TODO(rjkroege,sadrul): Provide a more elegant solution for this issue when
// running in single process mode.
std::vector<ozone::mojom::DeviceCursorRequest> pending_cursor_requests_;
std::vector<ozone::mojom::DrmDeviceRequest> pending_gpu_adapter_requests_;
bool drm_thread_started_;
// gpu_platform_support_host_ is the IPC bridge to the GPU process while
// host_drm_device_ is the mojo bridge to the Viz process. Only one can be in
// use at any time.
// TODO(rjkroege): Remove gpu_platform_support_host_ once ozone/drm with mojo
// has reached the stable channel.
// A raw pointer to either |gpu_platform_support_host_| or |host_drm_device_|
// is passed to |display_manager_| and |overlay_manager_| in IntializeUI.
// To avoid a use after free, the following two members should be declared
// before the two managers, so that they're deleted after them.
std::unique_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_;
// Objects in the host process.
std::unique_ptr<DrmDeviceConnector> drm_device_connector_;
scoped_refptr<HostDrmDevice> host_drm_device_;
base::PlatformThreadRef host_thread_;
std::unique_ptr<DeviceManager> device_manager_;
std::unique_ptr<BitmapCursorFactoryOzone> cursor_factory_ozone_;
std::unique_ptr<DrmWindowHostManager> window_manager_;
std::unique_ptr<DrmCursor> cursor_;
std::unique_ptr<EventFactoryEvdev> event_factory_ozone_;
std::unique_ptr<DrmDisplayHostManager> display_manager_;
std::unique_ptr<DrmOverlayManager> overlay_manager_;
#if BUILDFLAG(USE_XKBCOMMON)
XkbEvdevCodes xkb_evdev_code_converter_;
#endif
base::WeakPtrFactory<OzonePlatformGbm> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(OzonePlatformGbm);
};
} // namespace
OzonePlatform* CreateOzonePlatformGbm() {
return new OzonePlatformGbm;
}
} // namespace ui