core: Provide own dumb graphics buffer allocator

If a gbm_bo is allocated with GBM_BO_USE_WRITE, it will be backed by a
dumb buffer under the hood. However, it seems like neither gbm_bo_get_fd()
nor gbm_bo_get_plane_fd() would return valid file descriptors, which are
required to fill in DmaBufAttributes.

As an interim solution, this change makes the GbmGraphicsBufferAllocator
allocate dumb buffers on its own rather than delegate it to gbm.
This commit is contained in:
Vlad Zahorodnii 2023-06-05 14:50:20 +03:00
parent e81fa52c76
commit dc70be404a
2 changed files with 142 additions and 19 deletions

View file

@ -6,9 +6,13 @@
#include "core/gbmgraphicsbufferallocator.h"
#include "backends/drm/gbm_dmabuf.h" // FIXME: move dmaBufAttributesForBo() elsewhere
#include "utils/common.h"
#include <drm_fourcc.h>
#include <fcntl.h>
#include <gbm.h>
#include <sys/mman.h>
#include <xf86drm.h>
namespace KWin
{
@ -22,28 +26,48 @@ GbmGraphicsBufferAllocator::~GbmGraphicsBufferAllocator()
{
}
GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions &options)
static GraphicsBuffer *allocateDumb(gbm_device *device, const GraphicsBufferOptions &options)
{
if (options.software) {
gbm_bo *bo = gbm_bo_create(m_gbmDevice,
options.size.width(),
options.size.height(),
options.format,
GBM_BO_USE_SCANOUT | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR);
if (bo) {
std::optional<DmaBufAttributes> attributes = dmaBufAttributesForBo(bo);
if (!attributes.has_value()) {
gbm_bo_destroy(bo);
return nullptr;
}
attributes->modifier = DRM_FORMAT_MOD_LINEAR;
return new GbmGraphicsBuffer(std::move(attributes.value()), bo);
}
if (!options.modifiers.isEmpty()) {
return nullptr;
}
drm_mode_create_dumb createArgs{
.height = uint32_t(options.size.height()),
.width = uint32_t(options.size.width()),
.bpp = 32,
};
if (drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) {
qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_CREATE_DUMB failed:" << strerror(errno);
return nullptr;
}
int primeFd;
if (drmPrimeHandleToFD(gbm_device_get_fd(device), createArgs.handle, DRM_CLOEXEC, &primeFd) != 0) {
qCWarning(KWIN_CORE) << "drmPrimeHandleToFD() failed:" << strerror(errno);
drm_mode_destroy_dumb destroyArgs{
.handle = createArgs.handle,
};
drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
return nullptr;
}
return new DumbGraphicsBuffer(gbm_device_get_fd(device), createArgs.handle, DmaBufAttributes{
.planeCount = 1,
.width = options.size.width(),
.height = options.size.height(),
.format = options.format,
.modifier = DRM_FORMAT_MOD_LINEAR,
.fd = {FileDescriptor(primeFd), FileDescriptor{}, FileDescriptor{}, FileDescriptor{}},
.offset = {0, 0, 0, 0},
.pitch = {createArgs.pitch, 0, 0, 0},
});
}
static GraphicsBuffer *allocateDmaBuf(gbm_device *device, const GraphicsBufferOptions &options)
{
if (!options.modifiers.isEmpty() && !(options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_INVALID)) {
gbm_bo *bo = gbm_bo_create_with_modifiers(m_gbmDevice,
gbm_bo *bo = gbm_bo_create_with_modifiers(device,
options.size.width(),
options.size.height(),
options.format,
@ -62,11 +86,11 @@ GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions
uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
if (options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_LINEAR) {
flags |= GBM_BO_USE_LINEAR;
} else if (!modifiers.isEmpty() && !modifiers.contains(DRM_FORMAT_MOD_INVALID)) {
} else if (!options.modifiers.isEmpty() && !options.modifiers.contains(DRM_FORMAT_MOD_INVALID)) {
return nullptr;
}
gbm_bo *bo = gbm_bo_create(m_gbmDevice,
gbm_bo *bo = gbm_bo_create(device,
options.size.width(),
options.size.height(),
options.format,
@ -88,6 +112,15 @@ GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions
return nullptr;
}
GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions &options)
{
if (options.software) {
return allocateDumb(m_gbmDevice, options);
}
return allocateDmaBuf(m_gbmDevice, options);
}
GbmGraphicsBuffer::GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle)
: m_bo(handle)
, m_dmabufAttributes(std::move(attributes))
@ -145,4 +178,70 @@ void GbmGraphicsBuffer::unmap()
}
}
DumbGraphicsBuffer::DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes)
: m_drmFd(drmFd)
, m_handle(handle)
, m_size(attributes.pitch[0] * attributes.height)
, m_dmabufAttributes(std::move(attributes))
, m_hasAlphaChannel(alphaChannelFromDrmFormat(m_dmabufAttributes.format))
{
}
DumbGraphicsBuffer::~DumbGraphicsBuffer()
{
unmap();
drm_mode_destroy_dumb destroyArgs{
.handle = m_handle,
};
drmIoctl(m_drmFd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
}
QSize DumbGraphicsBuffer::size() const
{
return QSize(m_dmabufAttributes.width, m_dmabufAttributes.height);
}
bool DumbGraphicsBuffer::hasAlphaChannel() const
{
return m_hasAlphaChannel;
}
const DmaBufAttributes *DumbGraphicsBuffer::dmabufAttributes() const
{
return &m_dmabufAttributes;
}
void *DumbGraphicsBuffer::map(MapFlags flags)
{
if (m_data) {
return m_data;
}
drm_mode_map_dumb mapArgs{
.handle = m_handle,
};
if (drmIoctl(m_drmFd, DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) {
qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_MAP_DUMB failed:" << strerror(errno);
return nullptr;
}
void *address = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drmFd, mapArgs.offset);
if (address == MAP_FAILED) {
qCWarning(KWIN_CORE) << "mmap() failed:" << strerror(errno);
return nullptr;
}
m_data = address;
return m_data;
}
void DumbGraphicsBuffer::unmap()
{
if (m_data) {
munmap(m_data, m_size);
m_data = nullptr;
}
}
} // namespace KWin

View file

@ -39,6 +39,30 @@ private:
bool m_hasAlphaChannel;
};
class KWIN_EXPORT DumbGraphicsBuffer : public GraphicsBuffer
{
Q_OBJECT
public:
DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes);
~DumbGraphicsBuffer() override;
void *map(MapFlags flags) override;
void unmap() override;
QSize size() const override;
bool hasAlphaChannel() const override;
const DmaBufAttributes *dmabufAttributes() const override;
private:
int m_drmFd;
uint32_t m_handle;
void *m_data = nullptr;
size_t m_size = 0;
DmaBufAttributes m_dmabufAttributes;
bool m_hasAlphaChannel;
};
class KWIN_EXPORT GbmGraphicsBufferAllocator : public GraphicsBufferAllocator
{
public: