FULL PRODUCT VERSION :
8u60
ADDITIONAL OS VERSION INFORMATION :
Win 7 64
A DESCRIPTION OF THE PROBLEM :
Cause and a fix outline forJDK-8134837 (Jerky animations), JDK-8134843 (Choppy video)
The primary cause of these issues is pulse timer usage on Windows. There may be also other tickets that are a consequence of this bug.
On Windows, QuantumToolkit.runToolkit() does
....
pulseTimer.start(PULSE_INTERVAL);
PULSE_INTERVAL default is 16 and thus this results in Windows ::timeSetEvent(16) invoked by WinTimer.
This can work properly only by accident. Pulse time slips relative to vsync and render, then postPulse is periodically missed (during continous scene change).
Sometimes it is not noticeable but it is very easy to reproduce with high speed animation. Window resizes or other activity that interferes with pulse timing and message loop and may sometimes fix the issue by changing relative times of vsyncHint and timer.
--------------------------------
Detailed description:
First, 16 (~63FPS) is not a vsync interval on any device. Timer calls time will slip relative to vsync, skipping all vsyncHint and eventually miss next pulse.
Second, the whole idea of periodic timer is wrong when vsync is on. timeSetEvent merely has no enough precision to schedule periodic pulse at vsync intervals and it is also started at an arbitrary time relative to vsync. If timer is used at all, with vsync it should restart after present() and not run periodically decoupled from vsync. In this case it is also better not to use timeSetEvent but other Windows functions with nano interval.
Third, when d3d vsync is on and a frame is presented to the swap chain, no external timer is required at all to schedule next pulse. It is much better just to post new pulse immediately after render or current pulse (which vsyncHint already does on continously changed scene).
The following oscilogramm is recorded with live rendering scene (two images changing opacity and one slides over them every frame). It shows exactly how timer calls slip, pulse blocking increase and finally postPulse skipped and frame is not updated:
http://test.novisign.com/downloads/testdata/pulsemiss_oscilogram.mp4
Here is oscilogamm snapshot (time direction is to left):
http://test.novisign.com/downloads/testdata/pulsemiss.jpg
zoom with details:
http://test.novisign.com/downloads/testdata/pulsemiss_zoom.png
Obviously vsyncHint/postPulse never invokes pulse runnable here, hence not part of the problem. Just to be sure here is a run without vsyncHint showing the same problem:
http://test.novisign.com/downloads/testdata/timer_novsync.jpg
Disabling WinTimer completely, results in perfect pulse train from vsyncHint:
http://test.novisign.com/downloads/testdata/vsync_notimer.jpg
Animations and timestamps now are silky smooth!
The same good result is producd when postPulse is just invoked at the pulse end.
However, more work is required apart from just disabling timer. If scene did not change, vsyncHint will not be called at all, and invoking postPulse from pulse end instead of vsyncHint may produce busy loop if scene did not change or when minimized. Video and low fps animation timings also should be taken in account.
What we did now for quick and dirty fix, is just to skip postPulse in timer runnable if last pulse occured in less than vsync interval (actually 17ms threshold works equally well). This can be problematic for some corner cases, but meantime works perfect for us and we'll look into improving it later.
We did not check yet if there is some kind of similar problem on Linux.
----------------------
There are still occasional frame misses like this
http://test.novisign.com/downloads/testdata/vsync_notimer_miss.jpg
but they are not on javafx level and seem not related to the bug above. Here, D3D present() seems to block for two vsync intervals. We are not sure this is because of javafx D3D handling or inherent device/driver blocking (but it happens on wide variety of devices). This is normal behavior when d3d window is not the active window, but this is abnormal when the window is active.
We found that the combination of
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)
::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS)
pD3DContext->Get3DExDevice()->SetGPUThreadPriority(7)
improves the behavior, which is especially prominent on slow dual core devices.
Another common cause for weird sporadic render blocking is NVIDIA dual monitor setup and ANY application window being on second monitor. Applications occupying partially each monitor also frequently force all windowed d3d to 30fps. This seems driver or device bug or limitation. Going full screen restores 60fps on the full screen scene.
------------------------
Note that setting -Djavafx.animation.pulse=1000 almost solves the issue because then timer runs with 1 ms resolution and D3D cares for proper delay on rendering when vsync is on.
With pulse 1000hz ... 300Hz it runs on JDK8 almost smooth, similar to JDK7. Not sure what was done in JDK7 to solve this issue but JDK7 apparently works almost properly (very rare jerks) with vsync on and default pulse.
While overriding pulse frequency provides some workaround, it pumps thousands of useless timer messages through the message queue and each invokes native->java postPulse which does nothing at the end (and they may aggregate until render blocking ends, then come in bulk). This also can lead to crashes.
Also under the hood animation frame timestamp deltas are still weird (more than half of timestamp deltas are greater than 17ms). It can (and does) miss vsync here and there. On weak devices even increasing pulse frequency can not eliminate the issue completely and loads on CPU.
So increasing timer frequency, while improves, is not a viable workaround.
------------------------
If additional info is required please contact fedor.losev@gmail.com
REPRODUCIBILITY :
This bug can be reproduced always.
8u60
ADDITIONAL OS VERSION INFORMATION :
Win 7 64
A DESCRIPTION OF THE PROBLEM :
Cause and a fix outline for
The primary cause of these issues is pulse timer usage on Windows. There may be also other tickets that are a consequence of this bug.
On Windows, QuantumToolkit.runToolkit() does
....
pulseTimer.start(PULSE_INTERVAL);
PULSE_INTERVAL default is 16 and thus this results in Windows ::timeSetEvent(16) invoked by WinTimer.
This can work properly only by accident. Pulse time slips relative to vsync and render, then postPulse is periodically missed (during continous scene change).
Sometimes it is not noticeable but it is very easy to reproduce with high speed animation. Window resizes or other activity that interferes with pulse timing and message loop and may sometimes fix the issue by changing relative times of vsyncHint and timer.
--------------------------------
Detailed description:
First, 16 (~63FPS) is not a vsync interval on any device. Timer calls time will slip relative to vsync, skipping all vsyncHint and eventually miss next pulse.
Second, the whole idea of periodic timer is wrong when vsync is on. timeSetEvent merely has no enough precision to schedule periodic pulse at vsync intervals and it is also started at an arbitrary time relative to vsync. If timer is used at all, with vsync it should restart after present() and not run periodically decoupled from vsync. In this case it is also better not to use timeSetEvent but other Windows functions with nano interval.
Third, when d3d vsync is on and a frame is presented to the swap chain, no external timer is required at all to schedule next pulse. It is much better just to post new pulse immediately after render or current pulse (which vsyncHint already does on continously changed scene).
The following oscilogramm is recorded with live rendering scene (two images changing opacity and one slides over them every frame). It shows exactly how timer calls slip, pulse blocking increase and finally postPulse skipped and frame is not updated:
http://test.novisign.com/downloads/testdata/pulsemiss_oscilogram.mp4
Here is oscilogamm snapshot (time direction is to left):
http://test.novisign.com/downloads/testdata/pulsemiss.jpg
zoom with details:
http://test.novisign.com/downloads/testdata/pulsemiss_zoom.png
Obviously vsyncHint/postPulse never invokes pulse runnable here, hence not part of the problem. Just to be sure here is a run without vsyncHint showing the same problem:
http://test.novisign.com/downloads/testdata/timer_novsync.jpg
Disabling WinTimer completely, results in perfect pulse train from vsyncHint:
http://test.novisign.com/downloads/testdata/vsync_notimer.jpg
Animations and timestamps now are silky smooth!
The same good result is producd when postPulse is just invoked at the pulse end.
However, more work is required apart from just disabling timer. If scene did not change, vsyncHint will not be called at all, and invoking postPulse from pulse end instead of vsyncHint may produce busy loop if scene did not change or when minimized. Video and low fps animation timings also should be taken in account.
What we did now for quick and dirty fix, is just to skip postPulse in timer runnable if last pulse occured in less than vsync interval (actually 17ms threshold works equally well). This can be problematic for some corner cases, but meantime works perfect for us and we'll look into improving it later.
We did not check yet if there is some kind of similar problem on Linux.
----------------------
There are still occasional frame misses like this
http://test.novisign.com/downloads/testdata/vsync_notimer_miss.jpg
but they are not on javafx level and seem not related to the bug above. Here, D3D present() seems to block for two vsync intervals. We are not sure this is because of javafx D3D handling or inherent device/driver blocking (but it happens on wide variety of devices). This is normal behavior when d3d window is not the active window, but this is abnormal when the window is active.
We found that the combination of
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)
::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS)
pD3DContext->Get3DExDevice()->SetGPUThreadPriority(7)
improves the behavior, which is especially prominent on slow dual core devices.
Another common cause for weird sporadic render blocking is NVIDIA dual monitor setup and ANY application window being on second monitor. Applications occupying partially each monitor also frequently force all windowed d3d to 30fps. This seems driver or device bug or limitation. Going full screen restores 60fps on the full screen scene.
------------------------
Note that setting -Djavafx.animation.pulse=1000 almost solves the issue because then timer runs with 1 ms resolution and D3D cares for proper delay on rendering when vsync is on.
With pulse 1000hz ... 300Hz it runs on JDK8 almost smooth, similar to JDK7. Not sure what was done in JDK7 to solve this issue but JDK7 apparently works almost properly (very rare jerks) with vsync on and default pulse.
While overriding pulse frequency provides some workaround, it pumps thousands of useless timer messages through the message queue and each invokes native->java postPulse which does nothing at the end (and they may aggregate until render blocking ends, then come in bulk). This also can lead to crashes.
Also under the hood animation frame timestamp deltas are still weird (more than half of timestamp deltas are greater than 17ms). It can (and does) miss vsync here and there. On weak devices even increasing pulse frequency can not eliminate the issue completely and loads on CPU.
So increasing timer frequency, while improves, is not a viable workaround.
------------------------
If additional info is required please contact fedor.losev@gmail.com
REPRODUCIBILITY :
This bug can be reproduced always.
- duplicates
-
JDK-8170073 Misinterpreted comment on bug
- Closed
-
JDK-8134837 Jerky animations
- Closed
-
JDK-8134843 Choppy video on Windows
- Closed
-
JDK-8170073 Misinterpreted comment on bug
- Closed
- relates to
-
JDK-8134837 Jerky animations
- Closed
-
JDK-8134843 Choppy video on Windows
- Closed
(1 relates to)