diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index bf1632965b2c..3d9e13b51744 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2948,6 +2948,12 @@ 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. + + 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. + + + 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 d76ddd5a6687..a4f0ae54e90c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -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); // 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_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 diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml index 4419d24dd306..432b331eecd2 100644 --- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml +++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml @@ -17,12 +17,25 @@ https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html + + + + + 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. + + Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport. + + + + Marks the end of a debug label region. Removes the latest debug label region added by calling [method begin_debug_label_region]. + + @@ -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. + + + + + 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. + + @@ -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. + + + + + + + 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_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index 994b08af53bd..8a448afc08d1 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -58,7 +58,7 @@ HashMap OpenXRCompositionLayerExtension::get_requested_extension 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); } diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h index 4fefc416e610..34e330a60a5b 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.h +++ b/modules/openxr/extensions/openxr_composition_layer_extension.h @@ -49,7 +49,7 @@ public: virtual ~OpenXRCompositionLayerExtension() override; virtual HashMap 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_pre_render() override; 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..10dbe629f712 --- /dev/null +++ b/modules/openxr/extensions/openxr_debug_utils_extension.cpp @@ -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 + +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); + 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; +} 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..1ee4c2a101a0 --- /dev/null +++ b/modules/openxr/extensions/openxr_debug_utils_extension.h @@ -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 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 diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 8d05657afceb..09a9556dfa7e 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -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_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_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. // `on_process` is called as part of our OpenXR process handling, diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index e4ec318a4246..ecf7c05789f5 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,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)); } +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() { // This queries additional layers that are available and can be initialized when we create our OpenXR instance if (layer_properties != nullptr) { @@ -826,6 +867,10 @@ bool OpenXRAPI::create_session() { 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) { 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), "]"); 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 +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), "]"); 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 +1021,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 +1058,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 +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)) { 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 +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)) { 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: @@ -1309,6 +1366,8 @@ void OpenXRAPI::destroy_session() { wrapper->on_session_destroyed(); } + end_debug_label_region(); + xrDestroySession(session); 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.. XrFrameBeginInfo frame_begin_info = { XR_TYPE_FRAME_BEGIN_INFO, // type @@ -2333,6 +2395,8 @@ void OpenXRAPI::end_frame() { return; } + end_debug_label_region(); // Session frame # + // neither eye is rendered return; } @@ -2407,6 +2471,8 @@ void OpenXRAPI::end_frame() { print_line("OpenXR: failed to end frame! [", get_error_string(result), "]"); return; } + + end_debug_label_region(); // Session frame # } 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(); } + set_object_name(XR_OBJECT_TYPE_ACTION_SET, uint64_t(action_set.handle), p_name); + 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(); } + 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 88455379b995..0d1e4eb414ac 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -336,6 +336,7 @@ private: XrTime predicted_display_time = 0; XrSpace play_space = XR_NULL_HANDLE; double render_target_size_multiplier = 1.0; + uint64_t frame = 0; uint32_t view_count = 0; XrView *views = nullptr; @@ -422,6 +423,10 @@ public: 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); + 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; } 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..f3bc178d3a25 100644 --- a/modules/openxr/openxr_api_extension.cpp +++ b/modules/openxr/openxr_api_extension.cpp @@ -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_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("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_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); } +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() { 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..1b88b418f61f 100644 --- a/modules/openxr/openxr_api_extension.h +++ b/modules/openxr/openxr_api_extension.h @@ -64,6 +64,10 @@ public: 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); + 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_running(); diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 61c294eff58d..f3fda2517ce6 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -48,6 +48,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" @@ -133,6 +134,9 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension)); // 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)); }