Browse Source

Adding getters to RenderTarget and implementing override functionality for XR

Bastiaan Olij 2 years ago
parent
commit
c7656978ba
30 changed files with 798 additions and 325 deletions
  1. 33 0
      doc/classes/XRInterfaceExtension.xml
  2. 1 5
      drivers/gles3/rasterizer_gles3.cpp
  3. 32 120
      drivers/gles3/storage/texture_storage.cpp
  4. 21 17
      drivers/gles3/storage/texture_storage.h
  5. 1 0
      modules/openxr/SCsub
  6. 58 0
      modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
  7. 53 0
      modules/openxr/extensions/openxr_composition_layer_depth_extension.h
  8. 1 0
      modules/openxr/extensions/openxr_composition_layer_provider.h
  9. 2 1
      modules/openxr/extensions/openxr_extension_wrapper.h
  10. 33 41
      modules/openxr/extensions/openxr_vulkan_extension.cpp
  11. 3 3
      modules/openxr/extensions/openxr_vulkan_extension.h
  12. 145 55
      modules/openxr/openxr_api.cpp
  13. 23 8
      modules/openxr/openxr_api.h
  14. 18 0
      modules/openxr/openxr_interface.cpp
  15. 3 0
      modules/openxr/openxr_interface.h
  16. 19 5
      servers/rendering/dummy/storage/texture_storage.h
  17. 2 2
      servers/rendering/renderer_rd/environment/gi.cpp
  18. 3 3
      servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
  19. 1 5
      servers/rendering/renderer_rd/renderer_compositor_rd.cpp
  20. 5 5
      servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
  21. 56 2
      servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
  22. 4 8
      servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
  23. 139 27
      servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
  24. 57 7
      servers/rendering/renderer_rd/storage_rd/texture_storage.h
  25. 6 4
      servers/rendering/renderer_viewport.cpp
  26. 21 5
      servers/rendering/storage/texture_storage.h
  27. 13 0
      servers/xr/xr_interface.cpp
  28. 3 2
      servers/xr/xr_interface.h
  29. 36 0
      servers/xr/xr_interface_extension.cpp
  30. 6 0
      servers/xr/xr_interface_extension.h

+ 33 - 0
doc/classes/XRInterfaceExtension.xml

@@ -39,6 +39,18 @@
 				Returns the capabilities of this interface.
 			</description>
 		</method>
+		<method name="_get_color_texture" qualifiers="virtual">
+			<return type="RID" />
+			<description>
+				Return color texture into which to render (if applicable).
+			</description>
+		</method>
+		<method name="_get_depth_texture" qualifiers="virtual">
+			<return type="RID" />
+			<description>
+				Return depth texture into which to render (if applicable).
+			</description>
+		</method>
 		<method name="_get_name" qualifiers="virtual const">
 			<return type="StringName" />
 			<description>
@@ -100,6 +112,12 @@
 				Returns a [Transform3D] for a given view.
 			</description>
 		</method>
+		<method name="_get_velocity_texture" qualifiers="virtual">
+			<return type="RID" />
+			<description>
+				Return velocity texture into which to render (if applicable).
+			</description>
+		</method>
 		<method name="_get_view_count" qualifiers="virtual">
 			<return type="int" />
 			<description>
@@ -213,6 +231,16 @@
 				Blits our render results to screen optionally applying lens distortion. This can only be called while processing [code]_commit_views[/code].
 			</description>
 		</method>
+		<method name="get_color_texture">
+			<return type="RID" />
+			<description>
+			</description>
+		</method>
+		<method name="get_depth_texture">
+			<return type="RID" />
+			<description>
+			</description>
+		</method>
 		<method name="get_render_target_texture">
 			<return type="RID" />
 			<param index="0" name="render_target" type="RID" />
@@ -220,5 +248,10 @@
 				Returns a valid [RID] for a texture to which we should render the current frame if supported by the interface.
 			</description>
 		</method>
+		<method name="get_velocity_texture">
+			<return type="RID" />
+			<description>
+			</description>
+		</method>
 	</methods>
 </class>

+ 1 - 5
drivers/gles3/rasterizer_gles3.cpp

@@ -280,11 +280,7 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
 	GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
 	ERR_FAIL_COND(!rt);
 
-	if (rt->external.fbo != 0) {
-		glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
-	} else {
-		glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
-	}
+	glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
 	glReadBuffer(GL_COLOR_ATTACHMENT0);
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
 	// Flip content upside down to correct for coordinates.

+ 32 - 120
drivers/gles3/storage/texture_storage.cpp

@@ -1343,24 +1343,6 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
 		rt->fbo = 0;
 		rt->color = 0;
 	}
-	/*
-	if (rt->external.fbo != 0) {
-		// free this
-		glDeleteFramebuffers(1, &rt->external.fbo);
-
-		// clean up our texture
-		Texture *t = get_texture(rt->external.texture);
-		t->alloc_height = 0;
-		t->alloc_width = 0;
-		t->width = 0;
-		t->height = 0;
-		t->active = false;
-		texture_free(rt->external.texture);
-		memdelete(t);
-
-		rt->external.fbo = 0;
-	}
-	*/
 
 	Texture *tex = get_texture(rt->texture);
 	tex->alloc_height = 0;
@@ -1412,6 +1394,13 @@ void TextureStorage::render_target_set_position(RID p_render_target, int p_x, in
 	rt->position = Point2i(p_x, p_y);
 }
 
+Point2i TextureStorage::render_target_get_position(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, Point2i());
+
+	return rt->position;
+};
+
 void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND(!rt);
@@ -1428,9 +1417,9 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in
 }
 
 // TODO: convert to Size2i internally
-Size2i TextureStorage::render_target_get_size(RID p_render_target) {
+Size2i TextureStorage::render_target_get_size(RID p_render_target) const {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
-	ERR_FAIL_COND_V(!rt, Size2());
+	ERR_FAIL_COND_V(!rt, Size2i());
 
 	return rt->size;
 }
@@ -1439,105 +1428,7 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, RID());
 
-	if (rt->external.fbo == 0) {
-		return rt->texture;
-	} else {
-		return rt->external.texture;
-	}
-}
-
-void TextureStorage::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {
-	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
-	ERR_FAIL_COND(!rt);
-
-	if (p_texture_id == 0) {
-		if (rt->external.fbo != 0) {
-			// free this
-			glDeleteFramebuffers(1, &rt->external.fbo);
-
-			// and this
-			if (rt->external.depth != 0) {
-				glDeleteRenderbuffers(1, &rt->external.depth);
-			}
-
-			// clean up our texture
-			Texture *t = get_texture(rt->external.texture);
-			t->alloc_height = 0;
-			t->alloc_width = 0;
-			t->width = 0;
-			t->height = 0;
-			t->active = false;
-			texture_free(rt->external.texture);
-			//memdelete(t);
-
-			rt->external.fbo = 0;
-			rt->external.color = 0;
-			rt->external.depth = 0;
-		}
-	} else {
-		Texture *t;
-
-		if (rt->external.fbo == 0) {
-			// create our fbo
-			glGenFramebuffers(1, &rt->external.fbo);
-			glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo);
-
-			// allocate a texture
-			t = memnew(Texture);
-
-			t->type = Texture::TYPE_2D;
-			t->width = 0;
-			t->height = 0;
-			t->alloc_height = 0;
-			t->alloc_width = 0;
-			t->format = Image::FORMAT_RGBA8;
-			t->target = GL_TEXTURE_2D;
-			t->gl_format_cache = 0;
-			t->gl_internal_format_cache = 0;
-			t->gl_type_cache = 0;
-			t->total_data_size = 0;
-			t->mipmaps = 1;
-			t->active = true;
-			t->tex_id = 0;
-			t->render_target = rt;
-			t->is_render_target = true;
-
-			//rt->external.texture = make_rid(t);
-
-		} else {
-			// bind our frame buffer
-			glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo);
-
-			// find our texture
-			t = get_texture(rt->external.texture);
-		}
-
-		// set our texture
-		t->tex_id = p_texture_id;
-		rt->external.color = p_texture_id;
-
-		// size shouldn't be different
-		t->width = rt->size.x;
-		t->height = rt->size.y;
-		t->alloc_height = rt->size.x;
-		t->alloc_width = rt->size.y;
-
-		// Switch our texture on our frame buffer
-		{
-			// set our texture as the destination for our framebuffer
-			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0);
-		}
-
-		// check status and unbind
-		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-		glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
-
-		if (status != GL_FRAMEBUFFER_COMPLETE) {
-			WARN_PRINT("framebuffer fail, status: " + get_framebuffer_error(status));
-		}
-
-		ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE);
-	}
+	return rt->texture;
 }
 
 void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) {
@@ -1550,6 +1441,13 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t
 	_update_render_target(rt);
 }
 
+bool TextureStorage::render_target_get_transparent(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, false);
+
+	return rt->is_transparent;
+}
+
 void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND(!rt);
@@ -1564,7 +1462,14 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo
 	_update_render_target(rt);
 }
 
-bool TextureStorage::render_target_was_used(RID p_render_target) {
+bool TextureStorage::render_target_get_direct_to_screen(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, false);
+
+	return rt->direct_to_screen;
+}
+
+bool TextureStorage::render_target_was_used(RID p_render_target) const {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, false);
 
@@ -1591,6 +1496,13 @@ void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSA
 	_update_render_target(rt);
 }
 
+RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RS::VIEWPORT_MSAA_DISABLED);
+
+	return rt->msaa;
+}
+
 void TextureStorage::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND(!rt);

+ 21 - 17
drivers/gles3/storage/texture_storage.h

@@ -327,16 +327,6 @@ private:
 };
 
 struct RenderTarget {
-	struct External {
-		GLuint fbo = 0;
-		GLuint color = 0;
-		GLuint depth = 0;
-		RID texture;
-
-		External() {
-		}
-	} external;
-
 	Point2i position = Point2i(0, 0);
 	Size2i size = Size2i(0, 0);
 	int mipmap_count = 1;
@@ -524,17 +514,19 @@ public:
 
 	virtual RID render_target_create() override;
 	virtual void render_target_free(RID p_rid) override;
+
 	virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override;
+	virtual Point2i render_target_get_position(RID p_render_target) const override;
 	virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override;
-	Size2i render_target_get_size(RID p_render_target);
-	virtual RID render_target_get_texture(RID p_render_target) override;
-	virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override;
-
+	virtual Size2i render_target_get_size(RID p_render_target) const override;
 	virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override;
+	virtual bool render_target_get_transparent(RID p_render_target) const override;
 	virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override;
-	virtual bool render_target_was_used(RID p_render_target) override;
+	virtual bool render_target_get_direct_to_screen(RID p_render_target) const override;
+	virtual bool render_target_was_used(RID p_render_target) const override;
 	void render_target_clear_used(RID p_render_target);
 	virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
+	virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
 
 	// new
 	void render_target_set_as_unused(RID p_render_target) override {
@@ -554,8 +546,20 @@ public:
 	void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
 	void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
 	void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region);
-	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override{};
-	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override{};
+
+	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
+	virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
+	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }
+
+	virtual void render_target_set_override_color(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); }
+	virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); }
+	virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); }
+
+	virtual RID render_target_get_texture(RID p_render_target) override;
 
 	void bind_framebuffer(GLuint framebuffer) {
 		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

+ 1 - 0
modules/openxr/SCsub

@@ -92,6 +92,7 @@ if env["vulkan"]:
     env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
 
 env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_depth_extension.cpp")
 env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp")
 env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp")
 env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp")

+ 58 - 0
modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp

@@ -0,0 +1,58 @@
+/*************************************************************************/
+/*  openxr_composition_layer_depth_extension.cpp                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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_composition_layer_depth_extension.h"
+
+OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::singleton = nullptr;
+
+OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::get_singleton() {
+	return singleton;
+}
+
+OpenXRCompositionLayerDepthExtension::OpenXRCompositionLayerDepthExtension(OpenXRAPI *p_openxr_api) :
+		OpenXRExtensionWrapper(p_openxr_api) {
+	singleton = this;
+
+	request_extensions[XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME] = &available;
+}
+
+OpenXRCompositionLayerDepthExtension::~OpenXRCompositionLayerDepthExtension() {
+	singleton = nullptr;
+}
+
+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...
+
+	return nullptr;
+}

+ 53 - 0
modules/openxr/extensions/openxr_composition_layer_depth_extension.h

@@ -0,0 +1,53 @@
+/*************************************************************************/
+/*  openxr_composition_layer_depth_extension.h                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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_COMPOSITION_LAYER_DEPTH_EXTENSION_H
+#define OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H
+
+#include "openxr_composition_layer_provider.h"
+#include "openxr_extension_wrapper.h"
+
+class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
+public:
+	static OpenXRCompositionLayerDepthExtension *get_singleton();
+
+	OpenXRCompositionLayerDepthExtension(OpenXRAPI *p_openxr_api);
+	virtual ~OpenXRCompositionLayerDepthExtension() override;
+
+	bool is_available();
+	virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+
+private:
+	static OpenXRCompositionLayerDepthExtension *singleton;
+
+	bool available = false;
+};
+
+#endif // OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H

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

@@ -31,6 +31,7 @@
 #ifndef OPENXR_COMPOSITION_LAYER_PROVIDER_H
 #define OPENXR_COMPOSITION_LAYER_PROVIDER_H
 
+#include "openxr_extension_wrapper.h"
 #include <openxr/openxr.h>
 
 // Interface for OpenXR extensions that provide a composition layer.

+ 2 - 1
modules/openxr/extensions/openxr_extension_wrapper.h

@@ -100,11 +100,12 @@ public:
 class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper {
 public:
 	virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0;
+	virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) = 0;
 	virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0;
 	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) = 0;
 	virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0;
 	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0;
-	virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) = 0;
+	virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) = 0;
 
 	OpenXRGraphicsExtensionWrapper(OpenXRAPI *p_openxr_api) :
 			OpenXRExtensionWrapper(p_openxr_api){};

+ 33 - 41
modules/openxr/extensions/openxr_vulkan_extension.cpp

@@ -228,6 +228,12 @@ void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usab
 	p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_UINT);
 }
 
+void OpenXRVulkanExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) {
+	p_usable_swap_chains.push_back(VK_FORMAT_R32_SFLOAT);
+	p_usable_swap_chains.push_back(VK_FORMAT_D24_UNORM_S8_UINT);
+	p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT);
+}
+
 bool OpenXRVulkanExtension::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) {
 	XrSwapchainImageVulkanKHR *images = nullptr;
 
@@ -271,7 +277,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 
 	RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
 	RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
-	uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+	uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
 
 	switch (p_swapchain_format) {
 		case VK_FORMAT_R8G8B8A8_SRGB:
@@ -283,16 +289,32 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 			// 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 VK_FORMAT_B8G8R8A8_SRGB:
 			// format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
 			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			break;
 		case VK_FORMAT_R8G8B8A8_UINT:
 			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 			break;
 		case VK_FORMAT_B8G8R8A8_UINT:
 			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case VK_FORMAT_R32_SFLOAT:
+			format = RenderingDevice::DATA_FORMAT_R32_SFLOAT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case VK_FORMAT_D24_UNORM_S8_UINT:
+			format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case VK_FORMAT_D32_SFLOAT_S8_UINT:
+			format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 			break;
 		default:
 			// continue with our default value
@@ -328,8 +350,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 			break;
 	}
 
-	Vector<RID> image_rids;
-	Vector<RID> framebuffers;
+	Vector<RID> texture_rids;
 
 	// create Godot texture objects for each entry in our swapchain
 	for (uint64_t i = 0; i < swapchain_length; i++) {
@@ -344,19 +365,10 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 				1,
 				p_array_size);
 
-		image_rids.push_back(image_rid);
-
-		{
-			Vector<RID> fb;
-			fb.push_back(image_rid);
-
-			RID fb_rid = rendering_device->framebuffer_create(fb, RenderingDevice::INVALID_ID, p_array_size);
-			framebuffers.push_back(fb_rid);
-		}
+		texture_rids.push_back(image_rid);
 	}
 
-	data->image_rids = image_rids;
-	data->framebuffers = framebuffers;
+	data->texture_rids = texture_rids;
 
 	memfree(images);
 
@@ -377,26 +389,12 @@ bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z
 	return true;
 }
 
-bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) {
+RID OpenXRVulkanExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
 	SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
-	ERR_FAIL_NULL_V(data, false);
-	ERR_FAIL_COND_V(p_from_render_target.is_null(), false);
+	ERR_FAIL_NULL_V(data, RID());
 
-	RID source_image = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(p_from_render_target);
-	ERR_FAIL_COND_V(source_image.is_null(), false);
-
-	RID depth_image; // TODO implement
-
-	ERR_FAIL_INDEX_V(p_image_index, data->framebuffers.size(), false);
-	RID fb = data->framebuffers[p_image_index];
-	ERR_FAIL_COND_V(fb.is_null(), false);
-
-	// Our vulkan extension can only be used in conjunction with our vulkan renderer.
-	RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
-	ERR_FAIL_NULL_V(copy_effects, false);
-	copy_effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
-
-	return true;
+	ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
+	return data->texture_rids[p_image_index];
 }
 
 void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
@@ -411,17 +409,11 @@ void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g
 	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
 	ERR_FAIL_NULL(rendering_device);
 
-	for (int i = 0; i < data->image_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->image_rids[i]);
-	}
-	data->image_rids.clear();
-
-	for (int i = 0; i < data->framebuffers.size(); i++) {
+	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->framebuffers[i]);
+		rendering_device->free(data->texture_rids[i]);
 	}
-	data->framebuffers.clear();
+	data->texture_rids.clear();
 
 	memdelete(data);
 	*p_swapchain_graphics_data = nullptr;

+ 3 - 3
modules/openxr/extensions/openxr_vulkan_extension.h

@@ -69,11 +69,12 @@ public:
 	virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) 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 bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) override;
+	virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
 
 private:
 	static OpenXRVulkanExtension *singleton;
@@ -81,8 +82,7 @@ private:
 
 	struct SwapchainGraphicsData {
 		bool is_multiview;
-		Vector<RID> image_rids;
-		Vector<RID> framebuffers;
+		Vector<RID> texture_rids;
 	};
 
 	bool check_graphics_api_support(XrVersion p_desired_version);

+ 145 - 55
modules/openxr/openxr_api.cpp

@@ -49,6 +49,7 @@
 #include "extensions/openxr_vulkan_extension.h"
 #endif
 
+#include "extensions/openxr_composition_layer_depth_extension.h"
 #include "extensions/openxr_fb_passthrough_extension_wrapper.h"
 #include "extensions/openxr_hand_tracking_extension.h"
 #include "extensions/openxr_htc_vive_tracker_extension.h"
@@ -663,7 +664,7 @@ bool OpenXRAPI::is_swapchain_format_supported(int64_t p_swapchain_format) {
 	return false;
 }
 
-bool OpenXRAPI::create_main_swapchain() {
+bool OpenXRAPI::create_swapchains() {
 	ERR_FAIL_NULL_V(graphics_extension, false);
 	ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
 
@@ -681,34 +682,36 @@ bool OpenXRAPI::create_main_swapchain() {
 		already rendering the next frame.
 
 		Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
-		as we render 3D content into internal buffers that are copied into the swapchain, we don't get any of the performance gains
-		until such time as we implement VRS.
+		as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
 	*/
 
-	// Build a vector with swapchain formats we want to use, from best fit to worst
-	Vector<int64_t> usable_swapchain_formats;
-	int64_t swapchain_format_to_use = 0;
+	Size2 recommended_size = get_recommended_target_size();
 
-	graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
+	// We start with our color swapchain...
+	{
+		// Build a vector with swapchain formats we want to use, from best fit to worst
+		Vector<int64_t> usable_swapchain_formats;
+		int64_t swapchain_format_to_use = 0;
 
-	// now find out which one is supported
-	for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
-		if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
-			swapchain_format_to_use = usable_swapchain_formats[i];
-		}
-	}
+		graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
 
-	if (swapchain_format_to_use == 0) {
-		swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
-		print_line("Couldn't find usable swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
-	} else {
-		print_line("Using swap chain format:", get_swapchain_format_name(swapchain_format_to_use));
-	}
+		// now find out which one is supported
+		for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
+			if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
+				swapchain_format_to_use = usable_swapchain_formats[i];
+			}
+		}
 
-	Size2 recommended_size = get_recommended_target_size();
+		if (swapchain_format_to_use == 0) {
+			swapchain_format_to_use = 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(swapchain_format_to_use), "instead.");
+		} else {
+			print_line("Using color swap chain format:", get_swapchain_format_name(swapchain_format_to_use));
+		}
 
-	if (!create_swapchain(swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchain, &swapchain_graphics_data)) {
-		return false;
+		if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) {
+			return false;
+		}
 	}
 
 	views = (XrView *)memalloc(sizeof(XrView) * view_count);
@@ -717,18 +720,73 @@ bool OpenXRAPI::create_main_swapchain() {
 	projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
 	ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
 
+	// We create our depth swapchain if:
+	// - we support our depth layer extension
+	// - we have our spacewarp extension (not yet implemented)
+	if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
+		// Build a vector with swapchain formats we want to use, from best fit to worst
+		Vector<int64_t> usable_swapchain_formats;
+		int64_t swapchain_format_to_use = 0;
+
+		graphics_extension->get_usable_depth_formats(usable_swapchain_formats);
+
+		// now find out which one is supported
+		for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
+			if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
+				swapchain_format_to_use = usable_swapchain_formats[i];
+			}
+		}
+
+		if (swapchain_format_to_use == 0) {
+			swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
+			print_line("Couldn't find usable depth swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
+		} else {
+			print_line("Using depth swap chain format:", get_swapchain_format_name(swapchain_format_to_use));
+		}
+
+		if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) {
+			return false;
+		}
+
+		depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
+		ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
+	}
+
+	// We create our velocity swapchain if:
+	// - we have our spacewarp extension (not yet implemented)
+	{
+		// TBD
+	}
+
 	for (uint32_t i = 0; i < view_count; i++) {
 		views[i].type = XR_TYPE_VIEW;
 		views[i].next = nullptr;
 
 		projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
 		projection_views[i].next = nullptr;
-		projection_views[i].subImage.swapchain = swapchain;
+		projection_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain;
 		projection_views[i].subImage.imageArrayIndex = i;
 		projection_views[i].subImage.imageRect.offset.x = 0;
 		projection_views[i].subImage.imageRect.offset.y = 0;
 		projection_views[i].subImage.imageRect.extent.width = recommended_size.width;
 		projection_views[i].subImage.imageRect.extent.height = recommended_size.height;
+
+		if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
+			projection_views[i].next = &depth_views[i];
+
+			depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
+			depth_views[i].next = nullptr;
+			depth_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain;
+			depth_views[i].subImage.imageArrayIndex = 0;
+			depth_views[i].subImage.imageRect.offset.x = 0;
+			depth_views[i].subImage.imageRect.offset.y = 0;
+			depth_views[i].subImage.imageRect.extent.width = recommended_size.width;
+			depth_views[i].subImage.imageRect.extent.height = recommended_size.height;
+			depth_views[i].minDepth = 0.0;
+			depth_views[i].maxDepth = 1.0;
+			depth_views[i].nearZ = 0.01; // Near and far Z will be set to the correct values in fill_projection_matrix
+			depth_views[i].farZ = 100.0;
+		}
 	};
 
 	return true;
@@ -740,7 +798,7 @@ void OpenXRAPI::destroy_session() {
 	}
 
 	if (graphics_extension) {
-		graphics_extension->cleanup_swapchain_graphics_data(&swapchain_graphics_data);
+		graphics_extension->cleanup_swapchain_graphics_data(&swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data);
 	}
 
 	if (views != nullptr) {
@@ -753,9 +811,16 @@ void OpenXRAPI::destroy_session() {
 		projection_views = nullptr;
 	}
 
-	if (swapchain != XR_NULL_HANDLE) {
-		xrDestroySwapchain(swapchain);
-		swapchain = XR_NULL_HANDLE;
+	if (depth_views != nullptr) {
+		memfree(depth_views);
+		depth_views = nullptr;
+	}
+
+	for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+		if (swapchains[i].swapchain != XR_NULL_HANDLE) {
+			xrDestroySwapchain(swapchains[i].swapchain);
+			swapchains[i].swapchain = XR_NULL_HANDLE;
+		}
 	}
 
 	if (supported_swapchain_formats != nullptr) {
@@ -789,7 +854,7 @@ void OpenXRAPI::destroy_session() {
 	}
 }
 
-bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
+bool OpenXRAPI::create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
 	ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
 	ERR_FAIL_NULL_V(graphics_extension, false);
 
@@ -807,7 +872,7 @@ bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, u
 		XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
 		next_pointer, // next
 		0, // createFlags
-		XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, // usageFlags
+		p_usage_flags, // usageFlags
 		p_swapchain_format, // format
 		p_sample_count, // sampleCount
 		p_width, // width
@@ -871,7 +936,7 @@ bool OpenXRAPI::on_state_ready() {
 	// That will be very very ugly
 	// The other possibility is to create a separate OpenXRViewport type specifically for this goal as part of our OpenXR module
 
-	if (!create_main_swapchain()) {
+	if (!create_swapchains()) {
 		return false;
 	}
 
@@ -1304,6 +1369,15 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z
 		return false;
 	}
 
+	// if we're using depth views, make sure we update our near and far there...
+	if (depth_views != nullptr) {
+		for (uint32_t i = 0; i < view_count; i++) {
+			depth_views[i].nearZ = p_z_near;
+			depth_views[i].farZ = p_z_far;
+		}
+	}
+
+	// now update our projection
 	return graphics_extension->create_projection_fov(views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
 }
 
@@ -1442,15 +1516,15 @@ bool OpenXRAPI::process() {
 	return true;
 }
 
-bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index) {
-	ERR_FAIL_COND_V(image_acquired, true); // this was not released when it should be, error out and re-use...
+bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
+	ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // this was not released when it should be, error out and re-use...
 
 	XrResult result;
 	XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
 		XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type
 		nullptr // next
 	};
-	result = xrAcquireSwapchainImage(p_swapchain, &swapchain_image_acquire_info, &r_image_index);
+	result = xrAcquireSwapchainImage(p_swapchain.swapchain, &swapchain_image_acquire_info, &p_swapchain.image_index);
 	if (XR_FAILED(result)) {
 		print_line("OpenXR: failed to acquire swapchain image [", get_error_string(result), "]");
 		return false;
@@ -1462,7 +1536,7 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index)
 		17000000 // timeout in nanoseconds
 	};
 
-	result = xrWaitSwapchainImage(p_swapchain, &swapchain_image_wait_info);
+	result = xrWaitSwapchainImage(p_swapchain.swapchain, &swapchain_image_wait_info);
 	if (XR_FAILED(result)) {
 		print_line("OpenXR: failed to wait for swapchain image [", get_error_string(result), "]");
 		return false;
@@ -1471,12 +1545,12 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index)
 	return true;
 }
 
-bool OpenXRAPI::release_image(XrSwapchain p_swapchain) {
+bool OpenXRAPI::release_image(OpenXRSwapChainInfo &p_swapchain) {
 	XrSwapchainImageReleaseInfo swapchain_image_release_info = {
 		XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
 		nullptr // next
 	};
-	XrResult result = xrReleaseSwapchainImage(swapchain, &swapchain_image_release_info);
+	XrResult result = xrReleaseSwapchainImage(p_swapchain.swapchain, &swapchain_image_release_info);
 	if (XR_FAILED(result)) {
 		print_line("OpenXR: failed to release swapchain image! [", get_error_string(result), "]");
 		return false;
@@ -1590,28 +1664,41 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
 
 	// TODO: at some point in time we may support multiple viewports in which case we need to handle that...
 
+	// Acquire our images
+	for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+		if (!swapchains[i].image_acquired && swapchains[i].swapchain != XR_NULL_HANDLE) {
+			if (!acquire_image(swapchains[i])) {
+				return false;
+			}
+			swapchains[i].image_acquired = true;
+		}
+	}
+
 	return true;
 }
 
+RID OpenXRAPI::get_color_texture() {
+	if (swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
+		return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_COLOR].image_index);
+	} else {
+		return RID();
+	}
+}
+
+RID OpenXRAPI::get_depth_texture() {
+	if (swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) {
+		return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index);
+	} else {
+		return RID();
+	}
+}
+
 void OpenXRAPI::post_draw_viewport(RID p_render_target) {
 	if (!can_render()) {
 		return;
 	}
 
-	// TODO: at some point in time we may support multiple viewports in which case we need to handle that...
-
-	// TODO: if we can get PR 51179 to work properly we can change away from this approach and move this into get_external_texture or something
-	if (!image_acquired) {
-		if (!acquire_image(swapchain, image_index)) {
-			return;
-		}
-		image_acquired = true;
-
-		// print_line("OpenXR: acquired image " + itos(image_index) + ", copying...");
-
-		// Copy our buffer into our swap chain (remove once PR 51179 is done)
-		graphics_extension->copy_render_target_to_image(p_render_target, swapchain_graphics_data, image_index);
-	}
+	// Nothing to do here at this point in time...
 };
 
 void OpenXRAPI::end_frame() {
@@ -1623,7 +1710,7 @@ void OpenXRAPI::end_frame() {
 		return;
 	}
 
-	if (frame_state.shouldRender && view_pose_valid && !image_acquired) {
+	if (frame_state.shouldRender && view_pose_valid && !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
 		print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
 	}
 
@@ -1631,7 +1718,7 @@ void OpenXRAPI::end_frame() {
 	// - shouldRender set to true
 	// - a valid view pose for projection_views[eye].pose to submit layer
 	// - an image to render
-	if (!frame_state.shouldRender || !view_pose_valid || !image_acquired) {
+	if (!frame_state.shouldRender || !view_pose_valid || !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
 		// submit 0 layers when we shouldn't render
 		XrFrameEndInfo frame_end_info = {
 			XR_TYPE_FRAME_END_INFO, // type
@@ -1652,10 +1739,12 @@ void OpenXRAPI::end_frame() {
 	}
 
 	// release our swapchain image if we acquired it
-	if (image_acquired) {
-		image_acquired = false; // whether we succeed or not, consider this released.
+	for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+		if (swapchains[i].image_acquired) {
+			swapchains[i].image_acquired = false; // whether we succeed or not, consider this released.
 
-		release_image(swapchain);
+			release_image(swapchains[i]);
+		}
 	}
 
 	for (uint32_t eye = 0; eye < view_count; eye++) {
@@ -1763,6 +1852,7 @@ OpenXRAPI::OpenXRAPI() {
 
 	// register our other extensions
 	register_extension_wrapper(memnew(OpenXRPalmPoseExtension(this)));
+	register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension(this)));
 	register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this)));
 	register_extension_wrapper(memnew(OpenXRHandTrackingExtension(this)));
 	register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper(this)));

+ 23 - 8
modules/openxr/openxr_api.h

@@ -120,15 +120,28 @@ private:
 
 	OpenXRGraphicsExtensionWrapper *graphics_extension = nullptr;
 	XrSystemGraphicsProperties graphics_properties;
-	void *swapchain_graphics_data = nullptr;
-	uint32_t image_index = 0;
-	bool image_acquired = false;
 
 	uint32_t view_count = 0;
 	XrViewConfigurationView *view_configuration_views = nullptr;
 	XrView *views = nullptr;
 	XrCompositionLayerProjectionView *projection_views = nullptr;
-	XrSwapchain swapchain = XR_NULL_HANDLE;
+	XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
+
+	enum OpenXRSwapChainTypes {
+		OPENXR_SWAPCHAIN_COLOR,
+		OPENXR_SWAPCHAIN_DEPTH,
+		// OPENXR_SWAPCHAIN_VELOCITY,
+		OPENXR_SWAPCHAIN_MAX
+	};
+
+	struct OpenXRSwapChainInfo {
+		XrSwapchain swapchain = XR_NULL_HANDLE;
+		void *swapchain_graphics_data = nullptr;
+		uint32_t image_index = 0;
+		bool image_acquired = false;
+	};
+
+	OpenXRSwapChainInfo swapchains[OPENXR_SWAPCHAIN_MAX];
 
 	XrSpace play_space = XR_NULL_HANDLE;
 	XrSpace view_space = XR_NULL_HANDLE;
@@ -212,13 +225,13 @@ private:
 	bool setup_spaces();
 	bool load_supported_swapchain_formats();
 	bool is_swapchain_format_supported(int64_t p_swapchain_format);
-	bool create_main_swapchain();
+	bool create_swapchains();
 	void destroy_session();
 
 	// swapchains
-	bool create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
-	bool acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index);
-	bool release_image(XrSwapchain p_swapchain);
+	bool create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
+	bool acquire_image(OpenXRSwapChainInfo &p_swapchain);
+	bool release_image(OpenXRSwapChainInfo &p_swapchain);
 
 	// action map
 	struct Tracker { // Trackers represent tracked physical objects such as controllers, pucks, etc.
@@ -318,6 +331,8 @@ public:
 
 	void pre_render();
 	bool pre_draw_viewport(RID p_render_target);
+	RID get_color_texture();
+	RID get_depth_texture();
 	void post_draw_viewport(RID p_render_target);
 	void end_frame();
 

+ 18 - 0
modules/openxr/openxr_interface.cpp

@@ -648,6 +648,22 @@ Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_as
 	return cm;
 }
 
+RID OpenXRInterface::get_color_texture() {
+	if (openxr_api) {
+		return openxr_api->get_color_texture();
+	} else {
+		return RID();
+	}
+}
+
+RID OpenXRInterface::get_depth_texture() {
+	if (openxr_api) {
+		return openxr_api->get_depth_texture();
+	} else {
+		return RID();
+	}
+}
+
 void OpenXRInterface::process() {
 	if (openxr_api) {
 		// do our normal process
@@ -707,6 +723,7 @@ bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {
 Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
 	Vector<BlitToScreen> blit_to_screen;
 
+#ifndef ANDROID_ENABLED
 	// If separate HMD we should output one eye to screen
 	if (p_screen_rect != Rect2()) {
 		BlitToScreen blit;
@@ -732,6 +749,7 @@ Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, co
 		blit.dst_rect = dst_rect;
 		blit_to_screen.push_back(blit);
 	}
+#endif
 
 	if (openxr_api) {
 		openxr_api->post_draw_viewport(p_render_target);

+ 3 - 0
modules/openxr/openxr_interface.h

@@ -126,6 +126,9 @@ public:
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
 	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
 
+	virtual RID get_color_texture() override;
+	virtual RID get_depth_texture() override;
+
 	virtual void process() override;
 	virtual void pre_render() override;
 	bool pre_draw_viewport(RID p_render_target) override;

+ 19 - 5
servers/rendering/dummy/storage/texture_storage.h

@@ -157,14 +157,17 @@ public:
 	virtual RID render_target_create() override { return RID(); }
 	virtual void render_target_free(RID p_rid) override {}
 	virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override {}
+	virtual Point2i render_target_get_position(RID p_render_target) const override { return Point2i(); }
 	virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override {}
-	virtual RID render_target_get_texture(RID p_render_target) override { return RID(); }
-	virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {}
+	virtual Size2i render_target_get_size(RID p_render_target) const override { return Size2i(); }
 	virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override {}
+	virtual bool render_target_get_transparent(RID p_render_target) const override { return false; }
 	virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override {}
-	virtual bool render_target_was_used(RID p_render_target) override { return false; }
+	virtual bool render_target_get_direct_to_screen(RID p_render_target) const override { return false; }
+	virtual bool render_target_was_used(RID p_render_target) const override { return false; }
 	virtual void render_target_set_as_unused(RID p_render_target) override {}
 	virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override {}
+	virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override { return RS::VIEWPORT_MSAA_DISABLED; }
 
 	virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {}
 	virtual bool render_target_is_clear_requested(RID p_render_target) override { return false; }
@@ -176,8 +179,19 @@ public:
 	virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override { return Rect2i(); }
 	virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override {}
 
-	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override{};
-	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override{};
+	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
+	virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
+	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }
+
+	virtual void render_target_set_override_color(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); }
+	virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); }
+	virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) override {}
+	virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); }
+
+	virtual RID render_target_get_texture(RID p_render_target) override { return RID(); }
 };
 
 } // namespace RendererDummy

+ 2 - 2
servers/rendering/renderer_rd/environment/gi.cpp

@@ -1644,8 +1644,8 @@ void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projection
 		RD::get_singleton()->compute_list_end();
 	}
 
-	Size2 rtsize = texture_storage->render_target_get_size(p_render_target);
-	copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2(Vector2(), rtsize), true, false, false, false, RID(), p_view_count > 1);
+	Size2i rtsize = texture_storage->render_target_get_size(p_render_target);
+	copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2i(Point2i(), rtsize), true, false, false, false, RID(), p_view_count > 1);
 }
 
 void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) {

+ 3 - 3
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -2095,17 +2095,17 @@ void RenderForwardClustered::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD
 	RID render_target = p_render_buffers->get_render_target();
 
 	if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSAO && rb_data->ss_effects_data.ssao.ao_final.is_valid()) {
-		Size2 rtsize = texture_storage->render_target_get_size(render_target);
+		Size2i rtsize = texture_storage->render_target_get_size(render_target);
 		copy_effects->copy_to_fb_rect(rb_data->ss_effects_data.ssao.ao_final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, true);
 	}
 
 	if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSIL && rb_data->ss_effects_data.ssil.ssil_final.is_valid()) {
-		Size2 rtsize = texture_storage->render_target_get_size(render_target);
+		Size2i rtsize = texture_storage->render_target_get_size(render_target);
 		copy_effects->copy_to_fb_rect(rb_data->ss_effects_data.ssil.ssil_final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
 	}
 
 	if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && p_render_buffers->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) {
-		Size2 rtsize = texture_storage->render_target_get_size(render_target);
+		Size2i rtsize = texture_storage->render_target_get_size(render_target);
 		RID ambient_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT);
 		RID reflection_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION);
 		copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, p_render_buffers->get_view_count() > 1);

+ 1 - 5
servers/rendering/renderer_rd/renderer_compositor_rd.cpp

@@ -44,13 +44,9 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
 	}
 
 	for (int i = 0; i < p_amount; i++) {
-		RID texture = texture_storage->render_target_get_texture(p_render_targets[i].render_target);
-		ERR_CONTINUE(texture.is_null());
-		RID rd_texture = texture_storage->texture_get_rd_texture(texture);
+		RID rd_texture = texture_storage->render_target_get_rd_texture(p_render_targets[i].render_target);
 		ERR_CONTINUE(rd_texture.is_null());
 
-		// TODO if keep_3d_linear was set when rendering to this render target we need to add a linear->sRGB conversion in.
-
 		if (!render_target_descriptors.has(rd_texture) || !RD::get_singleton()->uniform_set_is_valid(render_target_descriptors[rd_texture])) {
 			Vector<RD::Uniform> uniforms;
 			RD::Uniform u;

+ 5 - 5
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -828,7 +828,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
 	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS) {
 		if (RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture().is_valid()) {
 			RID shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture();
-			Size2 rtsize = texture_storage->render_target_get_size(render_target);
+			Size2i rtsize = texture_storage->render_target_get_size(render_target);
 
 			copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, true);
 		}
@@ -838,7 +838,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
 		RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture();
 
 		if (decal_atlas.is_valid()) {
-			Size2 rtsize = texture_storage->render_target_get_size(render_target);
+			Size2i rtsize = texture_storage->render_target_get_size(render_target);
 
 			copy_effects->copy_to_fb_rect(decal_atlas, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, false, true);
 		}
@@ -846,7 +846,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
 
 	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) {
 		if (p_render_buffers->luminance.current.is_valid()) {
-			Size2 rtsize = texture_storage->render_target_get_size(render_target);
+			Size2i rtsize = texture_storage->render_target_get_size(render_target);
 
 			copy_effects->copy_to_fb_rect(p_render_buffers->luminance.current, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true);
 		}
@@ -859,13 +859,13 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
 
 	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) {
 		if (p_occlusion_buffer.is_valid()) {
-			Size2 rtsize = texture_storage->render_target_get_size(render_target);
+			Size2i rtsize = texture_storage->render_target_get_size(render_target);
 			copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize), true, false);
 		}
 	}
 
 	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(p_render_buffers).is_valid()) {
-		Size2 rtsize = texture_storage->render_target_get_size(render_target);
+		Size2i rtsize = texture_storage->render_target_get_size(render_target);
 		copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
 	}
 }

+ 56 - 2
servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp

@@ -178,7 +178,7 @@ void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_interna
 
 	// Create our depth buffer
 	{
-		// TODO If we have depth buffer supplied externally, pick this up
+		// TODO Lazy create this in case we've got an external depth buffer
 
 		RD::DataFormat format;
 		uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
@@ -490,6 +490,28 @@ Ref<RenderBufferCustomDataRD> RenderSceneBuffersRD::get_custom_data(const String
 	return ret;
 }
 
+// Depth texture
+
+RID RenderSceneBuffersRD::get_depth_texture() {
+	RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
+	RID depth = texture_storage->render_target_get_override_depth(render_target);
+	if (depth.is_valid()) {
+		return depth;
+	} else {
+		return get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH);
+	}
+}
+
+RID RenderSceneBuffersRD::get_depth_texture(const uint32_t p_layer) {
+	RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
+	RID depth_slice = texture_storage->render_target_get_override_depth_slice(render_target, p_layer);
+	if (depth_slice.is_valid()) {
+		return depth_slice;
+	} else {
+		return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, p_layer, 0);
+	}
+}
+
 // Velocity texture.
 
 void RenderSceneBuffersRD::ensure_velocity() {
@@ -516,6 +538,20 @@ void RenderSceneBuffersRD::ensure_velocity() {
 	}
 }
 
+bool RenderSceneBuffersRD::has_velocity_buffer(bool p_has_msaa) {
+	if (p_has_msaa) {
+		return has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA);
+	} else {
+		RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
+		RID velocity = texture_storage->render_target_get_override_velocity(render_target);
+		if (velocity.is_valid()) {
+			return true;
+		} else {
+			return has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY);
+		}
+	}
+}
+
 RID RenderSceneBuffersRD::get_velocity_buffer(bool p_get_msaa) {
 	if (p_get_msaa) {
 		if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA)) {
@@ -524,10 +560,28 @@ RID RenderSceneBuffersRD::get_velocity_buffer(bool p_get_msaa) {
 			return get_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA);
 		}
 	} else {
-		if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY)) {
+		RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
+		RID velocity = texture_storage->render_target_get_override_velocity(render_target);
+		if (velocity.is_valid()) {
+			return velocity;
+		} else if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY)) {
 			return RID();
 		} else {
 			return get_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY);
 		}
 	}
 }
+
+RID RenderSceneBuffersRD::get_velocity_buffer(bool p_get_msaa, uint32_t p_layer) {
+	if (p_get_msaa) {
+		return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, p_layer, 0);
+	} else {
+		RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
+		RID velocity_slice = texture_storage->render_target_get_override_velocity_slice(render_target, p_layer);
+		if (velocity_slice.is_valid()) {
+			return velocity_slice;
+		} else {
+			return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, p_layer, 0);
+		}
+	}
+}

+ 4 - 8
servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h

@@ -189,12 +189,8 @@ public:
 		return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR, p_layer, 0);
 	}
 
-	_FORCE_INLINE_ RID get_depth_texture() const {
-		return get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH);
-	}
-	_FORCE_INLINE_ RID get_depth_texture(const uint32_t p_layer) {
-		return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, p_layer, 0);
-	}
+	RID get_depth_texture();
+	RID get_depth_texture(const uint32_t p_layer);
 
 	// back buffer (color)
 	RID get_back_buffer_texture() const { return has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) ? get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) : RID(); } // We (re)use our blur texture here.
@@ -202,9 +198,9 @@ public:
 	// Velocity, currently only used by TAA (Clustered) but we'll be using this in other places soon too.
 
 	void ensure_velocity();
-	bool has_velocity_buffer(bool p_has_msaa) { return has_texture(RB_SCOPE_BUFFERS, p_has_msaa ? RB_TEX_VELOCITY_MSAA : RB_TEX_VELOCITY); }
+	bool has_velocity_buffer(bool p_has_msaa);
 	RID get_velocity_buffer(bool p_get_msaa);
-	RID get_velocity_buffer(bool p_get_msaa, uint32_t p_layer) { return get_texture_slice(RB_SCOPE_BUFFERS, p_get_msaa ? RB_TEX_VELOCITY_MSAA : RB_TEX_VELOCITY, p_layer, 0); }
+	RID get_velocity_buffer(bool p_get_msaa, uint32_t p_layer);
 
 	////////////////////////////////////////////////////////////////////////////////////////////////////////////
 	// Everything after this needs to be re-evaluated, this is all old implementation

+ 139 - 27
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -30,6 +30,7 @@
 
 #include "texture_storage.h"
 #include "../effects/copy_effects.h"
+#include "../framebuffer_cache_rd.h"
 #include "material_storage.h"
 #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
 
@@ -2359,10 +2360,26 @@ void TextureStorage::update_decal_buffer(const PagedArray<RID> &p_decals, const
 
 /* RENDER TARGET API */
 
+RID TextureStorage::RenderTarget::get_framebuffer() {
+	// Note that if we're using an overridden color buffer, we're likely cycling through a texture chain.
+	// this is where our framebuffer cache comes in clutch..
+
+	if (msaa != RS::VIEWPORT_MSAA_DISABLED) {
+		return FramebufferCacheRD::get_singleton()->get_cache_multiview(view_count, color_multisample, overridden.color.is_valid() ? overridden.color : color);
+	} else {
+		return FramebufferCacheRD::get_singleton()->get_cache_multiview(view_count, overridden.color.is_valid() ? overridden.color : color);
+	}
+}
+
 void TextureStorage::_clear_render_target(RenderTarget *rt) {
-	//free in reverse dependency order
-	if (rt->framebuffer.is_valid()) {
-		RD::get_singleton()->free(rt->framebuffer);
+	// clear overrides, we assume these are freed by the object that created them
+	rt->overridden.color = RID();
+	rt->overridden.depth = RID();
+	rt->overridden.velocity = RID();
+	rt->overridden.cached_slices.clear(); // these are automatically freed when their parent textures are freed so just clear
+
+	// free in reverse dependency order
+	if (rt->framebuffer_uniform_set.is_valid()) {
 		rt->framebuffer_uniform_set = RID(); //chain deleted
 	}
 
@@ -2384,7 +2401,6 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
 
 	_render_target_clear_sdf(rt);
 
-	rt->framebuffer = RID();
 	rt->color = RID();
 	rt->color_multisample = RID();
 }
@@ -2432,11 +2448,10 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
 		}
 	}
 
+	// TODO see if we can lazy create this once we actually use it as we may not need to create this if we have an overridden color buffer...
 	rt->color = RD::get_singleton()->texture_create(rd_color_attachment_format, rd_view);
 	ERR_FAIL_COND(rt->color.is_null());
 
-	Vector<RID> fb_textures;
-
 	if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) {
 		// Use the texture format of the color attachment for the multisample color attachment.
 		RD::TextureFormat rd_color_multisample_format = rd_color_attachment_format;
@@ -2450,15 +2465,8 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
 		RD::TextureView rd_view_multisample;
 		rd_color_multisample_format.is_resolve_buffer = false;
 		rt->color_multisample = RD::get_singleton()->texture_create(rd_color_multisample_format, rd_view_multisample);
-		fb_textures.push_back(rt->color_multisample);
 		ERR_FAIL_COND(rt->color_multisample.is_null());
 	}
-	fb_textures.push_back(rt->color);
-	rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count);
-	if (rt->framebuffer.is_null()) {
-		_clear_render_target(rt);
-		ERR_FAIL_COND(rt->framebuffer.is_null());
-	}
 
 	{ //update texture
 
@@ -2568,6 +2576,11 @@ void TextureStorage::render_target_set_position(RID p_render_target, int p_x, in
 	//unused for this render target
 }
 
+Point2i TextureStorage::render_target_get_position(RID p_render_target) const {
+	//unused for this render target
+	return Point2i();
+}
+
 void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND(!rt);
@@ -2579,6 +2592,13 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in
 	}
 }
 
+Size2i TextureStorage::render_target_get_size(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, Size2i());
+
+	return rt->size;
+}
+
 RID TextureStorage::render_target_get_texture(RID p_render_target) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, RID());
@@ -2586,7 +2606,84 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) {
 	return rt->texture;
 }
 
-void TextureStorage::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {
+void TextureStorage::render_target_set_override_color(RID p_render_target, RID p_texture) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+
+	rt->overridden.color = p_texture;
+}
+
+RID TextureStorage::render_target_get_override_color(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	return rt->overridden.color;
+}
+
+void TextureStorage::render_target_set_override_depth(RID p_render_target, RID p_texture) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+
+	rt->overridden.depth = p_texture;
+}
+
+RID TextureStorage::render_target_get_override_depth(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	return rt->overridden.depth;
+}
+
+RID TextureStorage::render_target_get_override_depth_slice(RID p_render_target, const uint32_t p_layer) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	if (rt->overridden.depth.is_null()) {
+		return RID();
+	} else if (rt->view_count == 1) {
+		return rt->overridden.depth;
+	} else {
+		RenderTarget::RTOverridden::SliceKey key(rt->overridden.depth, p_layer);
+
+		if (!rt->overridden.cached_slices.has(key)) {
+			rt->overridden.cached_slices[key] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->overridden.depth, p_layer, 0);
+		}
+
+		return rt->overridden.cached_slices[key];
+	}
+}
+
+void TextureStorage::render_target_set_override_velocity(RID p_render_target, RID p_texture) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+
+	rt->overridden.velocity = p_texture;
+}
+
+RID TextureStorage::render_target_get_override_velocity(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	return rt->overridden.velocity;
+}
+
+RID TextureStorage::render_target_get_override_velocity_slice(RID p_render_target, const uint32_t p_layer) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	if (rt->overridden.velocity.is_null()) {
+		return RID();
+	} else if (rt->view_count == 1) {
+		return rt->overridden.velocity;
+	} else {
+		RenderTarget::RTOverridden::SliceKey key(rt->overridden.velocity, p_layer);
+
+		if (!rt->overridden.cached_slices.has(key)) {
+			rt->overridden.cached_slices[key] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->overridden.velocity, p_layer, 0);
+		}
+
+		return rt->overridden.cached_slices[key];
+	}
 }
 
 void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_is_transparent) {
@@ -2596,10 +2693,21 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_i
 	_update_render_target(rt);
 }
 
+bool TextureStorage::render_target_get_transparent(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, false);
+
+	return rt->is_transparent;
+}
+
 void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, bool p_value) {
 }
 
-bool TextureStorage::render_target_was_used(RID p_render_target) {
+bool TextureStorage::render_target_get_direct_to_screen(RID p_render_target) const {
+	return false;
+}
+
+bool TextureStorage::render_target_was_used(RID p_render_target) const {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, false);
 	return rt->was_used;
@@ -2622,25 +2730,29 @@ void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSA
 	_update_render_target(rt);
 }
 
-Size2 TextureStorage::render_target_get_size(RID p_render_target) {
+RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) const {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
-	ERR_FAIL_COND_V(!rt, Size2());
+	ERR_FAIL_COND_V(!rt, RS::VIEWPORT_MSAA_DISABLED);
 
-	return rt->size;
+	return rt->msaa;
 }
 
 RID TextureStorage::render_target_get_rd_framebuffer(RID p_render_target) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, RID());
 
-	return rt->framebuffer;
+	return rt->get_framebuffer();
 }
 
 RID TextureStorage::render_target_get_rd_texture(RID p_render_target) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND_V(!rt, RID());
 
-	return rt->color;
+	if (rt->overridden.color.is_valid()) {
+		return rt->overridden.color;
+	} else {
+		return rt->color;
+	}
 }
 
 RID TextureStorage::render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer) {
@@ -2711,7 +2823,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
 	}
 	Vector<Color> clear_colors;
 	clear_colors.push_back(rt->clear_color);
-	RD::get_singleton()->draw_list_begin(rt->framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors);
+	RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors);
 	RD::get_singleton()->draw_list_end();
 	rt->clear_requested = false;
 }
@@ -3140,18 +3252,18 @@ void TextureStorage::render_target_set_vrs_mode(RID p_render_target, RS::Viewpor
 	rt->vrs_mode = p_mode;
 }
 
-void TextureStorage::render_target_set_vrs_texture(RID p_render_target, RID p_texture) {
+RS::ViewportVRSMode TextureStorage::render_target_get_vrs_mode(RID p_render_target) const {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
-	ERR_FAIL_COND(!rt);
+	ERR_FAIL_COND_V(!rt, RS::VIEWPORT_VRS_DISABLED);
 
-	rt->vrs_texture = p_texture;
+	return rt->vrs_mode;
 }
 
-RS::ViewportVRSMode TextureStorage::render_target_get_vrs_mode(RID p_render_target) const {
+void TextureStorage::render_target_set_vrs_texture(RID p_render_target, RID p_texture) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
-	ERR_FAIL_COND_V(!rt, RS::VIEWPORT_VRS_DISABLED);
+	ERR_FAIL_COND(!rt);
 
-	return rt->vrs_mode;
+	rt->vrs_texture = p_texture;
 }
 
 RID TextureStorage::render_target_get_vrs_texture(RID p_render_target) const {

+ 57 - 7
servers/rendering/renderer_rd/storage_rd/texture_storage.h

@@ -301,7 +301,6 @@ private:
 	struct RenderTarget {
 		Size2i size;
 		uint32_t view_count;
-		RID framebuffer;
 		RID color;
 		Vector<RID> color_slices;
 		RID color_multisample; // Needed when MSAA is enabled.
@@ -339,6 +338,43 @@ private:
 		RS::ViewportVRSMode vrs_mode = RS::VIEWPORT_VRS_DISABLED;
 		RID vrs_texture;
 
+		// overridden textures
+		struct RTOverridden {
+			RID color;
+			RID depth;
+			RID velocity;
+
+			// In a multiview scenario, which is the most likely where we
+			// override our destination textures, we need to obtain slices
+			// for each layer of these textures.
+			// These are likely changing every frame as we loop through
+			// texture chains hence we add a cache to manage these slices.
+			// For this we define a key using the RID of the texture and
+			// the layer for which we create a slice.
+			struct SliceKey {
+				RID rid;
+				uint32_t layer = 0;
+
+				bool operator==(const SliceKey &p_val) const {
+					return (rid == p_val.rid) && (layer == p_val.layer);
+				}
+
+				static uint32_t hash(const SliceKey &p_val) {
+					uint32_t h = hash_one_uint64(p_val.rid.get_id());
+					h = hash_murmur3_one_32(p_val.layer, h);
+					return hash_fmix32(h);
+				}
+
+				SliceKey() {}
+				SliceKey(RID p_rid, uint32_t p_layer) {
+					rid = p_rid;
+					layer = p_layer;
+				}
+			};
+
+			mutable HashMap<SliceKey, RID, SliceKey> cached_slices;
+		} overridden;
+
 		//texture generated for this owner (nor RD).
 		RID texture;
 		bool was_used;
@@ -346,6 +382,8 @@ private:
 		//clear request
 		bool clear_requested;
 		Color clear_color;
+
+		RID get_framebuffer();
 	};
 
 	mutable RID_Owner<RenderTarget> render_target_owner;
@@ -644,14 +682,17 @@ public:
 	virtual void render_target_free(RID p_rid) override;
 
 	virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override;
+	virtual Point2i render_target_get_position(RID p_render_target) const override;
 	virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override;
-	virtual RID render_target_get_texture(RID p_render_target) override;
-	virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override;
+	virtual Size2i render_target_get_size(RID p_render_target) const override;
 	virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override;
+	virtual bool render_target_get_transparent(RID p_render_target) const override;
 	virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override;
-	virtual bool render_target_was_used(RID p_render_target) override;
+	virtual bool render_target_get_direct_to_screen(RID p_render_target) const override;
+	virtual bool render_target_was_used(RID p_render_target) const override;
 	virtual void render_target_set_as_unused(RID p_render_target) override;
 	virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
+	virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
 
 	void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
 	void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
@@ -673,12 +714,21 @@ public:
 	bool render_target_is_sdf_enabled(RID p_render_target) const;
 
 	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override;
+	virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override;
 	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override;
+	virtual RID render_target_get_vrs_texture(RID p_render_target) const override;
 
-	RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const;
-	RID render_target_get_vrs_texture(RID p_render_target) const;
+	virtual void render_target_set_override_color(RID p_render_target, RID p_texture) override;
+	virtual RID render_target_get_override_color(RID p_render_target) const override;
+	virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) override;
+	virtual RID render_target_get_override_depth(RID p_render_target) const override;
+	RID render_target_get_override_depth_slice(RID p_render_target, const uint32_t p_layer) const;
+	virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) override;
+	virtual RID render_target_get_override_velocity(RID p_render_target) const override;
+	RID render_target_get_override_velocity_slice(RID p_render_target, const uint32_t p_layer) const;
+
+	virtual RID render_target_get_texture(RID p_render_target) override;
 
-	Size2 render_target_get_size(RID p_render_target);
 	RID render_target_get_rd_framebuffer(RID p_render_target);
 	RID render_target_get_rd_texture(RID p_render_target);
 	RID render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer);

+ 6 - 4
servers/rendering/renderer_viewport.cpp

@@ -664,9 +664,9 @@ void RendererViewport::draw_viewports() {
 
 		RSG::texture_storage->render_target_set_as_unused(vp->render_target);
 		if (vp->use_xr && xr_interface.is_valid()) {
-			// check for an external texture destination (disabled for now, not yet supported)
-			// RSG::texture_storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono));
-			RSG::texture_storage->render_target_set_external_texture(vp->render_target, 0);
+			RSG::texture_storage->render_target_set_override_color(vp->render_target, xr_interface->get_color_texture());
+			RSG::texture_storage->render_target_set_override_depth(vp->render_target, xr_interface->get_depth_texture());
+			RSG::texture_storage->render_target_set_override_velocity(vp->render_target, xr_interface->get_velocity_texture());
 
 			// render...
 			RSG::scene->set_debug_draw_mode(vp->debug_draw);
@@ -695,7 +695,9 @@ void RendererViewport::draw_viewports() {
 				}
 			}
 		} else {
-			RSG::texture_storage->render_target_set_external_texture(vp->render_target, 0);
+			RSG::texture_storage->render_target_set_override_color(vp->render_target, RID()); // TODO if fullscreen output, we can set this to our texture chain
+			RSG::texture_storage->render_target_set_override_depth(vp->render_target, RID());
+			RSG::texture_storage->render_target_set_override_velocity(vp->render_target, RID());
 
 			RSG::scene->set_debug_draw_mode(vp->debug_draw);
 

+ 21 - 5
servers/rendering/storage/texture_storage.h

@@ -131,15 +131,18 @@ public:
 	virtual RID render_target_create() = 0;
 	virtual void render_target_free(RID p_rid) = 0;
 
-	virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0;
-	virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) = 0;
-	virtual RID render_target_get_texture(RID p_render_target) = 0;
-	virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0;
+	virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0; // Q change input to const Point2i &p_position ?
+	virtual Point2i render_target_get_position(RID p_render_target) const = 0;
+	virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) = 0; // Q change input to const Size2i &p_size ?
+	virtual Size2i render_target_get_size(RID p_render_target) const = 0;
 	virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) = 0;
+	virtual bool render_target_get_transparent(RID p_render_target) const = 0;
 	virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) = 0;
-	virtual bool render_target_was_used(RID p_render_target) = 0;
+	virtual bool render_target_get_direct_to_screen(RID p_render_target) const = 0;
+	virtual bool render_target_was_used(RID p_render_target) const = 0;
 	virtual void render_target_set_as_unused(RID p_render_target) = 0;
 	virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) = 0;
+	virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const = 0;
 
 	virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) = 0;
 	virtual bool render_target_is_clear_requested(RID p_render_target) = 0;
@@ -152,7 +155,20 @@ public:
 	virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) = 0;
 
 	virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) = 0;
+	virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const = 0;
 	virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) = 0;
+	virtual RID render_target_get_vrs_texture(RID p_render_target) const = 0;
+
+	// override color, depth and velocity buffers (depth and velocity only for 3D)
+	virtual void render_target_set_override_color(RID p_render_target, RID p_texture) = 0;
+	virtual RID render_target_get_override_color(RID p_render_target) const = 0;
+	virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) = 0;
+	virtual RID render_target_get_override_depth(RID p_render_target) const = 0;
+	virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) = 0;
+	virtual RID render_target_get_override_velocity(RID p_render_target) const = 0;
+
+	// get textures
+	virtual RID render_target_get_texture(RID p_render_target) = 0;
 };
 
 #endif // TEXTURE_STORAGE_H

+ 13 - 0
servers/xr/xr_interface.cpp

@@ -241,6 +241,19 @@ RID XRInterface::get_vrs_texture() {
 }
 
 /** these are optional, so we want dummies **/
+
+RID XRInterface::get_color_texture() {
+	return RID();
+}
+
+RID XRInterface::get_depth_texture() {
+	return RID();
+}
+
+RID XRInterface::get_velocity_texture() {
+	return RID();
+}
+
 PackedStringArray XRInterface::get_suggested_tracker_names() const {
 	PackedStringArray arr;
 

+ 3 - 2
servers/xr/xr_interface.h

@@ -121,8 +121,9 @@ public:
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
 	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */
 	virtual RID get_vrs_texture(); /* obtain VRS texture */
-
-	// note, external color/depth/vrs texture support will be added here soon.
+	virtual RID get_color_texture(); /* obtain color output texture (if applicable) */
+	virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */
+	virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */
 
 	virtual void process() = 0;
 	virtual void pre_render(){};

+ 36 - 0
servers/xr/xr_interface_extension.cpp

@@ -74,6 +74,15 @@ void XRInterfaceExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_set_anchor_detection_is_enabled, "enabled");
 	GDVIRTUAL_BIND(_get_camera_feed_id);
 
+	// override output methods
+	GDVIRTUAL_BIND(_get_color_texture);
+	GDVIRTUAL_BIND(_get_depth_texture);
+	GDVIRTUAL_BIND(_get_velocity_texture);
+
+	ClassDB::bind_method(D_METHOD("get_color_texture"), &XRInterfaceExtension::get_color_texture);
+	ClassDB::bind_method(D_METHOD("get_depth_texture"), &XRInterfaceExtension::get_depth_texture);
+	ClassDB::bind_method(D_METHOD("get_velocity_texture"), &XRInterfaceExtension::get_velocity_texture);
+
 	// helper methods
 	ClassDB::bind_method(D_METHOD("add_blit", "render_target", "src_rect", "dst_rect", "use_layer", "layer", "apply_lens_distortion", "eye_center", "k1", "k2", "upscale", "aspect_ratio"), &XRInterfaceExtension::add_blit);
 	ClassDB::bind_method(D_METHOD("get_render_target_texture", "render_target"), &XRInterfaceExtension::get_render_target_texture);
@@ -283,6 +292,33 @@ RID XRInterfaceExtension::get_vrs_texture() {
 	}
 }
 
+RID XRInterfaceExtension::get_color_texture() {
+	RID texture;
+	if (GDVIRTUAL_CALL(_get_color_texture, texture)) {
+		return texture;
+	} else {
+		return RID();
+	}
+}
+
+RID XRInterfaceExtension::get_depth_texture() {
+	RID texture;
+	if (GDVIRTUAL_CALL(_get_depth_texture, texture)) {
+		return texture;
+	} else {
+		return RID();
+	}
+}
+
+RID XRInterfaceExtension::get_velocity_texture() {
+	RID texture;
+	if (GDVIRTUAL_CALL(_get_velocity_texture, texture)) {
+		return texture;
+	} else {
+		return RID();
+	}
+}
+
 void XRInterfaceExtension::add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer, uint32_t p_layer, bool p_apply_lens_distortion, Vector2 p_eye_center, double p_k1, double p_k2, double p_upscale, double p_aspect_ratio) {
 	BlitToScreen blit;
 

+ 6 - 0
servers/xr/xr_interface_extension.h

@@ -102,6 +102,9 @@ public:
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
 	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
 	virtual RID get_vrs_texture() override;
+	virtual RID get_color_texture() override;
+	virtual RID get_depth_texture() override;
+	virtual RID get_velocity_texture() override;
 
 	GDVIRTUAL0R(Size2, _get_render_target_size);
 	GDVIRTUAL0R(uint32_t, _get_view_count);
@@ -109,6 +112,9 @@ public:
 	GDVIRTUAL2R(Transform3D, _get_transform_for_view, uint32_t, const Transform3D &);
 	GDVIRTUAL4R(PackedFloat64Array, _get_projection_for_view, uint32_t, double, double, double);
 	GDVIRTUAL0R(RID, _get_vrs_texture);
+	GDVIRTUAL0R(RID, _get_color_texture);
+	GDVIRTUAL0R(RID, _get_depth_texture);
+	GDVIRTUAL0R(RID, _get_velocity_texture);
 
 	void add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer = false, uint32_t p_layer = 0, bool p_apply_lens_distortion = false, Vector2 p_eye_center = Vector2(), double p_k1 = 0.0, double p_k2 = 0.0, double p_upscale = 1.0, double p_aspect_ratio = 1.0);