From ae4c3c1a65e67534bde028c38de7968dc6792905 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 9 Dec 2022 23:25:03 +0200 Subject: [PATCH] backends/x11: Use PRESENT extension to get notified about frame completion Currently, we use a timer to get notified when a frame is presented, but there's a better way using PRESENT extension's PresentCompleteNotify events. Note that we implicitly rely on the fact that EGL implementation uses the PRESENT extension to present pixmaps, which is usually the case. QPainter backend needs some adjustments. --- CMakeLists.txt | 1 + src/backends/x11/windowed/CMakeLists.txt | 6 +- .../x11/windowed/x11_windowed_backend.cpp | 107 ++++++++++++------ .../x11/windowed/x11_windowed_backend.h | 6 + .../x11/windowed/x11_windowed_egl_backend.cpp | 3 - .../x11/windowed/x11_windowed_output.cpp | 27 ++--- .../x11/windowed/x11_windowed_output.h | 8 +- .../x11_windowed_qpainter_backend.cpp | 2 - 8 files changed, 100 insertions(+), 60 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48ab676e7b..fcd72dc541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,7 @@ find_package(XCB 1.10 REQUIRED COMPONENTS ICCCM IMAGE KEYSYMS + PRESENT RANDR RENDER SHAPE diff --git a/src/backends/x11/windowed/CMakeLists.txt b/src/backends/x11/windowed/CMakeLists.txt index 834d3838db..be3c5326fd 100644 --- a/src/backends/x11/windowed/CMakeLists.txt +++ b/src/backends/x11/windowed/CMakeLists.txt @@ -6,7 +6,11 @@ target_sources(kwin PRIVATE x11_windowed_qpainter_backend.cpp ) -target_link_libraries(kwin X11::XCB X11::X11) +target_link_libraries(kwin + X11::XCB + X11::X11 + XCB::PRESENT +) if (X11_Xi_FOUND) target_link_libraries(kwin X11::Xi) endif() diff --git a/src/backends/x11/windowed/x11_windowed_backend.cpp b/src/backends/x11/windowed/x11_windowed_backend.cpp index 2278142e26..7c65dffa71 100644 --- a/src/backends/x11/windowed/x11_windowed_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_backend.cpp @@ -25,11 +25,11 @@ #include // xcb #include +#include // X11 #include #include #if HAVE_X11_XINPUT -#include "../common/ge_event_mem_mover.h" #include #include #endif @@ -193,6 +193,24 @@ bool X11WindowedBackend::initialize() m_screen = it.data; } } + + const xcb_query_extension_reply_t *presentExtension = xcb_get_extension_data(m_connection, &xcb_present_id); + if (presentExtension && presentExtension->present) { + m_presentOpcode = presentExtension->major_opcode; + xcb_present_query_version_cookie_t cookie = xcb_present_query_version(m_connection, 1, 2); + xcb_present_query_version_reply_t *reply = xcb_present_query_version_reply(m_connection, cookie, nullptr); + if (!reply) { + qCWarning(KWIN_X11WINDOWED) << "Requested Present extension version is unsupported"; + return false; + } + m_presentMajorVersion = reply->major_version; + m_presentMinorVersion = reply->minor_version; + free(reply); + } else { + qCWarning(KWIN_X11WINDOWED) << "Present X11 extension is unavailable"; + return false; + } + initXInput(); XRenderUtils::init(m_connection, m_screen->root); createOutputs(); @@ -365,43 +383,15 @@ void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast(e)); } break; -#if HAVE_X11_XINPUT case XCB_GE_GENERIC: { - GeEventMemMover ge(e); - auto te = reinterpret_cast(e); - const X11WindowedOutput *output = findOutput(te->event); - if (!output) { - break; - } - - const QPointF position = output->mapFromGlobal(QPointF(fixed1616ToReal(te->root_x), fixed1616ToReal(te->root_y))); - - switch (ge->event_type) { - - case XI_TouchBegin: { - Q_EMIT m_touchDevice->touchDown(te->detail, position, te->time, m_touchDevice.get()); - Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); - break; - } - case XI_TouchUpdate: { - Q_EMIT m_touchDevice->touchMotion(te->detail, position, te->time, m_touchDevice.get()); - Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); - break; - } - case XI_TouchEnd: { - Q_EMIT m_touchDevice->touchUp(te->detail, te->time, m_touchDevice.get()); - Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); - break; - } - case XI_TouchOwnership: { - auto te = reinterpret_cast(e); - XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch); - break; - } + xcb_ge_generic_event_t *ev = reinterpret_cast(e); + if (ev->extension == m_presentOpcode) { + handlePresentEvent(ev); + } else if (ev->extension == m_xiOpcode) { + handleXinputEvent(ev); } break; } -#endif default: break; } @@ -558,6 +548,55 @@ void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event) } } +void X11WindowedBackend::handleXinputEvent(xcb_ge_generic_event_t *ge) +{ +#if HAVE_X11_XINPUT + auto te = reinterpret_cast(ge); + const X11WindowedOutput *output = findOutput(te->event); + if (!output) { + return; + } + + const QPointF position = output->mapFromGlobal(QPointF(fixed1616ToReal(te->root_x), fixed1616ToReal(te->root_y))); + + switch (ge->event_type) { + case XI_TouchBegin: { + Q_EMIT m_touchDevice->touchDown(te->detail, position, te->time, m_touchDevice.get()); + Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); + break; + } + case XI_TouchUpdate: { + Q_EMIT m_touchDevice->touchMotion(te->detail, position, te->time, m_touchDevice.get()); + Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); + break; + } + case XI_TouchEnd: { + Q_EMIT m_touchDevice->touchUp(te->detail, te->time, m_touchDevice.get()); + Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get()); + break; + } + case XI_TouchOwnership: { + auto te = reinterpret_cast(ge); + XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch); + break; + } + } +#endif +} + +void X11WindowedBackend::handlePresentEvent(xcb_ge_generic_event_t *ge) +{ + switch (ge->event_type) { + case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: { + xcb_present_complete_notify_event_t *completeNotify = reinterpret_cast(ge); + if (X11WindowedOutput *output = findOutput(completeNotify->window)) { + output->handlePresentCompleteNotify(completeNotify); + } + break; + } + } +} + xcb_window_t X11WindowedBackend::rootWindow() const { if (!m_screen) { diff --git a/src/backends/x11/windowed/x11_windowed_backend.h b/src/backends/x11/windowed/x11_windowed_backend.h index 9aa1951e4c..302b9aeafb 100644 --- a/src/backends/x11/windowed/x11_windowed_backend.h +++ b/src/backends/x11/windowed/x11_windowed_backend.h @@ -124,6 +124,8 @@ private: void handleClientMessage(xcb_client_message_event_t *event); void handleButtonPress(xcb_button_press_event_t *event); void handleExpose(xcb_expose_event_t *event); + void handleXinputEvent(xcb_ge_generic_event_t *event); + void handlePresentEvent(xcb_ge_generic_event_t *event); void updateSize(xcb_configure_notify_event_t *event); void initXInput(); X11WindowedOutput *findOutput(xcb_window_t window) const; @@ -149,6 +151,10 @@ private: int m_majorVersion = 0; int m_minorVersion = 0; + int m_presentOpcode = 0; + int m_presentMajorVersion = 0; + int m_presentMinorVersion = 0; + QVector m_outputs; }; diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp index e6ace1bcac..106408cebf 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp @@ -10,7 +10,6 @@ // kwin #include "basiceglsurfacetexture_internal.h" #include "basiceglsurfacetexture_wayland.h" -#include "softwarevsyncmonitor.h" #include "x11_windowed_backend.h" #include "x11_windowed_output.h" // kwin libs @@ -177,8 +176,6 @@ bool X11WindowedEglBackend::createSurfaces() void X11WindowedEglBackend::present(Output *output) { - static_cast(output)->vsyncMonitor()->arm(); - const auto &renderOutput = m_outputs[output]; presentSurface(renderOutput.primaryLayer->surface(), renderOutput.primaryLayer->lastDamage(), output->geometry()); } diff --git a/src/backends/x11/windowed/x11_windowed_output.cpp b/src/backends/x11/windowed/x11_windowed_output.cpp index 44cdb98646..6d6ab495fa 100644 --- a/src/backends/x11/windowed/x11_windowed_output.cpp +++ b/src/backends/x11/windowed/x11_windowed_output.cpp @@ -15,7 +15,6 @@ #include "core/renderloop_p.h" #include "composite.h" -#include "softwarevsyncmonitor.h" #include "x11_windowed_backend.h" #include @@ -90,7 +89,6 @@ void X11WindowedCursor::update(const QImage &image, const QPoint &hotspot) X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) : Output(backend) , m_renderLoop(std::make_unique()) - , m_vsyncMonitor(SoftwareVsyncMonitor::create()) , m_backend(backend) { m_window = xcb_generate_id(m_backend->connection()); @@ -100,12 +98,11 @@ X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) setInformation(Information{ .name = QStringLiteral("X11-%1").arg(identifier), }); - - connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &X11WindowedOutput::vblank); } X11WindowedOutput::~X11WindowedOutput() { + xcb_present_select_input(m_backend->connection(), m_presentEvent, m_window, 0); xcb_unmap_window(m_backend->connection(), m_window); xcb_destroy_window(m_backend->connection(), m_window); xcb_flush(m_backend->connection()); @@ -131,11 +128,6 @@ RenderLoop *X11WindowedOutput::renderLoop() const return m_renderLoop.get(); } -SoftwareVsyncMonitor *X11WindowedOutput::vsyncMonitor() const -{ - return m_vsyncMonitor.get(); -} - X11WindowedBackend *X11WindowedOutput::backend() const { return m_backend; @@ -160,7 +152,6 @@ void X11WindowedOutput::init(const QSize &pixelSize, qreal scale) { const int refreshRate = 60000; // TODO: get refresh rate via randr m_renderLoop->setRefreshRate(refreshRate); - m_vsyncMonitor->setRefreshRate(refreshRate); auto mode = std::make_shared(pixelSize, m_renderLoop->refreshRate()); @@ -199,6 +190,10 @@ void X11WindowedOutput::init(const QSize &pixelSize, qreal scale) // select xinput 2 events initXInputForWindow(); + const uint32_t presentEventMask = XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY; + m_presentEvent = xcb_generate_id(m_backend->connection()); + xcb_present_select_input(m_backend->connection(), m_presentEvent, m_window, presentEventMask); + m_winInfo = std::make_unique(m_backend->connection(), m_window, m_backend->screen()->root, NET::WMWindowType, NET::Properties2()); @@ -256,6 +251,12 @@ void X11WindowedOutput::resize(const QSize &pixelSize) setState(next); } +void X11WindowedOutput::handlePresentCompleteNotify(xcb_present_complete_notify_event_t *event) +{ + std::chrono::microseconds timestamp(event->ust); + RenderLoopPrivate::get(m_renderLoop.get())->notifyFrameCompleted(timestamp); +} + void X11WindowedOutput::setWindowTitle(const QString &title) { m_winInfo->setName(title.toUtf8().constData()); @@ -276,12 +277,6 @@ QPointF X11WindowedOutput::mapFromGlobal(const QPointF &pos) const return (pos - hostPosition() + internalPosition()) / scale(); } -void X11WindowedOutput::vblank(std::chrono::nanoseconds timestamp) -{ - RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop.get()); - renderLoopPrivate->notifyFrameCompleted(timestamp); -} - bool X11WindowedOutput::setCursor(const QImage &image, const QPoint &hotspot) { if (X11WindowedEglBackend *backend = qobject_cast(Compositor::self()->backend())) { diff --git a/src/backends/x11/windowed/x11_windowed_output.h b/src/backends/x11/windowed/x11_windowed_output.h index 64ba861d20..89cbf05e22 100644 --- a/src/backends/x11/windowed/x11_windowed_output.h +++ b/src/backends/x11/windowed/x11_windowed_output.h @@ -16,13 +16,13 @@ #include #include +#include class NETWinInfo; namespace KWin { -class SoftwareVsyncMonitor; class X11WindowedBackend; class X11WindowedOutput; class X11WindowedEglBackend; @@ -52,7 +52,6 @@ public: ~X11WindowedOutput() override; RenderLoop *renderLoop() const override; - SoftwareVsyncMonitor *vsyncMonitor() const; void init(const QSize &pixelSize, qreal scale); void resize(const QSize &pixelSize); @@ -81,16 +80,17 @@ public: void updateEnabled(bool enabled); + void handlePresentCompleteNotify(xcb_present_complete_notify_event_t *event); + private: void initXInputForWindow(); - void vblank(std::chrono::nanoseconds timestamp); void renderCursorOpengl(X11WindowedEglBackend *backend, const QImage &image, const QPoint &hotspot); void renderCursorQPainter(X11WindowedQPainterBackend *backend, const QImage &image, const QPoint &hotspot); xcb_window_t m_window = XCB_WINDOW_NONE; + xcb_present_event_t m_presentEvent = XCB_NONE; std::unique_ptr m_winInfo; std::unique_ptr m_renderLoop; - std::unique_ptr m_vsyncMonitor; std::unique_ptr m_cursor; QPoint m_hostPosition; QRegion m_exposedArea; diff --git a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp index f83367bf42..935c397141 100644 --- a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp @@ -7,7 +7,6 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "x11_windowed_qpainter_backend.h" -#include "softwarevsyncmonitor.h" #include "x11_windowed_backend.h" #include "x11_windowed_output.h" @@ -128,7 +127,6 @@ void X11WindowedQPainterBackend::removeOutput(Output *output) void X11WindowedQPainterBackend::present(Output *output) { - static_cast(output)->vsyncMonitor()->arm(); const auto &rendererOutput = m_outputs[output]; xcb_connection_t *c = m_backend->connection();