Browse Source

Merge pull request #89460 from dsnopek/openxr-composition-layers-multiple

OpenXR: Allow GDExtensions to provide multiple, ordered composition layers
Rémi Verschelde 1 year ago
parent
commit
36b33d9b72

+ 18 - 1
modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml

@@ -11,8 +11,25 @@
 	<methods>
 		<method name="_get_composition_layer" qualifiers="virtual">
 			<return type="int" />
+			<param index="0" name="index" type="int" />
 			<description>
-				Returns a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct to provide a composition layer. This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+				Returns a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct to provide the given composition layer.
+				This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+			</description>
+		</method>
+		<method name="_get_composition_layer_count" qualifiers="virtual">
+			<return type="int" />
+			<description>
+				Returns the number of composition layers this extension wrapper provides via [method _get_composition_layer].
+				This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+			</description>
+		</method>
+		<method name="_get_composition_layer_order" qualifiers="virtual">
+			<return type="int" />
+			<param index="0" name="index" type="int" />
+			<description>
+				Returns an integer that will be used to sort the given composition layer provided via [method _get_composition_layer]. Lower numbers will move the layer to the front of the list, and higher numbers to the end. The default projection layer has an order of [code]0[/code], so layers provided by this method should probably be above or below (but not exactly) [code]0[/code].
+				This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
 			</description>
 		</method>
 		<method name="_get_requested_extensions" qualifiers="virtual">

+ 9 - 2
modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp

@@ -56,8 +56,15 @@ bool OpenXRCompositionLayerDepthExtension::is_available() {
 	return available;
 }
 
-XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer() {
-	// Seems this is all done in our base layer... Just in case this changes...
+int OpenXRCompositionLayerDepthExtension::get_composition_layer_count() {
+	return 0;
+}
 
+XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer(int p_index) {
+	// Seems this is all done in our base layer... Just in case this changes...
 	return nullptr;
 }
+
+int OpenXRCompositionLayerDepthExtension::get_composition_layer_order(int p_index) {
+	return 0;
+}

+ 3 - 1
modules/openxr/extensions/openxr_composition_layer_depth_extension.h

@@ -43,7 +43,9 @@ public:
 
 	virtual HashMap<String, bool *> get_requested_extensions() override;
 	bool is_available();
-	virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+	virtual int get_composition_layer_count() override;
+	virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
+	virtual int get_composition_layer_order(int p_index) override;
 
 private:
 	static OpenXRCompositionLayerDepthExtension *singleton;

+ 3 - 1
modules/openxr/extensions/openxr_composition_layer_provider.h

@@ -38,7 +38,9 @@
 // Interface for OpenXR extensions that provide a composition layer.
 class OpenXRCompositionLayerProvider {
 public:
-	virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0;
+	virtual int get_composition_layer_count() = 0;
+	virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) = 0;
+	virtual int get_composition_layer_order(int p_index) = 0;
 
 	virtual ~OpenXRCompositionLayerProvider() {}
 };

+ 17 - 3
modules/openxr/extensions/openxr_extension_wrapper_extension.cpp

@@ -39,7 +39,9 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer");
 	GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer");
 	GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer");
-	GDVIRTUAL_BIND(_get_composition_layer);
+	GDVIRTUAL_BIND(_get_composition_layer_count);
+	GDVIRTUAL_BIND(_get_composition_layer, "index");
+	GDVIRTUAL_BIND(_get_composition_layer_order, "index");
 	GDVIRTUAL_BIND(_get_suggested_tracker_names);
 	GDVIRTUAL_BIND(_on_register_metadata);
 	GDVIRTUAL_BIND(_on_before_instance_created);
@@ -140,16 +142,28 @@ PackedStringArray OpenXRExtensionWrapperExtension::get_suggested_tracker_names()
 	return PackedStringArray();
 }
 
-XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer() {
+int OpenXRExtensionWrapperExtension::get_composition_layer_count() {
+	int count = 0;
+	GDVIRTUAL_CALL(_get_composition_layer_count, count);
+	return count;
+}
+
+XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer(int p_index) {
 	uint64_t pointer;
 
-	if (GDVIRTUAL_CALL(_get_composition_layer, pointer)) {
+	if (GDVIRTUAL_CALL(_get_composition_layer, p_index, pointer)) {
 		return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer);
 	}
 
 	return nullptr;
 }
 
+int OpenXRExtensionWrapperExtension::get_composition_layer_order(int p_index) {
+	int order = 0;
+	GDVIRTUAL_CALL(_get_composition_layer_order, p_index, order);
+	return order;
+}
+
 void OpenXRExtensionWrapperExtension::on_register_metadata() {
 	GDVIRTUAL_CALL(_on_register_metadata);
 }

+ 6 - 2
modules/openxr/extensions/openxr_extension_wrapper_extension.h

@@ -59,7 +59,9 @@ public:
 	virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
 	virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
 	virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override;
-	virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+	virtual int get_composition_layer_count() override;
+	virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
+	virtual int get_composition_layer_order(int p_index) override;
 
 	//TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
 	GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>);
@@ -67,7 +69,9 @@ public:
 	GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>);
 	GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>);
 	GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr<void>);
-	GDVIRTUAL0R(uint64_t, _get_composition_layer);
+	GDVIRTUAL0R(int, _get_composition_layer_count);
+	GDVIRTUAL1R(uint64_t, _get_composition_layer, int);
+	GDVIRTUAL1R(int, _get_composition_layer_order, int);
 
 	virtual PackedStringArray get_suggested_tracker_names() override;
 

+ 26 - 6
modules/openxr/openxr_api.cpp

@@ -2080,18 +2080,29 @@ void OpenXRAPI::end_frame() {
 		projection_views[eye].pose = views[eye].pose;
 	}
 
-	Vector<const XrCompositionLayerBaseHeader *> layers_list;
+	Vector<OrderedCompositionLayer> ordered_layers_list;
+	bool projection_layer_is_first = true;
 
 	// Add composition layers from providers
 	for (OpenXRCompositionLayerProvider *provider : composition_layer_providers) {
-		XrCompositionLayerBaseHeader *layer = provider->get_composition_layer();
-		if (layer) {
-			layers_list.push_back(layer);
+		for (int i = 0; i < provider->get_composition_layer_count(); i++) {
+			OrderedCompositionLayer layer = {
+				provider->get_composition_layer(i),
+				provider->get_composition_layer_order(i),
+			};
+			if (layer.composition_layer) {
+				ordered_layers_list.push_back(layer);
+				if (layer.sort_order == 0) {
+					WARN_PRINT_ONCE_ED("Composition layer returned sort order 0, it may be overwritten by projection layer.");
+				} else if (layer.sort_order < 0) {
+					projection_layer_is_first = false;
+				}
+			}
 		}
 	}
 
 	XrCompositionLayerFlags layer_flags = XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
-	if (layers_list.size() > 0 || environment_blend_mode != XR_ENVIRONMENT_BLEND_MODE_OPAQUE) {
+	if (!projection_layer_is_first || environment_blend_mode != XR_ENVIRONMENT_BLEND_MODE_OPAQUE) {
 		layer_flags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
 	}
 
@@ -2103,7 +2114,16 @@ void OpenXRAPI::end_frame() {
 		view_count, // viewCount
 		projection_views, // views
 	};
-	layers_list.push_back((const XrCompositionLayerBaseHeader *)&projection_layer);
+	ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 });
+
+	// Sort our layers.
+	ordered_layers_list.sort_custom<OrderedCompositionLayer>();
+
+	// Now make a list we can pass on to OpenXR.
+	Vector<const XrCompositionLayerBaseHeader *> layers_list;
+	for (OrderedCompositionLayer &ordered_layer : ordered_layers_list) {
+		layers_list.push_back(ordered_layer.composition_layer);
+	}
 
 	XrFrameEndInfo frame_end_info = {
 		XR_TYPE_FRAME_END_INFO, // type

+ 9 - 0
modules/openxr/openxr_api.h

@@ -287,6 +287,15 @@ private:
 	RID get_interaction_profile_rid(XrPath p_path);
 	XrPath get_interaction_profile_path(RID p_interaction_profile);
 
+	struct OrderedCompositionLayer {
+		const XrCompositionLayerBaseHeader *composition_layer;
+		int sort_order;
+
+		_FORCE_INLINE_ bool operator()(const OrderedCompositionLayer &a, const OrderedCompositionLayer &b) const {
+			return a.sort_order < b.sort_order || (a.sort_order == b.sort_order && uint64_t(a.composition_layer) < uint64_t(b.composition_layer));
+		}
+	};
+
 	// state changes
 	bool poll_events();
 	bool on_state_idle();