Skip to content

Commit

Permalink
Adding support for the OpenXR futures extension
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Feb 1, 2025
1 parent e549802 commit f0436b1
Show file tree
Hide file tree
Showing 41 changed files with 833 additions and 390 deletions.
2 changes: 2 additions & 0 deletions modules/openxr/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ def get_doc_classes():
"OpenXRActionSet",
"OpenXRActionMap",
"OpenXRAPIExtension",
"OpenXRExtensionWrapper",
"OpenXRExtensionWrapperExtension",
"OpenXRFutureExtension",
"OpenXRInteractionProfile",
"OpenXRInteractionProfileMetadata",
"OpenXRIPBinding",
Expand Down
11 changes: 11 additions & 0 deletions modules/openxr/doc_classes/OpenXRExtensionWrapper.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRExtensionWrapper" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Abstract wrapper class for OpenXR extension classes.
</brief_description>
<description>
This abstract wrapper class is used for all wrapper classes that implement the various OpenXR extensions.
</description>
<tutorials>
</tutorials>
</class>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRExtensionWrapperExtension" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="OpenXRExtensionWrapperExtension" inherits="OpenXRExtensionWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Allows clients to implement OpenXR extensions with GDExtension.
</brief_description>
Expand Down
40 changes: 40 additions & 0 deletions modules/openxr/doc_classes/OpenXRFutureExtension.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRFutureExtension" inherits="OpenXRExtensionWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The OpenXR Future extension allows for asynchronous APIs to be used.
</brief_description>
<description>
This is a support extension in OpenXR that allows other OpenXR extensions to start asynchronous functions and get a callback after this function finishes. It is not intended for consumption within GDScript but can be accessed from GDExtension.
</description>
<tutorials>
</tutorials>
<methods>
<method name="cancel_future">
<return type="void" />
<param index="0" name="future" type="int" />
<description>
Cancel an in-progress future. [param future] must be a [code]XrFutureEXT[/code] value previously returned by an API that started an asynchronous function.
</description>
</method>
<method name="get_active" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if futures are available in the OpenXR runtime used. This function will only return a usable result after OpenXR has been initialized.
</description>
</method>
<method name="get_singleton" qualifiers="static">
<return type="OpenXRFutureExtension" />
<description>
Returns our singleton instance of this class.
</description>
</method>
<method name="register_future">
<return type="void" />
<param index="0" name="future" type="int" />
<param index="1" name="callable" type="Callable" />
<description>
Register the [param callable] to invoke after a future completes processing. [param future] must be a [code]XrFutureEXT[/code] value previously returned by an API that started an asynchronous function.
</description>
</method>
</methods>
</class>
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
#include "openxr_extension_wrapper.h"

class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRCompositionLayerDepthExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRCompositionLayerDepthExtension *get_singleton();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class OpenXRViewportCompositionLayerProvider;

// OpenXRCompositionLayerExtension enables the extensions related to this functionality
class OpenXRCompositionLayerExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRCompositionLayerExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRCompositionLayerExtension *get_singleton();

Expand Down
5 changes: 5 additions & 0 deletions modules/openxr/extensions/openxr_debug_utils_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
#include "openxr_extension_wrapper.h"

class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDebugUtilsExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRDebugUtilsExtension *get_singleton();

Expand Down
5 changes: 5 additions & 0 deletions modules/openxr/extensions/openxr_dpad_binding_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
#include "openxr_extension_wrapper.h"

class OpenXRDPadBindingExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDPadBindingExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRDPadBindingExtension *get_singleton();

Expand Down
9 changes: 8 additions & 1 deletion modules/openxr/extensions/openxr_extension_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
#ifndef OPENXR_EXTENSION_WRAPPER_H
#define OPENXR_EXTENSION_WRAPPER_H

#include "core/core_bind.h"
#include "core/error/error_macros.h"
#include "core/math/projection.h"
#include "core/object/object.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
#include "core/variant/variant.h"
Expand All @@ -43,7 +45,12 @@ class OpenXRAPI;
class OpenXRActionMap;

// `OpenXRExtensionWrapper` allows us to implement OpenXR extensions.
class OpenXRExtensionWrapper {
class OpenXRExtensionWrapper : public Object {
GDCLASS(OpenXRExtensionWrapper, Object);

protected:
static void _bind_methods() {}

public:
// `get_requested_extensions` should return a list of OpenXR extensions related to this extension.
// If the bool * is a nullptr this extension is mandatory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
#include "core/variant/native_ptr.h"
#include "core/variant/typed_array.h"

class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRExtensionWrapperExtension, Object);
class OpenXRExtensionWrapperExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRExtensionWrapperExtension, OpenXRExtensionWrapper);

protected:
_THREAD_SAFE_CLASS_
Expand Down
10 changes: 5 additions & 5 deletions modules/openxr/extensions/openxr_eye_gaze_interaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ bool OpenXREyeGazeInteractionExtension::supports_eye_gaze_interaction() {
}

void OpenXREyeGazeInteractionExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(metadata);
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);

// Eyes top path
metadata->register_top_level_path("Eye gaze tracker", "/user/eyes_ext", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Eye gaze tracker", "/user/eyes_ext", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);

// Eye gaze interaction
metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
}

bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) {
Expand Down
5 changes: 5 additions & 0 deletions modules/openxr/extensions/openxr_eye_gaze_interaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
#include "openxr_extension_wrapper.h"

class OpenXREyeGazeInteractionExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXREyeGazeInteractionExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXREyeGazeInteractionExtension *get_singleton();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
#include "openxr_extension_wrapper.h"

class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDisplayRefreshRateExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRDisplayRefreshRateExtension *get_singleton();

Expand Down
5 changes: 5 additions & 0 deletions modules/openxr/extensions/openxr_fb_foveation_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
#include "openxr_fb_update_swapchain_extension.h"

class OpenXRFBFoveationExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBFoveationExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

public:
static OpenXRFBFoveationExtension *get_singleton();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
#include "openxr_extension_wrapper.h"

class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBUpdateSwapchainExtension, OpenXRExtensionWrapper);

protected:
static void _bind_methods() {}

friend class OpenXRFBFoveationExtension;

public:
Expand Down
170 changes: 170 additions & 0 deletions modules/openxr/extensions/openxr_future_extension.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**************************************************************************/
/* openxr_future_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "openxr_future_extension.h"

#include "../openxr_api.h"

OpenXRFutureExtension *OpenXRFutureExtension::singleton = nullptr;

OpenXRFutureExtension *OpenXRFutureExtension::get_singleton() {
return singleton;
}

OpenXRFutureExtension::OpenXRFutureExtension() {
singleton = this;
}

OpenXRFutureExtension::~OpenXRFutureExtension() {
singleton = nullptr;
}

void OpenXRFutureExtension::_bind_methods() {
ClassDB::bind_static_method("OpenXRFutureExtension", D_METHOD("get_singleton"), &OpenXRFutureExtension::get_singleton);
ClassDB::bind_method(D_METHOD("get_active"), &OpenXRFutureExtension::get_active);
ClassDB::bind_method(D_METHOD("register_future", "future", "callable"), &OpenXRFutureExtension::register_future);
ClassDB::bind_method(D_METHOD("cancel_future", "future"), &OpenXRFutureExtension::cancel_future);
}

HashMap<String, bool *> OpenXRFutureExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;

request_extensions[XR_EXT_FUTURE_EXTENSION_NAME] = &future_ext;

return request_extensions;
}

void OpenXRFutureExtension::on_instance_created(const XrInstance p_instance) {
if (future_ext) {
EXT_INIT_XR_FUNC(xrPollFutureEXT);
EXT_INIT_XR_FUNC(xrCancelFutureEXT);

future_ext = xrPollFutureEXT_ptr && xrCancelFutureEXT_ptr;
}
}

void OpenXRFutureExtension::on_instance_destroyed() {
xrPollFutureEXT_ptr = nullptr;
xrCancelFutureEXT_ptr = nullptr;
}

void OpenXRFutureExtension::on_session_destroyed() {
if (!get_active()) {
return;
}

OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);

// Cancel any running futures.
for (const KeyValue<uint64_t, Callable> &element : futures) {
XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
XrFutureEXT(element.key) // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}
}
futures.clear();
}

bool OpenXRFutureExtension::get_active() const {
return future_ext;
}

void OpenXRFutureExtension::register_future(uint64_t p_future, Callable p_callable) {
ERR_FAIL_COND(futures.has(p_future));

futures[p_future] = p_callable;
}

void OpenXRFutureExtension::cancel_future(uint64_t p_future) {
ERR_FAIL_COND(!futures.has(p_future));

OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);

XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
XrFutureEXT(p_future) // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}

futures.erase(p_future);
}

void OpenXRFutureExtension::on_process() {
if (!get_active()) {
return;
}

OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);

// Process futures
Vector<uint64_t> completed;
for (const KeyValue<uint64_t, Callable> &element : futures) {
XrFuturePollInfoEXT poll_info = {
XR_TYPE_FUTURE_POLL_INFO_EXT, // type
nullptr, // next
XrFutureEXT(element.key) // future
};
XrFuturePollResultEXT poll_result = {
XR_TYPE_FUTURE_POLL_RESULT_EXT, // type
nullptr, // next
XR_FUTURE_STATE_MAX_ENUM_EXT // state
};
XrResult result = xrPollFutureEXT_ptr(openxr_api->get_instance(), &poll_info, &poll_result);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to obtain future status [" + openxr_api->get_error_string(result) + "]");
// Maybe remove this depending on the error?
continue;
}
if (poll_result.state == XR_FUTURE_STATE_READY_EXT) {
// Call our callable.
element.value.call(element.key);

// Queue removing this
completed.push_back(element.key);
}
}

// Now clean up completed futures.
for (const uint64_t &future : completed) {
futures.erase(future);
}
}
Loading

0 comments on commit f0436b1

Please sign in to comment.