File indexing completed on 2024-11-10 04:56:38
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"