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;