Browse Source

Merge pull request #98872 from BastiaanOlij/openxr_khr_metal_enable

OpenXR: Add Metal support
Thaddeus Crews 8 months ago
parent
commit
d51fae25a7

+ 2 - 0
modules/openxr/extensions/SCsub

@@ -13,6 +13,8 @@ if env["platform"] == "android":
     env_openxr.add_source_files(module_obj, "platform/openxr_android_extension.cpp")
     env_openxr.add_source_files(module_obj, "platform/openxr_android_extension.cpp")
 if env["vulkan"]:
 if env["vulkan"]:
     env_openxr.add_source_files(module_obj, "platform/openxr_vulkan_extension.cpp")
     env_openxr.add_source_files(module_obj, "platform/openxr_vulkan_extension.cpp")
+if env["metal"]:
+    env_openxr.add_source_files(module_obj, "platform/openxr_metal_extension.mm")
 if env["opengl3"] and env["platform"] != "macos":
 if env["opengl3"] and env["platform"] != "macos":
     env_openxr.add_source_files(module_obj, "platform/openxr_opengl_extension.cpp")
     env_openxr.add_source_files(module_obj, "platform/openxr_opengl_extension.cpp")
 
 

+ 72 - 0
modules/openxr/extensions/platform/openxr_metal_extension.h

@@ -0,0 +1,72 @@
+/**************************************************************************/
+/*  openxr_metal_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_METAL_EXTENSION_H
+#define OPENXR_METAL_EXTENSION_H
+
+#include "../../openxr_api.h"
+#include "../../util.h"
+#include "../openxr_extension_wrapper.h"
+
+#include "core/templates/vector.h"
+
+// Always include this as late as possible.
+#include "../../openxr_platform_inc.h"
+
+class OpenXRMetalExtension : public OpenXRGraphicsExtensionWrapper {
+public:
+	virtual HashMap<String, bool *> get_requested_extensions() override;
+
+	virtual void on_instance_created(const XrInstance p_instance) override;
+	virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
+
+	virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
+	virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
+	virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
+	virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override;
+	virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
+	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
+	virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
+
+private:
+	static XrGraphicsBindingMetalKHR graphics_binding_metal;
+
+	struct SwapchainGraphicsData {
+		bool is_multiview;
+		Vector<RID> texture_rids;
+	};
+
+	bool check_graphics_api_support();
+
+	EXT_PROTO_XRRESULT_FUNC3(xrGetMetalGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsMetalKHR *), p_graphics_requirements)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
+};
+
+#endif // OPENXR_METAL_EXTENSION_H

+ 318 - 0
modules/openxr/extensions/platform/openxr_metal_extension.mm

@@ -0,0 +1,318 @@
+/**************************************************************************/
+/*  openxr_metal_extension.mm                                             */
+/**************************************************************************/
+/*                         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_metal_extension.h"
+
+#include "../../openxr_util.h"
+#include "drivers/metal/rendering_device_driver_metal.h"
+#include "servers/rendering/rendering_server_globals.h"
+
+HashMap<String, bool *> OpenXRMetalExtension::get_requested_extensions() {
+	HashMap<String, bool *> request_extensions;
+
+	request_extensions[XR_KHR_METAL_ENABLE_EXTENSION_NAME] = nullptr;
+
+	return request_extensions;
+}
+
+void OpenXRMetalExtension::on_instance_created(const XrInstance p_instance) {
+	// Obtain pointers to functions we're accessing here.
+	ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+	EXT_INIT_XR_FUNC(xrGetMetalGraphicsRequirementsKHR);
+	EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
+}
+
+bool OpenXRMetalExtension::check_graphics_api_support() {
+	ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+
+	// TODO We may need to do a callback like we do in Vulkan where we run this first
+	// and provide the obtained metalDevice to our Metal driver to use.
+	// But according to Stuart Macs only have 1 device so it should always be the
+	// same one and we should be able to get away with not doing this just yet.
+	// If we do go forward with this, this means that just like with Vulkan,
+	// we have to start with OpenXR before Metal can be setup, and we thus
+	// can't support applications that want to add XR as an optional/temporary
+	// feature that users enable when needed.
+
+	XrSystemId system_id = OpenXRAPI::get_singleton()->get_system_id();
+	XrInstance instance = OpenXRAPI::get_singleton()->get_instance();
+
+	XrGraphicsRequirementsMetalKHR metal_requirements;
+	metal_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR;
+	metal_requirements.next = nullptr;
+	metal_requirements.metalDevice = nullptr;
+
+	XrResult result = xrGetMetalGraphicsRequirementsKHR(instance, system_id, &metal_requirements);
+	if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get Metal graphics requirements!")) {
+		return false;
+	}
+
+	// See what metal device we are using...
+	RenderingServer *rendering_server = RenderingServer::get_singleton();
+	ERR_FAIL_NULL_V(rendering_server, false);
+	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
+	ERR_FAIL_NULL_V(rendering_device, false);
+
+	void *our_metal_device = (void *)rendering_device->get_driver_resource(RD::DRIVER_RESOURCE_LOGICAL_DEVICE);
+
+	// Make sure we're using the same one.
+	ERR_FAIL_COND_V(metal_requirements.metalDevice != our_metal_device, false);
+
+	return true;
+}
+
+XrGraphicsBindingMetalKHR OpenXRMetalExtension::graphics_binding_metal;
+
+void *OpenXRMetalExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
+	if (!check_graphics_api_support()) {
+		return p_next_pointer;
+	}
+
+	RenderingServer *rendering_server = RenderingServer::get_singleton();
+	ERR_FAIL_NULL_V(rendering_server, p_next_pointer);
+	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
+	ERR_FAIL_NULL_V(rendering_device, p_next_pointer);
+
+	graphics_binding_metal.type = XR_TYPE_GRAPHICS_BINDING_METAL_KHR;
+	graphics_binding_metal.next = p_next_pointer;
+	graphics_binding_metal.commandQueue = (void *)rendering_device->get_driver_resource(RD::DRIVER_RESOURCE_COMMAND_QUEUE);
+
+	return &graphics_binding_metal;
+}
+
+void OpenXRMetalExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
+	p_usable_swap_chains.push_back(MTLPixelFormatRGBA8Unorm_sRGB);
+	p_usable_swap_chains.push_back(MTLPixelFormatBGRA8Unorm_sRGB);
+	p_usable_swap_chains.push_back(MTLPixelFormatRGBA8Uint);
+}
+
+void OpenXRMetalExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) {
+	p_usable_swap_chains.push_back(MTLPixelFormatDepth24Unorm_Stencil8);
+	p_usable_swap_chains.push_back(MTLPixelFormatDepth32Float_Stencil8);
+	p_usable_swap_chains.push_back(MTLPixelFormatDepth32Float);
+}
+
+#define ENUM_TO_STRING_CASE(m_e) \
+	case m_e: {                  \
+		return String(#m_e);     \
+	} break;
+
+String OpenXRMetalExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
+	// This really should be in vulkan_context...
+	MTLPixelFormat format = MTLPixelFormat(p_swapchain_format);
+	switch (format) {
+		ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Unorm)
+		ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Unorm_sRGB)
+		ENUM_TO_STRING_CASE(MTLPixelFormatBGRA8Unorm)
+		ENUM_TO_STRING_CASE(MTLPixelFormatBGRA8Unorm_sRGB)
+		ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Uint)
+		ENUM_TO_STRING_CASE(MTLPixelFormatDepth24Unorm_Stencil8)
+		ENUM_TO_STRING_CASE(MTLPixelFormatDepth32Float_Stencil8)
+		ENUM_TO_STRING_CASE(MTLPixelFormatDepth32Float)
+		default: {
+			return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
+		} break;
+	}
+}
+
+bool OpenXRMetalExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) {
+	LocalVector<XrSwapchainImageMetalKHR, uint32_t, false, true> images;
+
+	RenderingServer *rendering_server = RenderingServer::get_singleton();
+	ERR_FAIL_NULL_V(rendering_server, false);
+	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
+	ERR_FAIL_NULL_V(rendering_device, false);
+
+	uint32_t swapchain_length;
+	XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to get swapchaim image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return false;
+	}
+
+	images.resize(swapchain_length);
+
+	for (XrSwapchainImageMetalKHR &image : images) {
+		image.type = XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR;
+		image.next = nullptr;
+		image.texture = nullptr;
+	}
+
+	result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to get swapchaim images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return false;
+	}
+
+	SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
+	if (data == nullptr) {
+		print_line("OpenXR: Failed to allocate memory for swapchain data");
+		return false;
+	}
+	*r_swapchain_graphics_data = data;
+	data->is_multiview = (p_array_size > 1);
+
+	RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
+	RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
+	uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
+
+	switch (p_swapchain_format) {
+		case MTLPixelFormatRGBA8Unorm_sRGB:
+			// Even though this is an sRGB framebuffer format we're using UNORM here.
+			// The reason here is because Godot does a linear to sRGB conversion while
+			// with the sRGB format, this conversion would be doubled by the hardware.
+			// This also means we're reading the values as is for our preview on screen.
+			// The OpenXR runtime however is still treating this as an sRGB format and
+			// will thus do an sRGB -> Linear conversion as expected.
+			//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
+			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case MTLPixelFormatBGRA8Unorm_sRGB:
+			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case MTLPixelFormatRGBA8Uint:
+			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case MTLPixelFormatDepth32Float:
+			format = RenderingDevice::DATA_FORMAT_D32_SFLOAT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case MTLPixelFormatDepth24Unorm_Stencil8:
+			format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case MTLPixelFormatDepth32Float_Stencil8:
+			format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		default:
+			// Continue with our default value.
+			print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
+			break;
+	}
+
+	switch (p_sample_count) {
+		case 1:
+			samples = RenderingDevice::TEXTURE_SAMPLES_1;
+			break;
+		case 2:
+			samples = RenderingDevice::TEXTURE_SAMPLES_2;
+			break;
+		case 4:
+			samples = RenderingDevice::TEXTURE_SAMPLES_4;
+			break;
+		case 8:
+			samples = RenderingDevice::TEXTURE_SAMPLES_8;
+			break;
+		case 16:
+			samples = RenderingDevice::TEXTURE_SAMPLES_16;
+			break;
+		case 32:
+			samples = RenderingDevice::TEXTURE_SAMPLES_32;
+			break;
+		case 64:
+			samples = RenderingDevice::TEXTURE_SAMPLES_64;
+			break;
+		default:
+			// Continue with our default value.
+			print_line("OpenXR: Unsupported sample count", p_sample_count);
+			break;
+	}
+
+	Vector<RID> texture_rids;
+
+	// Create Godot texture objects for each entry in our swapchain.
+	for (uint64_t i = 0; i < swapchain_length; i++) {
+		// Note, the formats we sent to render_device are ignored on metal.
+		RID image_rid = rendering_device->texture_create_from_extension(
+				p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
+				format,
+				samples,
+				usage_flags,
+				(uint64_t)images[i].texture,
+				p_width,
+				p_height,
+				1,
+				p_array_size);
+
+		texture_rids.push_back(image_rid);
+	}
+
+	data->texture_rids = texture_rids;
+
+	return true;
+}
+
+void OpenXRMetalExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
+	if (*p_swapchain_graphics_data == nullptr) {
+		return;
+	}
+
+	SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
+
+	RenderingServer *rendering_server = RenderingServer::get_singleton();
+	ERR_FAIL_NULL(rendering_server);
+	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
+	ERR_FAIL_NULL(rendering_device);
+
+	for (const RID &texture_rid : data->texture_rids) {
+		rendering_device->free(texture_rid);
+	}
+	data->texture_rids.clear();
+
+	memdelete(data);
+	*p_swapchain_graphics_data = nullptr;
+}
+
+bool OpenXRMetalExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
+	// Even though this is a Metal renderer we're using OpenGL coordinate systems.
+	OpenXRUtil::XrMatrix4x4f matrix;
+	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
+
+	for (int j = 0; j < 4; j++) {
+		for (int i = 0; i < 4; i++) {
+			r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
+		}
+	}
+
+	return true;
+}
+
+RID OpenXRMetalExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
+	SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
+	ERR_FAIL_NULL_V(data, RID());
+
+	ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
+	return data->texture_rids[p_image_index];
+}

+ 2 - 2
modules/openxr/extensions/platform/openxr_opengl_extension.cpp

@@ -315,8 +315,8 @@ void OpenXROpenGLExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g
 
 
 	SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
 	SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
 
 
-	for (int i = 0; i < data->texture_rids.size(); i++) {
-		texture_storage->texture_free(data->texture_rids[i]);
+	for (const RID &texture_rid : data->texture_rids) {
+		texture_storage->texture_free(texture_rid);
 	}
 	}
 	data->texture_rids.clear();
 	data->texture_rids.clear();
 
 

+ 8 - 8
modules/openxr/extensions/platform/openxr_vulkan_extension.cpp

@@ -291,12 +291,12 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 			// This also means we're reading the values as is for our preview on screen.
 			// This also means we're reading the values as is for our preview on screen.
 			// The OpenXR runtime however is still treating this as an sRGB format and
 			// The OpenXR runtime however is still treating this as an sRGB format and
 			// will thus do an sRGB -> Linear conversion as expected.
 			// will thus do an sRGB -> Linear conversion as expected.
-			// format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
+			//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
 			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
 			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
 			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			break;
 			break;
 		case VK_FORMAT_B8G8R8A8_SRGB:
 		case VK_FORMAT_B8G8R8A8_SRGB:
-			// format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
+			//format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
 			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
 			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
 			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			break;
 			break;
@@ -322,7 +322,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 			break;
 			break;
 		default:
 		default:
 			// continue with our default value
 			// continue with our default value
-			print_line("Unsupported swapchain format ", p_swapchain_format);
+			print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
 			break;
 			break;
 	}
 	}
 
 
@@ -350,7 +350,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 			break;
 			break;
 		default:
 		default:
 			// continue with our default value
 			// continue with our default value
-			print_line("Unsupported sample count ", p_sample_count);
+			print_line("OpenXR: Unsupported sample count", p_sample_count);
 			break;
 			break;
 	}
 	}
 
 
@@ -380,7 +380,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 }
 }
 
 
 bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
 bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
-	// Even though this is a Vulkan renderer we're using OpenGL coordinate systems
+	// Even though this is a Vulkan renderer we're using OpenGL coordinate systems.
 	OpenXRUtil::XrMatrix4x4f matrix;
 	OpenXRUtil::XrMatrix4x4f matrix;
 	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
 	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
 
 
@@ -413,9 +413,9 @@ void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g
 	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
 	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
 	ERR_FAIL_NULL(rendering_device);
 	ERR_FAIL_NULL(rendering_device);
 
 
-	for (int i = 0; i < data->texture_rids.size(); i++) {
-		// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain
-		rendering_device->free(data->texture_rids[i]);
+	for (const RID &texture_rid : data->texture_rids) {
+		// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain.
+		rendering_device->free(texture_rid);
 	}
 	}
 	data->texture_rids.clear();
 	data->texture_rids.clear();
 
 

+ 18 - 11
modules/openxr/openxr_api.cpp

@@ -49,6 +49,10 @@
 #include "extensions/platform/openxr_vulkan_extension.h"
 #include "extensions/platform/openxr_vulkan_extension.h"
 #endif
 #endif
 
 
+#ifdef METAL_ENABLED
+#include "extensions/platform/openxr_metal_extension.h"
+#endif
+
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 #include "extensions/platform/openxr_opengl_extension.h"
 #include "extensions/platform/openxr_opengl_extension.h"
 #endif
 #endif
@@ -1200,12 +1204,9 @@ bool OpenXRAPI::obtain_swapchain_formats() {
 			}
 			}
 		}
 		}
 
 
-		if (color_swapchain_format == 0) {
-			color_swapchain_format = usable_swapchain_formats[0]; // just use the first one and hope for the best...
-			print_line("Couldn't find usable color swap chain format, using", get_swapchain_format_name(color_swapchain_format), "instead.");
-		} else {
-			print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(color_swapchain_format));
-		}
+		ERR_FAIL_COND_V_MSG(color_swapchain_format == 0, false, "OpenXR: No usable color swap chain format available!");
+
+		print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(color_swapchain_format));
 	}
 	}
 
 
 	{
 	{
@@ -1222,11 +1223,9 @@ bool OpenXRAPI::obtain_swapchain_formats() {
 			}
 			}
 		}
 		}
 
 
-		if (depth_swapchain_format == 0) {
-			WARN_PRINT_ONCE("Couldn't find usable depth swap chain format, depth buffer will not be submitted if requested.");
-		} else {
-			print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(depth_swapchain_format));
-		}
+		ERR_FAIL_COND_V_MSG(depth_swapchain_format == 0, false, "OpenXR: No usable depth swap chain format available!");
+
+		print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(depth_swapchain_format));
 	}
 	}
 
 
 	return true;
 	return true;
@@ -1683,6 +1682,14 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 #else
 #else
 		// shouldn't be possible...
 		// shouldn't be possible...
 		ERR_FAIL_V(false);
 		ERR_FAIL_V(false);
+#endif
+	} else if (p_rendering_driver == "metal") {
+#ifdef METAL_ENABLED
+		graphics_extension = memnew(OpenXRMetalExtension);
+		register_extension_wrapper(graphics_extension);
+#else
+		// shouldn't be possible...
+		ERR_FAIL_V(false);
 #endif
 #endif
 	} else if (p_rendering_driver == "opengl3") {
 	} else if (p_rendering_driver == "opengl3") {
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)

+ 5 - 0
modules/openxr/openxr_platform_inc.h

@@ -39,6 +39,11 @@
 #include "drivers/vulkan/rendering_context_driver_vulkan.h"
 #include "drivers/vulkan/rendering_context_driver_vulkan.h"
 #endif // VULKAN_ENABLED
 #endif // VULKAN_ENABLED
 
 
+#ifdef METAL_ENABLED
+#define XR_USE_GRAPHICS_API_METAL
+#include "drivers/metal/rendering_context_driver_metal.h"
+#endif // METAL_ENABLED
+
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 #ifdef ANDROID_ENABLED
 #ifdef ANDROID_ENABLED
 #define XR_USE_GRAPHICS_API_OPENGL_ES
 #define XR_USE_GRAPHICS_API_OPENGL_ES