Issue
Getting the monitor's refresh rate with AWT/Swing:
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDisplayMode()
.getRefreshRate()
Question: Is there a similar method/procedure in getting the refresh rate with JavaFX? And if there is one, how to do it?
Solution
No equivalent public API in JavaFX
This is the DisplayMode.getRefreshRate()
javadoc for the awt API you are currently using:
Returns the refresh rate of the display, in hertz.
There is no equivalent public JavaFX API.
If the public api existed in JavaFX, it would probably be accessible via the Screen class, but there is no accessor for such functionality defined there.
There is an undocumented, unsupported, private API which would appear to offer similar functionality:
com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate()
JavaFX pulse processing
Related information on how JavaFX works can be found in the pulse documentation for the JavaFX architecture description.
A pulse is an event that indicates to the JavaFX scene graph that it is time to synchronize the state of the elements on the scene graph with Prism. A pulse is throttled at 60 frames per second (fps) maximum and is fired whenever animations are running on the scene graph. Even when animation is not running, a pulse is scheduled when something in the scene graph is changed. For example, if a position of a button is changed, a pulse is scheduled.
When a pulse is fired, the state of the elements on the scene graph is synchronized down to the rendering layer.
The screen hardware refresh rate is different from JavaFX internal pulse processing rate. Though I guess it is technically possible that these could be synchronized in the internal implementation (and that may happen in some cases), there is nothing in the public documentation which mentions whether that actually occurs.
A note on adaptive refresh technologies
Also note, with the advent of technologies such as gsync and free sync the refresh rate can be adapted to the content.
Vertical synchronization is an option in most systems in which the video card is prevented from doing anything visible to the display memory until after the monitor finishes its current refresh cycle . . . technologies like FreeSync and G-Sync reverse the concept and adapts the display's refresh rate to the content coming from the computer. Such technologies require specific support from both the video adapter and the display.
Such technologies make the notion of relying on a hardware refresh rate in the monitor a bit blurry at best.
Answers to FAQs
You state that your intention for querying the display refresh rate is:
Like drawing anything as fast, as long as it's still achievable by the monitor...
JavaFX is, by default intentionally capped at a 60fps frame-rate, regardless of the monitor specifications.
This cap can be overridden using undocumented system properties, as outlined here:
You could use the undocumented feature to remove the frame rate cap in JavaFX and then rely on the underlying video card driver and hardware to render at the maximum rate available within the underlying hardware and software capability for the screen.
Here is the code which deals with that in the openjfx source.
/*
* Called to set the value of PULSE_DURATION or PULSE_DURATION_NS based on
* the refresh rate of the primary screen (unless overridden by the
* FRAMERATE_PROP Setting). If the refresh rate can not be determined the
* default of 60hz is used.
*
* @param precision - precision in (1000 for ms or 1000000000 for ns)
*
* @return pulse duration value, either in ms or ns depending on the
* parameter.
*/
protected int getPulseDuration(int precision) {
int retVal = precision / 60;
// Allow Setting to override monitor refresh
if (Settings.get(FRAMERATE_PROP) != null) {
int overrideHz = Settings.getInt(FRAMERATE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else if (Settings.get(PULSE_PROP) != null) {
int overrideHz = Settings.getInt(PULSE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else {
// If not explicitly set in Settings, try to set based on
// refresh rate of display
int rate = Toolkit.getToolkit().getRefreshRate();
if (rate > 0) {
retVal = precision / rate;
}
// if unknown, use default
}
return retVal;
}
In the source, the framerate is governed by the undocumented property javafx.animation.framerate
, which corresponds to the setting FRAMERATE_PROP
in the above source and javafx.animation.pulse
which corresponds to the setting PULSE_PROP
.
The internal Toolkit class provides a fallback API to use the refresh rate of the display.
So perhaps you could achieve what you want by setting the the system property javafx.animation.framerate
, to the value of com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate()
or by setting both javafx.animation.framerate
and javafx.animation.pulse
to null. I have not tried this, so I do not know. Care would need to be taken to ensure that the values that you set are correctly maintained and not overridden by defaults.
You need to be aware that, if you do this, you are making use of undocumented APIs so this would require opening appropriate module exports so that the APIs are accessible and also may be removed or become incompatible without notice in future versions of the platform (though these settings have been unchanged for many years currently).
So I have been reading the link you provided, and to me, it seems JavaFX is simply stuck at 60fps because it prefers to?
By default, yes, it is a tradeoff.
For the types of applications generally developed using JavaFX 60fps refresh max within the pulse rendering mechanism is generally sufficient regardless of the underlying hardware capabilities of the device. Increasing the rate cap above that level is usually not a good tradeoff for most JavaFX applications as it means use of higher resources (CPU and Video processing capacity and power draw), for gains in refresh updates which are generally undetectable to most users and often not supported by underlying hardware.
That said, if you want to override this default limit, there are undocumented ways of doing so, as mentioned above.
Why not use the same method?
Using the AWT method may be a reasonable alternative in this case.
The hardware refresh won't change depending on the chosen UI toolkit.
Currently (JavaFX 17), the JavaFX system depends on the java.awt
package (javafx.base
module requires the java.desktop
module which contains all of the awt classes). So if JavaFX is available, so is the java.awt.DisplayMode
class.
So if the goal is to determine the hardware refresh rate, you can do that using the existing awt API.
The refresh rate won't change when using Swing or JavaFX right?
Hardware refresh rate will not change depending on the UI toolkit used.
The definition of the scene graph pulse rate in JavaFX for updating rendering of the scene graph is somewhat related to refresh rate, but is a JavaFX only concept because Swing does not make use of a scene graph during its rendering phase.
Answered By - jewelsea