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 Vectorviewport_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);