From 53064d9af3e60300cdc9728e07e487c0eed65272 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 23 Nov 2021 14:15:41 +0100 Subject: [PATCH] linuxdmabuf: implement dmabuf-feedback dmabuf-feedback allows the compositor to give the client feedback on what formats and modifiers are best to use, and for which devices it needs to allocate its buffers, which improves performance and efficiency. --- src/wayland/linuxdmabufv1clientbuffer.cpp | 196 ++++++++++++++++++++-- src/wayland/linuxdmabufv1clientbuffer.h | 36 +++- src/wayland/linuxdmabufv1clientbuffer_p.h | 38 ++++- src/wayland/surface_interface.cpp | 6 + src/wayland/surface_interface.h | 7 + src/wayland/surface_interface_p.h | 1 + 6 files changed, 265 insertions(+), 19 deletions(-) diff --git a/src/wayland/linuxdmabufv1clientbuffer.cpp b/src/wayland/linuxdmabufv1clientbuffer.cpp index cb1457a780..6520075c69 100644 --- a/src/wayland/linuxdmabufv1clientbuffer.cpp +++ b/src/wayland/linuxdmabufv1clientbuffer.cpp @@ -2,6 +2,7 @@ SPDX-FileCopyrightText: 2018 Fredrik Höglund SPDX-FileCopyrightText: 2019 Roman Gilg SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 Xaver Hugl Based on the libweston implementation, SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd. @@ -11,38 +12,65 @@ #include "linuxdmabufv1clientbuffer.h" #include "linuxdmabufv1clientbuffer_p.h" +#include "logging.h" +#include "surface_interface_p.h" + +#include +#include namespace KWaylandServer { -static const int s_version = 3; +static const int s_version = 4; LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display) : QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version) , q(q) + , defaultFeedback(new LinuxDmaBufV1Feedback(q)) { } void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource) { - for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) { - const uint32_t format = it.key(); - QSet modifiers = it.value(); - if (modifiers.isEmpty()) { - modifiers.insert(DRM_FORMAT_MOD_INVALID); - } + if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { + for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) { + const uint32_t format = it.key(); + QSet modifiers = it.value(); + if (modifiers.isEmpty()) { + modifiers.insert(DRM_FORMAT_MOD_INVALID); + } - for (const uint64_t &modifier : qAsConst(modifiers)) { - if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { - const uint32_t modifier_lo = modifier & 0xffffffff; - const uint32_t modifier_hi = modifier >> 32; - send_modifier(resource->handle, format, modifier_hi, modifier_lo); - } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { - send_format(resource->handle, format); + for (const uint64_t &modifier : qAsConst(modifiers)) { + if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { + const uint32_t modifier_lo = modifier & 0xffffffff; + const uint32_t modifier_hi = modifier >> 32; + send_modifier(resource->handle, format, modifier_hi, modifier_lo); + } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { + send_format(resource->handle, format); + } } } } } +void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) +{ + LinuxDmaBufV1FeedbackPrivate::get(defaultFeedback.data())->add(resource->client(), id, resource->version()); +} + +void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surfaceResource) +{ + auto surface = SurfaceInterface::get(surfaceResource); + if (!surface) { + qCWarning(KWAYLAND_SERVER) << "requested surface feedback for nonexistant surface!"; + return; + } + auto surfacePrivate = SurfaceInterfacePrivate::get(surface); + if (!surfacePrivate->dmabufFeedbackV1) { + surfacePrivate->dmabufFeedbackV1.reset(new LinuxDmaBufV1Feedback(q)); + } + LinuxDmaBufV1FeedbackPrivate::get(surfacePrivate->dmabufFeedbackV1.data())->add(resource->client(), id, resource->version()); +} + void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource) { wl_resource_destroy(resource->handle); @@ -58,6 +86,11 @@ void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_par new LinuxDmaBufParamsV1(q, paramsResource); } +LinuxDmaBufV1ClientBufferIntegrationPrivate *LinuxDmaBufV1ClientBufferIntegrationPrivate::get(LinuxDmaBufV1ClientBufferIntegration *integration) +{ + return integration->d.data(); +} + LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource) : QtWaylandServer::zwp_linux_buffer_params_v1(resource) , m_integration(integration) @@ -279,9 +312,18 @@ void LinuxDmaBufV1ClientBufferIntegration::setRendererInterface(RendererInterfac d->rendererInterface = rendererInterface; } -void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QHash> &set) +void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(dev_t mainDevice, const QHash> &set) { - d->supportedModifiers = set; + if (d->supportedModifiers != set || d->mainDevice != mainDevice) { + d->supportedModifiers = set; + d->mainDevice = mainDevice; + d->table.reset(new LinuxDmaBufV1FormatTable(set)); + LinuxDmaBufV1Feedback::Tranche tranche; + tranche.device = mainDevice; + tranche.flags = {}; + tranche.formatTable = set; + d->defaultFeedback->setTranches({tranche}); + } } static bool testAlphaChannel(uint32_t drmFormat) @@ -395,4 +437,126 @@ ClientBuffer::Origin LinuxDmaBufV1ClientBuffer::origin() const } } +LinuxDmaBufV1Feedback::LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegration *integration) + : d(new LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate::get(integration))) +{ +} + +LinuxDmaBufV1Feedback::~LinuxDmaBufV1Feedback() = default; + +void LinuxDmaBufV1Feedback::setTranches(const QVector &tranches) +{ + if (d->m_tranches != tranches) { + d->m_tranches = tranches; + const auto &map = d->resourceMap(); + for (const auto &resource : map) { + d->send(resource); + } + } +} + +LinuxDmaBufV1FeedbackPrivate *LinuxDmaBufV1FeedbackPrivate::get(LinuxDmaBufV1Feedback *q) +{ + return q->d.data(); +} + +LinuxDmaBufV1FeedbackPrivate::LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration) + : m_bufferintegration(bufferintegration) +{ +} + +bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2) +{ + return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable; +} + +void LinuxDmaBufV1FeedbackPrivate::send(Resource *resource) +{ + send_format_table(resource->handle, m_bufferintegration->table->fd, m_bufferintegration->table->size); + QByteArray bytes; + bytes.append(reinterpret_cast(&m_bufferintegration->mainDevice), sizeof(dev_t)); + send_main_device(resource->handle, bytes); + const auto &sendTranche = [this, resource](const LinuxDmaBufV1Feedback::Tranche &tranche) { + QByteArray targetDevice; + targetDevice.append(reinterpret_cast(&tranche.device), sizeof(dev_t)); + QByteArray indices; + for (auto it = tranche.formatTable.begin(); it != tranche.formatTable.end(); it++) { + const uint32_t format = it.key(); + for (const auto &mod : qAsConst(it.value())) { + uint16_t index = m_bufferintegration->table->indices[std::pair(format, mod)]; + indices.append(reinterpret_cast(&index), 2); + } + } + send_tranche_target_device(resource->handle, targetDevice); + send_tranche_formats(resource->handle, indices); + send_tranche_flags(resource->handle, static_cast(tranche.flags)); + send_tranche_done(resource->handle); + }; + for (const auto &tranche : qAsConst(m_tranches)) { + sendTranche(tranche); + } + // send default hints as the last fallback tranche + if (this != get(m_bufferintegration->defaultFeedback.data())) { + sendTranche(get(m_bufferintegration->defaultFeedback.data())->m_tranches[0]); + } + send_done(resource->handle); +} + +void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) +{ + send(resource); +} + +void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +struct linux_dmabuf_feedback_v1_table_entry { + uint32_t format; + uint32_t pad; // unused + uint64_t modifier; +}; + +LinuxDmaBufV1FormatTable::LinuxDmaBufV1FormatTable(const QHash> &supportedModifiers) +{ + QVector data; + for (auto it = supportedModifiers.begin(); it != supportedModifiers.end(); it++) { + const uint32_t format = it.key(); + for (const uint64_t &mod : *it) { + indices.insert({format, mod}, data.size()); + data.append({format, 0, mod}); + } + } + size = data.size() * sizeof(linux_dmabuf_feedback_v1_table_entry); + QScopedPointer tmp(new QTemporaryFile()); + if (!tmp->open()) { + qCWarning(KWAYLAND_SERVER) << "Failed to create keymap file:" << tmp->errorString(); + return; + } + fd = open(tmp->fileName().toUtf8().constData(), O_RDONLY | O_CLOEXEC); + if (fd < 0) { + qCWarning(KWAYLAND_SERVER) << "Could not create readonly shm fd!" << strerror(errno); + return; + } + unlink(tmp->fileName().toUtf8().constData()); + if (!tmp->resize(size)) { + qCWarning(KWAYLAND_SERVER) << "Failed to resize keymap file:" << tmp->errorString(); + return; + } + uchar *address = tmp->map(0, size); + if (!address) { + qCWarning(KWAYLAND_SERVER) << "Failed to map keymap file:" << tmp->errorString(); + return; + } + memcpy(address, data.data(), size); +} + +LinuxDmaBufV1FormatTable::~LinuxDmaBufV1FormatTable() +{ + if (fd != -1) { + close(fd); + } +} + } // namespace KWaylandServer diff --git a/src/wayland/linuxdmabufv1clientbuffer.h b/src/wayland/linuxdmabufv1clientbuffer.h index bdb13765d5..430ce5a376 100644 --- a/src/wayland/linuxdmabufv1clientbuffer.h +++ b/src/wayland/linuxdmabufv1clientbuffer.h @@ -2,6 +2,7 @@ SPDX-FileCopyrightText: 2018 Fredrik Höglund SPDX-FileCopyrightText: 2019 Roman Gilg SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 Xaver Hugl SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ @@ -12,11 +13,13 @@ #include #include +#include namespace KWaylandServer { class LinuxDmaBufV1ClientBufferPrivate; class LinuxDmaBufV1ClientBufferIntegrationPrivate; +class LinuxDmaBufV1FeedbackPrivate; /** * The LinuxDmaBufV1Plane type represents a plane in a client buffer. @@ -101,10 +104,41 @@ public: */ void setRendererInterface(RendererInterface *rendererInterface); - void setSupportedFormatsWithModifiers(const QHash> &set); + void setSupportedFormatsWithModifiers(dev_t mainDevice, const QHash> &set); private: + friend class LinuxDmaBufV1ClientBufferIntegrationPrivate; QScopedPointer d; }; +class KWAYLANDSERVER_EXPORT LinuxDmaBufV1Feedback : public QObject +{ + Q_OBJECT +public: + ~LinuxDmaBufV1Feedback() override; + + enum class TrancheFlag : uint32_t { + Scanout = 1, + }; + Q_DECLARE_FLAGS(TrancheFlags, TrancheFlag) + + struct Tranche { + dev_t device; + TrancheFlags flags; + QHash> formatTable; + }; + /** + * Sets the list of tranches for this feedback object, with lower indices + * indicating a higher priority / a more optimal configuration. + * The main device does not need to be included + */ + void setTranches(const QVector &tranches); + +private: + LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegration *integration); + friend class LinuxDmaBufV1ClientBufferIntegrationPrivate; + friend class LinuxDmaBufV1FeedbackPrivate; + QScopedPointer d; +}; + } // namespace KWaylandServer diff --git a/src/wayland/linuxdmabufv1clientbuffer_p.h b/src/wayland/linuxdmabufv1clientbuffer_p.h index f69a0c1e46..ee500ea5e3 100644 --- a/src/wayland/linuxdmabufv1clientbuffer_p.h +++ b/src/wayland/linuxdmabufv1clientbuffer_p.h @@ -2,6 +2,7 @@ SPDX-FileCopyrightText: 2018 Fredrik Höglund SPDX-FileCopyrightText: 2019 Roman Gilg SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 Xaver Hugl Based on the libweston implementation, SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd. @@ -21,11 +22,11 @@ #include #include -#include - namespace KWaylandServer { +class LinuxDmaBufV1FormatTable; + class LinuxDmaBufV1ClientBufferIntegrationPrivate : public QtWaylandServer::zwp_linux_dmabuf_v1 { public: @@ -33,12 +34,19 @@ public: LinuxDmaBufV1ClientBufferIntegration *q; LinuxDmaBufV1ClientBufferIntegration::RendererInterface *rendererInterface = nullptr; + QScopedPointer defaultFeedback; + QScopedPointer table; + dev_t mainDevice; QHash> supportedModifiers; + static LinuxDmaBufV1ClientBufferIntegrationPrivate *get(LinuxDmaBufV1ClientBufferIntegration *integration); + protected: void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override; void zwp_linux_dmabuf_v1_destroy(Resource *resource) override; void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override; + void zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) override; + void zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surface) override; }; class LinuxDmaBufV1ClientBufferPrivate : public ClientBufferPrivate, public QtWaylandServer::wl_buffer @@ -83,4 +91,30 @@ private: bool m_isUsed = false; }; +class LinuxDmaBufV1FormatTable +{ +public: + LinuxDmaBufV1FormatTable(const QHash> &supportedModifiers); + ~LinuxDmaBufV1FormatTable(); + + int fd = -1; + int size; + QMap, uint16_t> indices; +}; + +class LinuxDmaBufV1FeedbackPrivate : public QtWaylandServer::zwp_linux_dmabuf_feedback_v1 +{ +public: + LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration); + + static LinuxDmaBufV1FeedbackPrivate *get(LinuxDmaBufV1Feedback *q); + void send(Resource *resource); + + QVector m_tranches; + LinuxDmaBufV1ClientBufferIntegrationPrivate *m_bufferintegration; + +protected: + void zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) override; + void zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) override; +}; } diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index c8fdfb8601..9582ecdebb 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -10,6 +10,7 @@ #include "compositor_interface.h" #include "display.h" #include "idleinhibit_v1_interface_p.h" +#include "linuxdmabufv1clientbuffer.h" #include "pointerconstraints_v1_interface_p.h" #include "region_interface_p.h" #include "subcompositor_interface.h" @@ -945,6 +946,11 @@ bool SurfaceInterface::inhibitsIdle() const return !d->idleInhibitors.isEmpty(); } +LinuxDmaBufV1Feedback *SurfaceInterface::dmabufFeedbackV1() const +{ + return d->dmabufFeedbackV1.data(); +} + QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const { return d->surfaceToBufferMatrix.map(point); diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h index 6479f948e9..a0151d194f 100644 --- a/src/wayland/surface_interface.h +++ b/src/wayland/surface_interface.h @@ -27,6 +27,7 @@ class ShadowInterface; class SlideInterface; class SubSurfaceInterface; class SurfaceInterfacePrivate; +class LinuxDmaBufV1Feedback; /** * @brief Resource representing a wl_surface. @@ -52,6 +53,7 @@ class SurfaceInterfacePrivate; * @see ContrastInterface * @see ShadowInterface * @see SlideInterface + * @see LinuxDmaBufV1Feedback */ class KWAYLANDSERVER_EXPORT SurfaceInterface : public QObject { @@ -301,6 +303,11 @@ public: */ bool inhibitsIdle() const; + /** + * dmabuf feedback installed on this SurfaceInterface + */ + LinuxDmaBufV1Feedback *dmabufFeedbackV1() const; + /** * @returns The SurfaceInterface for the @p native resource. */ diff --git a/src/wayland/surface_interface_p.h b/src/wayland/surface_interface_p.h index a3c1ff59ca..b55e4f3da8 100644 --- a/src/wayland/surface_interface_p.h +++ b/src/wayland/surface_interface_p.h @@ -119,6 +119,7 @@ public: QVector idleInhibitors; ViewportInterface *viewportExtension = nullptr; + QScopedPointer dmabufFeedbackV1; ClientConnection *client = nullptr; protected: