https://bugs.kde.org/show_bug.cgi?id=527878
Bug ID: 527878
Summary: Wayland: periodic stutter of GL clients with triple
buffering on NVIDIA (FreeBSD); gone with
KWIN_DRM_DISABLE_TRIPLE_BUFFERING=1
Classification: Plasma
Product: kwin
Version First 6.7.5
Reported In:
Platform: FreeBSD Ports
OS: FreeBSD
Status: REPORTED
Severity: normal
Priority: NOR
Component: platform-drm
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Target Milestone: ---
## Summary
On Plasma Wayland with an NVIDIA GPU, continuously animating OpenGL clients
stutter in bursts of a few seconds, while KWin's own frame rate (Show FPS
effect) stays at 60. The same clients are smooth on Plasma X11. Setting
`KWIN_DRM_DISABLE_TRIPLE_BUFFERING=1` in the session environment makes them
smooth on Wayland as well.
## Environment
- FreeBSD 15.1-RELEASE amd64
- KWin / Plasma 6.7.5, KDE Frameworks 6.31.0, Qt 6.11.2
- NVIDIA RTX A400, drivers 595.112.07 and 615.78.08 (same behaviour, same fix),
nvidia-drm-612-kmod, drm-612-kmod 6.12.85
- Single output DP-2, 3440x1440 @ 59.97 Hz, scale 1, no VRR, no HDR
- Session environment: `KWIN_FORCE_SW_CURSOR=1` (the hardware cursor is not
usable on this setup)
## Steps to reproduce
1. Build the minimal Qt reproducer below (a white bar moving 8 px per frame in
a
`QOpenGLWidget`, no other libraries).
2. Log in to Plasma Wayland and run `./glbar`, or `GLBAR_PACED=1 ./glbar` to
advance on `frameSwapped` instead of a timer.
3. Watch the bar for 20–30 seconds, keeping the window in front and
unobstructed.
**Observed:** the bar jumps in bursts lasting 1–5 seconds every 5–40 seconds.
It
is worst right after launch and while the window is in front and unobstructed.
It
is often smooth while the window is partly covered. The Show FPS effect stays
at
60 throughout.
**Expected:** smooth motion, as on Plasma X11 with the same hardware and
driver.
## What was tested
| Change | Result |
|---|---|
| `QT_QPA_PLATFORM=xcb` (client through Xwayland/GLX) | still stutters |
| Paced on `frameSwapped` instead of a QTimer | still stutters |
| NVIDIA 615.78.08 → 595.112.07 | still stutters |
| `KWIN_USE_OVERLAYS=0` + `KWIN_DRM_NO_DIRECT_SCANOUT=1` | still stutters |
| **`KWIN_DRM_DISABLE_TRIPLE_BUFFERING=1`** | **smooth, on both 595.112.07 and
615.78.08** |
Client-side measurements with a real application (a Qt QOpenGLWidget plot):
- `frameSwapped` intervals stay at 59–61 per second, average 16.7 ms.
- During bursts about every third interval is 25–31 ms, followed by 3–6 ms
catch-up frames. The long interval then decreases steadily back to 20 ms over
2–5 seconds.
- 3 of 3 bursts recorded at 100 ms resolution started within about 150 ms of an
NVIDIA performance-state change (P5→P0, P3→P5), or of `nvidia-smi` exiting.
- kwin_wayland used a normal 2–16 ms of CPU per 100 ms during the bursts, with
no
involuntary context switches.
- With triple buffering disabled, the GPU still changes performance states and
the motion stays smooth.
My guess: the GPU clock changes make KWin's render times jump, KWin then
switches
between double and triple buffering, and each switch moves client frames by a
refresh period.
## Reproducer
`main.cpp`:
```cpp
#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QVBoxLayout>
#include <QLabel>
#include <QTimer>
class Bar : public QOpenGLWidget, protected QOpenGLFunctions {
public:
int x = 0;
protected:
void initializeGL() override { initializeOpenGLFunctions(); }
void paintGL() override {
glDisable(GL_SCISSOR_TEST);
glClearColor(0.1f, 0.1f, 0.15f, 1); glClear(GL_COLOR_BUFFER_BIT);
const qreal dpr = devicePixelRatioF();
const int w = width() * dpr, h = height() * dpr, bw = 40 * dpr;
glEnable(GL_SCISSOR_TEST);
glScissor(x % w, 0, bw, h);
glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
};
int main(int argc, char **argv) {
QApplication a(argc, argv);
QWidget win;
auto *lo = new QVBoxLayout(&win);
auto *bar = new Bar;
lo->addWidget(bar, 1);
lo->addWidget(new QLabel(qEnvironmentVariableIsSet("GLBAR_PACED") ? "paced
(frameSwapped)" : "QTimer 16 ms"));
auto step = [bar] { bar->x += 8; bar->update(); };
if(qEnvironmentVariableIsSet("GLBAR_PACED")) {
QObject::connect(bar, &QOpenGLWidget::frameSwapped, bar, step,
Qt::QueuedConnection);
QTimer::singleShot(0, bar, step);
} else {
auto *t = new QTimer(&win);
QObject::connect(t, &QTimer::timeout, bar, step);
t->start(1000 / 60);
}
win.resize(1400, 400);
win.show();
return a.exec();
}
```
`CMakeLists.txt`:
```cmake
cmake_minimum_required(VERSION 3.16)
project(glbar LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets)
add_executable(glbar main.cpp)
target_link_libraries(glbar Qt6::Widgets Qt6::OpenGLWidgets)
```
I can provide KWin debug output or further measurements if useful.
--
You are receiving this mail because:
You are watching all bug changes.