diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index b5ce49092b..19f3e85b9b 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -3617,6 +3617,10 @@
Applied foveation level if supported.
[b]Note:[/b] On platforms other than Android, if [member rendering/anti_aliasing/quality/msaa_3d] is enabled, this feature will be disabled.
+
+ If [code]true[/code] and foveation is also enabled, subsampled images will be used on Vulkan. This can improve the performance gain from foveated rendering, especially when using high foveation levels.
+ [b]Note:[/b]: Using subsampled images is incompatible with many screen-space rendering features or post-processing effects like FXAA or glow. If any such effects are enabled, subsampled images will automatically be disabled and a warning shown in the log.
+
Specify the default reference space.
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index f02fc2ecd1..0ba0adcbdd 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -740,6 +740,12 @@ public:
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(RID p_render_target) const override;
+ virtual void render_target_set_subsampled_enabled(RID p_render_target, bool p_enabled) override {}
+ virtual bool render_target_is_subsampled_enabled(RID p_render_target) const override { return false; }
+
+ virtual void render_target_set_subsampled_allowed(RID p_render_target, bool p_allowed) override {}
+ virtual bool render_target_is_subsampled_allowed(RID p_render_target) const override { return false; }
+
virtual RID render_target_get_texture(RID p_render_target) override;
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override;
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 6d92216bc6..9002d54d97 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -569,6 +569,7 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
_register_requested_device_extension(VK_KHR_MULTIVIEW_EXTENSION_NAME, false);
_register_requested_device_extension(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, false);
_register_requested_device_extension(VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME, false);
+ _register_requested_device_extension(VK_EXT_FRAGMENT_DENSITY_MAP_2_EXTENSION_NAME, false);
_register_requested_device_extension(VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_EXTENSION_NAME, false);
_register_requested_device_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false);
_register_requested_device_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
@@ -2194,6 +2195,11 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
create_info.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
}*/
+ bool use_subsampled_images = VulkanHooks::get_singleton() && VulkanHooks::get_singleton()->use_subsampled_images();
+ if (fdm_capabilities.attachment_supported && use_subsampled_images && p_format.is_subsampled && (p_format.usage_bits & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_RESOLVE_ATTACHMENT_BIT))) {
+ create_info.flags |= VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT;
+ }
+
if (fdm_capabilities.offset_supported && (p_format.usage_bits & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_RESOLVE_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) {
create_info.flags |= VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM;
}
@@ -2353,6 +2359,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
tex_info->vk_create_info = create_info;
tex_info->vk_view_create_info = image_view_create_info;
tex_info->allocation.handle = allocation;
+ tex_info->is_subsampled = (create_info.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) != 0;
#ifdef DEBUG_ENABLED
tex_info->transient = (p_format.usage_bits & TEXTURE_USAGE_TRANSIENT_BIT) != 0;
#endif
@@ -5108,6 +5115,11 @@ void RenderingDeviceDriverVulkan::command_clear_depth_stencil_texture(CommandBuf
void RenderingDeviceDriverVulkan::command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView p_regions) {
const TextureInfo *tex_info = (const TextureInfo *)p_dst_texture.id;
+ if (tex_info->is_subsampled) {
+ // Subsampled images can't be copied into, only used as attachments to a framebuffer.
+ return;
+ }
+
uint32_t pixel_size = get_image_format_pixel_size(tex_info->rd_format);
uint32_t block_size = get_compressed_image_format_block_byte_size(tex_info->rd_format);
uint32_t block_w, block_h;
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 3b07d68180..5342ee5e01 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -275,6 +275,7 @@ public:
VmaAllocation handle = nullptr;
VmaAllocationInfo info = {};
} allocation; // All 0/null if just a view.
+ bool is_subsampled = false;
#ifdef DEBUG_ENABLED
bool created_from_extension = false;
bool transient = false;
diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h
index c4f6b953ed..15c34a0f4f 100644
--- a/drivers/vulkan/vulkan_hooks.h
+++ b/drivers/vulkan/vulkan_hooks.h
@@ -47,5 +47,6 @@ public:
virtual void set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) = 0;
virtual bool use_fragment_density_offsets() = 0;
virtual void get_fragment_density_offsets(LocalVector &r_offsets, const Vector2i &p_granularity) = 0;
+ virtual bool use_subsampled_images() = 0;
static VulkanHooks *get_singleton() { return singleton; }
};
diff --git a/main/main.cpp b/main/main.cpp
index 98c75fadbf..255ac6d309 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2827,6 +2827,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
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");
GLOBAL_DEF_BASIC("xr/openxr/foveation_dynamic", false);
+ GLOBAL_DEF_BASIC("xr/openxr/foveation_with_subsampled_images", false);
GLOBAL_DEF_BASIC("xr/openxr/submit_depth_buffer", false);
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 29c53d5070..b2eb3e9905 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -176,11 +176,12 @@
If [code]true[/code], enables dynamic foveation adjustment. The interface must be initialized before this is accessible. If enabled, foveation will automatically be adjusted between low and [member foveation_level].
- [b]Note:[/b] Only works on the Compatibility renderer.
The foveation level, from [code]0[/code] (off) to [code]3[/code] (high). The interface must be initialized before this is accessible.
- [b]Note:[/b] Only works on the Compatibility renderer.
+
+
+ If [code]true[/code], enables subsampled images with foveation, which can provide a performance boost on Vulkan.
The render size multiplier for the current HMD. Must be set before the interface has been initialized.
diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
index 14d957700f..04820291d2 100644
--- a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
+++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
@@ -51,9 +51,13 @@ OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering
if (fov_level >= 0 && fov_level < 4) {
foveation_level = XrFoveationLevelFB(fov_level);
}
+
bool fov_dyn = GLOBAL_GET("xr/openxr/foveation_dynamic");
foveation_dynamic = fov_dyn ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB;
+ foveation_with_subsampled_images_enabled = GLOBAL_GET("xr/openxr/foveation_with_subsampled_images");
+ foveation_with_subsampled_images_active = foveation_with_subsampled_images_enabled;
+
swapchain_create_info_foveation_fb.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
swapchain_create_info_foveation_fb.next = nullptr;
swapchain_create_info_foveation_fb.flags = 0;
@@ -69,7 +73,7 @@ OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering
#ifdef VULKAN_ENABLED
meta_vulkan_swapchain_create_info.type = XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META;
meta_vulkan_swapchain_create_info.next = nullptr;
- meta_vulkan_swapchain_create_info.additionalCreateFlags = VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM;
+ meta_vulkan_swapchain_create_info.additionalCreateFlags = 0;
meta_vulkan_swapchain_create_info.additionalUsageFlags = 0;
#endif
@@ -150,7 +154,15 @@ void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer
next = &swapchain_create_info_foveation_fb;
#ifdef VULKAN_ENABLED
- if (meta_foveation_eye_tracked_ext && meta_vulkan_swapchain_create_info_ext && meta_foveation_eye_tracked_properties.supportsFoveationEyeTracked) {
+ if (meta_vulkan_swapchain_create_info_ext) {
+ meta_vulkan_swapchain_create_info.additionalCreateFlags = 0;
+ if (meta_foveation_eye_tracked_ext && meta_foveation_eye_tracked_properties.supportsFoveationEyeTracked) {
+ meta_vulkan_swapchain_create_info.additionalCreateFlags |= VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM;
+ }
+ if (foveation_with_subsampled_images_enabled && foveation_with_subsampled_images_active) {
+ meta_vulkan_swapchain_create_info.additionalCreateFlags |= VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT;
+ }
+
meta_vulkan_swapchain_create_info.next = next;
next = &meta_vulkan_swapchain_create_info;
}
@@ -226,6 +238,18 @@ void OpenXRFBFoveationExtension::get_fragment_density_offsets(LocalVector &r_offsets);
+ void set_foveation_with_subsampled_images_enabled(bool p_enabled);
+ bool is_foveation_with_subsampled_images_enabled() const;
+
+ void set_foveation_with_subsampled_images_active(bool p_active);
+
private:
static OpenXRFBFoveationExtension *singleton;
@@ -89,6 +94,8 @@ private:
// Configuration
XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB;
XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
+ bool foveation_with_subsampled_images_enabled = false;
+ bool foveation_with_subsampled_images_active = false;
void _update_profile_rt();
diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp
index 0b29435dd1..f61a006bdc 100644
--- a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp
@@ -229,6 +229,15 @@ void OpenXRVulkanExtension::get_fragment_density_offsets(LocalVector
}
}
+bool OpenXRVulkanExtension::use_subsampled_images() {
+ OpenXRFBFoveationExtension *fb_foveation = OpenXRFBFoveationExtension::get_singleton();
+ if (fb_foveation == nullptr) {
+ return false;
+ }
+
+ return fb_foveation->is_foveation_with_subsampled_images_enabled();
+}
+
XrGraphicsBindingVulkanKHR OpenXRVulkanExtension::graphics_binding_vulkan;
void *OpenXRVulkanExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.h b/modules/openxr/extensions/platform/openxr_vulkan_extension.h
index 37168dd91a..d3dd80f097 100644
--- a/modules/openxr/extensions/platform/openxr_vulkan_extension.h
+++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.h
@@ -54,6 +54,7 @@ public:
virtual bool get_physical_device(VkPhysicalDevice *r_device) override final;
virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override final;
virtual void set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) override final;
+ virtual bool use_subsampled_images() override final;
virtual bool use_fragment_density_offsets() override final;
virtual void get_fragment_density_offsets(LocalVector &r_offets, const Vector2i &p_granularity) override final;
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 04aa2cc49f..860317e60d 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -41,6 +41,7 @@
#include "core/profiling/profiling.h"
#include "core/version.h"
#include "servers/rendering/rendering_server.h"
+#include "servers/rendering/rendering_server_globals.h"
#include "openxr_platform_inc.h"
@@ -2430,15 +2431,6 @@ 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();
-
- // In with the new.
- create_main_swapchains(swapchain_size);
- }
-
void *view_locate_info_next_pointer = nullptr;
for (OpenXRExtensionWrapper *extension : frame_info_extensions) {
void *np = extension->set_view_locate_info_and_get_next_pointer(view_locate_info_next_pointer);
@@ -2526,6 +2518,39 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
return false;
}
+ Size2i swapchain_size = get_recommended_target_size();
+ bool should_recreate_swapchain = (swapchain_size != render_state.main_swapchain_size);
+
+ OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
+ if (fov_ext) {
+ bool subsampled_images_enabled = fov_ext->is_foveation_with_subsampled_images_enabled();
+
+ // Mark subsampled images as enabled on the render target (so we try to use them).
+ RSG::texture_storage->render_target_set_subsampled_enabled(p_render_target, subsampled_images_enabled);
+
+ // But check if they are "allowed", because they may not work if we are using
+ // any incompatible rendering features.
+ bool subsampled_images_allowed = RSG::texture_storage->render_target_is_subsampled_allowed(p_render_target);
+ fov_ext->set_foveation_with_subsampled_images_active(subsampled_images_allowed);
+
+ bool use_subsampled_images = subsampled_images_enabled && subsampled_images_allowed;
+ if (render_state.use_subsampled_images != use_subsampled_images) {
+ render_state.use_subsampled_images = use_subsampled_images;
+ if (!subsampled_images_allowed) {
+ WARN_PRINT("Foveation with subsampled images was enabled, but rendering features are in use that have forced it to be disabled.");
+ }
+ should_recreate_swapchain = true;
+ }
+ }
+
+ if (should_recreate_swapchain) {
+ // Out with the old.
+ free_main_swapchains();
+
+ // In with the new.
+ create_main_swapchains(swapchain_size);
+ }
+
// 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) {
@@ -2879,6 +2904,21 @@ void OpenXRAPI::set_foveation_dynamic(bool p_foveation_dynamic) {
}
}
+bool OpenXRAPI::get_foveation_with_subsampled_images() const {
+ OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
+ if (fov_ext != nullptr) {
+ return fov_ext->is_foveation_with_subsampled_images_enabled();
+ }
+ return false;
+}
+
+void OpenXRAPI::set_foveation_with_subsampled_images(bool p_enabled) {
+ OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
+ if (fov_ext != nullptr) {
+ fov_ext->set_foveation_with_subsampled_images_enabled(p_enabled);
+ }
+}
+
Size2 OpenXRAPI::get_play_space_bounds() const {
Size2 ret;
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 2bc7cd74f6..f241b54d6c 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -364,6 +364,7 @@ private:
LocalVector projection_views;
LocalVector depth_views; // Only used by Composition Layer Depth Extension if available
bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
+ bool use_subsampled_images = true; // We need to default to true for the warning to be shown if we fallback immediately at startup.
bool view_pose_valid = false;
double z_near = 0.0;
@@ -524,6 +525,9 @@ public:
bool get_foveation_dynamic() const;
void set_foveation_dynamic(bool p_foveation_dynamic);
+ bool get_foveation_with_subsampled_images() const;
+ void set_foveation_with_subsampled_images(bool p_enabled);
+
// Play space.
Size2 get_play_space_bounds() const;
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 65f14a4732..28a558df09 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -89,6 +89,10 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic");
+ ClassDB::bind_method(D_METHOD("get_foveation_with_subsampled_images"), &OpenXRInterface::get_foveation_with_subsampled_images);
+ ClassDB::bind_method(D_METHOD("set_foveation_with_subsampled_images", "enabled"), &OpenXRInterface::set_foveation_with_subsampled_images);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_with_subsampled_images"), "set_foveation_with_subsampled_images", "get_foveation_with_subsampled_images");
+
// Action sets
ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active);
ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active);
@@ -1025,6 +1029,22 @@ void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) {
}
}
+bool OpenXRInterface::get_foveation_with_subsampled_images() const {
+ if (openxr_api == nullptr) {
+ return false;
+ } else {
+ return openxr_api->get_foveation_with_subsampled_images();
+ }
+}
+
+void OpenXRInterface::set_foveation_with_subsampled_images(bool p_enabled) {
+ if (openxr_api == nullptr) {
+ return;
+ } else {
+ openxr_api->set_foveation_with_subsampled_images(p_enabled);
+ }
+}
+
Size2 OpenXRInterface::get_render_target_size() {
if (openxr_api == nullptr) {
return Size2();
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index d3fec0d4aa..0d028b072e 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -171,6 +171,9 @@ public:
bool get_foveation_dynamic() const;
void set_foveation_dynamic(bool p_foveation_dynamic);
+ bool get_foveation_with_subsampled_images() const;
+ void set_foveation_with_subsampled_images(bool p_enabled);
+
float get_vrs_min_radius() const;
void set_vrs_min_radius(float p_vrs_min_radius);
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index adb1ba7ecf..b13fe2a40c 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -215,6 +215,12 @@ public:
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override {}
virtual Rect2i render_target_get_render_region(RID p_render_target) const override { return Rect2i(); }
+ virtual void render_target_set_subsampled_enabled(RID p_render_target, bool p_enabled) override {}
+ virtual bool render_target_is_subsampled_enabled(RID p_render_target) const override { return false; }
+
+ virtual void render_target_set_subsampled_allowed(RID p_render_target, bool p_allowed) override {}
+ virtual bool render_target_is_subsampled_allowed(RID p_render_target) const override { return false; }
+
virtual RID render_target_get_texture(RID p_render_target) override { return RID(); }
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override {}
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 1232ad3353..878409fe1c 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -232,8 +232,13 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe
Vector textures;
int color_buffer_id = 0;
int depth_buffer_id = 1;
- textures.push_back(use_msaa ? render_buffers->get_color_msaa() : render_buffers->get_internal_texture()); // 0 - color buffer.
- textures.push_back(use_msaa ? render_buffers->get_depth_msaa() : render_buffers->get_depth_texture()); // 1 - depth buffer.
+ if (p_config_type == FB_CONFIG_RENDER_AND_POST_PASS && vrs_texture.is_valid() && texture_storage->render_target_is_subsampled_enabled(render_buffers->get_render_target())) {
+ textures.push_back(use_msaa ? render_buffers->get_color_msaa_subsampled() : render_buffers->get_color_subsampled()); // 0 - color buffer.
+ textures.push_back(use_msaa ? render_buffers->get_depth_msaa_subsampled() : render_buffers->get_depth_subsampled()); // 1 - depth buffer.
+ } else {
+ textures.push_back(use_msaa ? render_buffers->get_color_msaa() : render_buffers->get_internal_texture()); // 0 - color buffer.
+ textures.push_back(use_msaa ? render_buffers->get_depth_msaa() : render_buffers->get_depth_texture()); // 1 - depth buffer.
+ }
if (vrs_texture.is_valid()) {
textures.push_back(vrs_texture); // 2 - vrs texture.
}
@@ -997,6 +1002,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
ERR_FAIL(); //bug?
}
+ // Set subsampled images as not allowed on this render target, if we are using incompatible rendering features.
+ texture_storage->render_target_set_subsampled_allowed(render_target, using_subpass_post_process);
+
if (p_render_data->scene_data->view_count > 1) {
global_pipeline_data_required.use_multiview = true;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index de3b3554fd..1d68bacf00 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -641,6 +641,80 @@ RID RenderSceneBuffersRD::get_depth_texture(const uint32_t p_layer) {
}
}
+// Subsampled textures.
+
+RID RenderSceneBuffersRD::get_color_subsampled() {
+ const bool use_msaa = (msaa_3d != RSE::VIEWPORT_MSAA_DISABLED);
+
+ RD::TextureFormat tf;
+ tf.format = get_base_data_format();
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ }
+ tf.width = internal_size.x;
+ tf.height = internal_size.y;
+ tf.array_layers = view_count;
+ tf.usage_bits = get_color_usage_bits(use_msaa, false, can_be_storage);
+ tf.is_subsampled = true;
+
+ return create_texture_from_format(RB_SCOPE_BUFFERS, RB_TEX_COLOR_SUBSAMPLED, tf);
+}
+
+RID RenderSceneBuffersRD::get_color_msaa_subsampled() {
+ ERR_FAIL_COND_V(msaa_3d == RSE::VIEWPORT_MSAA_DISABLED, RID());
+
+ RD::TextureFormat tf;
+ tf.format = get_base_data_format();
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ }
+ tf.width = internal_size.x;
+ tf.height = internal_size.y;
+ tf.array_layers = view_count;
+ tf.samples = msaa_to_samples(msaa_3d);
+ tf.usage_bits = get_color_usage_bits(false, true, can_be_storage);
+ tf.is_discardable = true;
+ tf.is_subsampled = true;
+
+ return create_texture_from_format(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA_SUBSAMPLED, tf);
+}
+
+RID RenderSceneBuffersRD::get_depth_subsampled() {
+ const bool use_msaa = (msaa_3d != RSE::VIEWPORT_MSAA_DISABLED);
+
+ RD::TextureFormat tf;
+ tf.format = get_depth_format(use_msaa, false, can_be_storage);
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ }
+ tf.width = internal_size.x;
+ tf.height = internal_size.y;
+ tf.array_layers = view_count;
+ tf.usage_bits = get_depth_usage_bits(use_msaa, false, can_be_storage);
+ tf.is_subsampled = true;
+
+ return create_texture_from_format(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_SUBSAMPLED, tf);
+}
+
+RID RenderSceneBuffersRD::get_depth_msaa_subsampled() {
+ ERR_FAIL_COND_V(msaa_3d == RSE::VIEWPORT_MSAA_DISABLED, RID());
+
+ RD::TextureFormat tf;
+ tf.format = get_depth_format(false, true, can_be_storage);
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ }
+ tf.width = internal_size.x;
+ tf.height = internal_size.y;
+ tf.array_layers = view_count;
+ tf.samples = msaa_to_samples(msaa_3d);
+ tf.usage_bits = get_depth_usage_bits(false, true, can_be_storage);
+ tf.is_discardable = true;
+ tf.is_subsampled = true;
+
+ return create_texture_from_format(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA_SUBSAMPLED, tf);
+}
+
// Upscaled texture.
void RenderSceneBuffersRD::ensure_upscaled() {
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
index 86b9b3e0d8..a0d1a22c49 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
@@ -48,8 +48,12 @@
#define RB_TEX_COLOR SNAME("color")
#define RB_TEX_COLOR_MSAA SNAME("color_msaa")
#define RB_TEX_COLOR_UPSCALED SNAME("color_upscaled")
+#define RB_TEX_COLOR_SUBSAMPLED SNAME("color_subsampled")
+#define RB_TEX_COLOR_MSAA_SUBSAMPLED SNAME("color_msaa_subsampled")
#define RB_TEX_DEPTH SNAME("depth")
#define RB_TEX_DEPTH_MSAA SNAME("depth_msaa")
+#define RB_TEX_DEPTH_SUBSAMPLED SNAME("depth_subsampled")
+#define RB_TEX_DEPTH_MSAA_SUBSAMPLED SNAME("depth_msaa_subsampled")
#define RB_TEX_VELOCITY SNAME("velocity")
#define RB_TEX_VELOCITY_MSAA SNAME("velocity_msaa")
@@ -282,6 +286,11 @@ public:
return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, p_layer, 0);
}
+ RID get_color_subsampled();
+ RID get_color_msaa_subsampled();
+ RID get_depth_subsampled();
+ RID get_depth_msaa_subsampled();
+
// back buffer (color)
RID get_back_buffer_texture() const {
// Prefer returning the dedicated backbuffer color texture if it was created. Return the reused blur texture otherwise.
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index b34c8ef623..8eeaba09b3 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -4220,6 +4220,34 @@ Rect2i RendererRD::TextureStorage::render_target_get_render_region(RID p_render_
return rt->render_region;
}
+void RendererRD::TextureStorage::render_target_set_subsampled_enabled(RID p_render_target, bool p_enabled) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ rt->subsampled_enabled = p_enabled;
+}
+
+bool RendererRD::TextureStorage::render_target_is_subsampled_enabled(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL_V(rt, false);
+
+ return rt->subsampled_enabled;
+}
+
+void RendererRD::TextureStorage::render_target_set_subsampled_allowed(RID p_render_target, bool p_allowed) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ rt->subsampled_allowed = p_allowed;
+}
+
+bool RendererRD::TextureStorage::render_target_is_subsampled_allowed(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL_V(rt, false);
+
+ return rt->subsampled_allowed;
+}
+
void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_is_transparent) {
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 e2a8675d5c..bbbfba0ead 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -404,6 +404,8 @@ private:
RID vrs_texture;
Rect2i render_region;
+ bool subsampled_enabled = false;
+ bool subsampled_allowed = true;
// overridden textures
struct RTOverridden {
@@ -839,6 +841,12 @@ public:
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(RID p_render_target) const override;
+ virtual void render_target_set_subsampled_enabled(RID p_render_target, bool p_enabled) override;
+ virtual bool render_target_is_subsampled_enabled(RID p_render_target) const override;
+
+ virtual void render_target_set_subsampled_allowed(RID p_render_target, bool p_allowed) override;
+ virtual bool render_target_is_subsampled_allowed(RID p_render_target) const override;
+
virtual RID render_target_get_texture(RID p_render_target) override;
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override {}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 71ab4ce3a7..a95b9c0cf3 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -1332,6 +1332,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
texture.base_layer = 0;
texture.is_resolve_buffer = format.is_resolve_buffer;
texture.is_discardable = format.is_discardable;
+ texture.is_subsampled = format.is_subsampled;
texture.usage_flags = format.usage_bits & ~forced_usage_bits;
texture.samples = format.samples;
texture.allowed_shared_formats = format.shareable_formats;
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index d5fb3d43fc..0198fca151 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -356,6 +356,7 @@ public:
bool is_resolve_buffer = false;
bool is_discardable = false;
+ bool is_subsampled = false;
bool has_initial_data = false;
bool pending_clear = false;
diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h
index ac0706f8a9..a436544e48 100644
--- a/servers/rendering/rendering_device_commons.h
+++ b/servers/rendering/rendering_device_commons.h
@@ -444,6 +444,7 @@ public:
Vector shareable_formats;
bool is_resolve_buffer = false;
bool is_discardable = false;
+ bool is_subsampled = false;
bool operator==(const TextureFormat &b) const {
if (format != b.format) {
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index 21a286f885..fbc799adad 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -195,6 +195,12 @@ public:
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) = 0;
virtual Rect2i render_target_get_render_region(RID p_render_target) const = 0;
+ virtual void render_target_set_subsampled_enabled(RID p_render_target, bool p_enabled) = 0;
+ virtual bool render_target_is_subsampled_enabled(RID p_render_target) const = 0;
+
+ virtual void render_target_set_subsampled_allowed(RID p_render_target, bool p_allowed) = 0;
+ virtual bool render_target_is_subsampled_allowed(RID p_render_target) const = 0;
+
// get textures
virtual RID render_target_get_texture(RID p_render_target) = 0;