KWin
Loading...
Searching...
No Matches
linuxdmabufv1clientbuffer.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
3 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
4 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
5 SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
6
7 Based on the libweston implementation,
8 SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
9
10 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
11*/
12
14#include "core/renderbackend.h"
16#include "surface_p.h"
17#include "utils/common.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <unistd.h>
22
23namespace KWin
24{
25static const int s_version = 4;
26
28 : QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version)
29 , q(q)
30 , defaultFeedback(new LinuxDmaBufV1Feedback(this))
31{
32}
33
35{
36 if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
37 for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) {
38 const uint32_t &format = it.key();
39 const auto &modifiers = it.value();
40 for (const uint64_t &modifier : std::as_const(modifiers)) {
41 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
42 const uint32_t modifier_lo = modifier & 0xffffffff;
43 const uint32_t modifier_hi = modifier >> 32;
44 send_modifier(resource->handle, format, modifier_hi, modifier_lo);
45 } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
46 send_format(resource->handle, format);
47 }
48 }
49 }
50 }
51}
52
54{
55 LinuxDmaBufV1FeedbackPrivate::get(defaultFeedback.get())->add(resource->client(), id, resource->version());
56}
57
58void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surfaceResource)
59{
60 auto surface = SurfaceInterface::get(surfaceResource);
61 if (!surface) {
62 qCWarning(KWIN_CORE) << "requested surface feedback for nonexistant surface!";
63 return;
64 }
65 auto surfacePrivate = SurfaceInterfacePrivate::get(surface);
66 if (!surfacePrivate->dmabufFeedbackV1) {
67 surfacePrivate->dmabufFeedbackV1.reset(new LinuxDmaBufV1Feedback(this));
68 }
69 LinuxDmaBufV1FeedbackPrivate::get(surfacePrivate->dmabufFeedbackV1.get())->add(resource->client(), id, resource->version());
70}
71
73{
74 wl_resource_destroy(resource->handle);
75}
76
78{
79 wl_resource *paramsResource = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, resource->version(), params_id);
80 if (!paramsResource) {
81 wl_resource_post_no_memory(resource->handle);
82 return;
83 }
84 new LinuxDmaBufParamsV1(q, paramsResource);
85}
86
88 : QtWaylandServer::zwp_linux_buffer_params_v1(resource)
89 , m_integration(integration)
90{
91}
92
94{
95 delete this;
96}
97
99{
100 wl_resource_destroy(resource->handle);
101}
102
104 int32_t fd,
105 uint32_t plane_idx,
106 uint32_t offset,
107 uint32_t stride,
108 uint32_t modifier_hi,
109 uint32_t modifier_lo)
110{
111 if (Q_UNLIKELY(m_isUsed)) {
112 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
113 close(fd);
114 return;
115 }
116
117 if (Q_UNLIKELY(plane_idx >= 4)) {
118 wl_resource_post_error(resource->handle, error_plane_idx, "plane index %d is out of bounds", plane_idx);
119 close(fd);
120 return;
121 }
122
123 if (Q_UNLIKELY(m_attrs.fd[plane_idx].isValid())) {
124 wl_resource_post_error(resource->handle, error_plane_set, "the plane index %d was already set", plane_idx);
125 close(fd);
126 return;
127 }
128 m_attrs.fd[plane_idx] = FileDescriptor{fd};
129 m_attrs.offset[plane_idx] = offset;
130 m_attrs.pitch[plane_idx] = stride;
131 m_attrs.modifier = (quint64(modifier_hi) << 32) | modifier_lo;
132 m_attrs.planeCount++;
133}
134
135void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
136{
137 if (Q_UNLIKELY(m_isUsed)) {
138 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
139 return;
140 }
141
142 if (Q_UNLIKELY(!test(resource, width, height))) {
143 return;
144 }
145
146 RenderBackend *renderBackend = m_integration->renderBackend();
147 if (Q_UNLIKELY(!renderBackend)) {
148 send_failed(resource->handle);
149 return;
150 }
151
152 if (flags) {
153 send_failed(resource->handle);
154 return;
155 }
156
157 m_isUsed = true;
158
159 m_attrs.width = width;
160 m_attrs.height = height;
161 m_attrs.format = format;
162
163 auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
164 if (!renderBackend->testImportBuffer(clientBuffer)) {
165 send_failed(resource->handle);
166 delete clientBuffer;
167 return;
168 }
169
170 wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
171 if (!bufferResource) {
172 wl_resource_post_no_memory(resource->handle);
173 delete clientBuffer;
174 return;
175 }
176
177 clientBuffer->initialize(bufferResource);
178 send_created(resource->handle, bufferResource);
179}
180
182 uint32_t buffer_id,
183 int32_t width,
184 int32_t height,
185 uint32_t format,
186 uint32_t flags)
187{
188 if (Q_UNLIKELY(m_isUsed)) {
189 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
190 return;
191 }
192
193 if (Q_UNLIKELY(!test(resource, width, height))) {
194 return;
195 }
196
197 RenderBackend *renderBackend = m_integration->renderBackend();
198 if (Q_UNLIKELY(!renderBackend)) {
199 wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
200 return;
201 }
202
203 if (flags) {
204 wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "dma-buf flags are not supported");
205 return;
206 }
207
208 m_isUsed = true;
209
210 m_attrs.width = width;
211 m_attrs.height = height;
212 m_attrs.format = format;
213
214 auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
215 if (!renderBackend->testImportBuffer(clientBuffer)) {
216 wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
217 delete clientBuffer;
218 return;
219 }
220
221 wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
222 if (!bufferResource) {
223 wl_resource_post_no_memory(resource->handle);
224 delete clientBuffer;
225 return;
226 }
227
228 clientBuffer->initialize(bufferResource);
229}
230
231bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
232{
233 if (Q_UNLIKELY(!m_attrs.planeCount)) {
234 wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified");
235 return false;
236 }
237
238 // Check for holes in the dmabuf set (e.g. [0, 1, 3]).
239 for (int i = 0; i < m_attrs.planeCount; ++i) {
240 if (!m_attrs.fd[i].isValid()) {
241 wl_resource_post_error(resource->handle, error_incomplete, "no dmabuf has been added for plane %d", i);
242 return false;
243 }
244 }
245
246 if (Q_UNLIKELY(width == 0 || height == 0)) {
247 wl_resource_post_error(resource->handle, error_invalid_dimensions, "invalid width %d or height %d", width, height);
248 return false;
249 }
250
251 for (int i = 0; i < m_attrs.planeCount; ++i) {
252 // Check for overflows.
253 if (Q_UNLIKELY(uint64_t(m_attrs.offset[i]) + m_attrs.pitch[i] > UINT32_MAX)) {
254 wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
255 return false;
256 }
257
258 if (Q_UNLIKELY(i == 0 && uint64_t(m_attrs.offset[i]) + uint64_t(m_attrs.pitch[i]) * height > UINT32_MAX)) {
259 wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
260 return false;
261 }
262
263 // Don't report an error as it might be caused by the kernel not supporting
264 // seeking on dmabuf.
265 const off_t size = lseek(m_attrs.fd[i].get(), 0, SEEK_END);
266 if (size == -1) {
267 continue;
268 }
269
270 if (Q_UNLIKELY(m_attrs.offset[i] >= size)) {
271 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid offset %i for plane %d", m_attrs.offset[i], i);
272 return false;
273 }
274
275 if (Q_UNLIKELY(m_attrs.offset[i] + m_attrs.pitch[i] > size)) {
276 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid stride %i for plane %d", m_attrs.pitch[i], i);
277 return false;
278 }
279
280 // Only valid for first plane as other planes might be sub-sampled according to
281 // fourcc format.
282 if (Q_UNLIKELY(i == 0 && m_attrs.offset[i] + m_attrs.pitch[i] * height > size)) {
283 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid buffer stride of height for plane %d", i);
284 return false;
285 }
286 }
287
288 return true;
289}
290
296
300
302{
303 return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable;
304}
305
307{
308 return d->renderBackend;
309}
310
312{
313 d->renderBackend = renderBackend;
314}
315
316void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QList<LinuxDmaBufV1Feedback::Tranche> &tranches)
317{
318 if (LinuxDmaBufV1FeedbackPrivate::get(d->defaultFeedback.get())->m_tranches != tranches) {
319 QHash<uint32_t, QList<uint64_t>> set;
320 for (const auto &tranche : tranches) {
321 set.insert(tranche.formatTable);
322 }
323 d->supportedModifiers = set;
324 d->mainDevice = tranches.first().device;
325 d->table = std::make_unique<LinuxDmaBufV1FormatTable>(set);
326 d->defaultFeedback->setTranches(tranches);
327 }
328}
329
330void LinuxDmaBufV1ClientBuffer::buffer_destroy_resource(wl_resource *resource)
331{
333 buffer->m_resource = nullptr;
334 buffer->drop();
335 }
336}
337
338void LinuxDmaBufV1ClientBuffer::buffer_destroy(wl_client *client, wl_resource *resource)
339{
340 wl_resource_destroy(resource);
341}
342
343const struct wl_buffer_interface LinuxDmaBufV1ClientBuffer::implementation = {
344 .destroy = buffer_destroy,
345};
346
348{
349 m_attrs = std::move(attrs);
350 m_hasAlphaChannel = alphaChannelFromDrmFormat(m_attrs.format);
351}
352
353void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource)
354{
355 m_resource = resource;
356 wl_resource_set_implementation(resource, &implementation, this, buffer_destroy_resource);
357
358 connect(this, &GraphicsBuffer::released, [this]() {
359 wl_buffer_send_release(m_resource);
360 });
361}
362
364{
365 return &m_attrs;
366}
367
369{
370 return QSize(m_attrs.width, m_attrs.height);
371}
372
374{
375 return m_hasAlphaChannel;
376}
377
379{
380 if (wl_resource_instance_of(resource, &wl_buffer_interface, &implementation)) {
381 return static_cast<LinuxDmaBufV1ClientBuffer *>(wl_resource_get_user_data(resource));
382 }
383 return nullptr;
384}
385
386LinuxDmaBufV1Feedback::LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegrationPrivate *integration)
387 : d(new LinuxDmaBufV1FeedbackPrivate(integration))
388{
389}
390
392
393void LinuxDmaBufV1Feedback::setTranches(const QList<Tranche> &tranches)
394{
395 if (d->m_tranches != tranches) {
396 d->m_tranches = tranches;
397 const auto &map = d->resourceMap();
398 for (const auto &resource : map) {
399 d->send(resource);
400 }
401 }
402}
403
408
410 : m_bufferintegration(bufferintegration)
411{
412}
413
415{
416 send_format_table(resource->handle, m_bufferintegration->table->file.fd(), m_bufferintegration->table->file.size());
417 QByteArray bytes;
418 bytes.append(reinterpret_cast<const char *>(&m_bufferintegration->mainDevice), sizeof(dev_t));
419 send_main_device(resource->handle, bytes);
420 const auto &sendTranche = [this, resource](const LinuxDmaBufV1Feedback::Tranche &tranche) {
421 QByteArray targetDevice;
422 targetDevice.append(reinterpret_cast<const char *>(&tranche.device), sizeof(dev_t));
423 QByteArray indices;
424 for (auto it = tranche.formatTable.begin(); it != tranche.formatTable.end(); it++) {
425 const uint32_t format = it.key();
426 for (const auto &mod : std::as_const(it.value())) {
427 uint16_t index = m_bufferintegration->table->indices[std::pair<uint32_t, uint64_t>(format, mod)];
428 indices.append(reinterpret_cast<const char *>(&index), 2);
429 }
430 }
431 send_tranche_target_device(resource->handle, targetDevice);
432 send_tranche_formats(resource->handle, indices);
433 send_tranche_flags(resource->handle, static_cast<uint32_t>(tranche.flags));
434 send_tranche_done(resource->handle);
435 };
436 for (const auto &tranche : std::as_const(m_tranches)) {
437 sendTranche(tranche);
438 }
439 // send default hints as the last fallback tranche
440 const auto defaultFeedbackPrivate = get(m_bufferintegration->defaultFeedback.get());
441 if (this != defaultFeedbackPrivate) {
442 for (const auto &tranche : std::as_const(defaultFeedbackPrivate->m_tranches)) {
443 sendTranche(tranche);
444 }
445 }
446 send_done(resource->handle);
447}
448
453
455{
456 wl_resource_destroy(resource->handle);
457}
458
460{
461 uint32_t format;
462 uint32_t pad; // unused
463 uint64_t modifier;
464};
465
466LinuxDmaBufV1FormatTable::LinuxDmaBufV1FormatTable(const QHash<uint32_t, QList<uint64_t>> &supportedModifiers)
467{
468 QList<linux_dmabuf_feedback_v1_table_entry> data;
469 for (auto it = supportedModifiers.begin(); it != supportedModifiers.end(); it++) {
470 const uint32_t format = it.key();
471 for (const uint64_t &mod : *it) {
472 indices.insert({format, mod}, data.size());
473 data.append({format, 0, mod});
474 }
475 }
476
477 const auto size = data.size() * sizeof(linux_dmabuf_feedback_v1_table_entry);
478 file = RamFile("kwin-dmabuf-feedback-table", data.constData(), size, RamFile::Flag::SealWrite);
479 if (!file.isValid()) {
480 qCCritical(KWIN_CORE) << "Failed to create RamFile for LinuxDmaBufV1FormatTable";
481 return;
482 }
483}
484
485} // namespace KWin
486
487#include "moc_linuxdmabufv1clientbuffer_p.cpp"
488
489#include "moc_linuxdmabufv1clientbuffer.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
static bool alphaChannelFromDrmFormat(uint32_t format)
LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource)
void zwp_linux_buffer_params_v1_destroy(Resource *resource) override
void zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) override
void zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override
void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override
void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override
void setSupportedFormatsWithModifiers(const QList< LinuxDmaBufV1Feedback::Tranche > &tranches)
void setRenderBackend(RenderBackend *renderBackend)
std::unique_ptr< LinuxDmaBufV1FormatTable > table
void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override
void zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) override
std::unique_ptr< LinuxDmaBufV1Feedback > defaultFeedback
void zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surface) override
LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display)
void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override
void setTranches(const QList< Tranche > &tranches)
LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration)
static LinuxDmaBufV1FeedbackPrivate * get(LinuxDmaBufV1Feedback *q)
QList< LinuxDmaBufV1Feedback::Tranche > m_tranches
void zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) override
LinuxDmaBufV1ClientBufferIntegrationPrivate * m_bufferintegration
void zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) override
QMap< std::pair< uint32_t, uint64_t >, uint16_t > indices
LinuxDmaBufV1FormatTable(const QHash< uint32_t, QList< uint64_t > > &supportedModifiers)
Creates a file in memory.
Definition ramfile.h:47
bool isValid() const
Definition ramfile.cpp:131
@ SealWrite
Seal the file descriptor for writing.
virtual bool testImportBuffer(GraphicsBuffer *buffer)
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
static SurfaceInterfacePrivate * get(SurfaceInterface *surface)
Definition surface_p.h:99
GLenum format
Definition gltexture.cpp:49
bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2)
std::array< uint32_t, 4 > offset
std::array< uint32_t, 4 > pitch
std::array< FileDescriptor, 4 > fd
static LinuxDmaBufV1ClientBuffer * get(wl_resource *resource)
const DmaBufAttributes * dmabufAttributes() const override
LinuxDmaBufV1ClientBuffer(DmaBufAttributes &&attrs)
QHash< uint32_t, QList< uint64_t > > formatTable
uint64_t modifier
uint32_t pad
uint32_t format