JDK-8210547[linux] frame pacing etc.
Michael Zucchi
notzed at gmail.com
Thu Oct 2 09:59:01 UTC 2025
All,
Unfortunately I'm still unable to login to oracle.com to sign any OCA,
so the attached patch is just FYI. It seemed to create my account ok but
afterward confirming my email it redirected me to a page without being
logged in, and then any attempt to login fails. Firefox 140.x ESR using
a default profile of I reserve for finicky sites. Any suggestions?
Regardless, I spent a few days poking around including installing
ubuntu 24 lts, and building javafx on windows 10 (in a vm) to see what
was supposed to be happening.
Unlike D3D's SwapChain.Present(flags=0), glXSwapBuffers() is a not a
synchronous call[1]. It only inserts a synchronisation point into the
command queue which will block any calls that try to render to the back
buffer before it is swapped from the front - which means it
'effectively' blocks if there's a backlog, but only at some unknown
point in the future (depending on how many buffers the driver sets up).
As per [1] and the specification calling glFinish() immediately after
glXSwapBuffers() will synchronise to the display and make it behave
similarly to D3D's SwapChain.Present(0), but with GLX this only works
for a single window. Each additional window adds another whole frame so
they all run at 1/N the target rate.
I tried a number of approaches, some of them follow. I was focusing on
making it work on both a single and multiple window application, and
with consistent pulse rates.
"vsync" timers
--------------
One solution is to change the GtkGlass backend to export a non-zero
staticScreen_getVideoRefreshPeriod() which will cause Quantum to
assume the clock is accurate to vsync. And then implement something
that is either accurate or close-enough.
I've tried a few different variations:
0. Use gdk_timeout but use a dynamic timeout each frame based on the
reported frame-rate, and using logic to maintain long-term
accuracy.
Pros: No new mechanisms or races or build changes.
Fairly accurate over the lont term.
Can adapt if the monitor refresh rate changes at runtime.
Avoids vsyncHint() logic.
Cons: Jittery due to only millisecond precision and g_mainloop.
gdk_monitor_get_refresh_rate() must be correct (and exist)
1. As with the Mac backend, Gtk GlassTimer starts a thread that runs
it's own vblank timing. Use a hidden double-buffered Window and
call glXSwapBuffer() then glFinish() on a local context before the
callback.
This works very well for X11 but breaks on XWayland due to [2] where
it runs at 'jittery 60Hz' regardless of the actual frame-rate because
wayland and by extension Xwayland doesn't think hidden windows should
know about vsync.
Pros: For X11 Correct and accurate, should be well supported.
Avoids vsyncHint() logic.
Cons: Requires linking to libGL in gtk3glass.
Complicates errors/fallback a bit.
gdk_monitor_get_refresh_rate() must be correct (and exist)
XWayland is broken.
It is unclear whether the timer callback needs to run on the
g_mainloop, it doesn't seem to matter.
2. As with the Mac backend, Gtk GlassTimer starts a thread that runs
it's own timing. This time using clock_gettime() and usleep() and
some logic to avoid drift, handle suspend, and keep consistent
frame timing.
This works quite well as long as GdkMonitor returns an accurate frame
rate.
Pros: Simple, not perfect timing but fairly accurate in practice.
Avoids syncHint() logic.
Could just be implemented in Java if
GlassApplication.staticScreen_getVideoRefreshPeriod() is
accurate, although a frame-rate might be more useful.
Cons: Complicates errors/fallback a bit (if in native)
gdk_monitor_get_refresh_rate() must be correct (and exist)
It is unclear whether the timer callback needs to run on the
g_mainloop, it doesn't seem to matter.
glFinish at a different spot
----------------------------
After trying all of the above and reading some more internet forums and
articles I tried another approach: run a glFinish() once, after all
GlassScene's have been rendered.
Pros: Seems to work reliably and accurately.
Pretty simple patch that doesn't touch much.
Cons: Better Gtk GlassTimer would be nice.
Comments
--------
A better timer interface and for the animation.pulse value would be
nice, perhaps internally fps in decimal fixed-point like GdkMonitor
uses. A better Gtk GlassTimer using that, plus GLX syncing after
PaintCollector.renderAll().
Just for interest's sake I've attached a WIP patch of the last
suggestion. I thought they might provide more insight but I've attached
some frame-time plots from the various alternatives anyway. Note that
Wayland is on a machine with a frame-rate of 143.88, and the others 59.95.
Regards,
!Z
[1]
https://community.khronos.org/t/understanding-the-opengl-main-loop-swapbuffers/75593/12
[2] https://gitlab.freedesktop.org/xorg/xserver/-/issues/631
-------------- next part --------------
A non-text attachment was scrubbed...
Name: javafx-vsync.diff
Type: text/x-patch
Size: 4958 bytes
Desc: not available
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20251002/a3de2afb/javafx-vsync-0001.diff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: frametimes.png
Type: image/png
Size: 23375 bytes
Desc: not available
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20251002/a3de2afb/frametimes-0001.png>
More information about the openjfx-dev
mailing list