Merge pull request #72938 from dsnopek/webxr-frame-rate
[WebXR] Add support for getting and setting display refresh rate
This commit is contained in:
commit
ab7cb2a95d
|
@ -93,6 +93,18 @@
|
||||||
<link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link>
|
<link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
<methods>
|
<methods>
|
||||||
|
<method name="get_available_display_refresh_rates" qualifiers="const">
|
||||||
|
<return type="Array" />
|
||||||
|
<description>
|
||||||
|
Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the web browser and after the interface has been initialized.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_display_refresh_rate" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<description>
|
||||||
|
Returns the display refresh rate for the current HMD. Not supported on all HMDs and browsers. It may not report an accurate value until after using [method set_display_refresh_rate].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_input_source_target_ray_mode" qualifiers="const">
|
<method name="get_input_source_target_ray_mode" qualifiers="const">
|
||||||
<return type="int" enum="WebXRInterface.TargetRayMode" />
|
<return type="int" enum="WebXRInterface.TargetRayMode" />
|
||||||
<param index="0" name="input_source_id" type="int" />
|
<param index="0" name="input_source_id" type="int" />
|
||||||
|
@ -132,6 +144,13 @@
|
||||||
This method returns nothing, instead it emits the [signal session_supported] signal with the result.
|
This method returns nothing, instead it emits the [signal session_supported] signal with the result.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="set_display_refresh_rate">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="refresh_rate" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the display refresh rate for the current HMD. Not supported on all HMDs and browsers. It won't take effect right away until after [signal display_refresh_rate_changed] is emitted.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
|
<member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
|
||||||
|
@ -167,6 +186,11 @@
|
||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
<signals>
|
<signals>
|
||||||
|
<signal name="display_refresh_rate_changed">
|
||||||
|
<description>
|
||||||
|
Emitted after the display's refresh rate has changed.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
<signal name="reference_space_reset">
|
<signal name="reference_space_reset">
|
||||||
<description>
|
<description>
|
||||||
Emitted to indicate that the reference space has been reset or reconfigured.
|
Emitted to indicate that the reference space has been reset or reconfigured.
|
||||||
|
|
|
@ -90,6 +90,10 @@ extern bool godot_webxr_update_input_source(
|
||||||
extern char *godot_webxr_get_visibility_state();
|
extern char *godot_webxr_get_visibility_state();
|
||||||
extern int godot_webxr_get_bounds_geometry(float **r_points);
|
extern int godot_webxr_get_bounds_geometry(float **r_points);
|
||||||
|
|
||||||
|
extern float godot_webxr_get_frame_rate();
|
||||||
|
extern void godot_webxr_update_target_frame_rate(float p_frame_rate);
|
||||||
|
extern int godot_webxr_get_supported_frame_rates(float **r_frame_rates);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,6 +42,7 @@ const GodotWebXR = {
|
||||||
view_count: 1,
|
view_count: 1,
|
||||||
input_sources: new Array(16),
|
input_sources: new Array(16),
|
||||||
touches: new Array(5),
|
touches: new Array(5),
|
||||||
|
onsimpleevent: null,
|
||||||
|
|
||||||
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
|
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
|
||||||
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
|
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
|
||||||
|
@ -283,6 +284,9 @@ const GodotWebXR = {
|
||||||
GodotRuntime.free(c_str);
|
GodotRuntime.free(c_str);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Store onsimpleevent so we can use it later.
|
||||||
|
GodotWebXR.onsimpleevent = onsimpleevent;
|
||||||
|
|
||||||
const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
|
const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
|
||||||
const gl = GL.getContext(gl_context_handle).GLctx;
|
const gl = GL.getContext(gl_context_handle).GLctx;
|
||||||
GodotWebXR.gl = gl;
|
GodotWebXR.gl = gl;
|
||||||
|
@ -368,6 +372,7 @@ const GodotWebXR = {
|
||||||
GodotWebXR.view_count = 1;
|
GodotWebXR.view_count = 1;
|
||||||
GodotWebXR.input_sources = new Array(16);
|
GodotWebXR.input_sources = new Array(16);
|
||||||
GodotWebXR.touches = new Array(5);
|
GodotWebXR.touches = new Array(5);
|
||||||
|
GodotWebXR.onsimpleevent = null;
|
||||||
|
|
||||||
// Disable the monkey-patched window.requestAnimationFrame() and
|
// Disable the monkey-patched window.requestAnimationFrame() and
|
||||||
// pause/restart the main loop to activate it on all platforms.
|
// pause/restart the main loop to activate it on all platforms.
|
||||||
|
@ -594,6 +599,51 @@ const GodotWebXR = {
|
||||||
|
|
||||||
return point_count;
|
return point_count;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
godot_webxr_get_frame_rate__proxy: 'sync',
|
||||||
|
godot_webxr_get_frame_rate__sig: 'i',
|
||||||
|
godot_webxr_get_frame_rate: function () {
|
||||||
|
if (!GodotWebXR.session || GodotWebXR.session.frameRate === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return GodotWebXR.session.frameRate;
|
||||||
|
},
|
||||||
|
|
||||||
|
godot_webxr_update_target_frame_rate__proxy: 'sync',
|
||||||
|
godot_webxr_update_target_frame_rate__sig: 'vi',
|
||||||
|
godot_webxr_update_target_frame_rate: function (p_frame_rate) {
|
||||||
|
if (!GodotWebXR.session || GodotWebXR.session.updateTargetFrameRate === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GodotWebXR.session.updateTargetFrameRate(p_frame_rate).then(() => {
|
||||||
|
const c_str = GodotRuntime.allocString('display_refresh_rate_changed');
|
||||||
|
GodotWebXR.onsimpleevent(c_str);
|
||||||
|
GodotRuntime.free(c_str);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
godot_webxr_get_supported_frame_rates__proxy: 'sync',
|
||||||
|
godot_webxr_get_supported_frame_rates__sig: 'ii',
|
||||||
|
godot_webxr_get_supported_frame_rates: function (r_frame_rates) {
|
||||||
|
if (!GodotWebXR.session || GodotWebXR.session.supportedFrameRates === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame_rate_count = GodotWebXR.session.supportedFrameRates.length;
|
||||||
|
if (frame_rate_count === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = GodotRuntime.malloc(frame_rate_count * 4);
|
||||||
|
for (let i = 0; i < frame_rate_count; i++) {
|
||||||
|
GodotRuntime.setHeapValue(buf + (i * 4), GodotWebXR.session.supportedFrameRates[i], 'float');
|
||||||
|
}
|
||||||
|
GodotRuntime.setHeapValue(r_frame_rates, buf, 'i32');
|
||||||
|
|
||||||
|
return frame_rate_count;
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
autoAddDeps(GodotWebXR, '$GodotWebXR');
|
autoAddDeps(GodotWebXR, '$GodotWebXR');
|
||||||
|
|
|
@ -67,6 +67,16 @@ XRSession.prototype.inputSources;
|
||||||
*/
|
*/
|
||||||
XRSession.prototype.visibilityState;
|
XRSession.prototype.visibilityState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
XRSession.prototype.frameRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {?Float32Array}
|
||||||
|
*/
|
||||||
|
XRSession.prototype.supportedFrameRates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {?function (Event)}
|
* @type {?function (Event)}
|
||||||
*/
|
*/
|
||||||
|
@ -141,6 +151,12 @@ XRSession.prototype.end = function () {};
|
||||||
*/
|
*/
|
||||||
XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {};
|
XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} rate
|
||||||
|
* @return {Promise<undefined>}
|
||||||
|
*/
|
||||||
|
XRSession.prototype.updateTargetFrameRate = function (rate) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {function(number, XRFrame): undefined}
|
* @typedef {function(number, XRFrame): undefined}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -46,6 +46,9 @@ void WebXRInterface::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::get_input_source_tracker);
|
ClassDB::bind_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::get_input_source_tracker);
|
||||||
ClassDB::bind_method(D_METHOD("get_input_source_target_ray_mode", "input_source_id"), &WebXRInterface::get_input_source_target_ray_mode);
|
ClassDB::bind_method(D_METHOD("get_input_source_target_ray_mode", "input_source_id"), &WebXRInterface::get_input_source_target_ray_mode);
|
||||||
ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
|
ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &WebXRInterface::get_display_refresh_rate);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &WebXRInterface::set_display_refresh_rate);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &WebXRInterface::get_available_display_refresh_rates);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
|
||||||
|
@ -68,6 +71,7 @@ void WebXRInterface::_bind_methods() {
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("visibility_state_changed"));
|
ADD_SIGNAL(MethodInfo("visibility_state_changed"));
|
||||||
ADD_SIGNAL(MethodInfo("reference_space_reset"));
|
ADD_SIGNAL(MethodInfo("reference_space_reset"));
|
||||||
|
ADD_SIGNAL(MethodInfo("display_refresh_rate_changed"));
|
||||||
|
|
||||||
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN);
|
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN);
|
||||||
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE);
|
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE);
|
||||||
|
|
|
@ -66,6 +66,9 @@ public:
|
||||||
virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
|
virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
|
||||||
virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
|
virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
|
||||||
virtual String get_visibility_state() const = 0;
|
virtual String get_visibility_state() const = 0;
|
||||||
|
virtual float get_display_refresh_rate() const = 0;
|
||||||
|
virtual void set_display_refresh_rate(float p_refresh_rate) = 0;
|
||||||
|
virtual Array get_available_display_refresh_rates() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
VARIANT_ENUM_CAST(WebXRInterface::TargetRayMode);
|
VARIANT_ENUM_CAST(WebXRInterface::TargetRayMode);
|
||||||
|
|
|
@ -202,6 +202,30 @@ PackedVector3Array WebXRInterfaceJS::get_play_area() const {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float WebXRInterfaceJS::get_display_refresh_rate() const {
|
||||||
|
return godot_webxr_get_frame_rate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebXRInterfaceJS::set_display_refresh_rate(float p_refresh_rate) {
|
||||||
|
godot_webxr_update_target_frame_rate(p_refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array WebXRInterfaceJS::get_available_display_refresh_rates() const {
|
||||||
|
Array ret;
|
||||||
|
|
||||||
|
float *rates;
|
||||||
|
int rate_count = godot_webxr_get_supported_frame_rates(&rates);
|
||||||
|
if (rate_count > 0) {
|
||||||
|
ret.resize(rate_count);
|
||||||
|
for (int i = 0; i < rate_count; i++) {
|
||||||
|
ret[i] = rates[i];
|
||||||
|
}
|
||||||
|
free(rates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
StringName WebXRInterfaceJS::get_name() const {
|
StringName WebXRInterfaceJS::get_name() const {
|
||||||
return "WebXR";
|
return "WebXR";
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,6 +102,10 @@ public:
|
||||||
virtual String get_visibility_state() const override;
|
virtual String get_visibility_state() const override;
|
||||||
virtual PackedVector3Array get_play_area() const override;
|
virtual PackedVector3Array get_play_area() const override;
|
||||||
|
|
||||||
|
virtual float get_display_refresh_rate() const override;
|
||||||
|
virtual void set_display_refresh_rate(float p_refresh_rate) override;
|
||||||
|
virtual Array get_available_display_refresh_rates() const override;
|
||||||
|
|
||||||
virtual StringName get_name() const override;
|
virtual StringName get_name() const override;
|
||||||
virtual uint32_t get_capabilities() const override;
|
virtual uint32_t get_capabilities() const override;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue