diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 22c25dd5e57e..0bf9f8860c51 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2924,6 +2924,9 @@
Specify how OpenXR should blend in the environment. This is specific to certain AR and passthrough devices where camera images are blended in by the XR compositor.
+
+ Enables debug utilities on XR runtimes that supports the debug utils extension. Sets the maximum severity being reported (0 = disabled, 1 = error, 2 = warning, 3 = info, 4 = verbose).
+
Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed.
diff --git a/main/main.cpp b/main/main.cpp
index e42469b51b06..a920b3d9ec78 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2437,6 +2437,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
// OpenXR project extensions settings.
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_utils", PROPERTY_HINT_ENUM, "Disabled,Error,Warning,Info,Verbose"), "0");
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", true);
GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false);
diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
index 4419d24dd306..6d1fb8db58bc 100644
--- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
@@ -127,6 +127,15 @@
If set to [code]true[/code], an OpenXR extension is loaded which is capable of emulating the [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] blend mode.
+
+
+
+
+
+
+ Set the object name of an OpenXR object, used for debug output. [param object_type] must be a valid OpenXR [code]XrObjectType[/code] enum and [param object_handle] must be a valid OpenXR object handle.
+
+
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.cpp b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
new file mode 100644
index 000000000000..cbfe4f2c7a4d
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
@@ -0,0 +1,209 @@
+/**************************************************************************/
+/* openxr_debug_utils_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_debug_utils_extension.h"
+
+#include "../openxr_api.h"
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
+
+#include
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::singleton = nullptr;
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRDebugUtilsExtension::OpenXRDebugUtilsExtension() {
+ singleton = this;
+}
+
+OpenXRDebugUtilsExtension::~OpenXRDebugUtilsExtension() {
+ singleton = nullptr;
+}
+
+HashMap OpenXRDebugUtilsExtension::get_requested_extensions() {
+ HashMap request_extensions;
+
+ request_extensions[XR_EXT_DEBUG_UTILS_EXTENSION_NAME] = &debug_utils_ext;
+
+ return request_extensions;
+}
+
+void OpenXRDebugUtilsExtension::on_instance_created(const XrInstance p_instance) {
+ if (debug_utils_ext) {
+ EXT_INIT_XR_FUNC(xrCreateDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrDestroyDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrSetDebugUtilsObjectNameEXT);
+
+ debug_utils_ext = xrCreateDebugUtilsMessengerEXT_ptr && xrDestroyDebugUtilsMessengerEXT_ptr && xrSetDebugUtilsObjectNameEXT_ptr;
+ }
+
+ // On successful init, setup our default messenger.
+ if (debug_utils_ext) {
+ int max_severity = GLOBAL_GET("xr/openxr/extensions/debug_utils");
+ XrDebugUtilsMessageSeverityFlagsEXT message_severities = 0;
+
+ if (max_severity >= 1) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ }
+ if (max_severity >= 2) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ }
+ if (max_severity >= 3) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
+ }
+ if (max_severity >= 4) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
+ }
+
+ XrDebugUtilsMessengerCreateInfoEXT callback_info = {
+ XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // type
+ nullptr, // next
+ message_severities, // messageSeverities
+ XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT, // messageTypes
+ &OpenXRDebugUtilsExtension::_debug_callback, // userCallback
+ nullptr, // userData
+ };
+
+ XrResult result = xrCreateDebugUtilsMessengerEXT(p_instance, &callback_info, &default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to create debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+ }
+}
+
+void OpenXRDebugUtilsExtension::on_instance_destroyed() {
+ if (default_messenger != XR_NULL_HANDLE) {
+ XrResult result = xrDestroyDebugUtilsMessengerEXT(default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to destroy debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+
+ default_messenger = XR_NULL_HANDLE;
+ }
+
+ xrCreateDebugUtilsMessengerEXT_ptr = nullptr;
+ xrDestroyDebugUtilsMessengerEXT_ptr = nullptr;
+ xrSetDebugUtilsObjectNameEXT_ptr = nullptr;
+ debug_utils_ext = false;
+}
+
+bool OpenXRDebugUtilsExtension::get_active() {
+ return debug_utils_ext;
+}
+
+void OpenXRDebugUtilsExtension::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSetDebugUtilsObjectNameEXT_ptr);
+
+ const XrDebugUtilsObjectNameInfoEXT space_name_info = {
+ XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, // type
+ nullptr, // next
+ p_object_type, // objectType
+ p_object_handle, // objectHandle
+ p_object_name, // objectName
+ };
+
+ XrResult result = xrSetDebugUtilsObjectNameEXT_ptr(OpenXRAPI::get_singleton()->get_instance(), &space_name_info);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to set object name [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+XrBool32 OpenXRDebugUtilsExtension::_debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+
+ if (debug_utils) {
+ return debug_utils->debug_callback(p_message_severity, p_message_types, p_callback_data, p_user_data);
+ }
+
+ return XR_FALSE;
+}
+
+XrBool32 OpenXRDebugUtilsExtension::debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ String msg;
+
+ ERR_FAIL_NULL_V(p_callback_data, XR_FALSE);
+
+ if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
+ msg = ", type: General";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
+ msg = ", type: Validation";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
+ msg = ", type: Performance";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT) {
+ msg = ", type: Conformance";
+ } else {
+ msg = ", type: Unknown (" + String::num_uint64(p_message_types) + ")";
+ }
+
+ if (p_callback_data->functionName) {
+ msg += ", function Name: " + String(p_callback_data->functionName);
+ }
+ if (p_callback_data->messageId) {
+ msg += "\nMessage ID: " + String(p_callback_data->messageId);
+ }
+ if (p_callback_data->message) {
+ msg += "\nMessage: " + String(p_callback_data->message);
+ }
+
+ if (p_callback_data->objectCount > 0) {
+ String objects;
+
+ for (uint32_t i = 0; i < p_callback_data->objectCount; i++) {
+ if (!objects.is_empty()) {
+ objects += ", ";
+ }
+ objects += p_callback_data->objects[i].objectName;
+ }
+
+ msg += "\nObjects: " + objects;
+ }
+ /*
+ TODO: add in:
+ uint32_t sessionLabelCount;
+ XrDebugUtilsLabelEXT* sessionLabels;
+ */
+
+ if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
+ ERR_PRINT("OpenXR: Severity: Error" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
+ WARN_PRINT("OpenXR: Severity: Warning" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
+ print_line("OpenXR: Severity: Info" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
+ // This is a bit double because we won't output this unless verbose messaging in Godot is on.
+ print_verbose("OpenXR: Severity: Verbose" + msg);
+ }
+
+ return XR_FALSE;
+}
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.h b/modules/openxr/extensions/openxr_debug_utils_extension.h
new file mode 100644
index 000000000000..42e5ec07fb75
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* openxr_debug_utils_extension.h */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+#ifndef OPENXR_DEBUG_UTILS_EXTENSION_H
+#define OPENXR_DEBUG_UTILS_EXTENSION_H
+
+#include "../util.h"
+#include "openxr_extension_wrapper.h"
+
+class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRDebugUtilsExtension *get_singleton();
+
+ OpenXRDebugUtilsExtension();
+ virtual ~OpenXRDebugUtilsExtension() override;
+
+ virtual HashMap get_requested_extensions() override;
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+
+ bool get_active();
+
+ void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name);
+
+private:
+ static OpenXRDebugUtilsExtension *singleton;
+
+ // related extensions
+ bool debug_utils_ext = false;
+
+ // debug handlers
+ XrDebugUtilsMessengerEXT default_messenger = XR_NULL_HANDLE;
+
+ static XrBool32 _debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+ XrBool32 debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateDebugUtilsMessengerEXT, (XrInstance), p_instance, (const XrDebugUtilsMessengerCreateInfoEXT *), p_create_info, (XrDebugUtilsMessengerEXT *), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyDebugUtilsMessengerEXT, (XrDebugUtilsMessengerEXT), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC2(xrSetDebugUtilsObjectNameEXT, (XrInstance), p_instance, (const XrDebugUtilsObjectNameInfoEXT *), p_name_info)
+};
+
+#endif // OPENXR_DEBUG_UTILS_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 98e548415788..f8edcc4f0716 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -54,6 +54,7 @@
#endif
#include "extensions/openxr_composition_layer_depth_extension.h"
+#include "extensions/openxr_debug_utils_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
@@ -316,6 +317,19 @@ String OpenXRAPI::get_swapchain_format_name(int64_t p_swapchain_format) const {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
}
+void OpenXRAPI::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+ if (!debug_utils) {
+ // Not enabled? Ignore.
+ return;
+ } else if (!debug_utils->get_active()) {
+ // Not active? Ignore.
+ return;
+ }
+
+ debug_utils->set_object_name(p_object_type, p_object_handle, p_object_name.utf8().get_data());
+}
+
bool OpenXRAPI::load_layer_properties() {
// This queries additional layers that are available and can be initialized when we create our OpenXR instance
if (layer_properties != nullptr) {
@@ -580,6 +594,9 @@ bool OpenXRAPI::create_instance() {
wrapper->on_instance_created(instance);
}
+ // We can't register this till now, only now our debug util is properly setup.
+ set_object_name(XR_OBJECT_TYPE_INSTANCE, uint64_t(instance), "Main Godot OpenXR Instance");
+
return true;
}
@@ -826,6 +843,8 @@ bool OpenXRAPI::create_session() {
return false;
}
+ set_object_name(XR_OBJECT_TYPE_SESSION, uint64_t(session), "Main Godot OpenXR Session");
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_session_created(session);
}
@@ -916,6 +935,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.local_space), "Emulation local space");
}
if (local_floor_emulation.stage_space == XR_NULL_HANDLE) {
@@ -931,6 +952,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.stage_space), "Emulation stage space");
}
if (!will_emulate_local_floor) {
@@ -972,6 +995,8 @@ bool OpenXRAPI::setup_play_space() {
play_space = new_play_space;
reference_space = new_reference_space;
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(play_space), "Play space");
+
local_floor_emulation.enabled = will_emulate_local_floor;
local_floor_emulation.should_reset_floor_height = will_emulate_local_floor;
@@ -1007,6 +1032,8 @@ bool OpenXRAPI::setup_view_space() {
return false;
}
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(view_space), "View space");
+
return true;
}
@@ -1181,6 +1208,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main color swapchain");
}
// We create our depth swapchain if:
@@ -1191,6 +1220,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main depth swapchain");
}
// We create our velocity swapchain if:
@@ -2823,6 +2854,8 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
return RID();
}
+ set_object_name(XR_OBJECT_TYPE_ACTION_SET, uint64_t(action_set.handle), p_name);
+
return action_set_owner.make_rid(action_set);
}
@@ -2998,6 +3031,8 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
return RID();
}
+ set_object_name(XR_OBJECT_TYPE_ACTION, uint64_t(action.handle), p_name);
+
return action_owner.make_rid(action);
}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 81e9deb7ab73..f9bb1348ef4e 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -422,6 +422,7 @@ class OpenXRAPI {
XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result) const;
String get_swapchain_format_name(int64_t p_swapchain_format) const;
+ void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name);
OpenXRInterface *get_xr_interface() const { return xr_interface; }
void set_xr_interface(OpenXRInterface *p_xr_interface);
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
index a1744fa1dbc2..9acb894ebb00 100644
--- a/modules/openxr/openxr_api_extension.cpp
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -43,6 +43,7 @@ void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_proc_addr", "name"), &OpenXRAPIExtension::get_instance_proc_addr);
ClassDB::bind_method(D_METHOD("get_error_string", "result"), &OpenXRAPIExtension::get_error_string);
ClassDB::bind_method(D_METHOD("get_swapchain_format_name", "swapchain_format"), &OpenXRAPIExtension::get_swapchain_format_name);
+ ClassDB::bind_method(D_METHOD("set_object_name", "object_type", "object_handle", "object_name"), &OpenXRAPIExtension::set_object_name);
ClassDB::bind_method(D_METHOD("is_initialized"), &OpenXRAPIExtension::is_initialized);
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
@@ -116,6 +117,12 @@ String OpenXRAPIExtension::get_swapchain_format_name(int64_t p_swapchain_format)
return OpenXRAPI::get_singleton()->get_swapchain_format_name(p_swapchain_format);
}
+void OpenXRAPIExtension::set_object_name(int64_t p_object_type, uint64_t p_object_handle, const String &p_object_name) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ OpenXRAPI::get_singleton()->set_object_name(XrObjectType(p_object_type), p_object_handle, p_object_name);
+}
+
bool OpenXRAPIExtension::is_initialized() {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
return OpenXRAPI::get_singleton()->is_initialized();
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
index cff2c4738e04..b751b3e19612 100644
--- a/modules/openxr/openxr_api_extension.h
+++ b/modules/openxr/openxr_api_extension.h
@@ -64,6 +64,7 @@ class OpenXRAPIExtension : public RefCounted {
uint64_t get_instance_proc_addr(String p_name);
String get_error_string(uint64_t result);
String get_swapchain_format_name(int64_t p_swapchain_format);
+ void set_object_name(int64_t p_object_type, uint64_t p_object_handle, const String &p_object_name);
bool is_initialized();
bool is_running();
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 85514737f269..f6a850c92228 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -47,6 +47,7 @@
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_composition_layer_extension.h"
+#include "extensions/openxr_debug_utils_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_hand_interaction_extension.h"
@@ -128,6 +129,9 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandInteractionExtension));
// register gated extensions
+ if (int(GLOBAL_GET("xr/openxr/extensions/debug_utils")) > 0) {
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRDebugUtilsExtension));
+ }
if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandTrackingExtension));
}