1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include <wayland-drm-client-protocol.h>
#include <fcntl.h>
#include <xf86drm.h>
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_drm.h"
#include "wayland-util.h"
namespace ui {
namespace {
constexpr uint32_t kMinVersion = 2;
}
// static
constexpr char WaylandDrm::kInterfaceName[];
// static
void WaylandDrm::Instantiate(WaylandConnection* connection,
wl_registry* registry,
uint32_t name,
const std::string& interface,
uint32_t version) {
CHECK_EQ(interface, kInterfaceName) << "Expected \"" << kInterfaceName
<< "\" but got \"" << interface << "\"";
auto* buffer_factory = connection->buffer_factory();
if (buffer_factory->wayland_drm_ ||
!wl::CanBind(interface, version, kMinVersion, kMinVersion)) {
return;
}
auto wl_obj = wl::Bind<wl_drm>(registry, name, version);
if (!wl_obj) {
LOG(ERROR) << "Failed to bind wl_drm";
return;
}
buffer_factory->wayland_drm_ =
std::make_unique<WaylandDrm>(wl_obj.release(), connection);
}
WaylandDrm::WaylandDrm(wl_drm* drm, WaylandConnection* connection)
: wl_drm_(drm), connection_(connection) {
static constexpr wl_drm_listener kDrmListener = {
.device = &OnDevice,
.format = &OnFormat,
.authenticated = &OnAuthenticated,
.capabilities = &OnCapabilities,
};
wl_drm_add_listener(wl_drm_.get(), &kDrmListener, this);
connection_->Flush();
// A roundtrip after binding guarantees that the client has received all
// supported formats and capabilities of the device.
connection_->RoundTripQueue();
}
WaylandDrm::~WaylandDrm() = default;
bool WaylandDrm::SupportsDrmPrime() const {
return authenticated_ && !!wl_drm_;
}
void WaylandDrm::CreateBuffer(const base::ScopedFD& fd,
const gfx::Size& size,
const std::vector<uint32_t>& strides,
const std::vector<uint32_t>& offsets,
const std::vector<uint64_t>& modifiers,
uint32_t format,
uint32_t planes_count,
wl::OnRequestBufferCallback callback) {
// If the |planes_count| less than the maximum sizes of these arrays and the
// number of offsets and strides that |wl_drm| can receive, just initialize
// them to 0, which is totally ok.
std::array<uint32_t, 3> stride = {0};
std::array<uint32_t, 3> offset = {0};
for (size_t i = 0; i < planes_count; i++) {
stride[i] = strides[i];
offset[i] = offset[i];
}
wl::Object<wl_buffer> buffer(wl_drm_create_prime_buffer(
wl_drm_.get(), fd.get(), size.width(), size.height(), format, offset[0],
stride[0], offset[1], stride[1], offset[2], stride[2]));
connection_->Flush();
std::move(callback).Run(std::move(buffer));
}
bool WaylandDrm::CanCreateBufferImmed() const {
// Unlike the WaylandZwpLinuxDmabuf, the WaylandDrm always creates wl_buffers
// immediately.
return true;
}
void WaylandDrm::HandleDrmFailure(const std::string& error) {
LOG(WARNING) << error;
wl_drm_.reset();
}
void WaylandDrm::AddSupportedFourCCFormat(uint32_t fourcc_format) {
// Return on unsupported fourcc formats.
if (!IsValidBufferFormat(fourcc_format))
return;
gfx::BufferFormat format = GetBufferFormatFromFourCCFormat(fourcc_format);
// Modifiers are not supported by the |wl_drm|, but for consistency with the
// WaylandZwpLinuxDmabuf we use the same map type, which is passed to the
// WaylandBufferManagerGpu later during initialization stage of the GPU
// process.
std::vector<uint64_t> modifiers;
supported_buffer_formats_.emplace(format, std::move(modifiers));
}
void WaylandDrm::Authenticate(const char* drm_device_path) {
if (!wl_drm_)
return;
DCHECK(drm_device_path);
base::ScopedFD drm_fd(open(drm_device_path, O_RDWR));
if (!drm_fd.is_valid()) {
HandleDrmFailure("Drm open failed: " + std::string(drm_device_path));
return;
}
if (drmGetNodeTypeFromFd(drm_fd.get()) != DRM_NODE_PRIMARY) {
DrmDeviceAuthenticated(wl_drm_.get());
return;
}
drm_magic_t magic;
memset(&magic, 0, sizeof(magic));
if (drmGetMagic(drm_fd.get(), &magic)) {
HandleDrmFailure("Failed to get drm magic");
return;
}
wl_drm_authenticate(wl_drm_.get(), magic);
connection_->Flush();
// Do the roundtrip to make sure the server processes this request and
// authenticates us.
connection_->RoundTripQueue();
}
void WaylandDrm::DrmDeviceAuthenticated(wl_drm* drm) {
DCHECK(wl_drm_ && wl_drm_.get() == drm);
authenticated_ = true;
}
void WaylandDrm::HandleCapabilities(uint32_t value) {
if ((value & WL_DRM_CAPABILITY_PRIME) == 0) {
HandleDrmFailure("Drm prime capability is not supported");
}
}
// static
void WaylandDrm::OnDevice(void* data, wl_drm* drm, const char* path) {
auto* self = static_cast<WaylandDrm*>(data);
DCHECK(self && self->wl_drm_.get() == drm);
self->Authenticate(path);
}
// static
void WaylandDrm::OnFormat(void* data, wl_drm* drm, uint32_t format) {
auto* self = static_cast<WaylandDrm*>(data);
DCHECK(self && self->wl_drm_.get() == drm);
self->AddSupportedFourCCFormat(format);
}
// static
void WaylandDrm::OnAuthenticated(void* data, wl_drm* drm) {
auto* self = static_cast<WaylandDrm*>(data);
DCHECK(self);
self->DrmDeviceAuthenticated(drm);
}
// static
void WaylandDrm::OnCapabilities(void* data, wl_drm* drm, uint32_t value) {
auto* self = static_cast<WaylandDrm*>(data);
DCHECK(self && self->wl_drm_.get() == drm);
self->HandleCapabilities(value);
}
} // namespace ui
|