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.
This commit is contained in:
parent
e88a9e511a
commit
ae4c3c1a65
8 changed files with 100 additions and 60 deletions
|
@ -278,6 +278,7 @@ find_package(XCB 1.10 REQUIRED COMPONENTS
|
|||
ICCCM
|
||||
IMAGE
|
||||
KEYSYMS
|
||||
PRESENT
|
||||
RANDR
|
||||
RENDER
|
||||
SHAPE
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
#include <QSocketNotifier>
|
||||
// xcb
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/present.h>
|
||||
// X11
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <fixx11h.h>
|
||||
#if HAVE_X11_XINPUT
|
||||
#include "../common/ge_event_mem_mover.h"
|
||||
#include <X11/extensions/XI2proto.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#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<xcb_mapping_notify_event_t *>(e));
|
||||
}
|
||||
break;
|
||||
#if HAVE_X11_XINPUT
|
||||
case XCB_GE_GENERIC: {
|
||||
GeEventMemMover ge(e);
|
||||
auto te = reinterpret_cast<xXIDeviceEvent *>(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<xXITouchOwnershipEvent *>(e);
|
||||
XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch);
|
||||
break;
|
||||
}
|
||||
xcb_ge_generic_event_t *ev = reinterpret_cast<xcb_ge_generic_event_t *>(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<xXIDeviceEvent *>(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<xXITouchOwnershipEvent *>(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<xcb_present_complete_notify_event_t *>(ge);
|
||||
if (X11WindowedOutput *output = findOutput(completeNotify->window)) {
|
||||
output->handlePresentCompleteNotify(completeNotify);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xcb_window_t X11WindowedBackend::rootWindow() const
|
||||
{
|
||||
if (!m_screen) {
|
||||
|
|
|
@ -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<X11WindowedOutput *> m_outputs;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<X11WindowedOutput *>(output)->vsyncMonitor()->arm();
|
||||
|
||||
const auto &renderOutput = m_outputs[output];
|
||||
presentSurface(renderOutput.primaryLayer->surface(), renderOutput.primaryLayer->lastDamage(), output->geometry());
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include "core/renderloop_p.h"
|
||||
#include "composite.h"
|
||||
#include "softwarevsyncmonitor.h"
|
||||
#include "x11_windowed_backend.h"
|
||||
|
||||
#include <NETWM>
|
||||
|
@ -90,7 +89,6 @@ void X11WindowedCursor::update(const QImage &image, const QPoint &hotspot)
|
|||
X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend)
|
||||
: Output(backend)
|
||||
, m_renderLoop(std::make_unique<RenderLoop>())
|
||||
, 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<OutputMode>(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<NETWinInfo>(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<X11WindowedEglBackend *>(Compositor::self()->backend())) {
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
#include <QString>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/present.h>
|
||||
|
||||
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<NETWinInfo> m_winInfo;
|
||||
std::unique_ptr<RenderLoop> m_renderLoop;
|
||||
std::unique_ptr<SoftwareVsyncMonitor> m_vsyncMonitor;
|
||||
std::unique_ptr<X11WindowedCursor> m_cursor;
|
||||
QPoint m_hostPosition;
|
||||
QRegion m_exposedArea;
|
||||
|
|
|
@ -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<X11WindowedOutput *>(output)->vsyncMonitor()->arm();
|
||||
const auto &rendererOutput = m_outputs[output];
|
||||
|
||||
xcb_connection_t *c = m_backend->connection();
|
||||
|
|
Loading…
Reference in a new issue