File indexing completed on 2024-05-12 05:31:21

0001 /*
0002     SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "core/gbmgraphicsbufferallocator.h"
0008 
0009 #include <config-kwin.h>
0010 
0011 #include "core/graphicsbuffer.h"
0012 #include "utils/common.h"
0013 
0014 #include <drm_fourcc.h>
0015 #include <fcntl.h>
0016 #include <gbm.h>
0017 #include <sys/mman.h>
0018 #include <xf86drm.h>
0019 
0020 namespace KWin
0021 {
0022 
0023 static inline std::optional<DmaBufAttributes> dmaBufAttributesForBo(gbm_bo *bo)
0024 {
0025     DmaBufAttributes attributes;
0026     attributes.planeCount = gbm_bo_get_plane_count(bo);
0027     attributes.width = gbm_bo_get_width(bo);
0028     attributes.height = gbm_bo_get_height(bo);
0029     attributes.format = gbm_bo_get_format(bo);
0030     attributes.modifier = gbm_bo_get_modifier(bo);
0031 
0032 #if HAVE_GBM_BO_GET_FD_FOR_PLANE
0033     for (int i = 0; i < attributes.planeCount; ++i) {
0034         attributes.fd[i] = FileDescriptor{gbm_bo_get_fd_for_plane(bo, i)};
0035         if (!attributes.fd[i].isValid()) {
0036             qWarning() << "gbm_bo_get_fd_for_plane() failed:" << strerror(errno);
0037             return std::nullopt;
0038         }
0039         attributes.offset[i] = gbm_bo_get_offset(bo, i);
0040         attributes.pitch[i] = gbm_bo_get_stride_for_plane(bo, i);
0041     }
0042 #else
0043     if (attributes.planeCount > 1) {
0044         return attributes;
0045     }
0046 
0047     attributes.fd[0] = FileDescriptor{gbm_bo_get_fd(bo)};
0048     if (!attributes.fd[0].isValid()) {
0049         qWarning() << "gbm_bo_get_fd() failed:" << strerror(errno);
0050         return std::nullopt;
0051     }
0052     attributes.offset[0] = gbm_bo_get_offset(bo, 0);
0053     attributes.pitch[0] = gbm_bo_get_stride_for_plane(bo, 0);
0054 #endif
0055 
0056     return attributes;
0057 }
0058 
0059 class GbmGraphicsBuffer : public GraphicsBuffer
0060 {
0061     Q_OBJECT
0062 
0063 public:
0064     GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle);
0065     ~GbmGraphicsBuffer() override;
0066 
0067     Map map(MapFlags flags) override;
0068     void unmap() override;
0069 
0070     QSize size() const override;
0071     bool hasAlphaChannel() const override;
0072     const DmaBufAttributes *dmabufAttributes() const override;
0073 
0074 private:
0075     gbm_bo *m_bo;
0076     void *m_mapPtr = nullptr;
0077     void *m_mapData = nullptr;
0078     // the stride of the buffer mapping can be different from the stride of the buffer itself
0079     uint32_t m_mapStride = 0;
0080     DmaBufAttributes m_dmabufAttributes;
0081     QSize m_size;
0082     bool m_hasAlphaChannel;
0083 };
0084 
0085 class DumbGraphicsBuffer : public GraphicsBuffer
0086 {
0087     Q_OBJECT
0088 
0089 public:
0090     DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes);
0091     ~DumbGraphicsBuffer() override;
0092 
0093     Map map(MapFlags flags) override;
0094     void unmap() override;
0095 
0096     QSize size() const override;
0097     bool hasAlphaChannel() const override;
0098     const DmaBufAttributes *dmabufAttributes() const override;
0099 
0100 private:
0101     int m_drmFd;
0102     uint32_t m_handle;
0103     void *m_data = nullptr;
0104     size_t m_size = 0;
0105     DmaBufAttributes m_dmabufAttributes;
0106     bool m_hasAlphaChannel;
0107 };
0108 
0109 GbmGraphicsBufferAllocator::GbmGraphicsBufferAllocator(gbm_device *device)
0110     : m_gbmDevice(device)
0111 {
0112 }
0113 
0114 GbmGraphicsBufferAllocator::~GbmGraphicsBufferAllocator()
0115 {
0116 }
0117 
0118 static GraphicsBuffer *allocateDumb(gbm_device *device, const GraphicsBufferOptions &options)
0119 {
0120     if (!options.modifiers.isEmpty()) {
0121         return nullptr;
0122     }
0123 
0124     drm_mode_create_dumb createArgs{
0125         .height = uint32_t(options.size.height()),
0126         .width = uint32_t(options.size.width()),
0127         .bpp = 32,
0128     };
0129     if (drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) {
0130         qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_CREATE_DUMB failed:" << strerror(errno);
0131         return nullptr;
0132     }
0133 
0134     int primeFd;
0135     if (drmPrimeHandleToFD(gbm_device_get_fd(device), createArgs.handle, DRM_CLOEXEC, &primeFd) != 0) {
0136         qCWarning(KWIN_CORE) << "drmPrimeHandleToFD() failed:" << strerror(errno);
0137         drm_mode_destroy_dumb destroyArgs{
0138             .handle = createArgs.handle,
0139         };
0140         drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
0141         return nullptr;
0142     }
0143 
0144     return new DumbGraphicsBuffer(gbm_device_get_fd(device), createArgs.handle, DmaBufAttributes{
0145         .planeCount = 1,
0146         .width = options.size.width(),
0147         .height = options.size.height(),
0148         .format = options.format,
0149         .modifier = DRM_FORMAT_MOD_LINEAR,
0150         .fd = {FileDescriptor(primeFd), FileDescriptor{}, FileDescriptor{}, FileDescriptor{}},
0151         .offset = {0, 0, 0, 0},
0152         .pitch = {createArgs.pitch, 0, 0, 0},
0153     });
0154 }
0155 
0156 static GraphicsBuffer *allocateDmaBuf(gbm_device *device, const GraphicsBufferOptions &options)
0157 {
0158     if (!options.modifiers.isEmpty() && !(options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_INVALID)) {
0159         gbm_bo *bo = gbm_bo_create_with_modifiers(device,
0160                                                   options.size.width(),
0161                                                   options.size.height(),
0162                                                   options.format,
0163                                                   options.modifiers.constData(),
0164                                                   options.modifiers.size());
0165         if (bo) {
0166             std::optional<DmaBufAttributes> attributes = dmaBufAttributesForBo(bo);
0167             if (!attributes.has_value()) {
0168                 gbm_bo_destroy(bo);
0169                 return nullptr;
0170             }
0171             return new GbmGraphicsBuffer(std::move(attributes.value()), bo);
0172         }
0173     }
0174 
0175     uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
0176     if (options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_LINEAR) {
0177         flags |= GBM_BO_USE_LINEAR;
0178     } else if (!options.modifiers.isEmpty() && !options.modifiers.contains(DRM_FORMAT_MOD_INVALID)) {
0179         return nullptr;
0180     }
0181 
0182     gbm_bo *bo = gbm_bo_create(device,
0183                                options.size.width(),
0184                                options.size.height(),
0185                                options.format,
0186                                flags);
0187     if (bo) {
0188         std::optional<DmaBufAttributes> attributes = dmaBufAttributesForBo(bo);
0189         if (!attributes.has_value()) {
0190             gbm_bo_destroy(bo);
0191             return nullptr;
0192         }
0193         if (flags & GBM_BO_USE_LINEAR) {
0194             attributes->modifier = DRM_FORMAT_MOD_LINEAR;
0195         } else {
0196             attributes->modifier = DRM_FORMAT_MOD_INVALID;
0197         }
0198         return new GbmGraphicsBuffer(std::move(attributes.value()), bo);
0199     }
0200 
0201     return nullptr;
0202 }
0203 
0204 GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions &options)
0205 {
0206     if (options.software) {
0207         return allocateDumb(m_gbmDevice, options);
0208     }
0209 
0210     return allocateDmaBuf(m_gbmDevice, options);
0211 }
0212 
0213 GbmGraphicsBuffer::GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle)
0214     : m_bo(handle)
0215     , m_dmabufAttributes(std::move(attributes))
0216     , m_size(m_dmabufAttributes.width, m_dmabufAttributes.height)
0217     , m_hasAlphaChannel(alphaChannelFromDrmFormat(m_dmabufAttributes.format))
0218 {
0219 }
0220 
0221 GbmGraphicsBuffer::~GbmGraphicsBuffer()
0222 {
0223     unmap();
0224     gbm_bo_destroy(m_bo);
0225 }
0226 
0227 QSize GbmGraphicsBuffer::size() const
0228 {
0229     return m_size;
0230 }
0231 
0232 bool GbmGraphicsBuffer::hasAlphaChannel() const
0233 {
0234     return m_hasAlphaChannel;
0235 }
0236 
0237 const DmaBufAttributes *GbmGraphicsBuffer::dmabufAttributes() const
0238 {
0239     return &m_dmabufAttributes;
0240 }
0241 
0242 GraphicsBuffer::Map GbmGraphicsBuffer::map(MapFlags flags)
0243 {
0244     if (!m_mapPtr) {
0245         uint32_t access = 0;
0246         if (flags & MapFlag::Read) {
0247             access |= GBM_BO_TRANSFER_READ;
0248         }
0249         if (flags & MapFlag::Write) {
0250             access |= GBM_BO_TRANSFER_WRITE;
0251         }
0252         m_mapPtr = gbm_bo_map(m_bo, 0, 0, m_dmabufAttributes.width, m_dmabufAttributes.height, access, &m_mapStride, &m_mapData);
0253     }
0254     return Map{
0255         .data = m_mapPtr,
0256         .stride = m_mapStride,
0257     };
0258 }
0259 
0260 void GbmGraphicsBuffer::unmap()
0261 {
0262     if (m_mapPtr) {
0263         gbm_bo_unmap(m_bo, m_mapData);
0264         m_mapPtr = nullptr;
0265         m_mapData = nullptr;
0266     }
0267 }
0268 
0269 DumbGraphicsBuffer::DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes)
0270     : m_drmFd(drmFd)
0271     , m_handle(handle)
0272     , m_size(attributes.pitch[0] * attributes.height)
0273     , m_dmabufAttributes(std::move(attributes))
0274     , m_hasAlphaChannel(alphaChannelFromDrmFormat(m_dmabufAttributes.format))
0275 {
0276 }
0277 
0278 DumbGraphicsBuffer::~DumbGraphicsBuffer()
0279 {
0280     unmap();
0281 
0282     drm_mode_destroy_dumb destroyArgs{
0283         .handle = m_handle,
0284     };
0285     drmIoctl(m_drmFd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
0286 }
0287 
0288 QSize DumbGraphicsBuffer::size() const
0289 {
0290     return QSize(m_dmabufAttributes.width, m_dmabufAttributes.height);
0291 }
0292 
0293 bool DumbGraphicsBuffer::hasAlphaChannel() const
0294 {
0295     return m_hasAlphaChannel;
0296 }
0297 
0298 const DmaBufAttributes *DumbGraphicsBuffer::dmabufAttributes() const
0299 {
0300     return &m_dmabufAttributes;
0301 }
0302 
0303 GraphicsBuffer::Map DumbGraphicsBuffer::map(MapFlags flags)
0304 {
0305     if (!m_data) {
0306         drm_mode_map_dumb mapArgs{
0307             .handle = m_handle,
0308         };
0309         if (drmIoctl(m_drmFd, DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) {
0310             qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_MAP_DUMB failed:" << strerror(errno);
0311             return {};
0312         }
0313 
0314         void *address = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drmFd, mapArgs.offset);
0315         if (address == MAP_FAILED) {
0316             qCWarning(KWIN_CORE) << "mmap() failed:" << strerror(errno);
0317             return {};
0318         }
0319 
0320         m_data = address;
0321     }
0322     return Map{
0323         .data = m_data,
0324         .stride = m_dmabufAttributes.pitch[0],
0325     };
0326 }
0327 
0328 void DumbGraphicsBuffer::unmap()
0329 {
0330     if (m_data) {
0331         munmap(m_data, m_size);
0332         m_data = nullptr;
0333     }
0334 }
0335 
0336 } // namespace KWin
0337 
0338 #include "gbmgraphicsbufferallocator.moc"
0339 #include "moc_gbmgraphicsbufferallocator.cpp"