diff options
author | David Snopek <dsnopek@gmail.com> | 2023-02-08 21:02:13 -0600 |
---|---|---|
committer | David Snopek <dsnopek@gmail.com> | 2023-02-10 19:21:38 -0600 |
commit | 886f2270edd4c96ea357caa2a5da5a785a1ae415 (patch) | |
tree | e819c60ec42291cf4249428212571873d72f41af /modules/webxr | |
parent | 929333fe267f488638c76564237faff9d5d572fc (diff) | |
download | redot-engine-886f2270edd4c96ea357caa2a5da5a785a1ae415.tar.gz |
[WebXR] Add support for getting and setting display refresh rate
Diffstat (limited to 'modules/webxr')
-rw-r--r-- | modules/webxr/doc_classes/WebXRInterface.xml | 24 | ||||
-rw-r--r-- | modules/webxr/godot_webxr.h | 4 | ||||
-rw-r--r-- | modules/webxr/native/library_godot_webxr.js | 50 | ||||
-rw-r--r-- | modules/webxr/native/webxr.externs.js | 16 | ||||
-rw-r--r-- | modules/webxr/webxr_interface.cpp | 4 | ||||
-rw-r--r-- | modules/webxr/webxr_interface.h | 3 | ||||
-rw-r--r-- | modules/webxr/webxr_interface_js.cpp | 24 | ||||
-rw-r--r-- | modules/webxr/webxr_interface_js.h | 4 |
8 files changed, 129 insertions, 0 deletions
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index 0c18acbcb1..d7a2596abe 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -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> </tutorials> <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"> <return type="int" enum="WebXRInterface.TargetRayMode" /> <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. </description> </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> <members> <member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features"> @@ -167,6 +186,11 @@ </member> </members> <signals> + <signal name="display_refresh_rate_changed"> + <description> + Emitted after the display's refresh rate has changed. + </description> + </signal> <signal name="reference_space_reset"> <description> Emitted to indicate that the reference space has been reset or reconfigured. diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h index c636be1576..554ca0398b 100644 --- a/modules/webxr/godot_webxr.h +++ b/modules/webxr/godot_webxr.h @@ -90,6 +90,10 @@ extern bool godot_webxr_update_input_source( extern char *godot_webxr_get_visibility_state(); 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 } #endif diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 1c00ebebb4..7d797e11d9 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -42,6 +42,7 @@ const GodotWebXR = { view_count: 1, input_sources: new Array(16), touches: new Array(5), + onsimpleevent: null, // Monkey-patch the requestAnimationFrame() used by Emscripten for the main // loop, so that we can swap it out for XRSession.requestAnimationFrame() @@ -283,6 +284,9 @@ const GodotWebXR = { 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 = GL.getContext(gl_context_handle).GLctx; GodotWebXR.gl = gl; @@ -368,6 +372,7 @@ const GodotWebXR = { GodotWebXR.view_count = 1; GodotWebXR.input_sources = new Array(16); GodotWebXR.touches = new Array(5); + GodotWebXR.onsimpleevent = null; // Disable the monkey-patched window.requestAnimationFrame() and // pause/restart the main loop to activate it on all platforms. @@ -595,6 +600,51 @@ const GodotWebXR = { 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'); diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js index 4b88820b19..7f7c297acc 100644 --- a/modules/webxr/native/webxr.externs.js +++ b/modules/webxr/native/webxr.externs.js @@ -68,6 +68,16 @@ XRSession.prototype.inputSources; XRSession.prototype.visibilityState; /** + * @type {?number} + */ +XRSession.prototype.frameRate; + +/** + * @type {?Float32Array} + */ +XRSession.prototype.supportedFrameRates; + +/** * @type {?function (Event)} */ XRSession.prototype.onend; @@ -142,6 +152,12 @@ XRSession.prototype.end = function () {}; XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {}; /** + * @param {number} rate + * @return {Promise<undefined>} + */ +XRSession.prototype.updateTargetFrameRate = function (rate) {}; + +/** * @typedef {function(number, XRFrame): undefined} */ var XRFrameRequestCallback; diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp index d7f4247768..5e30d0c996 100644 --- a/modules/webxr/webxr_interface.cpp +++ b/modules/webxr/webxr_interface.cpp @@ -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_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_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, "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("reference_space_reset")); + ADD_SIGNAL(MethodInfo("display_refresh_rate_changed")); BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN); BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE); diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h index f18b4b40e6..abaa8c01f8 100644 --- a/modules/webxr/webxr_interface.h +++ b/modules/webxr/webxr_interface.h @@ -66,6 +66,9 @@ public: 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 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); diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 97b2eea4d7..9c8b08cf04 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -202,6 +202,30 @@ PackedVector3Array WebXRInterfaceJS::get_play_area() const { 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 { return "WebXR"; }; diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index 31d2e200c0..b9582eef89 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -102,6 +102,10 @@ public: virtual String get_visibility_state() 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 uint32_t get_capabilities() const override; |