KWin
Loading...
Searching...
No Matches
gbmgraphicsbufferallocator.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
8
9#include <config-kwin.h>
10
11#include "core/graphicsbuffer.h"
12#include "utils/common.h"
13
14#include <drm_fourcc.h>
15#include <fcntl.h>
16#include <gbm.h>
17#include <sys/mman.h>
18#include <xf86drm.h>
19
20namespace KWin
21{
22
23static inline std::optional<DmaBufAttributes> dmaBufAttributesForBo(gbm_bo *bo)
24{
25 DmaBufAttributes attributes;
26 attributes.planeCount = gbm_bo_get_plane_count(bo);
27 attributes.width = gbm_bo_get_width(bo);
28 attributes.height = gbm_bo_get_height(bo);
29 attributes.format = gbm_bo_get_format(bo);
30 attributes.modifier = gbm_bo_get_modifier(bo);
31
32#if HAVE_GBM_BO_GET_FD_FOR_PLANE
33 for (int i = 0; i < attributes.planeCount; ++i) {
34 attributes.fd[i] = FileDescriptor{gbm_bo_get_fd_for_plane(bo, i)};
35 if (!attributes.fd[i].isValid()) {
36 qWarning() << "gbm_bo_get_fd_for_plane() failed:" << strerror(errno);
37 return std::nullopt;
38 }
39 attributes.offset[i] = gbm_bo_get_offset(bo, i);
40 attributes.pitch[i] = gbm_bo_get_stride_for_plane(bo, i);
41 }
42#else
43 if (attributes.planeCount > 1) {
44 return attributes;
45 }
46
47 attributes.fd[0] = FileDescriptor{gbm_bo_get_fd(bo)};
48 if (!attributes.fd[0].isValid()) {
49 qWarning() << "gbm_bo_get_fd() failed:" << strerror(errno);
50 return std::nullopt;
51 }
52 attributes.offset[0] = gbm_bo_get_offset(bo, 0);
53 attributes.pitch[0] = gbm_bo_get_stride_for_plane(bo, 0);
54#endif
55
56 return attributes;
57}
58
60{
61 Q_OBJECT
62
63public:
64 GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle);
65 ~GbmGraphicsBuffer() override;
66
67 Map map(MapFlags flags) override;
68 void unmap() override;
69
70 QSize size() const override;
71 bool hasAlphaChannel() const override;
72 const DmaBufAttributes *dmabufAttributes() const override;
73
74private:
75 gbm_bo *m_bo;
76 void *m_mapPtr = nullptr;
77 void *m_mapData = nullptr;
78 // the stride of the buffer mapping can be different from the stride of the buffer itself
79 uint32_t m_mapStride = 0;
80 DmaBufAttributes m_dmabufAttributes;
81 QSize m_size;
82 bool m_hasAlphaChannel;
83};
84
86{
87 Q_OBJECT
88
89public:
90 DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes);
91 ~DumbGraphicsBuffer() override;
92
93 Map map(MapFlags flags) override;
94 void unmap() override;
95
96 QSize size() const override;
97 bool hasAlphaChannel() const override;
98 const DmaBufAttributes *dmabufAttributes() const override;
99
100private:
101 int m_drmFd;
102 uint32_t m_handle;
103 void *m_data = nullptr;
104 size_t m_size = 0;
105 DmaBufAttributes m_dmabufAttributes;
106 bool m_hasAlphaChannel;
107};
108
110 : m_gbmDevice(device)
111{
112}
113
117
118static GraphicsBuffer *allocateDumb(gbm_device *device, const GraphicsBufferOptions &options)
119{
120 if (!options.modifiers.isEmpty()) {
121 return nullptr;
122 }
123
124 drm_mode_create_dumb createArgs{
125 .height = uint32_t(options.size.height()),
126 .width = uint32_t(options.size.width()),
127 .bpp = 32,
128 };
129 if (drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) {
130 qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_CREATE_DUMB failed:" << strerror(errno);
131 return nullptr;
132 }
133
134 int primeFd;
135 if (drmPrimeHandleToFD(gbm_device_get_fd(device), createArgs.handle, DRM_CLOEXEC, &primeFd) != 0) {
136 qCWarning(KWIN_CORE) << "drmPrimeHandleToFD() failed:" << strerror(errno);
137 drm_mode_destroy_dumb destroyArgs{
138 .handle = createArgs.handle,
139 };
140 drmIoctl(gbm_device_get_fd(device), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
141 return nullptr;
142 }
143
144 return new DumbGraphicsBuffer(gbm_device_get_fd(device), createArgs.handle, DmaBufAttributes{
145 .planeCount = 1,
146 .width = options.size.width(),
147 .height = options.size.height(),
148 .format = options.format,
149 .modifier = DRM_FORMAT_MOD_LINEAR,
150 .fd = {FileDescriptor(primeFd), FileDescriptor{}, FileDescriptor{}, FileDescriptor{}},
151 .offset = {0, 0, 0, 0},
152 .pitch = {createArgs.pitch, 0, 0, 0},
153 });
154}
155
156static GraphicsBuffer *allocateDmaBuf(gbm_device *device, const GraphicsBufferOptions &options)
157{
158 if (!options.modifiers.isEmpty() && !(options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_INVALID)) {
159 gbm_bo *bo = gbm_bo_create_with_modifiers(device,
160 options.size.width(),
161 options.size.height(),
162 options.format,
163 options.modifiers.constData(),
164 options.modifiers.size());
165 if (bo) {
166 std::optional<DmaBufAttributes> attributes = dmaBufAttributesForBo(bo);
167 if (!attributes.has_value()) {
168 gbm_bo_destroy(bo);
169 return nullptr;
170 }
171 return new GbmGraphicsBuffer(std::move(attributes.value()), bo);
172 }
173 }
174
175 uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
176 if (options.modifiers.size() == 1 && options.modifiers.first() == DRM_FORMAT_MOD_LINEAR) {
177 flags |= GBM_BO_USE_LINEAR;
178 } else if (!options.modifiers.isEmpty() && !options.modifiers.contains(DRM_FORMAT_MOD_INVALID)) {
179 return nullptr;
180 }
181
182 gbm_bo *bo = gbm_bo_create(device,
183 options.size.width(),
184 options.size.height(),
185 options.format,
186 flags);
187 if (bo) {
188 std::optional<DmaBufAttributes> attributes = dmaBufAttributesForBo(bo);
189 if (!attributes.has_value()) {
190 gbm_bo_destroy(bo);
191 return nullptr;
192 }
193 if (flags & GBM_BO_USE_LINEAR) {
194 attributes->modifier = DRM_FORMAT_MOD_LINEAR;
195 } else {
196 attributes->modifier = DRM_FORMAT_MOD_INVALID;
197 }
198 return new GbmGraphicsBuffer(std::move(attributes.value()), bo);
199 }
200
201 return nullptr;
202}
203
204GraphicsBuffer *GbmGraphicsBufferAllocator::allocate(const GraphicsBufferOptions &options)
205{
206 if (options.software) {
207 return allocateDumb(m_gbmDevice, options);
208 }
209
210 return allocateDmaBuf(m_gbmDevice, options);
211}
212
213GbmGraphicsBuffer::GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle)
214 : m_bo(handle)
215 , m_dmabufAttributes(std::move(attributes))
216 , m_size(m_dmabufAttributes.width, m_dmabufAttributes.height)
217 , m_hasAlphaChannel(alphaChannelFromDrmFormat(m_dmabufAttributes.format))
218{
219}
220
222{
223 unmap();
224 gbm_bo_destroy(m_bo);
225}
226
228{
229 return m_size;
230}
231
233{
234 return m_hasAlphaChannel;
235}
236
238{
239 return &m_dmabufAttributes;
240}
241
243{
244 if (!m_mapPtr) {
245 uint32_t access = 0;
246 if (flags & MapFlag::Read) {
247 access |= GBM_BO_TRANSFER_READ;
248 }
249 if (flags & MapFlag::Write) {
250 access |= GBM_BO_TRANSFER_WRITE;
251 }
252 m_mapPtr = gbm_bo_map(m_bo, 0, 0, m_dmabufAttributes.width, m_dmabufAttributes.height, access, &m_mapStride, &m_mapData);
253 }
254 return Map{
255 .data = m_mapPtr,
256 .stride = m_mapStride,
257 };
258}
259
261{
262 if (m_mapPtr) {
263 gbm_bo_unmap(m_bo, m_mapData);
264 m_mapPtr = nullptr;
265 m_mapData = nullptr;
266 }
267}
268
269DumbGraphicsBuffer::DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes)
270 : m_drmFd(drmFd)
271 , m_handle(handle)
272 , m_size(attributes.pitch[0] * attributes.height)
273 , m_dmabufAttributes(std::move(attributes))
274 , m_hasAlphaChannel(alphaChannelFromDrmFormat(m_dmabufAttributes.format))
275{
276}
277
279{
280 unmap();
281
282 drm_mode_destroy_dumb destroyArgs{
283 .handle = m_handle,
284 };
285 drmIoctl(m_drmFd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
286}
287
289{
290 return QSize(m_dmabufAttributes.width, m_dmabufAttributes.height);
291}
292
294{
295 return m_hasAlphaChannel;
296}
297
299{
300 return &m_dmabufAttributes;
301}
302
304{
305 if (!m_data) {
306 drm_mode_map_dumb mapArgs{
307 .handle = m_handle,
308 };
309 if (drmIoctl(m_drmFd, DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) {
310 qCWarning(KWIN_CORE) << "DRM_IOCTL_MODE_MAP_DUMB failed:" << strerror(errno);
311 return {};
312 }
313
314 void *address = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drmFd, mapArgs.offset);
315 if (address == MAP_FAILED) {
316 qCWarning(KWIN_CORE) << "mmap() failed:" << strerror(errno);
317 return {};
318 }
319
320 m_data = address;
321 }
322 return Map{
323 .data = m_data,
324 .stride = m_dmabufAttributes.pitch[0],
325 };
326}
327
329{
330 if (m_data) {
331 munmap(m_data, m_size);
332 m_data = nullptr;
333 }
334}
335
336} // namespace KWin
337
338#include "gbmgraphicsbufferallocator.moc"
339#include "moc_gbmgraphicsbufferallocator.cpp"
Map map(MapFlags flags) override
DumbGraphicsBuffer(int drmFd, uint32_t handle, DmaBufAttributes attributes)
const DmaBufAttributes * dmabufAttributes() const override
const DmaBufAttributes * dmabufAttributes() const override
GbmGraphicsBuffer(DmaBufAttributes attributes, gbm_bo *handle)
Map map(MapFlags flags) override
int drmIoctl(int fd, unsigned long request, void *arg)
Definition mock_drm.cpp:333
GLenum format
Definition gltexture.cpp:49
Options * options
Definition main.cpp:73
std::array< uint32_t, 4 > pitch