kwin/src/backends/drm/drm_buffer_gbm.cpp
Xaver Hugl 6cdb1e6f64 backends/drm: add stricter checks for direct scanout
Buffers with implicit modifiers from another GPU must not be imported, as
the layouts may not be compatible.
For buffers with incompatible modifiers, direct scanout can also be rejected
early, saving some computational power.

BUG: 457851
2022-08-14 19:53:30 +02:00

214 lines
5.9 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_buffer_gbm.h"
#include "drm_gbm_surface.h"
#include "config-kwin.h"
#include "drm_backend.h"
#include "drm_gpu.h"
#include "drm_logging.h"
#include "kwineglutils_p.h"
#include "wayland/clientbuffer.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include <cerrno>
#include <drm_fourcc.h>
#include <gbm.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
namespace KWin
{
static std::array<uint32_t, 4> getHandles(gbm_bo *bo)
{
std::array<uint32_t, 4> ret;
int i = 0;
for (; i < gbm_bo_get_plane_count(bo); i++) {
ret[i] = gbm_bo_get_handle(bo).u32;
}
for (; i < 4; i++) {
ret[i] = 0;
}
return ret;
}
static std::array<uint32_t, 4> getStrides(gbm_bo *bo)
{
std::array<uint32_t, 4> ret;
int i = 0;
for (; i < gbm_bo_get_plane_count(bo); i++) {
ret[i] = gbm_bo_get_stride_for_plane(bo, i);
}
for (; i < 4; i++) {
ret[i] = 0;
}
return ret;
}
static std::array<uint32_t, 4> getOffsets(gbm_bo *bo)
{
std::array<uint32_t, 4> ret;
int i = 0;
for (; i < gbm_bo_get_plane_count(bo); i++) {
ret[i] = gbm_bo_get_offset(bo, i);
}
for (; i < 4; i++) {
ret[i] = 0;
}
return ret;
}
GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo, const std::shared_ptr<GbmSurface> &surface)
: DrmGpuBuffer(gpu, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)), gbm_bo_get_format(bo), gbm_bo_get_modifier(bo), getHandles(bo), getStrides(bo), getOffsets(bo), gbm_bo_get_plane_count(bo))
, m_bo(bo)
, m_surface(surface)
{
}
GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo)
: DrmGpuBuffer(gpu, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)), gbm_bo_get_format(bo), gbm_bo_get_modifier(bo), getHandles(bo), getStrides(bo), getOffsets(bo), gbm_bo_get_plane_count(bo))
, m_bo(bo)
{
}
GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer)
: DrmGpuBuffer(gpu, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)), gbm_bo_get_format(bo), gbm_bo_get_modifier(bo), getHandles(bo), getStrides(bo), getOffsets(bo), gbm_bo_get_plane_count(bo))
, m_bo(bo)
, m_clientBuffer(clientBuffer)
{
m_clientBuffer->ref();
}
GbmBuffer::~GbmBuffer()
{
if (m_clientBuffer) {
m_clientBuffer->unref();
}
if (m_mapping) {
gbm_bo_unmap(m_bo, m_mapping);
}
if (m_surface) {
m_surface->releaseBuffer(this);
} else {
gbm_bo_destroy(m_bo);
}
}
gbm_bo *GbmBuffer::bo() const
{
return m_bo;
}
void *GbmBuffer::mappedData() const
{
return m_data;
}
KWaylandServer::ClientBuffer *GbmBuffer::clientBuffer() const
{
return m_clientBuffer;
}
bool GbmBuffer::map(uint32_t flags)
{
if (m_data) {
return true;
}
uint32_t stride = m_strides[0];
m_data = gbm_bo_map(m_bo, 0, 0, m_size.width(), m_size.height(), flags, &stride, &m_mapping);
return m_data;
}
void GbmBuffer::createFds()
{
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
for (uint32_t i = 0; i < m_planeCount; i++) {
m_fds[i] = FileDescriptor(gbm_bo_get_fd_for_plane(m_bo, i));
if (!m_fds[i].isValid()) {
m_fds = {};
return;
}
}
return;
#else
if (m_planeCount > 1) {
return;
}
m_fds[0] = FileDescriptor(gbm_bo_get_fd(m_bo));
#endif
}
std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer)
{
const auto &attrs = clientBuffer->attributes();
gbm_bo *bo;
if (attrs.modifier != DRM_FORMAT_MOD_INVALID || attrs.offset[0] > 0 || attrs.planeCount > 1) {
gbm_import_fd_modifier_data data = {};
data.format = attrs.format;
data.width = static_cast<uint32_t>(attrs.width);
data.height = static_cast<uint32_t>(attrs.height);
data.num_fds = attrs.planeCount;
data.modifier = attrs.modifier;
for (int i = 0; i < attrs.planeCount; i++) {
data.fds[i] = attrs.fd[i].get();
data.offsets[i] = attrs.offset[i];
data.strides[i] = attrs.pitch[i];
}
bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
} else {
gbm_import_fd_data data = {};
data.fd = attrs.fd[0].get();
data.width = static_cast<uint32_t>(attrs.width);
data.height = static_cast<uint32_t>(attrs.height);
data.stride = attrs.pitch[0];
data.format = attrs.format;
bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
}
if (bo) {
return std::make_shared<GbmBuffer>(gpu, bo, clientBuffer);
} else {
return nullptr;
}
}
std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, GbmBuffer *buffer, uint32_t flags)
{
const auto &fds = buffer->fds();
if (!fds[0].isValid()) {
return nullptr;
}
const auto strides = buffer->strides();
const auto offsets = buffer->offsets();
gbm_import_fd_modifier_data data = {
.width = (uint32_t)buffer->size().width(),
.height = (uint32_t)buffer->size().height(),
.format = buffer->format(),
.num_fds = (uint32_t)buffer->planeCount(),
.fds = {},
.strides = {},
.offsets = {},
.modifier = buffer->modifier(),
};
for (uint32_t i = 0; i < data.num_fds; i++) {
data.fds[i] = fds[i].get();
data.strides[i] = strides[i];
data.offsets[i] = offsets[i];
}
gbm_bo *bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, flags);
if (bo) {
return std::make_shared<GbmBuffer>(gpu, bo);
} else {
return nullptr;
}
}
}