Add support for the debug utils extension in OpenXR

This commit is contained in:
Bastiaan Olij 2024-08-05 17:25:28 +10:00
parent db76de5de8
commit 08ffa5d89e
13 changed files with 514 additions and 3 deletions

View File

@ -2948,6 +2948,12 @@
<member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default="&quot;0&quot;"> <member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default="&quot;0&quot;">
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. 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.
</member> </member>
<member name="xr/openxr/extensions/debug_message_types" type="int" setter="" getter="" default="&quot;15&quot;">
Specifies the message types for which we request debug messages. Requires [member xr/openxr/extensions/debug_utils] to be set and the extension to be supported by the XR runtime.
</member>
<member name="xr/openxr/extensions/debug_utils" type="int" setter="" getter="" default="&quot;0&quot;">
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).
</member>
<member name="xr/openxr/extensions/eye_gaze_interaction" type="bool" setter="" getter="" default="false"> <member name="xr/openxr/extensions/eye_gaze_interaction" type="bool" setter="" getter="" default="false">
Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed. Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed.
</member> </member>

View File

@ -2555,6 +2555,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true); GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
// OpenXR project extensions settings. // 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(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_message_types", PROPERTY_HINT_FLAGS, "General,Validation,Performance,Conformance"), "15");
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false); GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT

View File

@ -17,12 +17,25 @@
<link title="XrPosef documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html</link> <link title="XrPosef documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html</link>
</tutorials> </tutorials>
<methods> <methods>
<method name="begin_debug_label_region">
<return type="void" />
<param index="0" name="label_name" type="String" />
<description>
Begins a new debug label region, this label will be reported in debug messages for any calls following this until [method end_debug_label_region] is called. Debug labels can be stacked.
</description>
</method>
<method name="can_render"> <method name="can_render">
<return type="bool" /> <return type="bool" />
<description> <description>
Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport. Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport.
</description> </description>
</method> </method>
<method name="end_debug_label_region">
<return type="void" />
<description>
Marks the end of a debug label region. Removes the latest debug label region added by calling [method begin_debug_label_region].
</description>
</method>
<method name="get_error_string"> <method name="get_error_string">
<return type="String" /> <return type="String" />
<param index="0" name="result" type="int" /> <param index="0" name="result" type="int" />
@ -88,6 +101,13 @@
Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer. Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer.
</description> </description>
</method> </method>
<method name="insert_debug_label">
<return type="void" />
<param index="0" name="label_name" type="String" />
<description>
Inserts a debug label, this label is reported in any debug message resulting from the OpenXR calls that follows, until any of [method begin_debug_label_region], [method end_debug_label_region], or [method insert_debug_label] is called.
</description>
</method>
<method name="is_environment_blend_mode_alpha_supported"> <method name="is_environment_blend_mode_alpha_supported">
<return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" /> <return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" />
<description> <description>
@ -127,6 +147,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. 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.
</description> </description>
</method> </method>
<method name="set_object_name">
<return type="void" />
<param index="0" name="object_type" type="int" />
<param index="1" name="object_handle" type="int" />
<param index="2" name="object_name" type="String" />
<description>
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.
</description>
</method>
<method name="transform_from_pose"> <method name="transform_from_pose">
<return type="Transform3D" /> <return type="Transform3D" />
<param index="0" name="pose" type="const void*" /> <param index="0" name="pose" type="const void*" />

View File

@ -58,7 +58,7 @@ HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extension
return request_extensions; return request_extensions;
} }
void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_instance) { void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) {
OpenXRAPI::get_singleton()->register_composition_layer_provider(this); OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
} }

View File

@ -49,7 +49,7 @@ public:
virtual ~OpenXRCompositionLayerExtension() override; virtual ~OpenXRCompositionLayerExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override; virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_session_created(const XrSession p_instance) override; virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override; virtual void on_session_destroyed() override;
virtual void on_pre_render() override; virtual void on_pre_render() override;

View File

@ -0,0 +1,287 @@
/**************************************************************************/
/* 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 <openxr/openxr.h>
OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::singleton = nullptr;
OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::get_singleton() {
return singleton;
}
OpenXRDebugUtilsExtension::OpenXRDebugUtilsExtension() {
singleton = this;
}
OpenXRDebugUtilsExtension::~OpenXRDebugUtilsExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRDebugUtilsExtension::get_requested_extensions() {
HashMap<String, bool *> 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);
EXT_INIT_XR_FUNC(xrSessionBeginDebugUtilsLabelRegionEXT);
EXT_INIT_XR_FUNC(xrSessionEndDebugUtilsLabelRegionEXT);
EXT_INIT_XR_FUNC(xrSessionInsertDebugUtilsLabelEXT);
debug_utils_ext = xrCreateDebugUtilsMessengerEXT_ptr && xrDestroyDebugUtilsMessengerEXT_ptr && xrSetDebugUtilsObjectNameEXT_ptr && xrSessionBeginDebugUtilsLabelRegionEXT_ptr && xrSessionEndDebugUtilsLabelRegionEXT_ptr && xrSessionInsertDebugUtilsLabelEXT_ptr;
} else {
WARN_PRINT("OpenXR: The debug utils extension is not available on this runtime. Debug logging is not enabled!");
}
// On successful init, setup our default messenger.
if (debug_utils_ext) {
int max_severity = GLOBAL_GET("xr/openxr/extensions/debug_utils");
int types = GLOBAL_GET("xr/openxr/extensions/debug_message_types");
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;
}
XrDebugUtilsMessageTypeFlagsEXT message_types = 0;
// These should match up but just to be safe and future proof...
if (types & 1) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
}
if (types & 2) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
}
if (types & 4) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
}
if (types & 8) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
}
XrDebugUtilsMessengerCreateInfoEXT callback_info = {
XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // type
nullptr, // next
message_severities, // messageSeverities
message_types, // 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) + "]");
}
set_object_name(XR_OBJECT_TYPE_INSTANCE, uint64_t(p_instance), "Main Godot OpenXR Instance");
}
}
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;
xrSessionBeginDebugUtilsLabelRegionEXT_ptr = nullptr;
xrSessionEndDebugUtilsLabelRegionEXT_ptr = nullptr;
xrSessionInsertDebugUtilsLabelEXT_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) + "]");
}
}
void OpenXRDebugUtilsExtension::begin_debug_label_region(const char *p_label_name) {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionBeginDebugUtilsLabelRegionEXT_ptr);
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
NULL, // next
p_label_name, // labelName
};
XrResult result = xrSessionBeginDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to begin label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
void OpenXRDebugUtilsExtension::end_debug_label_region() {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionEndDebugUtilsLabelRegionEXT_ptr);
XrResult result = xrSessionEndDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session());
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to end label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
void OpenXRDebugUtilsExtension::insert_debug_label(const char *p_label_name) {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionInsertDebugUtilsLabelEXT_ptr);
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
NULL, // next
p_label_name, // labelName
};
XrResult result = xrSessionInsertDebugUtilsLabelEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to insert label [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
XrBool32 XRAPI_PTR 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;
}
if (p_callback_data->sessionLabelCount > 0) {
String labels;
for (uint32_t i = 0; i < p_callback_data->sessionLabelCount; i++) {
if (!labels.is_empty()) {
labels += ", ";
}
labels += p_callback_data->sessionLabels[i].labelName;
}
msg += "\nLabels: " + labels;
}
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;
}

View File

@ -0,0 +1,76 @@
/**************************************************************************/
/* 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<String, bool *> 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);
void begin_debug_label_region(const char *p_label_name);
void end_debug_label_region();
void insert_debug_label(const char *p_label_name);
private:
static OpenXRDebugUtilsExtension *singleton;
// related extensions
bool debug_utils_ext = false;
// debug handlers
XrDebugUtilsMessengerEXT default_messenger = XR_NULL_HANDLE;
static XrBool32 XRAPI_PTR _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)
EXT_PROTO_XRRESULT_FUNC2(xrSessionBeginDebugUtilsLabelRegionEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
EXT_PROTO_XRRESULT_FUNC1(xrSessionEndDebugUtilsLabelRegionEXT, (XrSession), p_session)
EXT_PROTO_XRRESULT_FUNC2(xrSessionInsertDebugUtilsLabelEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
};
#endif // OPENXR_DEBUG_UTILS_EXTENSION_H

View File

@ -76,7 +76,7 @@ public:
virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance. virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance.
virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance. virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance.
virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance. virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successfully created our OpenXR session. virtual void on_session_created(const XrSession p_session) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session. virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session.
// `on_process` is called as part of our OpenXR process handling, // `on_process` is called as part of our OpenXR process handling,

View File

@ -54,6 +54,7 @@
#endif #endif
#include "extensions/openxr_composition_layer_depth_extension.h" #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_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h" #include "extensions/openxr_fb_foveation_extension.h"
@ -316,6 +317,46 @@ String OpenXRAPI::get_swapchain_format_name(int64_t p_swapchain_format) const {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format)); 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 || !debug_utils->get_active()) {
// Not enabled/active? Ignore.
return;
}
debug_utils->set_object_name(p_object_type, p_object_handle, p_object_name.utf8().get_data());
}
void OpenXRAPI::begin_debug_label_region(const String &p_label_name) {
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
if (!debug_utils || !debug_utils->get_active()) {
// Not enabled/active? Ignore.
return;
}
debug_utils->begin_debug_label_region(p_label_name.utf8().get_data());
}
void OpenXRAPI::end_debug_label_region() {
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
if (!debug_utils || !debug_utils->get_active()) {
// Not enabled/active? Ignore.
return;
}
debug_utils->end_debug_label_region();
}
void OpenXRAPI::insert_debug_label(const String &p_label_name) {
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
if (!debug_utils || !debug_utils->get_active()) {
// Not enabled/active? Ignore.
return;
}
debug_utils->insert_debug_label(p_label_name.utf8().get_data());
}
bool OpenXRAPI::load_layer_properties() { bool OpenXRAPI::load_layer_properties() {
// This queries additional layers that are available and can be initialized when we create our OpenXR instance // This queries additional layers that are available and can be initialized when we create our OpenXR instance
if (layer_properties != nullptr) { if (layer_properties != nullptr) {
@ -826,6 +867,10 @@ bool OpenXRAPI::create_session() {
return false; return false;
} }
set_object_name(XR_OBJECT_TYPE_SESSION, uint64_t(session), "Main Godot OpenXR Session");
begin_debug_label_region("Godot session active");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_session_created(session); wrapper->on_session_created(session);
} }
@ -916,6 +961,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false; 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) { if (local_floor_emulation.stage_space == XR_NULL_HANDLE) {
@ -931,6 +978,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false; 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) { if (!will_emulate_local_floor) {
@ -972,6 +1021,8 @@ bool OpenXRAPI::setup_play_space() {
play_space = new_play_space; play_space = new_play_space;
reference_space = new_reference_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.enabled = will_emulate_local_floor;
local_floor_emulation.should_reset_floor_height = will_emulate_local_floor; local_floor_emulation.should_reset_floor_height = will_emulate_local_floor;
@ -1007,6 +1058,8 @@ bool OpenXRAPI::setup_view_space() {
return false; return false;
} }
set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(view_space), "View space");
return true; return true;
} }
@ -1181,6 +1234,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)) { 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; 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: // We create our depth swapchain if:
@ -1191,6 +1246,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)) { 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; 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: // We create our velocity swapchain if:
@ -1309,6 +1366,8 @@ void OpenXRAPI::destroy_session() {
wrapper->on_session_destroyed(); wrapper->on_session_destroyed();
} }
end_debug_label_region();
xrDestroySession(session); xrDestroySession(session);
session = XR_NULL_HANDLE; session = XR_NULL_HANDLE;
} }
@ -2215,6 +2274,9 @@ void OpenXRAPI::pre_render() {
} }
} }
// We should get our frame no from the rendering server, but this will do.
begin_debug_label_region(String("Session Frame ") + String::num_uint64(++render_state.frame));
// let's start our frame.. // let's start our frame..
XrFrameBeginInfo frame_begin_info = { XrFrameBeginInfo frame_begin_info = {
XR_TYPE_FRAME_BEGIN_INFO, // type XR_TYPE_FRAME_BEGIN_INFO, // type
@ -2333,6 +2395,8 @@ void OpenXRAPI::end_frame() {
return; return;
} }
end_debug_label_region(); // Session frame #
// neither eye is rendered // neither eye is rendered
return; return;
} }
@ -2407,6 +2471,8 @@ void OpenXRAPI::end_frame() {
print_line("OpenXR: failed to end frame! [", get_error_string(result), "]"); print_line("OpenXR: failed to end frame! [", get_error_string(result), "]");
return; return;
} }
end_debug_label_region(); // Session frame #
} }
float OpenXRAPI::get_display_refresh_rate() const { float OpenXRAPI::get_display_refresh_rate() const {
@ -2822,6 +2888,8 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
return RID(); 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); return action_set_owner.make_rid(action_set);
} }
@ -2997,6 +3065,8 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
return RID(); return RID();
} }
set_object_name(XR_OBJECT_TYPE_ACTION, uint64_t(action.handle), p_name);
return action_owner.make_rid(action); return action_owner.make_rid(action);
} }

View File

@ -336,6 +336,7 @@ private:
XrTime predicted_display_time = 0; XrTime predicted_display_time = 0;
XrSpace play_space = XR_NULL_HANDLE; XrSpace play_space = XR_NULL_HANDLE;
double render_target_size_multiplier = 1.0; double render_target_size_multiplier = 1.0;
uint64_t frame = 0;
uint32_t view_count = 0; uint32_t view_count = 0;
XrView *views = nullptr; XrView *views = nullptr;
@ -422,6 +423,10 @@ public:
XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr); XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result) const; String get_error_string(XrResult result) const;
String get_swapchain_format_name(int64_t p_swapchain_format) 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);
void begin_debug_label_region(const String &p_label_name);
void end_debug_label_region();
void insert_debug_label(const String &p_label_name);
OpenXRInterface *get_xr_interface() const { return xr_interface; } OpenXRInterface *get_xr_interface() const { return xr_interface; }
void set_xr_interface(OpenXRInterface *p_xr_interface); void set_xr_interface(OpenXRInterface *p_xr_interface);

View File

@ -43,6 +43,10 @@ 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_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_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("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("begin_debug_label_region", "label_name"), &OpenXRAPIExtension::begin_debug_label_region);
ClassDB::bind_method(D_METHOD("end_debug_label_region"), &OpenXRAPIExtension::end_debug_label_region);
ClassDB::bind_method(D_METHOD("insert_debug_label", "label_name"), &OpenXRAPIExtension::insert_debug_label);
ClassDB::bind_method(D_METHOD("is_initialized"), &OpenXRAPIExtension::is_initialized); ClassDB::bind_method(D_METHOD("is_initialized"), &OpenXRAPIExtension::is_initialized);
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running); ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
@ -116,6 +120,30 @@ String OpenXRAPIExtension::get_swapchain_format_name(int64_t p_swapchain_format)
return OpenXRAPI::get_singleton()->get_swapchain_format_name(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);
}
void OpenXRAPIExtension::begin_debug_label_region(const String &p_label_name) {
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
OpenXRAPI::get_singleton()->begin_debug_label_region(p_label_name);
}
void OpenXRAPIExtension::end_debug_label_region() {
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
OpenXRAPI::get_singleton()->end_debug_label_region();
}
void OpenXRAPIExtension::insert_debug_label(const String &p_label_name) {
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
OpenXRAPI::get_singleton()->insert_debug_label(p_label_name);
}
bool OpenXRAPIExtension::is_initialized() { bool OpenXRAPIExtension::is_initialized() {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false); ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
return OpenXRAPI::get_singleton()->is_initialized(); return OpenXRAPI::get_singleton()->is_initialized();

View File

@ -64,6 +64,10 @@ public:
uint64_t get_instance_proc_addr(String p_name); uint64_t get_instance_proc_addr(String p_name);
String get_error_string(uint64_t result); String get_error_string(uint64_t result);
String get_swapchain_format_name(int64_t p_swapchain_format); 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);
void begin_debug_label_region(const String &p_label_name);
void end_debug_label_region();
void insert_debug_label(const String &p_label_name);
bool is_initialized(); bool is_initialized();
bool is_running(); bool is_running();

View File

@ -48,6 +48,7 @@
#include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_composition_layer_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_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_hand_interaction_extension.h" #include "extensions/openxr_hand_interaction_extension.h"
@ -133,6 +134,9 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension));
// register gated extensions // 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")) { if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandTrackingExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandTrackingExtension));
} }