File indexing completed on 2024-05-19 16:35:21

0001 /*
0002     SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
0003     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
0004     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0005     SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
0006 
0007     Based on the libweston implementation,
0008     SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
0009 
0010     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0011 */
0012 
0013 #include "linuxdmabufv1clientbuffer.h"
0014 #include "linuxdmabufv1clientbuffer_p.h"
0015 #include "surface_interface_p.h"
0016 #include "utils/common.h"
0017 
0018 #include <errno.h>
0019 #include <fcntl.h>
0020 #include <unistd.h>
0021 
0022 namespace KWaylandServer
0023 {
0024 static const int s_version = 4;
0025 
0026 LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display)
0027     : QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version)
0028     , q(q)
0029     , defaultFeedback(new LinuxDmaBufV1Feedback(this))
0030 {
0031 }
0032 
0033 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource)
0034 {
0035     if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
0036         for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) {
0037             const uint32_t &format = it.key();
0038             const auto &modifiers = it.value();
0039             for (const uint64_t &modifier : std::as_const(modifiers)) {
0040                 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
0041                     const uint32_t modifier_lo = modifier & 0xffffffff;
0042                     const uint32_t modifier_hi = modifier >> 32;
0043                     send_modifier(resource->handle, format, modifier_hi, modifier_lo);
0044                 } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
0045                     send_format(resource->handle, format);
0046                 }
0047             }
0048         }
0049     }
0050 }
0051 
0052 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id)
0053 {
0054     LinuxDmaBufV1FeedbackPrivate::get(defaultFeedback.get())->add(resource->client(), id, resource->version());
0055 }
0056 
0057 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surfaceResource)
0058 {
0059     auto surface = SurfaceInterface::get(surfaceResource);
0060     if (!surface) {
0061         qCWarning(KWIN_CORE) << "requested surface feedback for nonexistant surface!";
0062         return;
0063     }
0064     auto surfacePrivate = SurfaceInterfacePrivate::get(surface);
0065     if (!surfacePrivate->dmabufFeedbackV1) {
0066         surfacePrivate->dmabufFeedbackV1.reset(new LinuxDmaBufV1Feedback(this));
0067     }
0068     LinuxDmaBufV1FeedbackPrivate::get(surfacePrivate->dmabufFeedbackV1.get())->add(resource->client(), id, resource->version());
0069 }
0070 
0071 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource)
0072 {
0073     wl_resource_destroy(resource->handle);
0074 }
0075 
0076 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
0077 {
0078     wl_resource *paramsResource = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, resource->version(), params_id);
0079     if (!paramsResource) {
0080         wl_resource_post_no_memory(resource->handle);
0081         return;
0082     }
0083     new LinuxDmaBufParamsV1(q, paramsResource);
0084 }
0085 
0086 LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource)
0087     : QtWaylandServer::zwp_linux_buffer_params_v1(resource)
0088     , m_integration(integration)
0089 {
0090 }
0091 
0092 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource)
0093 {
0094     delete this;
0095 }
0096 
0097 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy(Resource *resource)
0098 {
0099     wl_resource_destroy(resource->handle);
0100 }
0101 
0102 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_add(Resource *resource,
0103                                                          int32_t fd,
0104                                                          uint32_t plane_idx,
0105                                                          uint32_t offset,
0106                                                          uint32_t stride,
0107                                                          uint32_t modifier_hi,
0108                                                          uint32_t modifier_lo)
0109 {
0110     if (Q_UNLIKELY(m_isUsed)) {
0111         wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
0112         close(fd);
0113         return;
0114     }
0115 
0116     if (Q_UNLIKELY(plane_idx >= 4)) {
0117         wl_resource_post_error(resource->handle, error_plane_idx, "plane index %d is out of bounds", plane_idx);
0118         close(fd);
0119         return;
0120     }
0121 
0122     if (Q_UNLIKELY(m_attrs.fd[plane_idx].isValid())) {
0123         wl_resource_post_error(resource->handle, error_plane_set, "the plane index %d was already set", plane_idx);
0124         close(fd);
0125         return;
0126     }
0127     m_attrs.fd[plane_idx] = KWin::FileDescriptor{fd};
0128     m_attrs.offset[plane_idx] = offset;
0129     m_attrs.pitch[plane_idx] = stride;
0130     m_attrs.modifier = (quint64(modifier_hi) << 32) | modifier_lo;
0131     m_attrs.planeCount++;
0132 }
0133 
0134 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
0135 {
0136     if (Q_UNLIKELY(m_isUsed)) {
0137         wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
0138         return;
0139     }
0140 
0141     if (Q_UNLIKELY(!test(resource, width, height))) {
0142         return;
0143     }
0144 
0145     m_isUsed = true;
0146 
0147     m_attrs.width = width;
0148     m_attrs.height = height;
0149     m_attrs.format = format;
0150 
0151     LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags);
0152     if (!clientBuffer) {
0153         send_failed(resource->handle);
0154         return;
0155     }
0156 
0157     wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
0158     if (!bufferResource) {
0159         delete clientBuffer;
0160         wl_resource_post_no_memory(resource->handle);
0161         return;
0162     }
0163 
0164     clientBuffer->initialize(bufferResource);
0165     send_created(resource->handle, bufferResource);
0166 
0167     DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
0168     displayPrivate->registerClientBuffer(clientBuffer);
0169 }
0170 
0171 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource,
0172                                                                   uint32_t buffer_id,
0173                                                                   int32_t width,
0174                                                                   int32_t height,
0175                                                                   uint32_t format,
0176                                                                   uint32_t flags)
0177 {
0178     if (Q_UNLIKELY(m_isUsed)) {
0179         wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
0180         return;
0181     }
0182 
0183     if (Q_UNLIKELY(!test(resource, width, height))) {
0184         return;
0185     }
0186 
0187     m_isUsed = true;
0188 
0189     m_attrs.width = width;
0190     m_attrs.height = height;
0191     m_attrs.format = format;
0192 
0193     LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags);
0194     if (!clientBuffer) {
0195         wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
0196         return;
0197     }
0198 
0199     wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
0200     if (!bufferResource) {
0201         delete clientBuffer;
0202         wl_resource_post_no_memory(resource->handle);
0203         return;
0204     }
0205 
0206     clientBuffer->initialize(bufferResource);
0207 
0208     DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
0209     displayPrivate->registerClientBuffer(clientBuffer);
0210 }
0211 
0212 bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
0213 {
0214     if (Q_UNLIKELY(!m_attrs.planeCount)) {
0215         wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified");
0216         return false;
0217     }
0218 
0219     // Check for holes in the dmabuf set (e.g. [0, 1, 3]).
0220     for (int i = 0; i < m_attrs.planeCount; ++i) {
0221         if (!m_attrs.fd[i].isValid()) {
0222             wl_resource_post_error(resource->handle, error_incomplete, "no dmabuf has been added for plane %d", i);
0223             return false;
0224         }
0225     }
0226 
0227     if (Q_UNLIKELY(width == 0 || height == 0)) {
0228         wl_resource_post_error(resource->handle, error_invalid_dimensions, "invalid width %d or height %d", width, height);
0229         return false;
0230     }
0231 
0232     for (int i = 0; i < m_attrs.planeCount; ++i) {
0233         // Check for overflows.
0234         if (Q_UNLIKELY(uint64_t(m_attrs.offset[i]) + m_attrs.pitch[i] > UINT32_MAX)) {
0235             wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
0236             return false;
0237         }
0238 
0239         if (Q_UNLIKELY(i == 0 && uint64_t(m_attrs.offset[i]) + uint64_t(m_attrs.pitch[i]) * height > UINT32_MAX)) {
0240             wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
0241             return false;
0242         }
0243 
0244         // Don't report an error as it might be caused by the kernel not supporting
0245         // seeking on dmabuf.
0246         const off_t size = lseek(m_attrs.fd[i].get(), 0, SEEK_END);
0247         if (size == -1) {
0248             continue;
0249         }
0250 
0251         if (Q_UNLIKELY(m_attrs.offset[i] >= size)) {
0252             wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid offset %i for plane %d", m_attrs.offset[i], i);
0253             return false;
0254         }
0255 
0256         if (Q_UNLIKELY(m_attrs.offset[i] + m_attrs.pitch[i] > size)) {
0257             wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid stride %i for plane %d", m_attrs.pitch[i], i);
0258             return false;
0259         }
0260 
0261         // Only valid for first plane as other planes might be sub-sampled according to
0262         // fourcc format.
0263         if (Q_UNLIKELY(i == 0 && m_attrs.offset[i] + m_attrs.pitch[i] * height > size)) {
0264             wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid buffer stride of height for plane %d", i);
0265             return false;
0266         }
0267     }
0268 
0269     return true;
0270 }
0271 
0272 LinuxDmaBufV1ClientBufferIntegration::LinuxDmaBufV1ClientBufferIntegration(Display *display)
0273     : ClientBufferIntegration(display)
0274     , d(new LinuxDmaBufV1ClientBufferIntegrationPrivate(this, display))
0275 {
0276 }
0277 
0278 LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration()
0279 {
0280 }
0281 
0282 LinuxDmaBufV1ClientBufferIntegration::RendererInterface *LinuxDmaBufV1ClientBufferIntegration::rendererInterface() const
0283 {
0284     return d->rendererInterface;
0285 }
0286 
0287 void LinuxDmaBufV1ClientBufferIntegration::setRendererInterface(RendererInterface *rendererInterface)
0288 {
0289     d->rendererInterface = rendererInterface;
0290 }
0291 
0292 bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2)
0293 {
0294     return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable;
0295 }
0296 
0297 void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QVector<LinuxDmaBufV1Feedback::Tranche> &tranches)
0298 {
0299     if (LinuxDmaBufV1FeedbackPrivate::get(d->defaultFeedback.get())->m_tranches != tranches) {
0300         QHash<uint32_t, QVector<uint64_t>> set;
0301         for (const auto &tranche : tranches) {
0302             set.insert(tranche.formatTable);
0303         }
0304         d->supportedModifiers = set;
0305         d->mainDevice = tranches.first().device;
0306         d->table.reset(new LinuxDmaBufV1FormatTable(set));
0307         d->defaultFeedback->setTranches(tranches);
0308     }
0309 }
0310 
0311 static bool testAlphaChannel(uint32_t drmFormat)
0312 {
0313     switch (drmFormat) {
0314     case DRM_FORMAT_ARGB4444:
0315     case DRM_FORMAT_ABGR4444:
0316     case DRM_FORMAT_RGBA4444:
0317     case DRM_FORMAT_BGRA4444:
0318 
0319     case DRM_FORMAT_ARGB1555:
0320     case DRM_FORMAT_ABGR1555:
0321     case DRM_FORMAT_RGBA5551:
0322     case DRM_FORMAT_BGRA5551:
0323 
0324     case DRM_FORMAT_ARGB8888:
0325     case DRM_FORMAT_ABGR8888:
0326     case DRM_FORMAT_RGBA8888:
0327     case DRM_FORMAT_BGRA8888:
0328 
0329     case DRM_FORMAT_ARGB2101010:
0330     case DRM_FORMAT_ABGR2101010:
0331     case DRM_FORMAT_RGBA1010102:
0332     case DRM_FORMAT_BGRA1010102:
0333 
0334     case DRM_FORMAT_XRGB8888_A8:
0335     case DRM_FORMAT_XBGR8888_A8:
0336     case DRM_FORMAT_RGBX8888_A8:
0337     case DRM_FORMAT_BGRX8888_A8:
0338     case DRM_FORMAT_RGB888_A8:
0339     case DRM_FORMAT_BGR888_A8:
0340     case DRM_FORMAT_RGB565_A8:
0341     case DRM_FORMAT_BGR565_A8:
0342         return true;
0343     default:
0344         return false;
0345     }
0346 }
0347 
0348 void LinuxDmaBufV1ClientBufferPrivate::buffer_destroy(Resource *resource)
0349 {
0350     wl_resource_destroy(resource->handle);
0351 }
0352 
0353 LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(KWin::DmaBufAttributes &&attrs, quint32 flags)
0354     : ClientBuffer(*new LinuxDmaBufV1ClientBufferPrivate)
0355 {
0356     Q_D(LinuxDmaBufV1ClientBuffer);
0357     d->attrs = std::move(attrs);
0358     d->flags = flags;
0359     d->hasAlphaChannel = testAlphaChannel(attrs.format);
0360 }
0361 
0362 LinuxDmaBufV1ClientBuffer::~LinuxDmaBufV1ClientBuffer() = default;
0363 
0364 void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource)
0365 {
0366     Q_D(LinuxDmaBufV1ClientBuffer);
0367     d->init(resource);
0368     ClientBuffer::initialize(resource);
0369 }
0370 
0371 quint32 LinuxDmaBufV1ClientBuffer::format() const
0372 {
0373     Q_D(const LinuxDmaBufV1ClientBuffer);
0374     return d->attrs.format;
0375 }
0376 
0377 quint32 LinuxDmaBufV1ClientBuffer::flags() const
0378 {
0379     Q_D(const LinuxDmaBufV1ClientBuffer);
0380     return d->flags;
0381 }
0382 
0383 const KWin::DmaBufAttributes &LinuxDmaBufV1ClientBuffer::attributes() const
0384 {
0385     Q_D(const LinuxDmaBufV1ClientBuffer);
0386     return d->attrs;
0387 }
0388 
0389 QSize LinuxDmaBufV1ClientBuffer::size() const
0390 {
0391     Q_D(const LinuxDmaBufV1ClientBuffer);
0392     return QSize(d->attrs.width, d->attrs.height);
0393 }
0394 
0395 bool LinuxDmaBufV1ClientBuffer::hasAlphaChannel() const
0396 {
0397     Q_D(const LinuxDmaBufV1ClientBuffer);
0398     return d->hasAlphaChannel;
0399 }
0400 
0401 ClientBuffer::Origin LinuxDmaBufV1ClientBuffer::origin() const
0402 {
0403     Q_D(const LinuxDmaBufV1ClientBuffer);
0404     if (d->flags & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) {
0405         return ClientBuffer::Origin::BottomLeft;
0406     } else {
0407         return ClientBuffer::Origin::TopLeft;
0408     }
0409 }
0410 
0411 LinuxDmaBufV1Feedback::LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegrationPrivate *integration)
0412     : d(new LinuxDmaBufV1FeedbackPrivate(integration))
0413 {
0414 }
0415 
0416 LinuxDmaBufV1Feedback::~LinuxDmaBufV1Feedback() = default;
0417 
0418 void LinuxDmaBufV1Feedback::setTranches(const QVector<Tranche> &tranches)
0419 {
0420     if (d->m_tranches != tranches) {
0421         d->m_tranches = tranches;
0422         const auto &map = d->resourceMap();
0423         for (const auto &resource : map) {
0424             d->send(resource);
0425         }
0426     }
0427 }
0428 
0429 LinuxDmaBufV1FeedbackPrivate *LinuxDmaBufV1FeedbackPrivate::get(LinuxDmaBufV1Feedback *q)
0430 {
0431     return q->d.get();
0432 }
0433 
0434 LinuxDmaBufV1FeedbackPrivate::LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration)
0435     : m_bufferintegration(bufferintegration)
0436 {
0437 }
0438 
0439 void LinuxDmaBufV1FeedbackPrivate::send(Resource *resource)
0440 {
0441     send_format_table(resource->handle, m_bufferintegration->table->file.fd(), m_bufferintegration->table->file.size());
0442     QByteArray bytes;
0443     bytes.append(reinterpret_cast<const char *>(&m_bufferintegration->mainDevice), sizeof(dev_t));
0444     send_main_device(resource->handle, bytes);
0445     const auto &sendTranche = [this, resource](const LinuxDmaBufV1Feedback::Tranche &tranche) {
0446         QByteArray targetDevice;
0447         targetDevice.append(reinterpret_cast<const char *>(&tranche.device), sizeof(dev_t));
0448         QByteArray indices;
0449         for (auto it = tranche.formatTable.begin(); it != tranche.formatTable.end(); it++) {
0450             const uint32_t format = it.key();
0451             for (const auto &mod : std::as_const(it.value())) {
0452                 uint16_t index = m_bufferintegration->table->indices[std::pair<uint32_t, uint64_t>(format, mod)];
0453                 indices.append(reinterpret_cast<const char *>(&index), 2);
0454             }
0455         }
0456         send_tranche_target_device(resource->handle, targetDevice);
0457         send_tranche_formats(resource->handle, indices);
0458         send_tranche_flags(resource->handle, static_cast<uint32_t>(tranche.flags));
0459         send_tranche_done(resource->handle);
0460     };
0461     for (const auto &tranche : std::as_const(m_tranches)) {
0462         sendTranche(tranche);
0463     }
0464     // send default hints as the last fallback tranche
0465     const auto defaultFeedbackPrivate = get(m_bufferintegration->defaultFeedback.get());
0466     if (this != defaultFeedbackPrivate) {
0467         for (const auto &tranche : std::as_const(defaultFeedbackPrivate->m_tranches)) {
0468             sendTranche(tranche);
0469         }
0470     }
0471     send_done(resource->handle);
0472 }
0473 
0474 void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource)
0475 {
0476     send(resource);
0477 }
0478 
0479 void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource)
0480 {
0481     wl_resource_destroy(resource->handle);
0482 }
0483 
0484 struct linux_dmabuf_feedback_v1_table_entry
0485 {
0486     uint32_t format;
0487     uint32_t pad; // unused
0488     uint64_t modifier;
0489 };
0490 
0491 LinuxDmaBufV1FormatTable::LinuxDmaBufV1FormatTable(const QHash<uint32_t, QVector<uint64_t>> &supportedModifiers)
0492 {
0493     QVector<linux_dmabuf_feedback_v1_table_entry> data;
0494     for (auto it = supportedModifiers.begin(); it != supportedModifiers.end(); it++) {
0495         const uint32_t format = it.key();
0496         for (const uint64_t &mod : *it) {
0497             indices.insert({format, mod}, data.size());
0498             data.append({format, 0, mod});
0499         }
0500     }
0501 
0502     const auto size = data.size() * sizeof(linux_dmabuf_feedback_v1_table_entry);
0503     file = KWin::RamFile("kwin-dmabuf-feedback-table", data.constData(), size, KWin::RamFile::Flag::SealWrite);
0504     if (!file.isValid()) {
0505         qCCritical(KWIN_CORE) << "Failed to create RamFile for LinuxDmaBufV1FormatTable";
0506         return;
0507     }
0508 }
0509 
0510 } // namespace KWaylandServer