<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>It's a constant value for all handler invocations for a given
      frame, I think that part is correct.  It should not matter that a
      later handler is doing its calculations for the next frame at a
      different point in real time; it would still be doing calculations
      for the next frame, and as long as it completes those before the
      next frame is rendered it will be "on time".</p>
    <p>So if I have two handlers, A and B, and A is called at (real)
      time X, and B is called at (real) time X + 200 ns, they both
      should get a "now" value that corresponds to the next frame to be
      rendered -- this doesn't have to be derived from System.nanoTime
      at all.  It only needs to start at some value and be incremented
      for each frame rendered (including missed frames).  AnimationTimer
      is not documented to say that the value is derived from nanoTime
      -- it is just happenstance that the value currently used
      corresponds closely to System.nanoTime.</p>
    <p>--John<br>
    </p>
    <div class="moz-cite-prefix">On 29/08/2024 20:53, Andy Goryachev
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:BL3PR10MB618523CFFAEA59ADD73C600FE5962@BL3PR10MB6185.namprd10.prod.outlook.com">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta name="Generator"
        content="Microsoft Word 15 (filtered medium)">
      <style>@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face
        {font-family:Aptos;
        panose-1:2 11 0 4 2 2 2 2 2 4;}@font-face
        {font-family:"Iosevka Fixed SS16";
        panose-1:2 0 5 9 3 0 0 0 0 4;}@font-face
        {font-family:"Times New Roman \(Body CS\)";
        panose-1:2 11 6 4 2 2 2 2 2 4;}p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:10.0pt;
        font-family:"Aptos",sans-serif;}span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Iosevka Fixed SS16";
        color:windowtext;}.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;
        mso-ligatures:none;}div.WordSection1
        {page:WordSection1;}</style>
      <div class="WordSection1">
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">This
            might be slightly off-topic, but I noticed that instead of
            computing 'now' time for AnimationTimer.handle(long) just
            before calling the actual handler, we do compute it once
            before sending to possibly multiple handlers.  Each
            subsequent invocation produces progressively larger
            discrepancy (AbstractPrimaryTimer:264).<o:p></o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Does
            not explain the issue John raised though.<o:p></o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy<o:p></o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
        <div id="mail-editor-reference-message-container">
          <div>
            <div>
              <div
style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
                <p class="MsoNormal" style="margin-bottom:12.0pt"><b><span
                      style="font-size:12.0pt;color:black">From:
                    </span></b><span
                    style="font-size:12.0pt;color:black">openjfx-dev
                    <a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev-retn@openjdk.org"><openjfx-dev-retn@openjdk.org></a> on behalf of
                    Michael Strauß <a class="moz-txt-link-rfc2396E" href="mailto:michaelstrau2@gmail.com"><michaelstrau2@gmail.com></a><br>
                    <b>Date: </b>Thursday, August 29, 2024 at 11:45<br>
                    <b>To: </b><br>
                    <b>Cc: </b><a class="moz-txt-link-abbreviated" href="mailto:openjfx-dev@openjdk.org">openjfx-dev@openjdk.org</a>
                    <a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev@openjdk.org"><openjfx-dev@openjdk.org></a><br>
                    <b>Subject: </b>Re: Bug: Times passed to
                    AnimationTimer should not fluctuate<o:p></o:p></span></p>
              </div>
              <div>
                <p class="MsoNormal"><span style="font-size:11.0pt">Yes,
                    that makes sense. In any case, we shouldn't be using
                    a system<br>
                    timer, but simply record the timestamp at v-sync,
                    and then pass this<br>
                    precise timestamp to all AnimationTimers. It
                    shouldn't matter when<br>
                    AnimationTimers are invoked between frames, as long
                    as the timestamp<br>
                    corresponds to the v-sync signal. (Well, unless the
                    timer callback<br>
                    measures its own time, which it shouldn't do.)<br>
                    <br>
                    <br>
                    On Thu, Aug 29, 2024 at 8:20</span><span
style="font-size:11.0pt;font-family:"Arial",sans-serif"> </span><span
                    style="font-size:11.0pt">PM John Hendrikx
                    <a class="moz-txt-link-rfc2396E" href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a> wrote:<br>
                    ><br>
                    > I think they're a bit separate.  Even with
                    VSync, the time it takes to kick the FX thread in
                    action is still going to be between 0-30ms.  If it
                    then passes `System.nanoTime()` to the
                    AnimationRunnables, you're basically saying that
                    they should render a frame at the precise time of
                    VSync-Time + random time it took to schedule the FX
                    thread... suffice to say that the extra accuracy of
                    the more accurate VSync timer (just like my far more
                    accurate timer) is made completely redundant by the
                    jitter introduced by the scheduler.<br>
                    ><br>
                    > This brings me back to my original point: we
                    should not be passing `System.nanoTime()` to
                    AnimationRunnables.  Passing `System.nanoTime()` is
                    basically asking to create a frame with a time index
                    that will NEVER be rendered, so why are we asking
                    Animations to use this value for calculating
                    animation locations/offsets/sizes ?<br>
                    ><br>
                    > This problem is also present on Mac and Linux,
                    just less noticeable because their schedulers
                    generally react within 0-2 ms (vs 0-30 ms on
                    Windows).  2 ms is "close enough" to the most
                    commonly used frame rates (60 fps, at 16.667 ms per
                    frame), but on Windows it can practically be a two
                    frame difference.<br>
                    ><br>
                    > Even in the absence of V-sync, when JavaFX
                    arbitrarily picks 60 Hz as its refresh frequency,
                    the times passed to AnimationTimer should be
                    multiples of 16.667 ms, not 16.667 ms + however long
                    it took to wake up the FX thread.  In other words
                    this code in AbstactPrimaryTimer:<br>
                    ><br>
                    > private long nextPulseTime = nanos();<br>
                    ><br>
                    > private long lastPulseDuration =
                    Integer.MIN_VALUE;<br>
                    ><br>
                    > @Override<br>
                    ><br>
                    > public void run() {<br>
                    ><br>
                    > if (paused) {<br>
                    ><br>
                    > return;<br>
                    ><br>
                    > }<br>
                    ><br>
                    > final long now = nanos();<br>
                    ><br>
                    > recordStart((nextPulseTime - now) / 1000000);<br>
                    ><br>
                    > timePulseImpl(now);<br>
                    ><br>
                    > recordEnd();<br>
                    ><br>
                    > updateNextPulseTime(now);<br>
                    ><br>
                    > // reschedule animation runnable if needed<br>
                    ><br>
                    > updateAnimationRunnable();<br>
                    ><br>
                    > }<br>
                    ><br>
                    > ...would be far better if it passed
                    "nextPulseTime" to `timePulseImpl` (which eventually
                    calls the AnimationRunnables) instead of "now".<br>
                    ><br>
                    > Note: this is assuming the adaptive pulse flag
                    is disabled.  If it is enabled, nextPulseTime won't
                    be a nice multiple of the frame rate -- so when this
                    is enabled we may want to round it up/down before
                    passing it to the AnimationRunnables.<br>
                    ><br>
                    > Note 2: you can **already** achieve far
                    smoother animation even on Windows by rounding the
                    value you get passed in to a multiple of
                    1/frameRate. This only works when you have access to
                    the this time. It won't solve Timeline calculations
                    -- they will still calculate positions and values
                    for frames that will never exist, subject to FX
                    thread scheduling jitter...<br>
                    ><br>
                    > --John<o:p></o:p></span></p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </blockquote>
  </body>
</html>