diff --git a/doc/classes/RenderSceneBuffersConfiguration.xml b/doc/classes/RenderSceneBuffersConfiguration.xml
index b5c37f3288f9..41761fecb161 100644
--- a/doc/classes/RenderSceneBuffersConfiguration.xml
+++ b/doc/classes/RenderSceneBuffersConfiguration.xml
@@ -37,7 +37,7 @@
Bias applied to mipmaps.
- The number of views we're rendering.
+ The number of views we're rendering. If larger than [code]1[/code] this will enable multiview rendering for stereoscopic or multiscopic views.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 2d983b0522db..17317b7c6f6d 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3915,15 +3915,17 @@
+
- Returns the render target for the viewport.
+ Returns the render target for the [param layer] of the viewport.
+
- Returns the viewport's last rendered frame.
+ Returns the viewport's last rendered frame for a [param layer].
@@ -4062,6 +4064,15 @@
Sets the viewport's global transformation matrix.
+
+
+
+
+
+ Sets the number of layers for this viewport. Each layer will be rendered separately.
+ [b]Note:[/b] This is an XR feature only, in other use cases the output of additional layers is discarded.
+
+
@@ -4180,10 +4191,12 @@
-
-
+
+
+
+
- Sets the viewport's width and height in pixels.
+ Sets the viewport's [param width], [param height] in pixels and the [param view_count] for the specified [param layer].
diff --git a/doc/classes/SubViewport.xml b/doc/classes/SubViewport.xml
index 9d41a5e36d63..5f096e64a290 100644
--- a/doc/classes/SubViewport.xml
+++ b/doc/classes/SubViewport.xml
@@ -36,6 +36,9 @@
If [code]true[/code], the 2D size override affects stretch as well.
+
+ The number of views we're rendering. If larger than [code]1[/code] this will enable multiview rendering for stereoscopic or multiscopic views.
+
diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml
index ede1ce31e789..b082b70b6219 100644
--- a/doc/classes/XRInterface.xml
+++ b/doc/classes/XRInterface.xml
@@ -23,6 +23,12 @@
Returns a combination of [enum Capabilities] flags providing information about the capabilities of this interface.
+
+
+
+ Returns the number of layers we require for rendering. Each layer may have different resolution, view count and camera data.
+
+
@@ -37,16 +43,18 @@
-
-
-
-
+
+
+
+
+
Returns the projection matrix for a view/eye.
+
Returns the resolution at which we should render our intermediate results before things like lens distortion are applied by the VR platform.
@@ -72,8 +80,9 @@
-
-
+
+
+
Returns the transform for a view/eye.
[param view] is the view/eye index.
@@ -82,6 +91,7 @@
+
Returns the number of views that need to be rendered for this device. 1 for Monoscopic, 2 for Stereoscopic.
diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml
index 8ac855035100..fd3ebbe998d3 100644
--- a/doc/classes/XRInterfaceExtension.xml
+++ b/doc/classes/XRInterfaceExtension.xml
@@ -46,12 +46,29 @@
Return color texture into which to render (if applicable).
+
+
+
+
+
+
Return depth texture into which to render (if applicable).
+
+
+
+
+
+
+
+
+
+
+
@@ -86,6 +103,12 @@
Returns the size of our render target for this interface, this overrides the size of the [Viewport] marked as the xr viewport.
+
+
+
+
+
+
@@ -125,12 +148,24 @@
Return velocity texture into which to render (if applicable).
+
+
+
+
+
+
Returns the number of views this interface requires, 1 for mono, 2 for stereoscopic.
+
+
+
+
+
+
@@ -239,11 +274,13 @@
+
+
@@ -256,6 +293,7 @@
+
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 6fa8f42b3f3f..ecadb5f3efc8 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -2548,7 +2548,7 @@ Size2i TextureStorage::render_target_get_size(RID p_render_target) const {
return rt->size;
}
-void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) {
+void TextureStorage::render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
ERR_FAIL_COND(rt->direct_to_screen);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 6119b8d24755..dbe3ca01a6d4 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -691,7 +691,7 @@ class TextureStorage : public RendererTextureStorage {
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(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
+ virtual void render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
virtual RID render_target_get_override_color(RID p_render_target) const override;
virtual RID render_target_get_override_depth(RID p_render_target) const override;
virtual RID render_target_get_override_velocity(RID p_render_target) const override;
diff --git a/editor/animation/animation_player_editor_plugin.cpp b/editor/animation/animation_player_editor_plugin.cpp
index 38ee401931e3..677753011446 100644
--- a/editor/animation/animation_player_editor_plugin.cpp
+++ b/editor/animation/animation_player_editor_plugin.cpp
@@ -1683,7 +1683,7 @@ void AnimationPlayerEditor::_allocate_onion_layers() {
// Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport.
onion.captures[i] = RS::get_singleton()->viewport_create();
- RS::get_singleton()->viewport_set_size(onion.captures[i], capture_size.width, capture_size.height);
+ RS::get_singleton()->viewport_set_size(onion.captures[i], 0, capture_size.width, capture_size.height, 1);
RS::get_singleton()->viewport_set_update_mode(onion.captures[i], RS::VIEWPORT_UPDATE_ALWAYS);
RS::get_singleton()->viewport_set_transparent_background(onion.captures[i], !is_present);
RS::get_singleton()->viewport_attach_canvas(onion.captures[i], onion.capture.canvas);
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index 51645cee54a6..9fd18d97b68f 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -148,7 +148,7 @@ Vector[> EditorInterface::make_mesh_previews(const Vector][viewport_create();
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ALWAYS);
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
- RS::get_singleton()->viewport_set_size(viewport, size, size);
+ RS::get_singleton()->viewport_set_size(viewport, 0, size, size, 1);
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
RS::get_singleton()->viewport_set_active(viewport, true);
RID viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
diff --git a/editor/inspector/editor_preview_plugins.cpp b/editor/inspector/editor_preview_plugins.cpp
index 52958f8b7e42..6c850509a9da 100644
--- a/editor/inspector/editor_preview_plugins.cpp
+++ b/editor/inspector/editor_preview_plugins.cpp
@@ -360,7 +360,7 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
viewport = RS::get_singleton()->viewport_create();
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
- RS::get_singleton()->viewport_set_size(viewport, 128, 128);
+ RS::get_singleton()->viewport_set_size(viewport, 0, 128, 128, 1);
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
RS::get_singleton()->viewport_set_active(viewport, true);
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
@@ -775,7 +775,7 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
viewport = RS::get_singleton()->viewport_create();
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
- RS::get_singleton()->viewport_set_size(viewport, 128, 128);
+ RS::get_singleton()->viewport_set_size(viewport, 0, 128, 128, 1);
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
RS::get_singleton()->viewport_set_active(viewport, true);
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
@@ -890,7 +890,7 @@ Ref EditorFontPreviewPlugin::generate(const Ref &p_from, co
EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
viewport = RS::get_singleton()->viewport_create();
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
- RS::get_singleton()->viewport_set_size(viewport, 128, 128);
+ RS::get_singleton()->viewport_set_size(viewport, 0, 128, 128, 1);
RS::get_singleton()->viewport_set_active(viewport, true);
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
diff --git a/main/main.cpp b/main/main.cpp
index 36d3c44b764b..622fdc5e27a8 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2756,7 +2756,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0");
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer"
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo,Quad,Observer"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage,Local Floor"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/foveation_level", PROPERTY_HINT_ENUM, "Off,Low,Medium,High"), "0");
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 5a9ea8dae290..ed476b77c964 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -341,7 +341,7 @@ void MobileVRInterface::set_vrs_strength(float p_vrs_strength) {
xr_vrs.set_vrs_strength(p_vrs_strength);
}
-uint32_t MobileVRInterface::get_view_count() {
+uint32_t MobileVRInterface::get_view_count(uint32_t p_layer) {
// needs stereo...
return 2;
}
@@ -431,9 +431,11 @@ bool MobileVRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return p_mode == XR_PLAY_AREA_3DOF;
}
-Size2 MobileVRInterface::get_render_target_size() {
+Size2 MobileVRInterface::get_render_target_size(uint32_t p_layer) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(p_layer > 0, Size2());
+
// we use half our window size
Size2 target_size = DisplayServer::get_singleton()->window_get_size();
@@ -464,8 +466,9 @@ Transform3D MobileVRInterface::get_camera_transform() {
return transform_for_eye;
}
-Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(p_layer > 0, Transform3D());
Transform3D transform_for_eye;
@@ -498,8 +501,9 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra
return transform_for_eye;
}
-Projection MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection MobileVRInterface::get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(p_layer > 0, Projection());
Projection eye;
@@ -580,7 +584,7 @@ RID MobileVRInterface::get_vrs_texture() {
uint32_t view_count = get_view_count();
for (uint32_t v = 0; v < view_count; v++) {
- Projection cm = get_projection_for_view(v, aspect_ratio, 0.1, 1000.0);
+ Projection cm = get_projection_for_view(0, v, aspect_ratio, 0.1, 1000.0);
Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0));
eye_foci.push_back(Vector2(center.x, center.y));
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index f09be9117a06..660ea31c6202 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -160,11 +160,11 @@ class MobileVRInterface : public XRInterface {
virtual XRInterface::PlayAreaMode get_play_area_mode() const override;
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
- virtual Size2 get_render_target_size() override;
- virtual uint32_t get_view_count() override;
+ virtual Size2 get_render_target_size(uint32_t p_layer = 0) override;
+ virtual uint32_t get_view_count(uint32_t p_layer = 0) override;
virtual Transform3D get_camera_transform() override;
- 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 Transform3D get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) override;
+ virtual Projection get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Vector post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 56badf59189b..ed214257b779 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -211,7 +211,7 @@ void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i
if (subviewport.viewport != p_viewport) {
if (subviewport.viewport.is_valid()) {
RID rt = rs->viewport_get_render_target(subviewport.viewport);
- RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());
+ RSG::texture_storage->render_target_set_override(rt, 0, RID(), RID(), RID(), RID());
}
subviewport.viewport = p_viewport;
@@ -333,7 +333,7 @@ void OpenXRViewportCompositionLayerProvider::on_pre_render() {
if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {
// Render to our XR swapchain image.
RID rt = rs->viewport_get_render_target(subviewport.viewport);
- RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID());
+ RSG::texture_storage->render_target_set_override(rt, 0, get_current_swapchain_texture(), RID(), RID(), RID());
}
}
}
diff --git a/modules/openxr/extensions/openxr_varjo_quad_view_extension.cpp b/modules/openxr/extensions/openxr_varjo_quad_view_extension.cpp
new file mode 100644
index 000000000000..b9ef6d37c4d0
--- /dev/null
+++ b/modules/openxr/extensions/openxr_varjo_quad_view_extension.cpp
@@ -0,0 +1,40 @@
+/**************************************************************************/
+/* openxr_varjo_quad_view_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_varjo_quad_view_extension.h"
+
+HashMap OpenXRVarjoQuadViewExtension::get_requested_extensions() {
+ HashMap request_extensions;
+
+ // Once we support OpenXR 1.1, we should only include this if we're initialising OpenXR 1.0
+ request_extensions[XR_VARJO_QUAD_VIEWS_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
diff --git a/modules/openxr/extensions/openxr_varjo_quad_view_extension.h b/modules/openxr/extensions/openxr_varjo_quad_view_extension.h
new file mode 100644
index 000000000000..f48cf07f4f23
--- /dev/null
+++ b/modules/openxr/extensions/openxr_varjo_quad_view_extension.h
@@ -0,0 +1,50 @@
+/**************************************************************************/
+/* openxr_varjo_quad_view_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. */
+/**************************************************************************/
+
+#pragma once
+
+#include "openxr_extension_wrapper.h"
+
+// In OpenXR 1.0 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET
+// was a Varjo only extension.
+// We enable it as a fallback if supported.
+
+class OpenXRVarjoQuadViewExtension : public OpenXRExtensionWrapper {
+ GDCLASS(OpenXRVarjoQuadViewExtension, OpenXRExtensionWrapper);
+
+protected:
+ static void _bind_methods() {}
+
+public:
+ virtual HashMap get_requested_extensions() override;
+
+private:
+ bool available = false;
+};
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index faa6b647d5c3..82d5eda42a9f 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1225,10 +1225,11 @@ bool OpenXRAPI::obtain_swapchain_formats() {
return true;
}
-bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
+bool OpenXRAPI::create_main_swapchains(uint32_t p_layer, Size2i p_size) {
ERR_NOT_ON_RENDER_THREAD_V(false);
ERR_FAIL_NULL_V(graphics_extension, false);
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
+ ERR_FAIL_COND_V(p_layer >= MIN(get_layer_count(), (uint32_t)OPENXR_LAYER_MAX), false);
/*
TODO: We need to improve on this, for now we're taking our old approach of creating our main swapchains and substituting
@@ -1245,16 +1246,17 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
*/
- render_state.main_swapchain_size = p_size;
+ render_state.main_swapchain_size[p_layer] = p_size;
uint32_t sample_count = 1;
+ uint32_t view_count = get_view_count(p_layer);
// We start with our color swapchain...
if (color_swapchain_format != 0) {
- if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_configuration_views.size())) {
+ if (!render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size[p_layer].width, render_state.main_swapchain_size[p_layer].height, sample_count, view_count)) {
return false;
}
- set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main color swapchain");
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Primary color swapchain");
}
// We create our depth swapchain if:
@@ -1262,11 +1264,11 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
// - we support our depth layer extension
// - we have our spacewarp extension (not yet implemented)
if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
- if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_configuration_views.size())) {
+ if (!render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size[p_layer].width, render_state.main_swapchain_size[p_layer].height, sample_count, view_count)) {
return false;
}
- set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain()), "Main depth swapchain");
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_DEPTH].get_swapchain()), "Primary depth swapchain");
}
// We create our velocity swapchain if:
@@ -1275,30 +1277,32 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
// TBD
}
- for (uint32_t i = 0; i < render_state.views.size(); i++) {
+ uint32_t view_offset = get_view_offset(p_layer);
+ ERR_FAIL_COND_V(view_offset + view_count > render_state.views.size(), false);
+ for (uint32_t i = view_offset; i < view_offset + view_count; i++) {
render_state.views[i].type = XR_TYPE_VIEW;
render_state.views[i].next = nullptr;
render_state.projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
render_state.projection_views[i].next = nullptr;
- render_state.projection_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
- render_state.projection_views[i].subImage.imageArrayIndex = i;
+ render_state.projection_views[i].subImage.swapchain = render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_COLOR].get_swapchain();
+ render_state.projection_views[i].subImage.imageArrayIndex = i - view_offset;
render_state.projection_views[i].subImage.imageRect.offset.x = 0;
render_state.projection_views[i].subImage.imageRect.offset.y = 0;
- render_state.projection_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
- render_state.projection_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
+ render_state.projection_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size[p_layer].width;
+ render_state.projection_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size[p_layer].height;
if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && !render_state.depth_views.is_empty()) {
render_state.projection_views[i].next = &render_state.depth_views[i];
render_state.depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
render_state.depth_views[i].next = nullptr;
- render_state.depth_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
- render_state.depth_views[i].subImage.imageArrayIndex = i;
+ render_state.depth_views[i].subImage.swapchain = render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
+ render_state.depth_views[i].subImage.imageArrayIndex = i - view_offset;
render_state.depth_views[i].subImage.imageRect.offset.x = 0;
render_state.depth_views[i].subImage.imageRect.offset.y = 0;
- render_state.depth_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
- render_state.depth_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
+ render_state.depth_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size[p_layer].width;
+ render_state.depth_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size[p_layer].height;
// OpenXR spec says that: minDepth < maxDepth.
render_state.depth_views[i].minDepth = 0.0;
render_state.depth_views[i].maxDepth = 1.0;
@@ -1308,10 +1312,6 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
}
};
- for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
- wrapper->on_main_swapchains_created();
- }
-
return true;
}
@@ -1334,7 +1334,9 @@ void OpenXRAPI::destroy_session() {
render_state.projection_views.clear();
render_state.depth_views.clear();
- free_main_swapchains();
+ for (uint32_t layer = 0; layer < OPENXR_LAYER_MAX; layer++) {
+ free_main_swapchains(layer);
+ }
OpenXRSwapChainInfo::free_queued();
supported_swapchain_formats.clear();
@@ -1516,8 +1518,52 @@ void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) {
form_factor = p_form_factor;
}
-uint32_t OpenXRAPI::get_view_count() {
- return view_configuration_views.size();
+uint32_t OpenXRAPI::get_layer_count() {
+ switch (view_configuration) {
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO:
+ return 1;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO:
+ return 1;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET: // Note: This is Varjo QuadView in OpenXR 1.0
+ return 2;
+ case XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+uint32_t OpenXRAPI::get_view_offset(uint32_t p_layer) {
+ // Returns the index for our first entry in our view/projection_views/depth_views buffers
+ switch (view_configuration) {
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO:
+ return 0;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO:
+ return 0;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET: // Note: This is Varjo QuadView in OpenXR 1.0
+ return p_layer == 1 ? 2 : 0;
+ case XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT:
+ ERR_FAIL_COND_V(p_layer > 1, 0);
+ return p_layer == 1 ? 2 : 0; // First layer is stereo, second layer is mono
+ default:
+ return 0;
+ }
+}
+
+uint32_t OpenXRAPI::get_view_count(uint32_t p_layer) {
+ switch (view_configuration) {
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO:
+ return p_layer == 0 ? 1 : 0;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO:
+ return p_layer == 0 ? 2 : 0;
+ case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET: // Note: This is Varjo QuadView in OpenXR 1.0
+ return p_layer <= 1 ? 2 : 0; // Both layers are stereo
+ case XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT:
+ return p_layer == 0 ? 2 : (p_layer == 1 ? 1 : 0); // First layer is stereo, second layer is mono
+ default:
+ // Unsupported, assume stereo..
+ return p_layer == 0 ? 2 : 0;
+ }
}
void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) {
@@ -1830,18 +1876,19 @@ XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) {
return hand_tracking->get_hand_tracker(hand)->hand_tracker;
}
-Size2 OpenXRAPI::get_recommended_target_size() {
+Size2 OpenXRAPI::get_recommended_target_size(uint32_t p_layer) {
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_COND_V(view_configuration_views.is_empty(), Size2());
Size2 target_size;
+ uint32_t view_offset = get_view_offset(p_layer);
if (rendering_server && rendering_server->is_on_render_thread()) {
- target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_state.render_target_size_multiplier;
- target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_state.render_target_size_multiplier;
+ target_size.width = view_configuration_views[view_offset].recommendedImageRectWidth * render_state.render_target_size_multiplier;
+ target_size.height = view_configuration_views[view_offset].recommendedImageRectHeight * render_state.render_target_size_multiplier;
} else {
- target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
- target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
+ target_size.width = view_configuration_views[view_offset].recommendedImageRectWidth * render_target_size_multiplier;
+ target_size.height = view_configuration_views[view_offset].recommendedImageRectHeight * render_target_size_multiplier;
}
return target_size;
@@ -1902,7 +1949,7 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
return confidence;
}
-bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
+bool OpenXRAPI::get_view_transform(uint32_t p_layers, uint32_t p_view, Transform3D &r_transform) {
ERR_NOT_ON_RENDER_THREAD_V(false);
if (!render_state.running) {
@@ -1915,12 +1962,17 @@ bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
}
// Note, the timing of this is set right before rendering, which is what we need here.
- r_transform = transform_from_pose(render_state.views[p_view].pose);
+ int32_t v = get_view_offset(p_layers) + p_view;
+ if (v < render_state.views.size()) {
+ r_transform = transform_from_pose(render_state.views[v].pose);
- return true;
+ return true;
+ }
+
+ return false;
}
-bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
+bool OpenXRAPI::get_view_projection(uint32_t p_layers, uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
ERR_NOT_ON_RENDER_THREAD_V(false);
ERR_FAIL_NULL_V(graphics_extension, false);
@@ -1933,7 +1985,10 @@ 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...
+ // Note, our near and far come from our camera so we can trust them to be
+ // the same for all layers and views.
+
+ // If we're using depth views, make sure we update our near and far there...
if (!render_state.depth_views.is_empty()) {
for (XrCompositionLayerDepthInfoKHR &depth_view : render_state.depth_views) {
// As we are using reverse-Z these need to be flipped.
@@ -1945,8 +2000,13 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z
render_state.z_near = p_z_near;
render_state.z_far = p_z_far;
- // now update our projection
- return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
+ int32_t v = get_view_offset(p_layers) + p_view;
+ if (v < render_state.views.size()) {
+ // now update our projection
+ return graphics_extension->create_projection_fov(render_state.views[v].fov, p_z_near, p_z_far, p_camera_matrix);
+ }
+
+ return false;
}
Vector2 OpenXRAPI::get_eye_focus(uint32_t p_view, float p_aspect) {
@@ -2231,9 +2291,9 @@ bool OpenXRAPI::process() {
return true;
}
-void OpenXRAPI::free_main_swapchains() {
+void OpenXRAPI::free_main_swapchains(uint32_t p_layer) {
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- render_state.main_swapchains[i].queue_free();
+ render_state.main_swapchains[p_layer][i].queue_free();
}
}
@@ -2250,13 +2310,24 @@ void OpenXRAPI::pre_render() {
// Process any swapchains that were queued to be freed
OpenXRSwapChainInfo::free_queued();
- Size2i swapchain_size = get_recommended_target_size();
- if (swapchain_size != render_state.main_swapchain_size) {
- // Out with the old.
- free_main_swapchains();
+ bool swapchains_updated = false;
+ for (uint32_t layer = 0; layer < get_layer_count(); layer++) {
+ Size2i swapchain_size = get_recommended_target_size(layer);
+ if (swapchain_size != render_state.main_swapchain_size[layer]) {
+ // Out with the old.
+ free_main_swapchains(layer);
+
+ // In with the new.
+ create_main_swapchains(layer, swapchain_size);
- // In with the new.
- create_main_swapchains(swapchain_size);
+ swapchains_updated = true;
+ }
+ }
+
+ if (swapchains_updated) {
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_main_swapchains_created();
+ }
}
void *view_locate_info_next_pointer = nullptr;
@@ -2347,10 +2418,12 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
}
// Acquire our images
- for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- if (!render_state.main_swapchains[i].is_image_acquired() && render_state.main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
- if (!render_state.main_swapchains[i].acquire(render_state.should_render)) {
- return false;
+ for (uint32_t l = 0; l < OPENXR_LAYER_MAX; l++) {
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ if (!render_state.main_swapchains[l][i].is_image_acquired() && render_state.main_swapchains[l][i].get_swapchain() != XR_NULL_HANDLE) {
+ if (!render_state.main_swapchains[l][i].acquire(render_state.should_render)) {
+ return false;
+ }
}
}
}
@@ -2362,24 +2435,27 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
return true;
}
-XrSwapchain OpenXRAPI::get_color_swapchain() {
+XrSwapchain OpenXRAPI::get_color_swapchain(uint32_t p_layer) {
ERR_NOT_ON_RENDER_THREAD_V(XR_NULL_HANDLE);
+ ERR_FAIL_COND_V(p_layer >= OPENXR_LAYER_MAX, XR_NULL_HANDLE);
- return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
+ return render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_COLOR].get_swapchain();
}
-RID OpenXRAPI::get_color_texture() {
+RID OpenXRAPI::get_color_texture(uint32_t p_layer) {
ERR_NOT_ON_RENDER_THREAD_V(RID());
+ ERR_FAIL_COND_V(p_layer >= OPENXR_LAYER_MAX, RID());
- return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
+ return render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_COLOR].get_image();
}
-RID OpenXRAPI::get_depth_texture() {
+RID OpenXRAPI::get_depth_texture(uint32_t p_layer) {
ERR_NOT_ON_RENDER_THREAD_V(RID());
+ ERR_FAIL_COND_V(p_layer >= OPENXR_LAYER_MAX, RID());
// Note, image will not be acquired if we didn't have a suitable swap chain format.
- if (render_state.submit_depth_buffer && render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].is_image_acquired()) {
- return render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
+ if (render_state.submit_depth_buffer && render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_DEPTH].is_image_acquired()) {
+ return render_state.main_swapchains[p_layer][OPENXR_SWAPCHAIN_DEPTH].get_image();
} else {
return RID();
}
@@ -2390,7 +2466,8 @@ RID OpenXRAPI::get_density_map_texture() {
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
if (fov_ext && fov_ext->is_enabled()) {
- return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_density_map();
+ // For now only supported on the first layer.
+ return render_state.main_swapchains[0][OPENXR_SWAPCHAIN_COLOR].get_density_map();
}
return RID();
@@ -2454,25 +2531,33 @@ void OpenXRAPI::end_frame() {
if (render_state.should_render && render_state.view_pose_valid) {
if (!render_state.has_xr_viewport) {
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
- } else if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
+ } else if (!render_state.main_swapchains[0][OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
print_line("OpenXR: No swapchain could be acquired to render to!");
}
}
- Rect2i new_render_region = (render_state.render_region != Rect2i()) ? render_state.render_region : Rect2i(Point2i(0, 0), render_state.main_swapchain_size);
+ for (uint32_t layer = 0; layer < get_layer_count(); layer++) {
+ // Render region is nonly done on the first layer, doesn't make sense for our insets.
+ Rect2i new_render_region = (layer == 0 && render_state.render_region != Rect2i()) ? render_state.render_region : Rect2i(Point2i(0, 0), render_state.main_swapchain_size[layer]);
- for (XrCompositionLayerProjectionView &projection_view : render_state.projection_views) {
- projection_view.subImage.imageRect.offset.x = new_render_region.position.x;
- projection_view.subImage.imageRect.offset.y = new_render_region.position.y;
- projection_view.subImage.imageRect.extent.width = new_render_region.size.width;
- projection_view.subImage.imageRect.extent.height = new_render_region.size.height;
- }
- if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && !render_state.depth_views.is_empty()) {
- for (XrCompositionLayerDepthInfoKHR &depth_view : render_state.depth_views) {
- depth_view.subImage.imageRect.offset.x = new_render_region.position.x;
- depth_view.subImage.imageRect.offset.y = new_render_region.position.y;
- depth_view.subImage.imageRect.extent.width = new_render_region.size.width;
- depth_view.subImage.imageRect.extent.height = new_render_region.size.height;
+ uint32_t view_offset = get_view_offset(layer);
+ uint32_t view_count = get_view_count(layer);
+ for (uint32_t i = view_offset; i < view_offset + view_count; i++) {
+ if (i < render_state.projection_views.size()) {
+ XrCompositionLayerProjectionView &projection_view = render_state.projection_views[i];
+ projection_view.subImage.imageRect.offset.x = new_render_region.position.x;
+ projection_view.subImage.imageRect.offset.y = new_render_region.position.y;
+ projection_view.subImage.imageRect.extent.width = new_render_region.size.width;
+ projection_view.subImage.imageRect.extent.height = new_render_region.size.height;
+ }
+
+ if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && i < render_state.depth_views.size()) {
+ XrCompositionLayerDepthInfoKHR &depth_view = render_state.depth_views[i];
+ depth_view.subImage.imageRect.offset.x = new_render_region.position.x;
+ depth_view.subImage.imageRect.offset.y = new_render_region.position.y;
+ depth_view.subImage.imageRect.extent.width = new_render_region.size.width;
+ depth_view.subImage.imageRect.extent.height = new_render_region.size.height;
+ }
}
}
@@ -2480,7 +2565,7 @@ void OpenXRAPI::end_frame() {
// - should_render set to true
// - a valid view pose for projection_views[eye].pose to submit layer
// - an image to render
- if (!render_state.should_render || !render_state.view_pose_valid || !render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
+ if (!render_state.should_render || !render_state.view_pose_valid || !render_state.main_swapchains[0][OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
// submit 0 layers when we shouldn't render
XrFrameEndInfo frame_end_info = {
XR_TYPE_FRAME_END_INFO, // type
@@ -2502,10 +2587,12 @@ void OpenXRAPI::end_frame() {
return;
}
- // release our swapchain image if we acquired it
- for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- if (render_state.main_swapchains[i].is_image_acquired()) {
- render_state.main_swapchains[i].release();
+ // release our swapchain images if we acquired them
+ for (uint32_t l = 0; l < OPENXR_LAYER_MAX; l++) {
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ if (render_state.main_swapchains[l][i].is_image_acquired()) {
+ render_state.main_swapchains[l][i].release();
+ }
}
}
@@ -2737,14 +2824,12 @@ OpenXRAPI::OpenXRAPI() {
case 1: {
view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
} break;
- /* we don't support quad and observer configurations (yet)
case 2: {
view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO;
} break;
case 3: {
view_configuration = XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT;
} break;
- */
default:
break;
}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 9f8f8fec7420..8b5d533722ba 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -146,6 +146,12 @@ class OpenXRAPI {
LocalVector view_configuration_views;
+ enum OpenXRLayers {
+ OPENXR_LAYER_PRIMARY,
+ OPENXR_LAYER_SECONDARY,
+ OPENXR_LAYER_MAX
+ };
+
enum OpenXRSwapChainTypes {
OPENXR_SWAPCHAIN_COLOR,
OPENXR_SWAPCHAIN_DEPTH,
@@ -260,8 +266,8 @@ class OpenXRAPI {
bool load_supported_swapchain_formats();
bool is_swapchain_format_supported(int64_t p_swapchain_format);
bool obtain_swapchain_formats();
- bool create_main_swapchains(Size2i p_size);
- void free_main_swapchains();
+ bool create_main_swapchains(uint32_t p_layer, Size2i p_size);
+ void free_main_swapchains(uint32_t p_layer);
void destroy_session();
// action map
@@ -359,8 +365,8 @@ class OpenXRAPI {
nullptr // views
};
- Size2i main_swapchain_size;
- OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX];
+ Size2i main_swapchain_size[OPENXR_LAYER_MAX];
+ OpenXRSwapChainInfo main_swapchains[OPENXR_LAYER_MAX][OPENXR_SWAPCHAIN_MAX];
} render_state;
static void _allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer);
@@ -466,7 +472,10 @@ class OpenXRAPI {
void set_form_factor(XrFormFactor p_form_factor);
XrFormFactor get_form_factor() const { return form_factor; }
- uint32_t get_view_count();
+ uint32_t get_layer_count();
+ uint32_t get_view_offset(uint32_t p_layer = 0);
+ uint32_t get_view_count(uint32_t p_layer = 0);
+
void set_view_configuration(XrViewConfigurationType p_view_configuration);
XrViewConfigurationType get_view_configuration() const { return view_configuration; }
@@ -493,18 +502,18 @@ class OpenXRAPI {
XrHandTrackerEXT get_hand_tracker(int p_hand_index);
- Size2 get_recommended_target_size();
+ Size2 get_recommended_target_size(uint32_t p_layer);
XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
- bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
- bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
+ bool get_view_transform(uint32_t p_layers, uint32_t p_view, Transform3D &r_transform);
+ bool get_view_projection(uint32_t p_layers, uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
Vector2 get_eye_focus(uint32_t p_view, float p_aspect);
bool process();
void pre_render();
bool pre_draw_viewport(RID p_render_target);
- XrSwapchain get_color_swapchain();
- RID get_color_texture();
- RID get_depth_texture();
+ XrSwapchain get_color_swapchain(uint32_t p_layer = 0);
+ RID get_color_texture(uint32_t p_layer = 0);
+ RID get_depth_texture(uint32_t p_layer = 0);
RID get_density_map_texture();
void set_velocity_texture(RID p_render_target);
RID get_velocity_texture();
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 351e478b8876..4d5add60341d 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -1015,17 +1015,28 @@ void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) {
}
}
-Size2 OpenXRInterface::get_render_target_size() {
+uint32_t OpenXRInterface::get_layer_count() {
+ if (openxr_api == nullptr) {
+ return 1;
+ } else {
+ return openxr_api->get_layer_count();
+ }
+}
+
+Size2 OpenXRInterface::get_render_target_size(uint32_t p_layer) {
if (openxr_api == nullptr) {
return Size2();
} else {
- return openxr_api->get_recommended_target_size();
+ return openxr_api->get_recommended_target_size(p_layer);
}
}
-uint32_t OpenXRInterface::get_view_count() {
- // TODO set this based on our configuration
- return 2;
+uint32_t OpenXRInterface::get_view_count(uint32_t p_layer) {
+ if (openxr_api == nullptr) {
+ return 1;
+ } else {
+ return openxr_api->get_view_count(p_layer);
+ }
}
void OpenXRInterface::_set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye) {
@@ -1057,18 +1068,20 @@ Transform3D OpenXRInterface::get_camera_transform() {
return hmd_transform;
}
-Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, Transform3D());
- ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds.");
+ ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(p_layer), Transform3D(), "View index outside bounds.");
+
+ // TODO alter get_view_transform to support layers
Transform3D t;
- if (openxr_api && openxr_api->get_view_transform(p_view, t)) {
+ if (openxr_api && openxr_api->get_view_transform(p_layer, p_view, t)) {
// update our cached value if we have a valid transform
- transform_for_view[p_view] = t;
+ transform_for_view[p_layer][p_view] = t;
} else {
// reuse cached value
- t = transform_for_view[p_view];
+ t = transform_for_view[p_layer][p_view];
}
// Apply our world scale
@@ -1078,12 +1091,12 @@ Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Trans
return p_cam_transform * xr_server->get_reference_frame() * t;
}
-Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection OpenXRInterface::get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
Projection cm;
- ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds.");
+ ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(p_layer), cm, "View index outside bounds.");
if (openxr_api) {
- if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {
+ if (openxr_api->get_view_projection(p_layer, p_view, p_z_near, p_z_far, cm)) {
return cm;
}
}
@@ -1102,32 +1115,34 @@ Rect2i OpenXRInterface::get_render_region() {
}
}
-RID OpenXRInterface::get_color_texture() {
+RID OpenXRInterface::get_color_texture(uint32_t p_layer) {
if (openxr_api) {
- return openxr_api->get_color_texture();
+ return openxr_api->get_color_texture(p_layer);
} else {
return RID();
}
}
-RID OpenXRInterface::get_depth_texture() {
+RID OpenXRInterface::get_depth_texture(uint32_t p_layer) {
if (openxr_api) {
- return openxr_api->get_depth_texture();
+ return openxr_api->get_depth_texture(p_layer);
} else {
return RID();
}
}
-RID OpenXRInterface::get_velocity_texture() {
- if (openxr_api) {
+RID OpenXRInterface::get_velocity_texture(uint32_t p_layer) {
+ if (openxr_api && p_layer == 0) {
+ // Only supported on the first layer.
return openxr_api->get_velocity_texture();
} else {
return RID();
}
}
-RID OpenXRInterface::get_velocity_depth_texture() {
- if (openxr_api) {
+RID OpenXRInterface::get_velocity_depth_texture(uint32_t p_layer) {
+ if (openxr_api && p_layer == 0) {
+ // Only supported on the first layer.
return openxr_api->get_velocity_depth_texture();
} else {
return RID();
@@ -1636,8 +1651,8 @@ OpenXRInterface::OpenXRInterface() {
// while we don't have head tracking, don't put the headset on the floor...
_set_default_pos(head_transform, 1.0, 0);
- _set_default_pos(transform_for_view[0], 1.0, 1);
- _set_default_pos(transform_for_view[1], 1.0, 2);
+ _set_default_pos(transform_for_view[0][0], 1.0, 1);
+ _set_default_pos(transform_for_view[0][1], 1.0, 2);
}
OpenXRInterface::~OpenXRInterface() {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index 71466293748d..a5e375b87f1d 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -78,7 +78,7 @@ class OpenXRInterface : public XRInterface {
Vector3 head_linear_velocity;
Vector3 head_angular_velocity;
XRPose::TrackingConfidence head_confidence;
- Transform3D transform_for_view[2]; // We currently assume 2, but could be 4 for VARJO which we do not support yet
+ Transform3D transform_for_view[2][2];
XRVRS xr_vrs;
@@ -176,18 +176,19 @@ class OpenXRInterface : public XRInterface {
float get_vrs_strength() const;
void set_vrs_strength(float p_vrs_strength);
- virtual Size2 get_render_target_size() override;
- virtual uint32_t get_view_count() override;
+ virtual uint32_t get_layer_count() override;
+ virtual Size2 get_render_target_size(uint32_t p_layer = 0) override;
+ virtual uint32_t get_view_count(uint32_t p_layer = 0) override;
virtual Transform3D get_camera_transform() override;
- 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 Transform3D get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) override;
+ virtual Projection get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Rect2i get_render_region() override;
- virtual RID get_color_texture() override;
- virtual RID get_depth_texture() override;
- virtual RID get_velocity_texture() override;
- virtual RID get_velocity_depth_texture() override;
+ virtual RID get_color_texture(uint32_t p_layer = 0) override;
+ virtual RID get_depth_texture(uint32_t p_layer = 0) override;
+ virtual RID get_velocity_texture(uint32_t p_layer = 0) override;
+ virtual RID get_velocity_depth_texture(uint32_t p_layer = 0) override;
virtual Size2i get_velocity_target_size() override;
virtual void process() override;
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index d168da1bab4b..963586beb78b 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -73,6 +73,7 @@
#include "extensions/openxr_pico_controller_extension.h"
#include "extensions/openxr_render_model_extension.h"
#include "extensions/openxr_valve_analog_threshold_extension.h"
+#include "extensions/openxr_varjo_quad_view_extension.h"
#include "extensions/openxr_visibility_mask_extension.h"
#include "extensions/openxr_wmr_controller_extension.h"
@@ -157,6 +158,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRMxInkExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPerformanceSettingsExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRVarjoQuadViewExtension));
// Futures extension has to be registered as a singleton so extensions can access it.
OpenXRFutureExtension *future_extension = memnew(OpenXRFutureExtension);
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 26fca5bb3e0c..f0e143491ef2 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -277,7 +277,9 @@ uint32_t WebXRInterfaceJS::get_capabilities() const {
return XRInterface::XR_STEREO | XRInterface::XR_MONO | XRInterface::XR_VR | XRInterface::XR_AR;
}
-uint32_t WebXRInterfaceJS::get_view_count() {
+uint32_t WebXRInterfaceJS::get_view_count(uint32_t p_layer) {
+ ERR_FAIL_COND_V(p_layer > 0, 0);
+
return godot_webxr_get_view_count();
}
@@ -416,7 +418,9 @@ Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) {
return transform;
}
-Size2 WebXRInterfaceJS::get_render_target_size() {
+Size2 WebXRInterfaceJS::get_render_target_size(uint32_t p_layer) {
+ ERR_FAIL_COND_V(p_layer > 0, Size2());
+
if (render_targetsize.width != 0 && render_targetsize.height != 0) {
return render_targetsize;
}
@@ -454,10 +458,11 @@ Transform3D WebXRInterfaceJS::get_camera_transform() {
return camera_transform;
}
-Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, p_cam_transform);
ERR_FAIL_COND_V(!initialized, p_cam_transform);
+ ERR_FAIL_COND_V(p_layer > 0, p_cam_transform);
float js_matrix[16];
bool has_transform = godot_webxr_get_transform_for_view(p_view, js_matrix);
@@ -473,10 +478,11 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran
return p_cam_transform * xr_server->get_reference_frame() * transform_for_view;
}
-Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
Projection view;
ERR_FAIL_COND_V(!initialized, view);
+ ERR_FAIL_COND_V(p_layer > 0, view);
float js_matrix[16];
bool has_projection = godot_webxr_get_projection_for_view(p_view, js_matrix);
@@ -582,15 +588,18 @@ RID WebXRInterfaceJS::_get_texture(unsigned int p_texture_id) {
return texture;
}
-RID WebXRInterfaceJS::get_color_texture() {
+RID WebXRInterfaceJS::get_color_texture(uint32_t p_layer) {
+ ERR_FAIL_COND_V(p_layer > 0, RID());
return color_texture;
}
-RID WebXRInterfaceJS::get_depth_texture() {
+RID WebXRInterfaceJS::get_depth_texture(uint32_t p_layer) {
+ ERR_FAIL_COND_V(p_layer > 0, RID());
return depth_texture;
}
-RID WebXRInterfaceJS::get_velocity_texture() {
+RID WebXRInterfaceJS::get_velocity_texture(uint32_t p_layer) {
+ ERR_FAIL_COND_V(p_layer > 0, RID());
unsigned int texture_id = godot_webxr_get_velocity_texture();
if (texture_id == 0) {
return RID();
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 49be0922705d..37d4f229444e 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -126,16 +126,16 @@ class WebXRInterfaceJS : public WebXRInterface {
virtual void uninitialize() override;
virtual Dictionary get_system_info() override;
- virtual Size2 get_render_target_size() override;
- virtual uint32_t get_view_count() override;
+ virtual Size2 get_render_target_size(uint32_t p_layer = 0) override;
+ virtual uint32_t get_view_count(uint32_t p_layer = 0) override;
virtual Transform3D get_camera_transform() override;
- 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 Transform3D get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) override;
+ virtual Projection get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual bool pre_draw_viewport(RID p_render_target) override;
virtual Vector post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
- virtual RID get_color_texture() override;
- virtual RID get_depth_texture() override;
- virtual RID get_velocity_texture() override;
+ virtual RID get_color_texture(uint32_t p_layer = 0) override;
+ virtual RID get_depth_texture(uint32_t p_layer = 0) override;
+ virtual RID get_velocity_texture(uint32_t p_layer = 0) override;
virtual void process() override;
diff --git a/scene/3d/xr/xr_nodes.cpp b/scene/3d/xr/xr_nodes.cpp
index d4321d442de5..1b8fef9453c6 100644
--- a/scene/3d/xr/xr_nodes.cpp
+++ b/scene/3d/xr/xr_nodes.cpp
@@ -118,7 +118,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
Vector3 ray;
// Just use the first view, if multiple views are supported this function has no good result
- Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, 0, viewport_size.aspect(), get_near(), get_far());
Vector2 screen_he = cm.get_viewport_half_extents();
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized();
@@ -141,7 +141,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// Just use the first view, if multiple views are supported this function has no good result
- Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, 0, viewport_size.aspect(), get_near(), get_far());
Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
@@ -171,7 +171,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// Just use the first view, if multiple views are supported this function has no good result
- Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, 0, viewport_size.aspect(), get_near(), get_far());
Vector2 vp_he = cm.get_viewport_half_extents();
@@ -200,7 +200,7 @@ Vector XRCamera3D::get_frustum() const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
- Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, 0, viewport_size.aspect(), get_near(), get_far());
return cm.get_projection_planes(get_camera_transform());
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index f02ab52a9d03..0d7446207f1a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -638,6 +638,14 @@ void Viewport::_notification(int p_what) {
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
} break;
+#ifndef XR_DISABLED
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (use_xr) {
+ _check_xr_size();
+ }
+ } break;
+#endif // XR_DISABLED
+
case NOTIFICATION_PATH_RENAMED: {
_update_viewport_path();
} break;
@@ -1039,7 +1047,7 @@ void Viewport::set_use_oversampling(bool p_oversampling) {
return;
}
use_font_oversampling = p_oversampling;
- _set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
+ _set_size(_get_size(), view_count, _get_size_2d_override(), _is_size_allocated());
}
bool Viewport::is_using_oversampling() const {
@@ -1053,7 +1061,7 @@ void Viewport::set_oversampling_override(float p_oversampling) {
return;
}
font_oversampling_override = p_oversampling;
- _set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
+ _set_size(_get_size(), view_count, _get_size_2d_override(), _is_size_allocated());
}
float Viewport::get_oversampling_override() const {
@@ -1061,7 +1069,7 @@ float Viewport::get_oversampling_override() const {
return font_oversampling_override;
}
-bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override, bool p_allocated) {
+bool Viewport::_set_size(const Size2i &p_size, const int p_view_count, const Size2 &p_size_2d_override, bool p_allocated) {
Transform2D stretch_transform_new = Transform2D();
float new_font_oversampling = 1.0;
if (is_size_2d_override_stretch_enabled() && p_size_2d_override.width > 0 && p_size_2d_override.height > 0) {
@@ -1082,7 +1090,7 @@ bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override,
}
Size2i new_size = p_size.maxi(2);
- if (size == new_size && size_allocated == p_allocated && stretch_transform == stretch_transform_new && p_size_2d_override == size_2d_override && new_font_oversampling == font_oversampling) {
+ if (size == new_size && view_count == p_view_count && size_allocated == p_allocated && stretch_transform == stretch_transform_new && p_size_2d_override == size_2d_override && new_font_oversampling == font_oversampling) {
return false;
}
@@ -1095,24 +1103,17 @@ bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override,
}
size = new_size;
+ view_count = p_view_count;
size_allocated = p_allocated;
size_2d_override = p_size_2d_override;
stretch_transform = stretch_transform_new;
font_oversampling = new_font_oversampling;
-#ifndef _3D_DISABLED
- if (!use_xr) {
-#endif
-
- if (p_allocated) {
- RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
- } else {
- RS::get_singleton()->viewport_set_size(viewport, 0, 0);
- }
-
-#ifndef _3D_DISABLED
- } // if (!use_xr)
-#endif
+ if (p_allocated) {
+ RS::get_singleton()->viewport_set_size(viewport, 0, size.width, size.height, view_count);
+ } else {
+ RS::get_singleton()->viewport_set_size(viewport, 0, 0, 0, 0);
+ }
_update_global_transform();
update_configuration_warnings();
@@ -1138,20 +1139,37 @@ bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override,
return true;
}
-Size2i Viewport::_get_size() const {
-#ifndef XR_DISABLED
- if (use_xr) {
- if (XRServer::get_singleton() != nullptr) {
- Ref xr_interface = XRServer::get_singleton()->get_primary_interface();
- if (xr_interface.is_valid() && xr_interface->is_initialized()) {
- Size2 xr_size = xr_interface->get_render_target_size();
- return (Size2i)xr_size;
+void Viewport::_check_xr_size() {
+ // If our viewport has the use_xr flag set, our size and layout is managed by the XRServer.
+ if (use_xr && XRServer::get_singleton() != nullptr) {
+ Ref xr_interface = XRServer::get_singleton()->get_primary_interface();
+ if (xr_interface.is_valid() && xr_interface->is_initialized()) {
+ uint32_t layer_count = xr_interface->get_layer_count();
+ RS::get_singleton()->viewport_set_layer_count(viewport, layer_count);
+
+ for (uint32_t layer = 0; layer < layer_count; layer++) {
+ Size2 xr_size = xr_interface->get_render_target_size(layer);
+ int xr_view_count = xr_interface->get_view_count(layer);
+
+ if (layer == 0) {
+ // We set our first view checking all our other properties.
+ // Q: Should we check for changes here and skip if there are none?
+ _set_size((Size2i)xr_size, xr_view_count, Size2i(0, 0), true);
+ } else {
+ // Set additional layer.
+ // Q: Should we check for changes here and skip if there are none?
+ RS::get_singleton()->viewport_set_size(viewport, layer, (int)xr_size.width, (int)xr_size.height, xr_view_count);
+ }
}
+ } else {
+ // Set to default and prevent rendering for now (unless in editor, so we get a preview).
+ RS::get_singleton()->viewport_set_layer_count(viewport, 1);
+ _set_size(Engine::get_singleton()->is_editor_hint() ? Size2i(512, 512) : Size2i(0, 0), 1, Size2i(0, 0), false);
}
- return Size2i();
}
-#endif // XR_DISABLED
+}
+Size2i Viewport::_get_size() const {
return size;
}
@@ -1159,6 +1177,10 @@ Size2 Viewport::_get_size_2d_override() const {
return size_2d_override;
}
+int Viewport::_get_view_count() const {
+ return view_count;
+}
+
bool Viewport::_is_size_allocated() const {
return size_allocated;
}
@@ -4807,22 +4829,49 @@ void Viewport::set_use_xr(bool p_use_xr) {
RS::get_singleton()->viewport_set_use_xr(viewport, use_xr);
- if (!use_xr) {
- // Set viewport to previous size when exiting XR.
- if (size_allocated) {
- RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
- } else {
- RS::get_singleton()->viewport_set_size(viewport, 0, 0);
- }
+#ifndef XR_DISABLED
+ if (use_xr) {
+ // Note: use_xr is ONLY used for the primary XR viewport.
+ // Any additional (sub)viewports for composition layers and
+ // other purposes should not have use_xr set.
+ _check_xr_size();
+
+ // Enable internal process as we need to check for recommended size changes each frame
+ // (though size changes should only happen sporadically).
+ set_process_internal(true);
+ } else
+#endif // XR_DISABLED
+ {
+ // No longer check for recommended size changes in internal process.
+ set_process_internal(false);
+
+ // Change the layer count back to 1, however it is possible to add
+ // layers later on to our viewport even if we're not dealing with
+ // and XR viewport!
+ RS::get_singleton()->viewport_set_layer_count(viewport, 1);
// Reset render target override textures.
RID rt = RS::get_singleton()->viewport_get_render_target(viewport);
- RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());
+ RSG::texture_storage->render_target_set_override(rt, 0, RID(), RID(), RID(), RID());
+
+ // Q: Now that we properly set the size even when using XR,
+ // this wouldn't do anything meaningful.
+ // Do we want to set the size back to some default,
+ // and espectially set the view_count back to 1?
+ // Is there even a use case where someone turns XR off and
+ // continues?
+ //if (size_allocated) {
+ // RS::get_singleton()->viewport_set_size(viewport, 0, size.width, size.height, view_count);
+ //} else {
+ // RS::get_singleton()->viewport_set_size(viewport, 0, 0, 0, 0);
+ //}
}
+
+ notify_property_list_changed();
}
}
-bool Viewport::is_using_xr() {
+bool Viewport::is_using_xr() const {
ERR_READ_THREAD_GUARD_V(false);
return use_xr;
}
@@ -5377,17 +5426,17 @@ Viewport::~Viewport() {
void SubViewport::set_size(const Size2i &p_size) {
ERR_MAIN_THREAD_GUARD;
- _internal_set_size(p_size);
+ _internal_set_size(p_size, _get_view_count());
}
void SubViewport::set_size_force(const Size2i &p_size) {
ERR_MAIN_THREAD_GUARD;
// Use only for setting the size from the parent SubViewportContainer with enabled stretch mode.
// Don't expose function to scripting.
- _internal_set_size(p_size, true);
+ _internal_set_size(p_size, _get_view_count(), true);
}
-void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
+void SubViewport::_internal_set_size(const Size2i &p_size, const int p_view_count, bool p_force) {
SubViewportContainer *c = Object::cast_to(get_parent());
if (!p_force && c && c->is_stretch_enabled()) {
#ifdef DEBUG_ENABLED
@@ -5396,7 +5445,7 @@ void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
return;
}
- _set_size(p_size, _get_size_2d_override(), true);
+ _set_size(p_size, p_view_count, _get_size_2d_override(), true);
if (c) {
c->update_minimum_size();
@@ -5409,9 +5458,21 @@ Size2i SubViewport::get_size() const {
return _get_size();
}
+void SubViewport::set_view_count(const int p_view_count) {
+ ERR_MAIN_THREAD_GUARD;
+
+ _internal_set_size(_get_size(), p_view_count);
+}
+
+int SubViewport::get_view_count() const {
+ ERR_READ_THREAD_GUARD_V(1);
+
+ return _get_view_count();
+}
+
void SubViewport::set_size_2d_override(const Size2i &p_size) {
ERR_MAIN_THREAD_GUARD;
- _set_size(_get_size(), p_size, true);
+ _set_size(_get_size(), _get_view_count(), p_size, true);
}
Size2i SubViewport::get_size_2d_override() const {
@@ -5429,7 +5490,7 @@ void SubViewport::set_size_2d_override_stretch(bool p_enable) {
}
size_2d_override_stretch = p_enable;
- _set_size(_get_size(), _get_size_2d_override(), true);
+ _set_size(_get_size(), _get_view_count(), _get_size_2d_override(), true);
}
bool SubViewport::is_size_2d_override_stretch_enabled() const {
@@ -5535,6 +5596,9 @@ void SubViewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size_2d_override_stretch", "enable"), &SubViewport::set_size_2d_override_stretch);
ClassDB::bind_method(D_METHOD("is_size_2d_override_stretch_enabled"), &SubViewport::is_size_2d_override_stretch_enabled);
+ ClassDB::bind_method(D_METHOD("set_view_count", "view_count"), &SubViewport::set_view_count);
+ ClassDB::bind_method(D_METHOD("get_view_count"), &SubViewport::get_view_count);
+
ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &SubViewport::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &SubViewport::get_update_mode);
@@ -5544,6 +5608,7 @@ void SubViewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size_2d_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_2d_override", "get_size_2d_override");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "view_count"), "set_view_count", "get_view_count");
ADD_GROUP("Render Target", "render_target_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,When Parent Visible,Always"), "set_update_mode", "get_update_mode");
@@ -5563,7 +5628,9 @@ void SubViewport::_validate_property(PropertyInfo &p_property) const {
if (!Engine::get_singleton()->is_editor_hint()) {
return;
}
- if (p_property.name == "size") {
+ if (is_using_xr() && (p_property.name == "size" || p_property.name == "size_2d_override" || p_property.name == "size_2d_override_stretch" || p_property.name == "view_count")) {
+ p_property.usage = PROPERTY_USAGE_NONE; // Managed by XR
+ } else if (p_property.name == "size") {
SubViewportContainer *parent_svc = Object::cast_to(get_parent());
if (parent_svc && parent_svc->is_stretch_enabled()) {
p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY;
@@ -5574,5 +5641,5 @@ void SubViewport::_validate_property(PropertyInfo &p_property) const {
}
SubViewport::SubViewport() {
- RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
+ RS::get_singleton()->viewport_set_size(get_viewport_rid(), 0, get_size().width, get_size().height, 1);
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 0ed3864b08ed..500ca6715ac2 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -256,6 +256,7 @@ class Viewport : public Node {
Transform2D stretch_transform;
Size2i size = Size2i(512, 512);
+ int view_count = 1;
Size2 size_2d_override;
bool size_allocated = false;
@@ -502,10 +503,12 @@ class Viewport : public Node {
void _window_start_resize(SubWindowResize p_edge, Window *p_window);
protected:
- bool _set_size(const Size2i &p_size, const Size2 &p_size_2d_override, bool p_allocated);
+ bool _set_size(const Size2i &p_size, const int p_view_count, const Size2 &p_size_2d_override, bool p_allocated);
+ void _check_xr_size();
Size2i _get_size() const;
Size2 _get_size_2d_override() const;
+ int _get_view_count() const;
bool _is_size_allocated() const;
void _notification(int p_what);
@@ -849,7 +852,7 @@ class Viewport : public Node {
bool is_using_own_world_3d() const;
void set_use_xr(bool p_use_xr);
- bool is_using_xr();
+ bool is_using_xr() const;
#endif // _3D_DISABLED
Viewport();
@@ -879,7 +882,7 @@ class SubViewport : public Viewport {
ClearMode clear_mode = CLEAR_MODE_ALWAYS;
bool size_2d_override_stretch = false;
- void _internal_set_size(const Size2i &p_size, bool p_force = false);
+ void _internal_set_size(const Size2i &p_size, const int p_view_count, bool p_force = false);
protected:
static void _bind_methods();
@@ -891,6 +894,9 @@ class SubViewport : public Viewport {
Size2i get_size() const;
void set_size_force(const Size2i &p_size);
+ void set_view_count(const int p_view_count);
+ int get_view_count() const;
+
void set_size_2d_override(const Size2i &p_size);
Size2i get_size_2d_override() const;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index ec04feca747e..cd9604b2b984 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1299,13 +1299,21 @@ void Window::_update_viewport_size() {
}
}
- bool allocate = is_inside_tree() && visible && (window_id != DisplayServer::INVALID_WINDOW_ID || embedder != nullptr);
- _set_size(final_size, final_size_override, allocate);
+ // If `use_xr` set, we should skip this logic.
+#ifndef XR_DISABLED
+ if (is_using_xr()) {
+ return;
+ } else
+#endif // XR_DISABLED
+ {
+ bool allocate = is_inside_tree() && visible && (window_id != DisplayServer::INVALID_WINDOW_ID || embedder != nullptr);
+ _set_size(final_size, 1, final_size_override, allocate);
- if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), attach_to_screen_rect, window_id);
- } else if (!is_embedded()) {
- RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), Rect2i(), DisplayServer::INVALID_WINDOW_ID);
+ if (window_id != DisplayServer::INVALID_WINDOW_ID) {
+ RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), attach_to_screen_rect, window_id);
+ } else if (!is_embedded()) {
+ RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), Rect2i(), DisplayServer::INVALID_WINDOW_ID);
+ }
}
notification(NOTIFICATION_WM_SIZE_CHANGED);
@@ -1315,7 +1323,7 @@ void Window::_update_viewport_size() {
Viewport::set_oversampling_override(scale);
Size2 s = Size2(final_size.width * scale, final_size.height * scale).ceil();
RS::get_singleton()->viewport_set_global_canvas_transform(get_viewport_rid(), global_canvas_transform * scale * content_scale_factor);
- RS::get_singleton()->viewport_set_size(get_viewport_rid(), s.width, s.height);
+ RS::get_singleton()->viewport_set_size(get_viewport_rid(), 0, s.width, s.height, 1);
embedder->_sub_window_update(this);
}
}
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index afda5e054c7a..0e139f9c04dd 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -200,7 +200,7 @@ class TextureStorage : public RendererTextureStorage {
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(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override {}
+ virtual void render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override {}
virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); }
virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); }
virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); }
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 3706eb3779b8..7d5d03012565 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -3524,7 +3524,7 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) {
return rt->texture;
}
-void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) {
+void TextureStorage::render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 744f2f88eff0..b59623a15f74 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -789,7 +789,7 @@ class TextureStorage : public RendererTextureStorage {
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;
- virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
+ virtual void render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
virtual RID render_target_get_override_color(RID p_render_target) const 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;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 0c84348b6991..0628a2d80b12 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2589,7 +2589,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
return animated_material_found;
}
-void RendererSceneCull::render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, RenderInfo *r_render_info) {
+void RendererSceneCull::render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, uint32_t p_viewport_layer, uint32_t p_first_view, uint32_t p_view_count, RenderInfo *r_render_info) {
#ifndef _3D_DISABLED
Camera *camera = camera_owner.get_or_null(p_camera);
@@ -2613,6 +2613,9 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu
taa_frame_count = float(RSG::rasterizer->get_frame_number() % p_jitter_phase_count);
}
+ // Note: For now we're only implementing layer and views support for XR interfaces,
+ // but long term plans is to make this available for normal views too.
+
RendererSceneRender::CameraData camera_data;
// Setup Camera(s)
@@ -2665,8 +2668,7 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu
Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS];
Projection projections[RendererSceneRender::MAX_RENDER_VIEWS];
- uint32_t view_count = p_xr_interface->get_view_count();
- ERR_FAIL_COND_MSG(view_count == 0 || view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported");
+ ERR_FAIL_COND_MSG(p_view_count == 0 || p_view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported");
float aspect = p_viewport_size.width / (float)p_viewport_size.height;
@@ -2674,23 +2676,23 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu
// We ignore our camera position, it will have been positioned with a slightly old tracking position.
// Instead we take our origin point and have our XR interface add fresh tracking data! Whoohoo!
- for (uint32_t v = 0; v < view_count; v++) {
- transforms[v] = p_xr_interface->get_transform_for_view(v, world_origin);
- projections[v] = p_xr_interface->get_projection_for_view(v, aspect, camera->znear, camera->zfar);
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ transforms[v] = p_xr_interface->get_transform_for_view(p_viewport_layer, p_first_view + v, world_origin);
+ projections[v] = p_xr_interface->get_projection_for_view(p_viewport_layer, p_first_view + v, aspect, camera->znear, camera->zfar);
}
// If requested, we move the views to be rendered as if the HMD is at the XROrigin.
if (unlikely(xr_server->is_camera_locked_to_origin())) {
Transform3D camera_reset = p_xr_interface->get_camera_transform().affine_inverse() * xr_server->get_reference_frame().affine_inverse();
- for (uint32_t v = 0; v < view_count; v++) {
+ for (uint32_t v = 0; v < p_view_count; v++) {
transforms[v] *= camera_reset;
}
}
- if (view_count == 1) {
+ if (p_view_count == 1) {
camera_data.set_camera(transforms[0], projections[0], false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
- } else if (view_count == 2) {
- camera_data.set_multiview_camera(view_count, transforms, projections, false, false, camera->vaspect);
+ } else if (p_view_count == 2) {
+ camera_data.set_multiview_camera(p_view_count, transforms, projections, false, false, camera->vaspect);
} else {
// this won't be called (see fail check above) but keeping this comment to indicate we may support more then 2 views in the future...
}
@@ -2704,6 +2706,9 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu
// For now just cull on the first camera
RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_orthogonal);
+ // TODO we need to make sure we pass and properly use our "first_view" and "view_count" values here.
+ // Right now we're always going back to using the view_count in our render buffers, but that is now the total view count,
+ // and we might be processing just a slice of those.
_render_scene(&camera_data, p_render_buffers, environment, camera->attributes, compositor, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info);
#endif
}
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 6f7c303ff081..516441c3e596 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1156,7 +1156,7 @@ class RendererSceneCull : public RenderingMethod {
void _render_scene(const RendererSceneRender::CameraData *p_camera_data, const Ref &p_render_buffers, RID p_environment, RID p_force_camera_attributes, RID p_compositor, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows = true, RenderInfo *r_render_info = nullptr);
void render_empty_scene(const Ref &p_render_buffers, RID p_scenario, RID p_shadow_atlas);
- void render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, uint32_t p_viewport_layer = 0, uint32_t p_first_view = 0, uint32_t p_view_count = 1, RenderingMethod::RenderInfo *r_render_info = nullptr);
void update_dirty_instances() const;
void render_particle_colliders();
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index f87c90071fee..6a68d9942419 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -43,6 +43,8 @@
#endif // XR_DISABLED
static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) {
+ ERR_FAIL_COND_V(p_viewport->viewport_layers.is_empty(), Transform2D());
+
Transform2D xf = p_viewport->global_transform;
Vector2 pixel_snap_offset;
@@ -52,8 +54,8 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport,
// to the camera transform. Also, if the viewport size is not divisible
// by 2, the center point is offset by 0.5 px and we need to add 0.5
// before rounding to cancel it out.
- pixel_snap_offset.x = (p_viewport->size.width % 2) ? 0.0 : -0.5;
- pixel_snap_offset.y = (p_viewport->size.height % 2) ? 0.0 : -0.5;
+ pixel_snap_offset.x = (p_viewport->viewport_layers[0].size.width % 2) ? 0.0 : -0.5;
+ pixel_snap_offset.y = (p_viewport->viewport_layers[0].size.height % 2) ? 0.0 : -0.5;
}
float scale = 1.0;
@@ -87,6 +89,17 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport,
return xf;
}
+RendererViewport::ViewportLayer::ViewportLayer() {
+ render_target = RSG::texture_storage->render_target_create();
+}
+
+RendererViewport::ViewportLayer::~ViewportLayer() {
+ if (render_target.is_valid()) {
+ RSG::texture_storage->render_target_free(render_target);
+ render_target = RID();
+ }
+}
+
Vector RendererViewport::_sort_active_viewports() {
// We need to sort the viewports in a "topological order", children first and
// parents last. We also need to keep sibling viewports in the original order
@@ -125,10 +138,16 @@ Vector RendererViewport::_sort_active_viewports()
return result;
}
-void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
- if (p_viewport->render_buffers.is_valid()) {
- if (p_viewport->size.width == 0 || p_viewport->size.height == 0) {
- p_viewport->render_buffers.unref();
+void RendererViewport::_configure_all_3d_render_buffers(Viewport *p_viewport) {
+ for (ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ _configure_3d_render_buffers(p_viewport, &viewport_layer);
+ }
+}
+
+void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport, ViewportLayer *p_viewport_layer) {
+ if (p_viewport_layer->render_buffers.is_valid()) {
+ if (p_viewport_layer->size.width == 0 || p_viewport_layer->size.height == 0) {
+ p_viewport_layer->render_buffers.unref();
} else {
const float EPSILON = 0.0001;
float scaling_3d_scale = p_viewport->scaling_3d_scale;
@@ -211,8 +230,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR:
// Clamp 3D rendering resolution to reasonable values supported on most hardware.
// This prevents freezing the engine or outright crashing on lower-end GPUs.
- target_width = p_viewport->size.width;
- target_height = p_viewport->size.height;
+ target_width = p_viewport_layer->size.width;
+ target_height = p_viewport_layer->size.height;
render_width = CLAMP(target_width * scaling_3d_scale, 1, 16384);
render_height = CLAMP(target_height * scaling_3d_scale, 1, 16384);
break;
@@ -220,14 +239,14 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
case RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL:
case RS::VIEWPORT_SCALING_3D_MODE_FSR:
case RS::VIEWPORT_SCALING_3D_MODE_FSR2:
- target_width = p_viewport->size.width;
- target_height = p_viewport->size.height;
+ target_width = p_viewport_layer->size.width;
+ target_height = p_viewport_layer->size.height;
render_width = MAX(target_width * scaling_3d_scale, 1.0); // target_width / (target_width * scaling)
render_height = MAX(target_height * scaling_3d_scale, 1.0);
break;
case RS::VIEWPORT_SCALING_3D_MODE_OFF:
- target_width = p_viewport->size.width;
- target_height = p_viewport->size.height;
+ target_width = p_viewport_layer->size.width;
+ target_height = p_viewport_layer->size.height;
render_width = target_width;
render_height = target_height;
break;
@@ -236,8 +255,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling.", scaling_3d_mode));
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
scaling_3d_scale = 1.0;
- target_width = p_viewport->size.width;
- target_height = p_viewport->size.height;
+ target_width = p_viewport_layer->size.width;
+ target_height = p_viewport_layer->size.height;
render_width = target_width;
render_height = target_height;
break;
@@ -253,18 +272,18 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
jitter_phase_count = 16;
}
- p_viewport->internal_size = Size2(render_width, render_height);
- p_viewport->jitter_phase_count = jitter_phase_count;
+ p_viewport_layer->internal_size = Size2(render_width, render_height);
+ p_viewport_layer->jitter_phase_count = jitter_phase_count;
// At resolution scales lower than 1.0, use negative texture mipmap bias
// to compensate for the loss of sharpness.
const float texture_mipmap_bias = std::log2(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias;
RenderSceneBuffersConfiguration rb_config;
- rb_config.set_render_target(p_viewport->render_target);
+ rb_config.set_render_target(p_viewport_layer->render_target);
rb_config.set_internal_size(Size2i(render_width, render_height));
rb_config.set_target_size(Size2(target_width, target_height));
- rb_config.set_view_count(p_viewport->view_count);
+ rb_config.set_view_count(p_viewport_layer->view_count);
rb_config.set_scaling_3d_mode(scaling_3d_mode);
rb_config.set_msaa_3d(msaa_3d);
rb_config.set_screen_space_aa(p_viewport->screen_space_aa);
@@ -274,13 +293,15 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
rb_config.set_use_taa(use_taa);
rb_config.set_use_debanding(p_viewport->use_debanding);
- p_viewport->render_buffers->configure(&rb_config);
+ p_viewport_layer->render_buffers->configure(&rb_config);
}
}
}
void RendererViewport::_draw_3d(Viewport *p_viewport) {
#ifndef _3D_DISABLED
+ ERR_FAIL_COND(p_viewport->viewport_layers.is_empty());
+
RENDER_TIMESTAMP("> Render 3D Scene");
Ref xr_interface;
@@ -292,10 +313,18 @@ void RendererViewport::_draw_3d(Viewport *p_viewport) {
if (p_viewport->use_occlusion_culling) {
if (p_viewport->occlusion_buffer_dirty) {
- float aspect = p_viewport->size.aspect();
+ Size2 size = p_viewport->viewport_layers[0].size;
+ for (uint32_t i = 1; i < p_viewport->viewport_layers.size(); i++) {
+ const Size2 &layer_size = p_viewport->viewport_layers[i].size;
+ if (size.x < layer_size.x || size.y < layer_size.y) {
+ size = layer_size;
+ }
+ }
+
+ float aspect = size.aspect();
int max_size = occlusion_rays_per_thread * WorkerThreadPool::get_singleton()->get_thread_count();
- int viewport_size = p_viewport->size.width * p_viewport->size.height;
+ int viewport_size = size.width * size.height;
max_size = CLAMP(max_size, viewport_size / (32 * 32), viewport_size / (2 * 2)); // At least one depth pixel for every 16x16 region. At most one depth pixel for every 2x2 region.
float height = Math::sqrt(max_size / aspect);
@@ -305,14 +334,25 @@ void RendererViewport::_draw_3d(Viewport *p_viewport) {
}
}
- float screen_mesh_lod_threshold = p_viewport->mesh_lod_threshold / float(p_viewport->size.width);
- RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->internal_size, p_viewport->jitter_phase_count, screen_mesh_lod_threshold, p_viewport->shadow_atlas, xr_interface, &p_viewport->render_info);
+ uint32_t layer = 0;
+ for (const ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ for (uint32_t first_view = 0; first_view < viewport_layer.view_count; first_view += RendererSceneRender::MAX_RENDER_VIEWS) {
+ uint32_t view_count = MIN(viewport_layer.view_count - first_view, uint32_t(RendererSceneRender::MAX_RENDER_VIEWS));
+
+ float screen_mesh_lod_threshold = p_viewport->mesh_lod_threshold / float(viewport_layer.size.width);
+ RSG::scene->render_camera(viewport_layer.render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, viewport_layer.internal_size, viewport_layer.jitter_phase_count, screen_mesh_lod_threshold, p_viewport->shadow_atlas, xr_interface, layer, first_view, view_count, &p_viewport->render_info);
+ }
+
+ layer++;
+ }
RENDER_TIMESTAMP("< Render 3D Scene");
#endif // _3D_DISABLED
}
void RendererViewport::_draw_viewport(Viewport *p_viewport) {
+ ERR_FAIL_COND(p_viewport->viewport_layers.is_empty());
+
if (p_viewport->measure_render_time) {
String rt_id = "vp_begin_" + itos(p_viewport->self.get_id());
RSG::utilities->capture_timestamp(rt_id);
@@ -326,7 +366,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
/* Camera should always be BEFORE any other 3D */
- bool can_draw_2d = !p_viewport->disable_2d && p_viewport->view_count == 1; // Stereo rendering does not support 2D, no depth data
+ bool can_draw_2d = !p_viewport->disable_2d && p_viewport->viewport_layers.size() == 1 && p_viewport->viewport_layers[0].view_count == 1; // Can't do 2D on multiple layers or stereo rendering.
bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front
int scenario_canvas_max_layer = 0;
bool force_clear_render_target = false;
@@ -352,17 +392,23 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
bool can_draw_3d = RSG::scene->is_camera(p_viewport->camera) && !p_viewport->disable_3d;
- if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) {
- //wants to draw 3D but there is no render buffer, create
- p_viewport->render_buffers = RSG::scene->render_buffers_create();
+ if (scenario_draw_canvas_bg || can_draw_3d) {
+ for (ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ if (!viewport_layer.render_buffers.is_valid()) {
+ //wants to draw 3D but there is no render buffer, create
+ viewport_layer.render_buffers = RSG::scene->render_buffers_create();
- _configure_3d_render_buffers(p_viewport);
+ _configure_3d_render_buffers(p_viewport, &viewport_layer);
+ }
+ }
}
Color bgcolor = p_viewport->transparent_bg ? Color(0, 0, 0, 0) : RSG::texture_storage->get_default_clear_color();
if (p_viewport->clear_mode != RS::VIEWPORT_CLEAR_NEVER) {
- RSG::texture_storage->render_target_request_clear(p_viewport->render_target, bgcolor);
+ for (const ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ RSG::texture_storage->render_target_request_clear(viewport_layer.render_target, bgcolor);
+ }
if (p_viewport->clear_mode == RS::VIEWPORT_CLEAR_ONLY_NEXT_FRAME) {
p_viewport->clear_mode = RS::VIEWPORT_CLEAR_NEVER;
}
@@ -370,15 +416,19 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (!scenario_draw_canvas_bg && can_draw_3d) {
if (force_clear_render_target) {
- RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);
+ for (const ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ RSG::texture_storage->render_target_do_clear_request(viewport_layer.render_target);
+ }
}
_draw_3d(p_viewport);
}
if (can_draw_2d) {
+ const ViewportLayer &viewport_layer = p_viewport->viewport_layers[0];
+
RBMap canvas_map;
- Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);
+ Rect2 clip_rect(0, 0, viewport_layer.size.x, viewport_layer.size.y);
RendererCanvasRender::Light *lights = nullptr;
RendererCanvasRender::Light *lights_with_shadow = nullptr;
@@ -388,7 +438,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (p_viewport->sdf_active) {
// Process SDF.
- Rect2 sdf_rect = RSG::texture_storage->render_target_get_sdf_rect(p_viewport->render_target);
+ Rect2 sdf_rect = RSG::texture_storage->render_target_get_sdf_rect(viewport_layer.render_target);
RendererCanvasRender::LightOccluderInstance *occluders = nullptr;
@@ -417,12 +467,12 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
}
}
- RSG::canvas_render->render_sdf(p_viewport->render_target, occluders);
- RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, true);
+ RSG::canvas_render->render_sdf(viewport_layer.render_target, occluders);
+ RSG::texture_storage->render_target_mark_sdf_enabled(viewport_layer.render_target, true);
p_viewport->sdf_active = false; // If used, gets set active again.
} else {
- RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, false);
+ RSG::texture_storage->render_target_mark_sdf_enabled(viewport_layer.render_target, false);
}
Rect2 shadow_rect;
@@ -641,9 +691,9 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (scenario_draw_canvas_bg && canvas_map.begin() && canvas_map.begin()->key.get_layer() > scenario_canvas_max_layer) {
// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.
// Clear now otherwise we copy over garbage from the render target.
- RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);
+ RSG::texture_storage->render_target_do_clear_request(viewport_layer.render_target);
if (!can_draw_3d) {
- RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
+ RSG::scene->render_empty_scene(viewport_layer.render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else {
_draw_3d(p_viewport);
}
@@ -676,7 +726,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
ptr = ptr->filter_next_ptr;
}
- RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel, p_viewport->canvas_cull_mask, &p_viewport->render_info);
+ RSG::canvas->render_canvas(viewport_layer.render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel, p_viewport->canvas_cull_mask, &p_viewport->render_info);
if (RSG::canvas->was_sdf_used()) {
p_viewport->sdf_active = true;
}
@@ -684,9 +734,9 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (scenario_draw_canvas_bg && E.key.get_layer() >= scenario_canvas_max_layer) {
// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.
// Clear now otherwise we copy over garbage from the render target.
- RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);
+ RSG::texture_storage->render_target_do_clear_request(viewport_layer.render_target);
if (!can_draw_3d) {
- RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
+ RSG::scene->render_empty_scene(viewport_layer.render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else {
_draw_3d(p_viewport);
}
@@ -698,23 +748,25 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (scenario_draw_canvas_bg) {
// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.
// Clear now otherwise we copy over garbage from the render target.
- RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);
+ RSG::texture_storage->render_target_do_clear_request(viewport_layer.render_target);
if (!can_draw_3d) {
- RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
+ RSG::scene->render_empty_scene(viewport_layer.render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else {
_draw_3d(p_viewport);
}
}
}
- if (RSG::texture_storage->render_target_is_clear_requested(p_viewport->render_target)) {
- //was never cleared in the end, force clear it
- RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);
- }
+ for (const ViewportLayer &viewport_layer : p_viewport->viewport_layers) {
+ if (RSG::texture_storage->render_target_is_clear_requested(viewport_layer.render_target)) {
+ //was never cleared in the end, force clear it
+ RSG::texture_storage->render_target_do_clear_request(viewport_layer.render_target);
+ }
- if (RSG::texture_storage->render_target_get_msaa_needs_resolve(p_viewport->render_target)) {
- WARN_PRINT_ONCE("2D MSAA is enabled while there is no 2D content. Disable 2D MSAA for better performance.");
- RSG::texture_storage->render_target_do_msaa_resolve(p_viewport->render_target);
+ if (RSG::texture_storage->render_target_get_msaa_needs_resolve(viewport_layer.render_target)) {
+ WARN_PRINT_ONCE("2D MSAA is enabled while there is no 2D content. Disable 2D MSAA for better performance.");
+ RSG::texture_storage->render_target_do_msaa_resolve(viewport_layer.render_target);
+ }
}
if (p_viewport->measure_render_time) {
@@ -761,27 +813,20 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
continue;
}
- if (!vp->render_target.is_valid()) {
+ bool render_targets_valid = !vp->viewport_layers.is_empty();
+ for (const ViewportLayer &viewport_layer : vp->viewport_layers) {
+ render_targets_valid &= viewport_layer.render_target.is_valid();
+ }
+ if (!render_targets_valid) {
continue;
}
- //ERR_CONTINUE(!vp->render_target.is_valid());
+ //ERR_CONTINUE(!render_targets_valid);
bool visible = vp->viewport_to_screen_rect != Rect2();
#ifndef XR_DISABLED
if (vp->use_xr) {
- if (xr_interface.is_valid()) {
- // Ignore update mode we have to commit frames to our XR interface
- visible = true;
-
- // Override our size, make sure it matches our required size and is created as a stereo target
- Size2 xr_size = xr_interface->get_render_target_size();
- _viewport_set_size(vp, xr_size.width, xr_size.height, xr_interface->get_view_count());
- } else {
- // don't render anything
- visible = false;
- vp->size = Size2();
- }
+ visible = xr_interface.is_valid();
} else
#endif // XR_DISABLED
{
@@ -789,7 +834,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
visible = true;
}
- if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_VISIBLE && RSG::texture_storage->render_target_was_used(vp->render_target)) {
+ if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_VISIBLE && RSG::texture_storage->render_target_was_used(vp->viewport_layers[0].render_target)) {
visible = true;
}
@@ -801,7 +846,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
}
}
- visible = visible && vp->size.x > 1 && vp->size.y > 1;
+ visible = visible && vp->viewport_layers[0].size.x > 1 && vp->viewport_layers[0].size.y > 1;
if (visible) {
vp->last_pass = draw_viewports_pass;
@@ -821,30 +866,41 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
RENDER_TIMESTAMP("> Render Viewport " + itos(i));
- RSG::texture_storage->render_target_set_as_unused(vp->render_target);
+ for (const ViewportLayer &viewport_layer : vp->viewport_layers) {
+ RSG::texture_storage->render_target_set_as_unused(viewport_layer.render_target);
+ }
#ifndef XR_DISABLED
if (vp->use_xr && xr_interface.is_valid()) {
- // Inform XR interface we're about to render its viewport,
+ // Inform XR interface we're about to render its viewport based on the first render target,
// if this returns false we don't render.
// This usually is a result of the player taking off their headset and OpenXR telling us to skip
// rendering frames.
- if (xr_interface->pre_draw_viewport(vp->render_target)) {
- RSG::texture_storage->render_target_set_override(vp->render_target,
- xr_interface->get_color_texture(),
- xr_interface->get_depth_texture(),
- xr_interface->get_velocity_texture(),
- xr_interface->get_velocity_depth_texture());
-
- RSG::texture_storage->render_target_set_velocity_target_size(vp->render_target, xr_interface->get_velocity_target_size());
+ if (xr_interface->pre_draw_viewport(vp->viewport_layers[0].render_target)) {
+ // Q Do we want to move this logic to earlier.
+ uint32_t layer = 0;
+ for (const ViewportLayer &viewport_layer : vp->viewport_layers) {
+ RSG::texture_storage->render_target_set_override(viewport_layer.render_target,
+ layer,
+ xr_interface->get_color_texture(layer),
+ xr_interface->get_depth_texture(layer),
+ xr_interface->get_velocity_texture(layer),
+ xr_interface->get_velocity_depth_texture(layer));
+
+ if (layer == 0) {
+ // Q: do we need to support layers for these as well.
+ RSG::texture_storage->render_target_set_velocity_target_size(viewport_layer.render_target, xr_interface->get_velocity_target_size());
+
+ if (xr_interface->get_velocity_texture().is_valid()) {
+ _viewport_set_force_motion_vectors(vp, true);
+ } else {
+ _viewport_set_force_motion_vectors(vp, false);
+ }
- if (xr_interface->get_velocity_texture().is_valid()) {
- _viewport_set_force_motion_vectors(vp, true);
- } else {
- _viewport_set_force_motion_vectors(vp, false);
+ RSG::texture_storage->render_target_set_render_region(viewport_layer.render_target, xr_interface->get_render_region());
+ }
+ layer++;
}
- RSG::texture_storage->render_target_set_render_region(vp->render_target, xr_interface->get_render_region());
-
// render...
RSG::scene->set_debug_draw_mode(vp->debug_draw);
@@ -852,7 +908,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
_draw_viewport(vp);
// commit our eyes
- Vector blits = xr_interface->post_draw_viewport(vp->render_target, vp->viewport_to_screen_rect);
+ Vector blits = xr_interface->post_draw_viewport(vp->viewport_layers[0].render_target, vp->viewport_to_screen_rect);
if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID) {
if (RSG::rasterizer->is_opengl()) {
if (blits.size() > 0) {
@@ -881,12 +937,12 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) {
//copy to screen if set as such
BlitToScreen blit;
- blit.render_target = vp->render_target;
+ blit.render_target = vp->viewport_layers[0].render_target;
if (vp->viewport_to_screen_rect != Rect2()) {
blit.dst_rect = vp->viewport_to_screen_rect;
} else {
blit.dst_rect.position = Vector2();
- blit.dst_rect.size = vp->size;
+ blit.dst_rect.size = vp->viewport_layers[0].size;
}
if (RSG::rasterizer->is_opengl()) {
@@ -940,7 +996,6 @@ void RendererViewport::viewport_initialize(RID p_rid) {
viewport_owner.initialize_rid(p_rid);
Viewport *viewport = viewport_owner.get_or_null(p_rid);
viewport->self = p_rid;
- viewport->render_target = RSG::texture_storage->render_target_create();
viewport->shadow_atlas = RSG::light_storage->shadow_atlas_create();
viewport->viewport_render_direct_to_screen = false;
@@ -956,13 +1011,6 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) {
}
viewport->use_xr = p_use_xr;
-
- // Re-configure the 3D render buffers when disabling XR. They'll get
- // re-configured when enabling XR in draw_viewports().
- if (!p_use_xr) {
- viewport->view_count = 1;
- _configure_3d_render_buffers(viewport);
- }
}
void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::ViewportScaling3DMode p_mode) {
@@ -987,7 +1035,7 @@ void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::Viewport
num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;
}
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpness) {
@@ -995,7 +1043,7 @@ void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpn
ERR_FAIL_NULL(viewport);
viewport->fsr_sharpness = p_sharpness;
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) {
@@ -1003,7 +1051,7 @@ void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_
ERR_FAIL_NULL(viewport);
viewport->texture_mipmap_bias = p_mipmap_bias;
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_anisotropic_filtering_level(RID p_viewport, RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) {
@@ -1011,7 +1059,7 @@ void RendererViewport::viewport_set_anisotropic_filtering_level(RID p_viewport,
ERR_FAIL_NULL(viewport);
viewport->anisotropic_filtering_level = p_anisotropic_filtering_level;
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) {
@@ -1026,29 +1074,37 @@ void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_sca
}
viewport->scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
-void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) {
- ERR_FAIL_COND(p_width < 0 || p_height < 0);
+void RendererViewport::viewport_set_layer_count(RID p_viewport, uint32_t p_layer_count) {
+ ERR_FAIL_COND(p_layer_count == 0);
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
- ERR_FAIL_COND_MSG(viewport->use_xr, "Cannot set viewport size when using XR");
- _viewport_set_size(viewport, p_width, p_height, 1);
+ viewport->viewport_layers.resize(p_layer_count);
}
-void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count) {
+void RendererViewport::viewport_set_size(RID p_viewport, uint32_t p_layer, int p_width, int p_height, int p_view_count) {
+ ERR_FAIL_COND(p_width < 0 || p_height < 0 || p_view_count < 0);
+
+ Viewport *viewport = viewport_owner.get_or_null(p_viewport);
+ ERR_FAIL_NULL(viewport);
+ ERR_FAIL_UNSIGNED_INDEX(p_layer, viewport->viewport_layers.size());
+
+ // Select the layer we're updating.
+ ViewportLayer &viewport_layer = viewport->viewport_layers[p_layer];
+
Size2i new_size(p_width, p_height);
- if (p_viewport->size != new_size || p_viewport->view_count != p_view_count) {
- p_viewport->size = new_size;
- p_viewport->view_count = p_view_count;
+ if (viewport_layer.size != new_size || viewport_layer.view_count != (uint32_t)p_view_count) {
+ viewport_layer.size = new_size;
+ viewport_layer.view_count = p_view_count;
- RSG::texture_storage->render_target_set_size(p_viewport->render_target, p_width, p_height, p_view_count);
- _configure_3d_render_buffers(p_viewport);
+ RSG::texture_storage->render_target_set_size(viewport_layer.render_target, p_width, p_height, p_view_count);
+ _configure_3d_render_buffers(viewport, &viewport_layer);
- p_viewport->occlusion_buffer_dirty = true;
+ viewport->occlusion_buffer_dirty = true;
}
}
@@ -1090,13 +1146,15 @@ void RendererViewport::viewport_set_clear_mode(RID p_viewport, RS::ViewportClear
void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect, DisplayServer::WindowID p_screen) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
+ ERR_FAIL_COND(viewport->viewport_layers.is_empty());
+ const ViewportLayer &viewport_layer = viewport->viewport_layers[0];
if (p_screen != DisplayServer::INVALID_WINDOW_ID) {
// If using OpenGL we can optimize this operation by rendering directly to system_fbo
// instead of rendering to fbo and copying to system_fbo after
if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {
- RSG::texture_storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->view_count);
- RSG::texture_storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y);
+ RSG::texture_storage->render_target_set_size(viewport_layer.render_target, p_rect.size.x, p_rect.size.y, viewport_layer.view_count);
+ RSG::texture_storage->render_target_set_position(viewport_layer.render_target, p_rect.position.x, p_rect.position.y);
}
viewport->viewport_to_screen_rect = p_rect;
@@ -1104,8 +1162,8 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_
} else {
// if render_direct_to_screen was used, reset size and position
if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {
- RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0);
- RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count);
+ RSG::texture_storage->render_target_set_position(viewport_layer.render_target, 0, 0);
+ RSG::texture_storage->render_target_set_size(viewport_layer.render_target, viewport_layer.size.x, viewport_layer.size.y, viewport_layer.view_count);
}
viewport->viewport_to_screen_rect = Rect2();
@@ -1116,6 +1174,8 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_
void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
+ ERR_FAIL_COND(viewport->viewport_layers.is_empty());
+ const ViewportLayer &viewport_layer = viewport->viewport_layers[0];
if (p_enable == viewport->viewport_render_direct_to_screen) {
return;
@@ -1123,17 +1183,17 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool
// if disabled, reset render_target size and position
if (!p_enable) {
- RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0);
- RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count);
+ RSG::texture_storage->render_target_set_position(viewport_layer.render_target, 0, 0);
+ RSG::texture_storage->render_target_set_size(viewport_layer.render_target, viewport_layer.size.x, viewport_layer.size.y, viewport_layer.view_count);
}
- RSG::texture_storage->render_target_set_direct_to_screen(viewport->render_target, p_enable);
+ RSG::texture_storage->render_target_set_direct_to_screen(viewport_layer.render_target, p_enable);
viewport->viewport_render_direct_to_screen = p_enable;
// if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation
if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) {
- RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->view_count);
- RSG::texture_storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y);
+ RSG::texture_storage->render_target_set_size(viewport_layer.render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport_layer.view_count);
+ RSG::texture_storage->render_target_set_position(viewport_layer.render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y);
}
}
@@ -1151,18 +1211,20 @@ RS::ViewportUpdateMode RendererViewport::viewport_get_update_mode(RID p_viewport
return viewport->update_mode;
}
-RID RendererViewport::viewport_get_render_target(RID p_viewport) const {
+RID RendererViewport::viewport_get_render_target(RID p_viewport, uint32_t p_index) const {
const Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL_V(viewport, RID());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, viewport->viewport_layers.size(), RID());
- return viewport->render_target;
+ return viewport->viewport_layers[p_index].render_target;
}
-RID RendererViewport::viewport_get_texture(RID p_viewport) const {
+RID RendererViewport::viewport_get_texture(RID p_viewport, uint32_t p_index) const {
const Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL_V(viewport, RID());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, viewport->viewport_layers.size(), RID());
- return RSG::texture_storage->render_target_get_texture(viewport->render_target);
+ return RSG::texture_storage->render_target_get_texture(viewport->viewport_layers[p_index].render_target);
}
RID RendererViewport::viewport_get_occluder_debug_texture(RID p_viewport) const {
@@ -1284,7 +1346,9 @@ void RendererViewport::viewport_set_transparent_background(RID p_viewport, bool
return;
}
- RSG::texture_storage->render_target_set_transparent(viewport->render_target, p_enabled);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_transparent(viewport_layer.render_target, p_enabled);
+ }
viewport->transparent_bg = p_enabled;
}
@@ -1329,7 +1393,10 @@ void RendererViewport::viewport_set_msaa_2d(RID p_viewport, RS::ViewportMSAA p_m
return;
}
viewport->msaa_2d = p_msaa;
- RSG::texture_storage->render_target_set_msaa(viewport->render_target, p_msaa);
+
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_msaa(viewport_layer.render_target, p_msaa);
+ }
}
void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_msaa) {
@@ -1340,7 +1407,8 @@ void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_m
return;
}
viewport->msaa_3d = p_msaa;
- _configure_3d_render_buffers(viewport);
+
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) {
@@ -1351,7 +1419,10 @@ void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d
return;
}
viewport->use_hdr_2d = p_use_hdr_2d;
- RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d);
+
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_use_hdr(viewport_layer.render_target, p_use_hdr_2d);
+ }
}
bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const {
@@ -1370,7 +1441,8 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport
return;
}
viewport->screen_space_aa = p_mode;
- _configure_3d_render_buffers(viewport);
+
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) {
@@ -1390,7 +1462,7 @@ void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) {
num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;
}
- _configure_3d_render_buffers(viewport);
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) {
@@ -1401,8 +1473,11 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb
return;
}
viewport->use_debanding = p_use_debanding;
- RSG::texture_storage->render_target_set_use_debanding(viewport->render_target, p_use_debanding);
- _configure_3d_render_buffers(viewport);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_use_debanding(viewport_layer.render_target, p_use_debanding);
+ }
+
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_force_motion_vectors(RID p_viewport, bool p_force_motion_vectors) {
@@ -1425,7 +1500,7 @@ void RendererViewport::_viewport_set_force_motion_vectors(RendererViewport::View
num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;
}
- _configure_3d_render_buffers(p_viewport);
+ _configure_all_3d_render_buffers(p_viewport);
}
void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) {
@@ -1547,7 +1622,9 @@ void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::V
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
- RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport_layer.render_target, p_size, p_scale);
+ }
}
RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const {
@@ -1568,34 +1645,37 @@ void RendererViewport::viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
- RSG::texture_storage->render_target_set_vrs_mode(viewport->render_target, p_mode);
- _configure_3d_render_buffers(viewport);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_vrs_mode(viewport_layer.render_target, p_mode);
+ }
+ _configure_all_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
- RSG::texture_storage->render_target_set_vrs_update_mode(viewport->render_target, p_mode);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_vrs_update_mode(viewport_layer.render_target, p_mode);
+ }
}
void RendererViewport::viewport_set_vrs_texture(RID p_viewport, RID p_texture) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
- RSG::texture_storage->render_target_set_vrs_texture(viewport->render_target, p_texture);
- _configure_3d_render_buffers(viewport);
+ for (const ViewportLayer &viewport_layer : viewport->viewport_layers) {
+ RSG::texture_storage->render_target_set_vrs_texture(viewport_layer.render_target, p_texture);
+ }
+ _configure_all_3d_render_buffers(viewport);
}
bool RendererViewport::free(RID p_rid) {
if (viewport_owner.owns(p_rid)) {
Viewport *viewport = viewport_owner.get_or_null(p_rid);
- RSG::texture_storage->render_target_free(viewport->render_target);
+ viewport->viewport_layers.clear();
RSG::light_storage->shadow_atlas_free(viewport->shadow_atlas);
- if (viewport->render_buffers.is_valid()) {
- viewport->render_buffers.unref();
- }
while (viewport->canvas_map.begin()) {
viewport_remove_canvas(p_rid, viewport->canvas_map.begin()->key);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index 2aec25428026..126c7fb26cb7 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -41,6 +41,22 @@ class RendererViewport {
struct CanvasBase {
};
+ class ViewportLayer {
+ public:
+ Size2i internal_size;
+ Size2i size;
+ uint32_t view_count = 1;
+ uint32_t jitter_phase_count = 0;
+
+ RID render_target;
+ RID render_target_texture;
+
+ Ref render_buffers;
+
+ ViewportLayer();
+ ~ViewportLayer();
+ };
+
struct Viewport {
RID self;
RID parent;
@@ -48,9 +64,8 @@ class RendererViewport {
// use xr interface to override camera positioning and projection matrices and control output
bool use_xr = false;
- Size2i internal_size;
- Size2i size;
- uint32_t view_count;
+ LocalVector viewport_layers;
+
RID camera;
RID scenario;
@@ -60,11 +75,7 @@ class RendererViewport {
float texture_mipmap_bias = 0.0f;
RS::ViewportAnisotropicFiltering anisotropic_filtering_level = RenderingServer::VIEWPORT_ANISOTROPY_4X;
bool fsr_enabled = false;
- uint32_t jitter_phase_count = 0;
RS::ViewportUpdateMode update_mode = RenderingServer::VIEWPORT_UPDATE_WHEN_VISIBLE;
- RID render_target;
- RID render_target_texture;
- Ref render_buffers;
RS::ViewportMSAA msaa_2d = RenderingServer::VIEWPORT_MSAA_DISABLED;
RS::ViewportMSAA msaa_3d = RenderingServer::VIEWPORT_MSAA_DISABLED;
@@ -153,7 +164,8 @@ class RendererViewport {
RenderingMethod::RenderInfo render_info;
Viewport() {
- view_count = 1;
+ viewport_layers.resize(1);
+
update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE;
clear_mode = RS::VIEWPORT_CLEAR_ALWAYS;
transparent_bg = false;
@@ -201,10 +213,10 @@ class RendererViewport {
private:
Vector _sort_active_viewports();
- void _viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count);
bool _viewport_requires_motion_vectors(Viewport *p_viewport);
void _viewport_set_force_motion_vectors(Viewport *p_viewport, bool p_force_motion_vectors);
- void _configure_3d_render_buffers(Viewport *p_viewport);
+ void _configure_all_3d_render_buffers(Viewport *p_viewport);
+ void _configure_3d_render_buffers(Viewport *p_viewport, ViewportLayer *p_viewport_layer);
void _draw_3d(Viewport *p_viewport);
void _draw_viewport(Viewport *p_viewport);
@@ -218,7 +230,8 @@ class RendererViewport {
void viewport_set_use_xr(RID p_viewport, bool p_use_xr);
- void viewport_set_size(RID p_viewport, int p_width, int p_height);
+ void viewport_set_layer_count(RID p_viewport, uint32_t p_layer_count);
+ void viewport_set_size(RID p_viewport, uint32_t p_layer, int p_width, int p_height, int p_view_count);
void viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect = Rect2(), DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
void viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable);
@@ -238,8 +251,8 @@ class RendererViewport {
void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode);
- RID viewport_get_render_target(RID p_viewport) const;
- RID viewport_get_texture(RID p_viewport) const;
+ RID viewport_get_render_target(RID p_viewport, uint32_t p_layer = 0) const;
+ RID viewport_get_texture(RID p_viewport, uint32_t p_layer = 0) const;
RID viewport_get_occluder_debug_texture(RID p_viewport) const;
void viewport_set_prev_camera_data(RID p_viewport, const RendererSceneRender::CameraData *p_camera_data);
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index 38e173bf48f2..88ffefd71f4b 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -346,7 +346,7 @@ class RenderingMethod {
int info[RS::VIEWPORT_RENDER_INFO_TYPE_MAX][RS::VIEWPORT_RENDER_INFO_MAX] = {};
};
- virtual void render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, RenderInfo *r_render_info = nullptr) = 0;
+ virtual void render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, uint32_t p_jitter_phase_count, float p_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, uint32_t p_viewport_layer = 0, uint32_t p_first_view = 0, uint32_t p_view_count = 1, RenderInfo *r_render_info = nullptr) = 0;
virtual void update() = 0;
virtual void render_probes() = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 149a71b82fd6..54553d6d78a6 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -691,7 +691,8 @@ class RenderingServerDefault : public RenderingServer {
FUNCRIDSPLIT(viewport)
FUNC2(viewport_set_use_xr, RID, bool)
- FUNC3(viewport_set_size, RID, int, int)
+ FUNC2(viewport_set_layer_count, RID, uint32_t)
+ FUNC5(viewport_set_size, RID, uint32_t, int, int, int)
FUNC2(viewport_set_active, RID, bool)
FUNC2(viewport_set_parent_viewport, RID, RID)
@@ -710,8 +711,8 @@ class RenderingServerDefault : public RenderingServer {
FUNC2(viewport_set_update_mode, RID, ViewportUpdateMode)
FUNC1RC(ViewportUpdateMode, viewport_get_update_mode, RID)
- FUNC1RC(RID, viewport_get_render_target, RID)
- FUNC1RC(RID, viewport_get_texture, RID)
+ FUNC2RC(RID, viewport_get_render_target, RID, uint32_t)
+ FUNC2RC(RID, viewport_get_texture, RID, uint32_t)
FUNC2(viewport_set_disable_2d, RID, bool)
FUNC2(viewport_set_environment_mode, RID, ViewportEnvironmentMode)
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index 135bcc39fc50..678bb98a044c 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -179,7 +179,7 @@ class RendererTextureStorage {
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(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) = 0;
+ virtual void render_target_set_override(RID p_render_target, int p_index, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) = 0;
virtual RID render_target_get_override_color(RID p_render_target) const = 0;
virtual RID render_target_get_override_depth(RID p_render_target) const = 0;
virtual RID render_target_get_override_velocity(RID p_render_target) const = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 8202dff8cbe2..b751fb6ae7c7 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2822,7 +2822,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_create"), &RenderingServer::viewport_create);
ClassDB::bind_method(D_METHOD("viewport_set_use_xr", "viewport", "use_xr"), &RenderingServer::viewport_set_use_xr);
- ClassDB::bind_method(D_METHOD("viewport_set_size", "viewport", "width", "height"), &RenderingServer::viewport_set_size);
+ ClassDB::bind_method(D_METHOD("viewport_set_layer_count", "viewport", "layer_count"), &RenderingServer::viewport_set_layer_count);
+ ClassDB::bind_method(D_METHOD("viewport_set_size", "viewport", "layer", "width", "height", "view_count"), &RenderingServer::viewport_set_size);
ClassDB::bind_method(D_METHOD("viewport_set_active", "viewport", "active"), &RenderingServer::viewport_set_active);
ClassDB::bind_method(D_METHOD("viewport_set_parent_viewport", "viewport", "parent_viewport"), &RenderingServer::viewport_set_parent_viewport);
ClassDB::bind_method(D_METHOD("viewport_attach_to_screen", "viewport", "rect", "screen"), &RenderingServer::viewport_attach_to_screen, DEFVAL(Rect2()), DEFVAL(DisplayServer::MAIN_WINDOW_ID));
@@ -2837,8 +2838,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_update_mode", "viewport", "update_mode"), &RenderingServer::viewport_set_update_mode);
ClassDB::bind_method(D_METHOD("viewport_get_update_mode", "viewport"), &RenderingServer::viewport_get_update_mode);
ClassDB::bind_method(D_METHOD("viewport_set_clear_mode", "viewport", "clear_mode"), &RenderingServer::viewport_set_clear_mode);
- ClassDB::bind_method(D_METHOD("viewport_get_render_target", "viewport"), &RenderingServer::viewport_get_render_target);
- ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport"), &RenderingServer::viewport_get_texture);
+ ClassDB::bind_method(D_METHOD("viewport_get_render_target", "viewport", "layer"), &RenderingServer::viewport_get_render_target, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport", "layer"), &RenderingServer::viewport_get_texture, DEFVAL(0));
ClassDB::bind_method(D_METHOD("viewport_set_disable_3d", "viewport", "disable"), &RenderingServer::viewport_set_disable_3d);
ClassDB::bind_method(D_METHOD("viewport_set_disable_2d", "viewport", "disable"), &RenderingServer::viewport_set_disable_2d);
ClassDB::bind_method(D_METHOD("viewport_set_environment_mode", "viewport", "mode"), &RenderingServer::viewport_set_environment_mode);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index a5a62b9653f4..25a8ffaefc07 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -979,7 +979,8 @@ class RenderingServer : public Object {
}
virtual void viewport_set_use_xr(RID p_viewport, bool p_use_xr) = 0;
- virtual void viewport_set_size(RID p_viewport, int p_width, int p_height) = 0;
+ virtual void viewport_set_layer_count(RID p_viewport, uint32_t p_layer_count) = 0;
+ virtual void viewport_set_size(RID p_viewport, uint32_t p_layer, int p_width, int p_height, int p_view_count) = 0;
virtual void viewport_set_active(RID p_viewport, bool p_active) = 0;
virtual void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) = 0;
virtual void viewport_set_canvas_cull_mask(RID p_viewport, uint32_t p_canvas_cull_mask) = 0;
@@ -1012,8 +1013,8 @@ class RenderingServer : public Object {
virtual void viewport_set_clear_mode(RID p_viewport, ViewportClearMode p_clear_mode) = 0;
- virtual RID viewport_get_render_target(RID p_viewport) const = 0;
- virtual RID viewport_get_texture(RID p_viewport) const = 0;
+ virtual RID viewport_get_render_target(RID p_viewport, uint32_t p_layer = 0) const = 0;
+ virtual RID viewport_get_texture(RID p_viewport, uint32_t p_layer = 0) const = 0;
enum ViewportEnvironmentMode {
VIEWPORT_ENVIRONMENT_DISABLED,
diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp
index b752e7885d56..b34f47f3e87f 100644
--- a/servers/xr/xr_interface.cpp
+++ b/servers/xr/xr_interface.cpp
@@ -46,8 +46,9 @@ void XRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status);
- ClassDB::bind_method(D_METHOD("get_render_target_size"), &XRInterface::get_render_target_size);
- ClassDB::bind_method(D_METHOD("get_view_count"), &XRInterface::get_view_count);
+ ClassDB::bind_method(D_METHOD("get_layer_count"), &XRInterface::get_layer_count);
+ ClassDB::bind_method(D_METHOD("get_render_target_size", "layer"), &XRInterface::get_render_target_size, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_view_count", "layer"), &XRInterface::get_view_count, DEFVAL(0));
ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "tracker_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRInterface::trigger_haptic_pulse);
@@ -72,8 +73,8 @@ void XRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_passthrough_enabled"), &XRInterface::is_passthrough_enabled);
ClassDB::bind_method(D_METHOD("start_passthrough"), &XRInterface::start_passthrough);
ClassDB::bind_method(D_METHOD("stop_passthrough"), &XRInterface::stop_passthrough);
- ClassDB::bind_method(D_METHOD("get_transform_for_view", "view", "cam_transform"), &XRInterface::get_transform_for_view);
- ClassDB::bind_method(D_METHOD("get_projection_for_view", "view", "aspect", "near", "far"), &XRInterface::get_projection_for_view);
+ ClassDB::bind_method(D_METHOD("get_transform_for_view", "layer", "view", "cam_transform"), &XRInterface::get_transform_for_view);
+ ClassDB::bind_method(D_METHOD("get_projection_for_view", "layer", "view", "aspect", "near", "far"), &XRInterface::get_projection_for_view);
/** environment blend mode. */
ClassDB::bind_method(D_METHOD("get_supported_environment_blend_modes"), &XRInterface::get_supported_environment_blend_modes);
@@ -179,19 +180,19 @@ RID XRInterface::get_vrs_texture() {
/** these are optional, so we want dummies **/
-RID XRInterface::get_color_texture() {
+RID XRInterface::get_color_texture(uint32_t p_layer) {
return RID();
}
-RID XRInterface::get_depth_texture() {
+RID XRInterface::get_depth_texture(uint32_t p_layer) {
return RID();
}
-RID XRInterface::get_velocity_texture() {
+RID XRInterface::get_velocity_texture(uint32_t p_layer) {
return RID();
}
-RID XRInterface::get_velocity_depth_texture() {
+RID XRInterface::get_velocity_depth_texture(uint32_t p_layer) {
return RID();
}
diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h
index 53d6e9567bae..e4bb654f4758 100644
--- a/servers/xr/xr_interface.h
+++ b/servers/xr/xr_interface.h
@@ -134,16 +134,17 @@ class XRInterface : public RefCounted {
virtual void process() = 0;
// These methods can be called from both main and render thread.
- virtual Size2 get_render_target_size() = 0; /* returns the recommended render target size per eye for this device */
- virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */
+ virtual uint32_t get_layer_count() { return 1; } /* Returns the number of layers we need. */
+ virtual Size2 get_render_target_size(uint32_t p_layer = 0) = 0; /* returns the recommended render target size per eye for this device */
+ virtual uint32_t get_view_count(uint32_t p_layer = 0) = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */
// These methods are called from the rendering thread.
- 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_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 RID get_velocity_depth_texture();
+ virtual Transform3D get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
+ virtual Projection get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */
+ virtual RID get_color_texture(uint32_t p_layer = 0); /* obtain color output texture (if applicable) */
+ virtual RID get_depth_texture(uint32_t p_layer = 0); /* obtain depth output texture (if applicable, used for reprojection) */
+ virtual RID get_velocity_texture(uint32_t p_layer = 0); /* obtain velocity output texture (if applicable, used for spacewarp) */
+ virtual RID get_velocity_depth_texture(uint32_t p_layer = 0);
virtual Size2i get_velocity_target_size();
virtual Rect2i get_render_region();
virtual void pre_render() {}
diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp
index 782c358f615a..fc61b7a6317c 100644
--- a/servers/xr/xr_interface_extension.cpp
+++ b/servers/xr/xr_interface_extension.cpp
@@ -45,8 +45,9 @@ void XRInterfaceExtension::_bind_methods() {
GDVIRTUAL_BIND(_set_play_area_mode, "mode");
GDVIRTUAL_BIND(_get_play_area);
- GDVIRTUAL_BIND(_get_render_target_size);
- GDVIRTUAL_BIND(_get_view_count);
+ GDVIRTUAL_BIND(_get_layer_count);
+ GDVIRTUAL_BIND(_get_render_target_size2, "layer");
+ GDVIRTUAL_BIND(_get_view_count2, "layer");
GDVIRTUAL_BIND(_get_camera_transform);
GDVIRTUAL_BIND(_get_transform_for_view, "view", "cam_transform");
GDVIRTUAL_BIND(_get_projection_for_view, "view", "aspect", "z_near", "z_far");
@@ -74,18 +75,26 @@ void XRInterfaceExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_camera_feed_id);
// override output methods
- GDVIRTUAL_BIND(_get_color_texture);
- GDVIRTUAL_BIND(_get_depth_texture);
- GDVIRTUAL_BIND(_get_velocity_texture);
+ GDVIRTUAL_BIND(_get_color_texture2, "layer");
+ GDVIRTUAL_BIND(_get_depth_texture2, "layer");
+ GDVIRTUAL_BIND(_get_velocity_texture2, "layer");
- 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);
+ ClassDB::bind_method(D_METHOD("get_color_texture", "layer"), &XRInterfaceExtension::get_color_texture, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_depth_texture", "layer"), &XRInterfaceExtension::get_depth_texture, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_velocity_texture", "layer"), &XRInterfaceExtension::get_velocity_texture, DEFVAL(0));
// 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);
// ClassDB::bind_method(D_METHOD("get_render_target_depth", "render_target"), &XRInterfaceExtension::get_render_target_depth);
+
+#ifndef DISABLE_DEPRECATED
+ GDVIRTUAL_BIND(_get_render_target_size);
+ GDVIRTUAL_BIND(_get_view_count);
+ GDVIRTUAL_BIND(_get_color_texture);
+ GDVIRTUAL_BIND(_get_depth_texture);
+ GDVIRTUAL_BIND(_get_velocity_texture);
+#endif
}
StringName XRInterfaceExtension::get_name() const {
@@ -194,15 +203,47 @@ int XRInterfaceExtension::get_camera_feed_id() {
return feed_id;
}
-Size2 XRInterfaceExtension::get_render_target_size() {
+uint32_t XRInterfaceExtension::get_layer_count() {
+ uint32_t layer_count = 1;
+ GDVIRTUAL_CALL(_get_layer_count, layer_count);
+ return layer_count;
+}
+
+Size2 XRInterfaceExtension::get_render_target_size(uint32_t p_layer) {
Size2 size;
+
+ if (GDVIRTUAL_CALL(_get_render_target_size2, p_layer, size)) {
+ return size;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return size;
+ }
+
+ // Fall back on old implementation
GDVIRTUAL_CALL(_get_render_target_size, size);
+#endif
+
return size;
}
-uint32_t XRInterfaceExtension::get_view_count() {
+uint32_t XRInterfaceExtension::get_view_count(uint32_t p_layer) {
uint32_t view_count = 1;
+
+ if (GDVIRTUAL_CALL(_get_view_count2, p_layer, view_count)) {
+ return view_count;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return 0;
+ }
+
+ // Fall back on old implementation
GDVIRTUAL_CALL(_get_view_count, view_count);
+#endif
+
return view_count;
}
@@ -212,16 +253,42 @@ Transform3D XRInterfaceExtension::get_camera_transform() {
return transform;
}
-Transform3D XRInterfaceExtension::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+Transform3D XRInterfaceExtension::get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) {
Transform3D transform;
+
+ if (GDVIRTUAL_CALL(_get_transform_for_view2, p_layer, p_view, p_cam_transform, transform)) {
+ return transform;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return Transform3D();
+ }
+
GDVIRTUAL_CALL(_get_transform_for_view, p_view, p_cam_transform, transform);
+#endif
+
return transform;
}
-Projection XRInterfaceExtension::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection XRInterfaceExtension::get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
Projection cm;
PackedFloat64Array arr;
+ if (GDVIRTUAL_CALL(_get_projection_for_view2, p_layer, p_view, p_aspect, p_z_near, p_z_far, arr)) {
+ ERR_FAIL_COND_V_MSG(arr.size() != 16, Projection(), "Projection matrix must contain 16 floats");
+ real_t *m = (real_t *)cm.columns;
+ for (int i = 0; i < 16; i++) {
+ m[i] = arr[i];
+ }
+ return cm;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return Projection();
+ }
+
if (GDVIRTUAL_CALL(_get_projection_for_view, p_view, p_aspect, p_z_near, p_z_far, arr)) {
ERR_FAIL_COND_V_MSG(arr.size() != 16, Projection(), "Projection matrix must contain 16 floats");
real_t *m = (real_t *)cm.columns;
@@ -230,6 +297,7 @@ Projection XRInterfaceExtension::get_projection_for_view(uint32_t p_view, double
}
return cm;
}
+#endif
return Projection();
}
@@ -251,21 +319,57 @@ XRInterface::VRSTextureFormat XRInterfaceExtension::get_vrs_texture_format() {
return vrs_texture_format;
}
-RID XRInterfaceExtension::get_color_texture() {
+RID XRInterfaceExtension::get_color_texture(uint32_t p_layer) {
RID texture;
+
+ if (GDVIRTUAL_CALL(_get_color_texture2, p_layer, texture)) {
+ return texture;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return RID();
+ }
+
GDVIRTUAL_CALL(_get_color_texture, texture);
+#endif
+
return texture;
}
-RID XRInterfaceExtension::get_depth_texture() {
+RID XRInterfaceExtension::get_depth_texture(uint32_t p_layer) {
RID texture;
+
+ if (GDVIRTUAL_CALL(_get_depth_texture2, p_layer, texture)) {
+ return texture;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return RID();
+ }
+
GDVIRTUAL_CALL(_get_depth_texture, texture);
+#endif
+
return texture;
}
-RID XRInterfaceExtension::get_velocity_texture() {
+RID XRInterfaceExtension::get_velocity_texture(uint32_t p_layer) {
RID texture;
+
+ if (GDVIRTUAL_CALL(_get_velocity_texture2, p_layer, texture)) {
+ return texture;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_layer > 0) {
+ return RID();
+ }
+
GDVIRTUAL_CALL(_get_velocity_texture, texture);
+#endif
+
return texture;
}
diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h
index fd427ff68f5c..d8bd99aa105c 100644
--- a/servers/xr/xr_interface_extension.h
+++ b/servers/xr/xr_interface_extension.h
@@ -97,27 +97,39 @@ class XRInterfaceExtension : public XRInterface {
/** rendering and internal **/
- virtual Size2 get_render_target_size() override;
- virtual uint32_t get_view_count() override;
+ virtual uint32_t get_layer_count() override;
+ virtual Size2 get_render_target_size(uint32_t p_layer = 0) override;
+ virtual uint32_t get_view_count(uint32_t p_layer = 0) override;
virtual Transform3D get_camera_transform() override;
- 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 Transform3D get_transform_for_view(uint32_t p_layer, uint32_t p_view, const Transform3D &p_cam_transform) override;
+ virtual Projection get_projection_for_view(uint32_t p_layer, uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual RID get_vrs_texture() override;
virtual VRSTextureFormat get_vrs_texture_format() override;
- virtual RID get_color_texture() override;
- virtual RID get_depth_texture() override;
- virtual RID get_velocity_texture() override;
+ virtual RID get_color_texture(uint32_t p_layer = 0) override;
+ virtual RID get_depth_texture(uint32_t p_layer = 0) override;
+ virtual RID get_velocity_texture(uint32_t p_layer = 0) override;
+ GDVIRTUAL0R(uint32_t, _get_layer_count);
+ GDVIRTUAL1R(Size2, _get_render_target_size2, uint32_t);
+ GDVIRTUAL1R(uint32_t, _get_view_count2, uint32_t);
+ GDVIRTUAL0R(Transform3D, _get_camera_transform);
+ GDVIRTUAL3R(Transform3D, _get_transform_for_view2, uint32_t, uint32_t, const Transform3D &);
+ GDVIRTUAL5R(PackedFloat64Array, _get_projection_for_view2, uint32_t, uint32_t, double, double, double);
+ GDVIRTUAL0R(RID, _get_vrs_texture);
+ GDVIRTUAL0R(VRSTextureFormat, _get_vrs_texture_format);
+ GDVIRTUAL1R(RID, _get_color_texture2, uint32_t);
+ GDVIRTUAL1R(RID, _get_depth_texture2, uint32_t);
+ GDVIRTUAL1R(RID, _get_velocity_texture2, uint32_t);
+
+#ifndef DISABLE_DEPRECATED
GDVIRTUAL0R(Size2, _get_render_target_size);
GDVIRTUAL0R(uint32_t, _get_view_count);
- GDVIRTUAL0R(Transform3D, _get_camera_transform);
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(VRSTextureFormat, _get_vrs_texture_format);
GDVIRTUAL0R(RID, _get_color_texture);
GDVIRTUAL0R(RID, _get_depth_texture);
GDVIRTUAL0R(RID, _get_velocity_texture);
+#endif
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);
]