Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WebXR] Add support for getting and setting display refresh rate #72938

Merged
merged 1 commit into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions modules/webxr/doc_classes/WebXRInterface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions modules/webxr/godot_webxr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 50 additions & 0 deletions modules/webxr/native/library_godot_webxr.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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');
Expand Down
16 changes: 16 additions & 0 deletions modules/webxr/native/webxr.externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ XRSession.prototype.inputSources;
*/
XRSession.prototype.visibilityState;

/**
* @type {?number}
*/
XRSession.prototype.frameRate;

/**
* @type {?Float32Array}
*/
XRSession.prototype.supportedFrameRates;

/**
* @type {?function (Event)}
*/
Expand Down Expand Up @@ -141,6 +151,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}
*/
Expand Down
4 changes: 4 additions & 0 deletions modules/webxr/webxr_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions modules/webxr/webxr_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class WebXRInterface : public XRInterface {
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);
Expand Down
24 changes: 24 additions & 0 deletions modules/webxr/webxr_interface_js.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
};
Expand Down
4 changes: 4 additions & 0 deletions modules/webxr/webxr_interface_js.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class WebXRInterfaceJS : public WebXRInterface {
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;

Expand Down