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: