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