File indexing completed on 2025-04-20 10:57:33

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
0006     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "drm_buffer_gbm.h"
0011 #include "drm_gbm_surface.h"
0012 
0013 #include "config-kwin.h"
0014 #include "drm_backend.h"
0015 #include "drm_gpu.h"
0016 #include "drm_logging.h"
0017 #include "kwineglutils_p.h"
0018 #include "wayland/clientbuffer.h"
0019 #include "wayland/linuxdmabufv1clientbuffer.h"
0020 
0021 #include <cerrno>
0022 #include <drm_fourcc.h>
0023 #include <gbm.h>
0024 #include <sys/mman.h>
0025 #include <unistd.h>
0026 #include <xf86drm.h>
0027 #include <xf86drmMode.h>
0028 
0029 namespace KWin
0030 {
0031 
0032 static std::array<uint32_t, 4> getHandles(gbm_bo *bo)
0033 {
0034     std::array<uint32_t, 4> ret;
0035     int i = 0;
0036     for (; i < gbm_bo_get_plane_count(bo); i++) {
0037         ret[i] = gbm_bo_get_handle(bo).u32;
0038     }
0039     for (; i < 4; i++) {
0040         ret[i] = 0;
0041     }
0042     return ret;
0043 }
0044 
0045 static std::array<uint32_t, 4> getStrides(gbm_bo *bo)
0046 {
0047     std::array<uint32_t, 4> ret;
0048     int i = 0;
0049     for (; i < gbm_bo_get_plane_count(bo); i++) {
0050         ret[i] = gbm_bo_get_stride_for_plane(bo, i);
0051     }
0052     for (; i < 4; i++) {
0053         ret[i] = 0;
0054     }
0055     return ret;
0056 }
0057 
0058 static std::array<uint32_t, 4> getOffsets(gbm_bo *bo)
0059 {
0060     std::array<uint32_t, 4> ret;
0061     int i = 0;
0062     for (; i < gbm_bo_get_plane_count(bo); i++) {
0063         ret[i] = gbm_bo_get_offset(bo, i);
0064     }
0065     for (; i < 4; i++) {
0066         ret[i] = 0;
0067     }
0068     return ret;
0069 }
0070 
0071 GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo, const std::shared_ptr<GbmSurface> &surface)
0072     : 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))
0073     , m_bo(bo)
0074     , m_surface(surface)
0075     , m_flags(surface->flags())
0076 {
0077 }
0078 
0079 GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo, uint32_t flags)
0080     : 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))
0081     , m_bo(bo)
0082     , m_flags(flags)
0083 {
0084 }
0085 
0086 GbmBuffer::GbmBuffer(DrmGpu *gpu, gbm_bo *bo, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer, uint32_t flags)
0087     : 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))
0088     , m_bo(bo)
0089     , m_clientBuffer(clientBuffer)
0090     , m_flags(flags)
0091 {
0092     m_clientBuffer->ref();
0093 }
0094 
0095 GbmBuffer::~GbmBuffer()
0096 {
0097     if (m_clientBuffer) {
0098         m_clientBuffer->unref();
0099     }
0100     if (m_mapping) {
0101         gbm_bo_unmap(m_bo, m_mapping);
0102     }
0103     if (m_surface) {
0104         m_surface->releaseBuffer(this);
0105     } else {
0106         gbm_bo_destroy(m_bo);
0107     }
0108 }
0109 
0110 gbm_bo *GbmBuffer::bo() const
0111 {
0112     return m_bo;
0113 }
0114 
0115 void *GbmBuffer::mappedData() const
0116 {
0117     return m_data;
0118 }
0119 
0120 KWaylandServer::ClientBuffer *GbmBuffer::clientBuffer() const
0121 {
0122     return m_clientBuffer;
0123 }
0124 
0125 uint32_t GbmBuffer::flags() const
0126 {
0127     return m_flags;
0128 }
0129 
0130 GbmBuffer::Map GbmBuffer::map(uint32_t flags)
0131 {
0132     if (!m_data) {
0133         m_data = gbm_bo_map(m_bo, 0, 0, m_size.width(), m_size.height(), flags, &m_mapStride, &m_mapping);
0134     }
0135     return Map{
0136         .data = m_data,
0137         .stride = m_mapStride,
0138     };
0139 }
0140 
0141 void GbmBuffer::createFds()
0142 {
0143 #if HAVE_GBM_BO_GET_FD_FOR_PLANE
0144     for (uint32_t i = 0; i < m_planeCount; i++) {
0145         m_fds[i] = FileDescriptor(gbm_bo_get_fd_for_plane(m_bo, i));
0146         if (!m_fds[i].isValid()) {
0147             m_fds = {};
0148             return;
0149         }
0150     }
0151     return;
0152 #else
0153     if (m_planeCount > 1) {
0154         return;
0155     }
0156     m_fds[0] = FileDescriptor(gbm_bo_get_fd(m_bo));
0157 #endif
0158 }
0159 
0160 std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer)
0161 {
0162     const auto &attrs = clientBuffer->attributes();
0163     gbm_bo *bo;
0164     if (attrs.modifier != DRM_FORMAT_MOD_INVALID || attrs.offset[0] > 0 || attrs.planeCount > 1) {
0165         gbm_import_fd_modifier_data data = {};
0166         data.format = attrs.format;
0167         data.width = static_cast<uint32_t>(attrs.width);
0168         data.height = static_cast<uint32_t>(attrs.height);
0169         data.num_fds = attrs.planeCount;
0170         data.modifier = attrs.modifier;
0171         for (int i = 0; i < attrs.planeCount; i++) {
0172             data.fds[i] = attrs.fd[i].get();
0173             data.offsets[i] = attrs.offset[i];
0174             data.strides[i] = attrs.pitch[i];
0175         }
0176         bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
0177     } else {
0178         gbm_import_fd_data data = {};
0179         data.fd = attrs.fd[0].get();
0180         data.width = static_cast<uint32_t>(attrs.width);
0181         data.height = static_cast<uint32_t>(attrs.height);
0182         data.stride = attrs.pitch[0];
0183         data.format = attrs.format;
0184         bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
0185     }
0186     if (bo) {
0187         return std::make_shared<GbmBuffer>(gpu, bo, clientBuffer, GBM_BO_USE_SCANOUT);
0188     } else {
0189         return nullptr;
0190     }
0191 }
0192 
0193 std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, GbmBuffer *buffer, uint32_t flags)
0194 {
0195     const auto &fds = buffer->fds();
0196     if (!fds[0].isValid()) {
0197         return nullptr;
0198     }
0199     const auto strides = buffer->strides();
0200     const auto offsets = buffer->offsets();
0201     gbm_import_fd_modifier_data data = {
0202         .width = (uint32_t)buffer->size().width(),
0203         .height = (uint32_t)buffer->size().height(),
0204         .format = buffer->format(),
0205         .num_fds = (uint32_t)buffer->planeCount(),
0206         .fds = {},
0207         .strides = {},
0208         .offsets = {},
0209         .modifier = buffer->modifier(),
0210     };
0211     for (uint32_t i = 0; i < data.num_fds; i++) {
0212         data.fds[i] = fds[i].get();
0213         data.strides[i] = strides[i];
0214         data.offsets[i] = offsets[i];
0215     }
0216     gbm_bo *bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, flags);
0217     if (bo) {
0218         return std::make_shared<GbmBuffer>(gpu, bo, flags);
0219     } else {
0220         return nullptr;
0221     }
0222 }
0223 }