File indexing completed on 2024-05-12 05:32:22

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