[client] display: use trusted nominal cadence

Always track Wayland surface outputs alongside fractional scaling and use
RandR modes for X11 cadence. Remove render-derived feedback so a
throttled client cannot teach the producer to remain throttled.

Release scheduling after nominal output timing remains unavailable.
This commit is contained in:
Geoffrey McRae
2026-08-05 11:50:48 +10:00
parent bca59df783
commit 2c74f4a0f9
8 changed files with 128 additions and 80 deletions

View File

@@ -10,6 +10,7 @@ pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
xinerama
xcursor
xpresent
xrandr
xkbcommon
)

View File

@@ -38,6 +38,7 @@
#include <X11/extensions/scrnsaver.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/Xpresent.h>
#include <X11/extensions/Xrandr.h>
#include <X11/Xcursor/Xcursor.h>
#include <X11/XKBlib.h>
@@ -94,6 +95,67 @@ static void x11XPresentEvent(XGenericEventCookie *cookie);
static void x11UpdateKeyboardGroup(void);
static void x11GrabPointer(void);
static uint64_t x11ModePeriod(
const XRRScreenResources * resources, RRMode modeID)
{
for (int i = 0; i < resources->nmode; ++i)
{
const XRRModeInfo * mode = resources->modes + i;
if (mode->id != modeID || !mode->dotClock ||
!mode->hTotal || !mode->vTotal)
continue;
uint64_t pixels = (uint64_t)mode->hTotal * mode->vTotal;
if (mode->modeFlags & RR_DoubleScan)
pixels *= 2;
if (mode->modeFlags & RR_Interlace)
pixels /= 2;
return pixels / mode->dotClock * 1000000000ULL +
pixels % mode->dotClock * 1000000000ULL / mode->dotClock;
}
return 0;
}
static void x11UpdateFramePeriod(void)
{
uint64_t fastest = 0;
const Window root = DefaultRootWindow(x11.display);
XRRScreenResources * resources =
XRRGetScreenResourcesCurrent(x11.display, root);
if (!resources)
{
atomic_store(&x11.nominalPeriod, 0);
return;
}
for (int i = 0; i < resources->ncrtc; ++i)
{
XRRCrtcInfo * crtc =
XRRGetCrtcInfo(x11.display, resources, resources->crtcs[i]);
if (!crtc)
continue;
const bool intersects = crtc->mode && crtc->width && crtc->height &&
x11.rect.x < crtc->x + (int)crtc->width &&
x11.rect.x + x11.rect.w > crtc->x &&
x11.rect.y < crtc->y + (int)crtc->height &&
x11.rect.y + x11.rect.h > crtc->y;
if (intersects)
{
const uint64_t candidate = x11ModePeriod(resources, crtc->mode);
if (candidate && (!fastest || candidate < fastest))
fastest = candidate;
}
XRRFreeCrtcInfo(crtc);
}
XRRFreeScreenResources(resources);
atomic_store(&x11.nominalPeriod, fastest);
}
static void x11DoPresent(uint64_t msc)
{
static bool startup = true;
@@ -284,6 +346,12 @@ static bool x11Init(const LG_DSInitParams params)
x11.display = XOpenDisplay(NULL);
x11.jitRender = params.jitRender;
x11.wm = &X11WM_Default;
x11.rect = (struct Rect) {
.x = params.x,
.y = params.y,
.w = params.w,
.h = params.h,
};
XSetWindowAttributes swa =
{
@@ -689,6 +757,18 @@ static bool x11Init(const LG_DSInitParams params)
/* default to the square cursor */
XDefineCursor(x11.display, x11.window, x11.cursors[LG_POINTER_SQUARE]);
x11.xrandrSupported =
XRRQueryExtension(x11.display, &x11.xrandrEvent, &error);
if (x11.xrandrSupported)
{
XRRSelectInput(x11.display, DefaultRootWindow(x11.display),
RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask |
RROutputChangeNotifyMask);
x11UpdateFramePeriod();
}
else
DEBUG_WARN("X11 nominal output refresh is unavailable");
if (x11.jitRender)
{
x11.frameEvent = lgCreateEvent(true, 0);
@@ -899,6 +979,16 @@ static int x11EventThread(void * unused)
XEvent xe;
XNextEvent(x11.display, &xe);
if (x11.xrandrSupported &&
(xe.type == x11.xrandrEvent + RRScreenChangeNotify ||
xe.type == x11.xrandrEvent + RRNotify))
{
if (xe.type == x11.xrandrEvent + RRScreenChangeNotify)
XRRUpdateConfiguration(&xe);
x11UpdateFramePeriod();
continue;
}
// call the clipboard handling code
if (x11CBEventThread(&xe))
continue;
@@ -932,6 +1022,8 @@ static int x11EventThread(void * unused)
x11.rect.y = y;
x11.rect.w = xe.xconfigure.width;
x11.rect.h = xe.xconfigure.height;
if (x11.xrandrSupported)
x11UpdateFramePeriod();
app_updateWindowPos(x, y);
@@ -1507,7 +1599,7 @@ static void x11XPresentEvent(XGenericEventCookie *cookie)
static bool x11GetFramePeriod(uint64_t * period)
{
const uint64_t value = atomic_load(&x11.presentPeriod);
const uint64_t value = atomic_load(&x11.nominalPeriod);
if (!value)
return false;

View File

@@ -76,11 +76,15 @@ struct X11DSState
bool jitRender;
_Atomic(uint64_t) presentMsc, presentUst;
_Atomic(uint64_t) presentPeriod;
_Atomic(uint64_t) nominalPeriod;
uint32_t presentSerial;
Pixmap presentPixmap;
XserverRegion presentRegion;
LGEvent * frameEvent;
bool xrandrSupported;
int xrandrEvent;
LGThread * eventThread;
int xinputOp;