/**************************************************************************/ /* render_forward_clustered.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 "render_forward_clustered.h" #include "core/config/project_settings.h" #include "servers/rendering/renderer_rd/environment/fog.h" #include "servers/rendering/renderer_rd/framebuffer_cache_rd.h" #include "servers/rendering/renderer_rd/storage_rd/light_storage.h" #include "servers/rendering/renderer_rd/storage_rd/mesh_storage.h" #include "servers/rendering/renderer_rd/storage_rd/particles_storage.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" #include "servers/rendering/rendering_device.h" #include "servers/rendering/rendering_server_default.h" #include "servers/rendering/storage/ltc_lut.gen.h" #include "modules/modules_enabled.gen.h" #ifdef MODULE_TEXTURE_STREAMING_ENABLED #include "modules/texture_streaming/texture_streaming.h" #endif using namespace RendererSceneRenderImplementation; #define PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION 1 #define FADE_ALPHA_PASS_THRESHOLD 0.999 void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular() { ERR_FAIL_NULL(render_buffers); if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR)) { bool msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, get_specular_format(), get_specular_usage_bits(msaa, false, render_buffers->get_can_be_storage())); if (msaa) { render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, get_specular_format(), get_specular_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples()); } } } void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_normal_roughness_texture() { ERR_FAIL_NULL(render_buffers); if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS)) { bool msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS, get_normal_roughness_format(), get_normal_roughness_usage_bits(msaa, false, render_buffers->get_can_be_storage())); if (msaa) { render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS_MSAA, get_normal_roughness_format(), get_normal_roughness_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples()); } } } void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi() { ERR_FAIL_NULL(render_buffers); if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI)) { bool msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, get_voxelgi_format(), get_voxelgi_usage_bits(msaa, false, render_buffers->get_can_be_storage())); if (msaa) { render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, get_voxelgi_format(), get_voxelgi_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples()); } } } void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_fsr2(RendererRD::FSR2Effect *p_effect) { if (fsr2_context == nullptr) { fsr2_context = p_effect->create_context(render_buffers->get_internal_size(), render_buffers->get_target_size()); } } #ifdef METAL_MFXTEMPORAL_ENABLED bool RenderForwardClustered::RenderBufferDataForwardClustered::ensure_mfx_temporal(RendererRD::MFXTemporalEffect *p_effect) { if (mfx_temporal_context == nullptr) { RendererRD::MFXTemporalEffect::CreateParams params; params.input_size = render_buffers->get_internal_size(); params.output_size = render_buffers->get_target_size(); params.input_format = render_buffers->get_base_data_format(); params.depth_format = render_buffers->get_depth_format(false, false, render_buffers->get_can_be_storage()); params.motion_format = render_buffers->get_velocity_format(); params.reactive_format = render_buffers->get_base_data_format(); // Reactive is derived from input. params.output_format = render_buffers->get_base_data_format(); params.motion_vector_scale = render_buffers->get_internal_size(); mfx_temporal_context = p_effect->create_context(params); return true; } return false; } #endif void RenderForwardClustered::RenderBufferDataForwardClustered::free_data() { // JIC, should already have been cleared if (render_buffers) { render_buffers->clear_context(RB_SCOPE_FORWARD_CLUSTERED); render_buffers->clear_context(RB_SCOPE_SSDS); render_buffers->clear_context(RB_SCOPE_SSIL); render_buffers->clear_context(RB_SCOPE_SSAO); render_buffers->clear_context(RB_SCOPE_SSR); render_buffers->clear_context(RB_SCOPE_SSCS); } if (cluster_builder) { memdelete(cluster_builder); cluster_builder = nullptr; } if (fsr2_context) { memdelete(fsr2_context); fsr2_context = nullptr; } #ifdef METAL_MFXTEMPORAL_ENABLED if (mfx_temporal_context) { memdelete(mfx_temporal_context); mfx_temporal_context = nullptr; } #endif if (!render_sdfgi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_sdfgi_uniform_set)) { RD::get_singleton()->free_rid(render_sdfgi_uniform_set); } } void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RenderSceneBuffersRD *p_render_buffers) { if (render_buffers) { // JIC free_data(); } render_buffers = p_render_buffers; ERR_FAIL_NULL(render_buffers); if (cluster_builder == nullptr) { cluster_builder = memnew(ClusterBuilderRD); } cluster_builder->set_shared(RenderForwardClustered::get_singleton()->get_cluster_builder_shared()); RID sampler = RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); // Note: Color and depth buffer is only used for our debug mode, we don't support stereo rendering for this. cluster_builder->setup(p_render_buffers->get_internal_size(), p_render_buffers->get_max_cluster_elements(), p_render_buffers->get_depth_texture(0), sampler, p_render_buffers->get_internal_texture(0)); } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_only_fb() { ERR_FAIL_NULL_V(render_buffers, RID()); bool use_msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture(); RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) { RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE); return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth, vrs_texture); } else { return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth); } } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(uint32_t p_color_pass_flags) { ERR_FAIL_NULL_V(render_buffers, RID()); bool use_msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; int v_count = (p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) ? render_buffers->get_view_count() : 1; RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture(); RID specular; if (p_color_pass_flags & COLOR_PASS_FLAG_SEPARATE_SPECULAR) { ensure_specular(); specular = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_SPECULAR_MSAA : RB_TEX_SPECULAR); } RID velocity_buffer; if (p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) { render_buffers->ensure_velocity(); velocity_buffer = render_buffers->get_velocity_buffer(use_msaa); } RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) { RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE); return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth, vrs_texture); } else { return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth); } } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_depth_fb(DepthFrameBufferType p_type) { ERR_FAIL_NULL_V(render_buffers, RID()); bool use_msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); switch (p_type) { case DEPTH_FB: { return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth); } break; case DEPTH_FB_ROUGHNESS: { ensure_normal_roughness_texture(); RID normal_roughness_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_NORMAL_ROUGHNESS_MSAA : RB_TEX_NORMAL_ROUGHNESS); return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth, normal_roughness_buffer); } break; case DEPTH_FB_ROUGHNESS_VOXELGI: { ensure_normal_roughness_texture(); ensure_voxelgi(); RID normal_roughness_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_NORMAL_ROUGHNESS_MSAA : RB_TEX_NORMAL_ROUGHNESS); RID voxelgi_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_VOXEL_GI_MSAA : RB_TEX_VOXEL_GI); return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth, normal_roughness_buffer, voxelgi_buffer); } break; default: { ERR_FAIL_V(RID()); } break; } } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_only_fb() { bool use_msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; RID specular = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_SPECULAR_MSAA : RB_TEX_SPECULAR); return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), specular); } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_velocity_only_fb() { bool use_msaa = render_buffers->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED; RID velocity = render_buffers->get_texture(RB_SCOPE_BUFFERS, use_msaa ? RB_TEX_VELOCITY_MSAA : RB_TEX_VELOCITY); return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), velocity); } RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_format() { return RD::DATA_FORMAT_R16G16B16A16_SFLOAT; } uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) { return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage); } RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_format() { return RD::DATA_FORMAT_R8G8B8A8_UNORM; } uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) { return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage); } RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_format() { return RD::DATA_FORMAT_R8G8_UINT; } uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) { return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage); } void RenderForwardClustered::setup_render_buffer_data(Ref p_render_buffers) { Ref data; data.instantiate(); p_render_buffers->set_custom_data(RB_SCOPE_FORWARD_CLUSTERED, data); Ref rbgi; rbgi.instantiate(); p_render_buffers->set_custom_data(RB_SCOPE_GI, rbgi); } bool RenderForwardClustered::free(RID p_rid) { if (RendererSceneRenderRD::free(p_rid)) { return true; } return false; } void RenderForwardClustered::update() { RendererSceneRenderRD::update(); _update_global_pipeline_data_requirements_from_project(); _update_global_pipeline_data_requirements_from_light_storage(); } /// RENDERING /// template void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); RD::DrawListID draw_list = p_draw_list; RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format; //global scope bindings RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_base_uniform_set, SCENE_UNIFORM_SET); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_params->render_pass_uniform_set, RENDER_PASS_UNIFORM_SET); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, scene_shader.default_vec4_xform_uniform_set, TRANSFORMS_UNIFORM_SET); RID prev_material_uniform_set; RID prev_vertex_array_rd; RID prev_index_array_rd; RID prev_xforms_uniform_set; SceneShaderForwardClustered::ShaderData *shader = nullptr; SceneShaderForwardClustered::ShaderData *prev_shader = nullptr; SceneShaderForwardClustered::ShaderData::PipelineKey pipeline_key; uint32_t pipeline_hash = 0; uint32_t prev_pipeline_hash = 0; bool shadow_pass = (p_pass_mode == PASS_MODE_SHADOW) || (p_pass_mode == PASS_MODE_SHADOW_DP); SceneState::PushConstant push_constant; if constexpr (p_pass_mode == PASS_MODE_DEPTH_MATERIAL) { push_constant.uv_offset = Math::make_half_float(p_params->uv_offset.y) << 16; push_constant.uv_offset |= Math::make_half_float(p_params->uv_offset.x); } else { push_constant.uv_offset = 0; } bool should_request_redraw = false; for (uint32_t i = p_from_element; i < p_to_element; i++) { const GeometryInstanceSurfaceDataCache *surf = p_params->elements[i]; const RenderElementInfo &element_info = p_params->element_info[i]; if (p_pass_mode == PASS_MODE_COLOR && surf->color_pass_inclusion_mask && (p_color_pass_flags & surf->color_pass_inclusion_mask) == 0) { // Some surfaces can be repeated in multiple render lists. We exclude them from being rendered on the color pass based on the // features supported by the pass compared to the exclusion mask. continue; } if (surf->owner->instance_count == 0) { continue; } push_constant.base_index = i + p_params->element_offset; RID material_uniform_set; void *mesh_surface; if (shadow_pass || p_pass_mode == PASS_MODE_DEPTH) { //regular depth pass can use these too material_uniform_set = surf->material_uniform_set_shadow; shader = surf->shader_shadow; mesh_surface = surf->surface_shadow; } else { #ifdef DEBUG_ENABLED if (unlikely(get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_LIGHTING)) { material_uniform_set = scene_shader.default_material_uniform_set; shader = scene_shader.default_material_shader_ptr; } else if (unlikely(get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_OVERDRAW)) { material_uniform_set = scene_shader.overdraw_material_uniform_set; shader = scene_shader.overdraw_material_shader_ptr; } else if (unlikely(get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_PSSM_SPLITS)) { material_uniform_set = scene_shader.debug_shadow_splits_material_uniform_set; shader = scene_shader.debug_shadow_splits_material_shader_ptr; } else { #endif material_uniform_set = surf->material_uniform_set; shader = surf->shader; surf->material->set_as_used(); #ifdef DEBUG_ENABLED } #endif mesh_surface = surf->surface; } if (!mesh_surface) { continue; } //request a redraw if one of the shaders uses TIME if (shader->uses_time) { should_request_redraw = true; } // Determine the cull variant. SceneShaderForwardClustered::ShaderData::CullVariant cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_MAX; if constexpr (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || p_pass_mode == PASS_MODE_SDF) { cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_DOUBLE_SIDED; } else { if constexpr (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) { if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS) { cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_DOUBLE_SIDED; } } if (cull_variant == SceneShaderForwardClustered::ShaderData::CULL_VARIANT_MAX) { bool mirror = surf->owner->mirror; if (p_params->reverse_cull) { mirror = !mirror; } cull_variant = mirror ? SceneShaderForwardClustered::ShaderData::CULL_VARIANT_REVERSED : SceneShaderForwardClustered::ShaderData::CULL_VARIANT_NORMAL; } } pipeline_key.primitive_type = surf->primitive; RID xforms_uniform_set = surf->owner->transforms_uniform_set; SceneShaderForwardClustered::ShaderSpecialization pipeline_specialization = p_params->base_specialization; pipeline_specialization.multimesh = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH); pipeline_specialization.multimesh_format_2d = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D); pipeline_specialization.multimesh_has_color = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR); pipeline_specialization.multimesh_has_custom_data = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA); pipeline_specialization.material_feedback = p_params->use_material_feedback && surf->material->material_feedback_rid.is_valid(); if constexpr (p_pass_mode == PASS_MODE_COLOR) { pipeline_specialization.use_light_soft_shadows = element_info.uses_softshadow; pipeline_specialization.use_light_projector = element_info.uses_projector; pipeline_specialization.use_directional_soft_shadows = p_params->use_directional_soft_shadow; pipeline_specialization.use_lightmap_specular = element_info.uses_lightmap_specular; } pipeline_key.color_pass_flags = 0; switch (p_pass_mode) { case PASS_MODE_COLOR: { if (element_info.uses_lightmap) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_LIGHTMAP; } else { pipeline_specialization.use_forward_gi = element_info.uses_forward_gi; } if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_SEPARATE_SPECULAR) != 0) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR; } if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) != 0) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS; } if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_TRANSPARENT) != 0) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_TRANSPARENT; } if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) != 0) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW; } pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_COLOR_PASS; } break; case PASS_MODE_SHADOW: case PASS_MODE_DEPTH: { pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS; } break; case PASS_MODE_SHADOW_DP: { ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass"); pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP; } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS; } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI; } break; case PASS_MODE_DEPTH_MATERIAL: { ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass"); pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL; } break; case PASS_MODE_SDF: { // Note, SDF is prepared in world space, this shouldn't be a multiview buffer even when stereoscopic rendering is used. ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for SDF pass"); pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF; } break; } pipeline_key.framebuffer_format_id = framebuffer_format; pipeline_key.wireframe = p_params->force_wireframe; pipeline_key.ubershader = 0; bool emulate_point_size = shader->uses_point_size && scene_shader.emulate_point_size; const RD::PolygonCullMode cull_mode = shader->get_cull_mode_from_cull_variant(cull_variant); RID vertex_array_rd; RID index_array_rd; RID pipeline_rd; const uint32_t ubershader_iterations = 2; bool pipeline_valid = false; while (pipeline_key.ubershader < ubershader_iterations) { // Skeleton and blend shape. RD::VertexFormatID vertex_format = -1; bool pipeline_motion_vectors = pipeline_key.color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS; uint64_t input_mask = shader->get_vertex_input_mask(pipeline_key.version, pipeline_key.color_pass_flags, pipeline_key.ubershader); if (surf->owner->mesh_instance.is_valid()) { mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, input_mask, pipeline_motion_vectors, emulate_point_size, vertex_array_rd, vertex_format); } else { mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, input_mask, pipeline_motion_vectors, emulate_point_size, vertex_array_rd, vertex_format); } pipeline_key.vertex_format_id = vertex_format; if (pipeline_key.ubershader) { pipeline_key.shader_specialization = {}; pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED; } else { pipeline_key.shader_specialization = pipeline_specialization; pipeline_key.cull_mode = cull_mode; } pipeline_hash = pipeline_key.hash(); if (shader != prev_shader || pipeline_hash != prev_pipeline_hash) { RSE::PipelineSource pipeline_source = pipeline_key.ubershader ? RSE::PIPELINE_SOURCE_DRAW : RSE::PIPELINE_SOURCE_SPECIALIZATION; pipeline_rd = shader->pipeline_hash_map.get_pipeline(pipeline_key, pipeline_hash, pipeline_key.ubershader, pipeline_source); if (pipeline_rd.is_valid()) { pipeline_valid = true; prev_shader = shader; prev_pipeline_hash = pipeline_hash; break; } else { pipeline_key.ubershader++; } } else { // The same pipeline is bound already. pipeline_valid = true; break; } } if (pipeline_valid) { if (!emulate_point_size) { index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index); } else { index_array_rd = RID(); } if (prev_vertex_array_rd != vertex_array_rd) { RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd); prev_vertex_array_rd = vertex_array_rd; } if (prev_index_array_rd != index_array_rd) { if (index_array_rd.is_valid()) { RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd); } prev_index_array_rd = index_array_rd; } if (!pipeline_rd.is_null()) { RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd); } if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) { RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET); prev_xforms_uniform_set = xforms_uniform_set; } if (material_uniform_set != prev_material_uniform_set) { // Update uniform set. if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } prev_material_uniform_set = material_uniform_set; } if (surf->owner->base_flags & INSTANCE_DATA_FLAG_PARTICLES) { particles_storage->particles_get_instance_buffer_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset); } else if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) { mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset); } else { push_constant.multimesh_motion_vectors_current_offset = 0; push_constant.multimesh_motion_vectors_previous_offset = 0; } size_t push_constant_size = 0; if (pipeline_key.ubershader) { push_constant_size = sizeof(SceneState::PushConstant); push_constant.ubershader.specialization = pipeline_specialization; push_constant.ubershader.constants = {}; push_constant.ubershader.constants.cull_mode = cull_mode; } else { push_constant_size = sizeof(SceneState::PushConstant) - sizeof(SceneState::PushConstantUbershader); } RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, push_constant_size); uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat; if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) { instance_count /= surf->owner->trail_steps; } bool indirect = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT); if (emulate_point_size) { if (indirect) { WARN_PRINT("Indirect draws are not supported when emulating point size."); } RD::get_singleton()->draw_list_draw(draw_list, false, mesh_storage->mesh_surface_get_vertex_count(mesh_surface), instance_count * 6); } else if (indirect) { RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), mesh_storage->_multimesh_get_command_buffer_rd_rid(surf->owner->data->base), surf->surface_index * sizeof(uint32_t) * mesh_storage->INDIRECT_MULTIMESH_COMMAND_STRIDE, 1, 0); } else { RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); } } i += element_info.repeat - 1; //skip equal elements } // Make the actual redraw request if (should_request_redraw) { RenderingServerDefault::redraw_request(); } } void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element) { //use template for faster performance (pass mode comparisons are inlined) switch (p_params->pass_mode) { #define VALID_FLAG_COMBINATION(f) \ case f: { \ _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); \ } break; case PASS_MODE_COLOR: { switch (p_params->color_pass_flags) { VALID_FLAG_COMBINATION(0); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_TRANSPARENT); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_TRANSPARENT | COLOR_PASS_FLAG_MULTIVIEW); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_TRANSPARENT | COLOR_PASS_FLAG_MOTION_VECTORS); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_SEPARATE_SPECULAR); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_SEPARATE_SPECULAR | COLOR_PASS_FLAG_MULTIVIEW); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_SEPARATE_SPECULAR | COLOR_PASS_FLAG_MOTION_VECTORS); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_MULTIVIEW); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_MULTIVIEW | COLOR_PASS_FLAG_MOTION_VECTORS); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_MOTION_VECTORS); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_SEPARATE_SPECULAR | COLOR_PASS_FLAG_MULTIVIEW | COLOR_PASS_FLAG_MOTION_VECTORS); VALID_FLAG_COMBINATION(COLOR_PASS_FLAG_TRANSPARENT | COLOR_PASS_FLAG_MULTIVIEW | COLOR_PASS_FLAG_MOTION_VECTORS); default: { ERR_FAIL_MSG("Invalid color pass flag combination " + itos(p_params->color_pass_flags)); } } } break; case PASS_MODE_SHADOW: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_SHADOW_DP: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_DEPTH: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_DEPTH_MATERIAL: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; case PASS_MODE_SDF: { _render_list_template(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element); } break; default: { // Unknown pass mode. } break; } } void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField p_draw_flags, const Vector &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer); p_params->framebuffer_format = fb_format; RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_draw_flags, p_clear_color_values, p_clear_depth_value, p_clear_stencil_value, p_region); _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); RD::get_singleton()->draw_list_end(); } uint32_t RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Size2 &p_viewport_size, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier, bool p_pancake_shadows) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); Ref rd = p_render_data->render_buffers; RID env = is_environment(p_render_data->environment) ? p_render_data->environment : RID(); RID reflection_probe_instance = p_render_data->reflection_probe.is_valid() ? light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe) : RID(); // May do this earlier in RenderSceneRenderRD::render_scene uint32_t uniform_buffer_index = scene_state.used_uniform_buffer_count; ++scene_state.used_uniform_buffer_count; if (uniform_buffer_index >= scene_state.uniform_buffers.size()) { uint32_t from = scene_state.uniform_buffers.size(); scene_state.uniform_buffers.resize(uniform_buffer_index + 1); for (uint32_t i = from; i < scene_state.uniform_buffers.size(); i++) { scene_state.uniform_buffers[i] = p_render_data->scene_data->create_uniform_buffer(); } } float luminance_multiplier = rd.is_valid() ? rd->get_luminance_multiplier() : 1.0; p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[uniform_buffer_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_pancake_shadows, p_screen_size, p_viewport_size, p_default_bg_color, luminance_multiplier, p_opaque_render_buffers, p_apply_alpha_multiplier); // now do implementation UBO scene_state.ubo.cluster_shift = Math::get_shift_from_power_of_2(p_render_data->cluster_size); scene_state.ubo.max_cluster_element_count_div_32 = p_render_data->cluster_max_elements / 32; { uint32_t cluster_screen_width = Math::division_round_up((uint32_t)p_screen_size.width, p_render_data->cluster_size); uint32_t cluster_screen_height = Math::division_round_up((uint32_t)p_screen_size.height, p_render_data->cluster_size); scene_state.ubo.cluster_type_size = cluster_screen_width * cluster_screen_height * (scene_state.ubo.max_cluster_element_count_div_32 + 32); scene_state.ubo.cluster_width = cluster_screen_width; } scene_state.ubo.gi_upscale_for_msaa = false; scene_state.ubo.volumetric_fog_enabled = false; if (rd.is_valid()) { if (rd->get_msaa_3d() != RSE::VIEWPORT_MSAA_DISABLED) { scene_state.ubo.gi_upscale_for_msaa = true; } if (rd->has_custom_data(RB_SCOPE_FOG)) { Ref fog = rd->get_custom_data(RB_SCOPE_FOG); scene_state.ubo.volumetric_fog_enabled = true; float fog_end = fog->length; if (fog_end > 0.0) { scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end; } else { scene_state.ubo.volumetric_fog_inv_length = 1.0; } float fog_detail_spread = fog->spread; //reverse lookup if (fog_detail_spread > 0.0) { scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread; } else { scene_state.ubo.volumetric_fog_detail_spread = 1.0; } } } if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_UNSHADED) { scene_state.ubo.ss_effects_flags = 0; } else if (p_render_data->reflection_probe.is_null() && is_environment(p_render_data->environment)) { scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_channel_affect(p_render_data->environment); scene_state.ubo.ssao_light_affect = environment_get_ssao_direct_light_affect(p_render_data->environment); uint32_t ss_flags = 0; if (p_opaque_render_buffers) { ss_flags |= environment_get_ssao_enabled(p_render_data->environment) ? SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO : 0; ss_flags |= environment_get_ssil_enabled(p_render_data->environment) ? SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL : 0; ss_flags |= environment_get_ssr_enabled(p_render_data->environment) ? SCREEN_SPACE_EFFECTS_FLAGS_USE_SSR : 0; ss_flags |= bool(GLOBAL_GET_CACHED(bool, "rendering/lights_and_shadows/contact_shadow/enabled")) ? SCREEN_SPACE_EFFECTS_FLAGS_USE_SSCS : 0; if (rd.is_valid()) { Ref rb_data; if (rd->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { rb_data = rd->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ss_flags |= (rb_data.is_valid() && !rb_data->ss_effects_data.ssr.half_size) ? SCREEN_SPACE_EFFECTS_FLAGS_RESOLVE_SSR : 0; } } } scene_state.ubo.ss_effects_flags = ss_flags; } else { scene_state.ubo.ss_effects_flags = 0; } if (uniform_buffer_index >= scene_state.implementation_uniform_buffers.size()) { uint32_t from = scene_state.implementation_uniform_buffers.size(); scene_state.implementation_uniform_buffers.resize(uniform_buffer_index + 1); for (uint32_t i = from; i < scene_state.implementation_uniform_buffers.size(); i++) { scene_state.implementation_uniform_buffers[i] = RD::get_singleton()->uniform_buffer_create(sizeof(SceneState::UBO)); } } RD::get_singleton()->buffer_update(scene_state.implementation_uniform_buffers[uniform_buffer_index], 0, sizeof(SceneState::UBO), &scene_state.ubo); return uniform_buffer_index; } void RenderForwardClustered::SceneState::grow_instance_buffer(RenderListType p_render_list, uint32_t p_req_element_count, bool p_append) { if (p_req_element_count > 0) { if (instance_buffer[p_render_list].get_size(0u) < p_req_element_count * sizeof(SceneState::InstanceData)) { instance_buffer[p_render_list].uninit(); uint32_t new_size = Math::nearest_power_of_2_templated(MAX(uint64_t(INSTANCE_DATA_BUFFER_MIN_SIZE), p_req_element_count)); instance_buffer[p_render_list].set_storage_size(0u, new_size * sizeof(SceneState::InstanceData)); curr_gpu_ptr[p_render_list] = nullptr; } const bool must_remap = instance_buffer[p_render_list].prepare_for_map(p_append); if (must_remap) { curr_gpu_ptr[p_render_list] = nullptr; } } } void RenderForwardClustered::_fill_instance_data(RenderListType p_render_list, int *p_render_info, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) { RenderList *rl = &render_list[p_render_list]; uint32_t element_total = p_max_elements >= 0 ? uint32_t(p_max_elements) : rl->elements.size(); rl->element_info.resize(p_offset + element_total); // If p_offset == 0, grow_instance_buffer resets and increment the buffer. // If this behavior ever changes, _render_shadow_begin may need to change. scene_state.grow_instance_buffer(p_render_list, p_offset + element_total, p_offset != 0u); if (!scene_state.curr_gpu_ptr[p_render_list] && element_total > 0u) { // The old buffer was replaced for another larger one. We must start copying from scratch. element_total += p_offset; p_offset = 0u; scene_state.curr_gpu_ptr[p_render_list] = reinterpret_cast(scene_state.instance_buffer[p_render_list].map_raw_for_upload(0u)); } if (p_render_info) { p_render_info[RSE::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += element_total; } uint32_t repeats = 0; GeometryInstanceSurfaceDataCache *prev_surface = nullptr; for (uint32_t i = 0; i < element_total; i++) { GeometryInstanceSurfaceDataCache *surface = rl->elements[i + p_offset]; GeometryInstanceForwardClustered *inst = surface->owner; SceneState::InstanceData instance_data; if (likely(inst->store_transform_cache)) { RendererRD::MaterialStorage::store_transform_transposed_3x4(inst->transform, instance_data.transform); RendererRD::MaterialStorage::store_transform_transposed_3x4(inst->prev_transform, instance_data.prev_transform); #ifdef REAL_T_IS_DOUBLE // Split the origin into two components, the float approximation and the missing precision. // In the shader we will combine these back together to restore the lost precision. RendererRD::MaterialStorage::split_double(inst->transform.origin.x, &instance_data.transform[3], &instance_data.model_precision[0]); RendererRD::MaterialStorage::split_double(inst->transform.origin.y, &instance_data.transform[7], &instance_data.model_precision[1]); RendererRD::MaterialStorage::split_double(inst->transform.origin.z, &instance_data.transform[11], &instance_data.model_precision[2]); RendererRD::MaterialStorage::split_double(inst->prev_transform.origin.x, &instance_data.prev_transform[3], &instance_data.prev_model_precision[0]); RendererRD::MaterialStorage::split_double(inst->prev_transform.origin.y, &instance_data.prev_transform[7], &instance_data.prev_model_precision[1]); RendererRD::MaterialStorage::split_double(inst->prev_transform.origin.z, &instance_data.prev_transform[11], &instance_data.prev_model_precision[2]); #endif } else { RendererRD::MaterialStorage::store_transform_transposed_3x4(Transform3D(), instance_data.transform); RendererRD::MaterialStorage::store_transform_transposed_3x4(Transform3D(), instance_data.prev_transform); #ifdef REAL_T_IS_DOUBLE memset(instance_data.model_precision, 0, sizeof(instance_data.model_precision)); memset(instance_data.prev_model_precision, 0, sizeof(instance_data.prev_model_precision)); #endif } instance_data.flags = inst->flags_cache; instance_data.gi_offset = inst->gi_offset_cache; instance_data.layer_mask = inst->layer_mask; instance_data.instance_uniforms_ofs = uint32_t(inst->shader_uniforms_offset); instance_data.set_lightmap_uv_scale(inst->lightmap_uv_scale); AABB surface_aabb = AABB(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0)); uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(surface->surface); Vector4 uv_scale = Vector4(0.0, 0.0, 0.0, 0.0); if (format & RSE::ARRAY_FLAG_COMPRESS_ATTRIBUTES) { surface_aabb = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_aabb(surface->surface); uv_scale = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_uv_scale(surface->surface); } uint32_t material_feedback_index = UINT32_MAX; #ifdef MODULE_TEXTURE_STREAMING_ENABLED if (surface->material->material_feedback_rid.is_valid()) { material_feedback_index = TextureStreaming::get_singleton()->feedback_buffer_material_index(surface->material->material_feedback_rid); } #endif instance_data.set_compressed_aabb(surface_aabb); instance_data.material_feedback_index = material_feedback_index; instance_data.set_uv_scale(uv_scale); scene_state.curr_gpu_ptr[p_render_list][i + p_offset] = instance_data; const bool cant_repeat = instance_data.flags & INSTANCE_DATA_FLAG_MULTIMESH || inst->mesh_instance.is_valid(); if (prev_surface != nullptr && !cant_repeat && prev_surface->sort.sort_key1 == surface->sort.sort_key1 && prev_surface->sort.sort_key2 == surface->sort.sort_key2 && prev_surface->uses_lightmap_specular == surface->uses_lightmap_specular && inst->mirror == prev_surface->owner->mirror && repeats < RenderElementInfo::MAX_REPEATS) { //this element is the same as the previous one, count repeats to draw it using instancing repeats++; } else { if (repeats > 0) { for (uint32_t j = 1; j <= repeats; j++) { rl->element_info[p_offset + i - j].repeat = j; } } repeats = 1; if (p_render_info) { p_render_info[RSE::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; } } RenderElementInfo &element_info = rl->element_info[p_offset + i]; // Manually bit shift and OR the "uses_lightmap_specular" value to avoid redundant memory stores generated by the compiler. element_info.value = uint32_t(surface->sort.sort_key1 & 0xFFF) | (uint32_t(surface->uses_lightmap_specular) << 12); if (cant_repeat) { prev_surface = nullptr; } else { prev_surface = surface; } } if (repeats > 0) { for (uint32_t j = 1; j <= repeats; j++) { rl->element_info[p_offset + element_total - j].repeat = j; } } if (p_update_buffer && element_total > 0u) { RenderingDevice::get_singleton()->buffer_flush(scene_state.instance_buffer[p_render_list]._get(0u)); } } _FORCE_INLINE_ static uint32_t _indices_to_primitives(RSE::PrimitiveType p_primitive, uint32_t p_indices) { static const uint32_t divisor[RSE::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; static const uint32_t subtractor[RSE::PRIMITIVE_MAX] = { 0, 0, 1, 0, 2 }; return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi, bool p_using_opaque_gi, bool p_using_motion_pass, bool p_append) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); uint64_t frame = RSG::rasterizer->get_frame_number(); if (p_render_list == RENDER_LIST_OPAQUE) { scene_state.used_sss = false; scene_state.used_screen_texture = false; scene_state.used_normal_texture = false; scene_state.used_depth_texture = false; scene_state.used_lightmap = false; scene_state.used_opaque_stencil = false; } uint32_t lightmap_captures_used = 0; Plane near_plane = Plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin); near_plane.d += p_render_data->scene_data->cam_projection.get_z_near(); float z_max = p_render_data->scene_data->cam_projection.get_z_far() - p_render_data->scene_data->cam_projection.get_z_near(); RenderList *rl = &render_list[p_render_list]; _update_dirty_geometry_instances(); if (!p_append) { rl->clear(); if (p_render_list == RENDER_LIST_OPAQUE) { // Opaque fills motion and alpha lists. render_list[RENDER_LIST_MOTION].clear(); render_list[RENDER_LIST_ALPHA].clear(); } } //fill list for (int i = 0; i < (int)p_render_data->instances->size(); i++) { GeometryInstanceForwardClustered *inst = static_cast((*p_render_data->instances)[i]); Vector3 center = inst->transform.origin; if (p_render_data->scene_data->cam_orthogonal) { if (inst->use_aabb_center) { center = inst->transformed_aabb.get_support(-near_plane.normal); } inst->depth = near_plane.distance_to(center) - inst->sorting_offset; } else { if (inst->use_aabb_center) { center = inst->transformed_aabb.position + (inst->transformed_aabb.size * 0.5); } inst->depth = p_render_data->scene_data->cam_transform.origin.distance_to(center) - inst->sorting_offset; } uint32_t depth_layer = CLAMP(int(inst->depth * 16 / z_max), 0, 15); uint32_t flags = inst->base_flags; //fill flags if appropriate if (inst->non_uniform_scale) { flags |= INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE; } bool uses_lightmap = false; bool uses_lightmap_specular = false; bool uses_gi = false; bool uses_motion = false; float fade_alpha = 1.0; if (inst->fade_near || inst->fade_far) { float fade_dist = inst->transformed_aabb.get_center().distance_to(p_render_data->scene_data->cam_transform.origin); // Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player. if (inst->fade_far && fade_dist > inst->fade_far_begin) { fade_alpha = Math::smoothstep(0.0f, 1.0f, 1.0f - (fade_dist - inst->fade_far_begin) / (inst->fade_far_end - inst->fade_far_begin)); } else if (inst->fade_near && fade_dist < inst->fade_near_end) { fade_alpha = Math::smoothstep(0.0f, 1.0f, (fade_dist - inst->fade_near_begin) / (inst->fade_near_end - inst->fade_near_begin)); } } fade_alpha *= inst->force_alpha * inst->parent_fade_alpha; flags = (flags & ~INSTANCE_DATA_FLAGS_FADE_MASK) | (uint32_t(fade_alpha * 255.0) << INSTANCE_DATA_FLAGS_FADE_SHIFT); if (p_render_list == RENDER_LIST_OPAQUE) { // Setup GI if (inst->lightmap_instance.is_valid()) { // find index of the lightmap_instance of the instance being rendered int32_t lightmap_cull_index = -1; for (uint32_t j = 0; j < scene_state.lightmaps_used; j++) { if (scene_state.lightmap_ids[j] == inst->lightmap_instance) { lightmap_cull_index = j; break; } } if (lightmap_cull_index >= 0) { inst->gi_offset_cache = inst->lightmap_slice_index << 16; inst->gi_offset_cache |= lightmap_cull_index; flags |= INSTANCE_DATA_FLAG_USE_LIGHTMAP; if (scene_state.lightmap_has_sh[lightmap_cull_index]) { flags |= INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP; uses_lightmap_specular = scene_state.lightmap_has_specular[lightmap_cull_index]; } uses_lightmap = true; } else { inst->gi_offset_cache = 0xFFFFFFFF; } } else if (inst->lightmap_sh) { if (lightmap_captures_used < scene_state.max_lightmap_captures) { const Color *src_capture = inst->lightmap_sh->sh; LightmapCaptureData &lcd = scene_state.lightmap_captures[lightmap_captures_used]; for (int j = 0; j < 9; j++) { lcd.sh[j * 4 + 0] = src_capture[j].r; lcd.sh[j * 4 + 1] = src_capture[j].g; lcd.sh[j * 4 + 2] = src_capture[j].b; lcd.sh[j * 4 + 3] = src_capture[j].a; } flags |= INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE; inst->gi_offset_cache = lightmap_captures_used; lightmap_captures_used++; uses_lightmap = true; } } else { if (p_using_opaque_gi) { flags |= INSTANCE_DATA_FLAG_USE_GI_BUFFERS; } if (inst->voxel_gi_instances[0].is_valid()) { uint32_t probe0_index = 0xFFFF; uint32_t probe1_index = 0xFFFF; for (uint32_t j = 0; j < scene_state.voxelgis_used; j++) { if (scene_state.voxelgi_ids[j] == inst->voxel_gi_instances[0]) { probe0_index = j; } else if (scene_state.voxelgi_ids[j] == inst->voxel_gi_instances[1]) { probe1_index = j; } } if (probe0_index == 0xFFFF && probe1_index != 0xFFFF) { //0 must always exist if a probe exists SWAP(probe0_index, probe1_index); } inst->gi_offset_cache = probe0_index | (probe1_index << 16); flags |= INSTANCE_DATA_FLAG_USE_VOXEL_GI; uses_gi = true; } else { if (p_using_sdfgi && inst->can_sdfgi) { flags |= INSTANCE_DATA_FLAG_USE_SDFGI; uses_gi = true; } inst->gi_offset_cache = 0xFFFFFFFF; } } if (p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI || p_pass_mode == PASS_MODE_COLOR) { bool transform_changed = inst->transform_status == GeometryInstanceForwardClustered::TransformStatus::MOVED; bool has_mesh_instance = inst->mesh_instance.is_valid(); bool uses_particles = inst->base_flags & INSTANCE_DATA_FLAG_PARTICLES; bool is_multimesh_with_motion = !uses_particles && (inst->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) && mesh_storage->_multimesh_uses_motion_vectors_offsets(inst->data->base); bool is_dynamic = transform_changed || has_mesh_instance || uses_particles || is_multimesh_with_motion; if (p_pass_mode == PASS_MODE_COLOR && p_using_motion_pass) { uses_motion = is_dynamic; } else if (is_dynamic) { flags |= INSTANCE_DATA_FLAGS_DYNAMIC; } } } inst->flags_cache = flags; GeometryInstanceSurfaceDataCache *surf = inst->surface_caches; float lod_distance = 0.0; if (p_render_data->scene_data->cam_orthogonal) { lod_distance = 1.0; } else { Vector3 aabb_min = inst->transformed_aabb.position; Vector3 aabb_max = inst->transformed_aabb.position + inst->transformed_aabb.size; Vector3 camera_position = p_render_data->scene_data->main_cam_transform.origin; Vector3 surface_distance = Vector3(0.0, 0.0, 0.0).max(aabb_min - camera_position).max(camera_position - aabb_max); lod_distance = surface_distance.length(); } if (unlikely(inst->transform_status != GeometryInstanceForwardClustered::TransformStatus::NONE && frame > inst->prev_transform_change_frame && inst->prev_transform_change_frame)) { inst->prev_transform = inst->transform; inst->transform_status = GeometryInstanceForwardClustered::TransformStatus::NONE; } while (surf) { surf->sort.uses_forward_gi = 0; surf->sort.uses_lightmap = 0; surf->uses_lightmap_specular = false; // LOD if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { uint32_t indices = 0; surf->sort.lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, lod_distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, indices); if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); if (p_render_list == RENDER_LIST_OPAQUE) { //opaque p_render_data->render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RSE::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices; } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow p_render_data->render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RSE::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices; } } } else { surf->sort.lod_index = 0; if (p_render_data->render_info) { // This does not include primitives rendered via indirect draw calls. uint32_t to_draw = mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface); to_draw = _indices_to_primitives(surf->primitive, to_draw); to_draw *= inst->instance_count; if (p_render_list == RENDER_LIST_OPAQUE) { //opaque p_render_data->render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RSE::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += to_draw; } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow p_render_data->render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RSE::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += to_draw; } } } // ADD Element if (p_pass_mode == PASS_MODE_COLOR) { #ifdef DEBUG_ENABLED bool force_alpha = unlikely(get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_OVERDRAW); #else bool force_alpha = false; #endif if (fade_alpha < FADE_ALPHA_PASS_THRESHOLD) { force_alpha = true; } if (!force_alpha && (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE))) { rl->add_element(surf); } if (force_alpha || (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { surf->color_pass_inclusion_mask = COLOR_PASS_FLAG_TRANSPARENT; render_list[RENDER_LIST_ALPHA].add_element(surf); if (uses_gi) { surf->sort.uses_forward_gi = 1; } } else if (p_using_motion_pass && (uses_motion || (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_MOTION_VECTOR))) { surf->color_pass_inclusion_mask = COLOR_PASS_FLAG_MOTION_VECTORS; render_list[RENDER_LIST_MOTION].add_element(surf); } else { surf->color_pass_inclusion_mask = 0; } if (uses_lightmap) { surf->sort.uses_lightmap = 1; surf->uses_lightmap_specular = uses_lightmap_specular; scene_state.used_lightmap = true; } if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING) { scene_state.used_sss = true; } if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SCREEN_TEXTURE) { scene_state.used_screen_texture = true; } if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_NORMAL_TEXTURE) { scene_state.used_normal_texture = true; } if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DEPTH_TEXTURE) { scene_state.used_depth_texture = true; } if ((surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_STENCIL) && !force_alpha && (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE))) { scene_state.used_opaque_stencil = true; } } else if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) { if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) { rl->add_element(surf); } } else if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL) { if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { rl->add_element(surf); } } else { if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) { rl->add_element(surf); } } surf->sort.depth_layer = depth_layer; surf = surf->next; } } if (p_render_list == RENDER_LIST_OPAQUE && lightmap_captures_used) { RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures); } } void RenderForwardClustered::_setup_voxelgis(const PagedArray &p_voxelgis) { scene_state.voxelgis_used = MIN(p_voxelgis.size(), uint32_t(MAX_VOXEL_GI_INSTANCESS)); for (uint32_t i = 0; i < scene_state.voxelgis_used; i++) { scene_state.voxelgi_ids[i] = p_voxelgis[i]; } } void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray &p_lightmaps, const Transform3D &p_cam_transform) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); scene_state.lightmaps_used = 0; for (int i = 0; i < (int)p_lightmaps.size(); i++) { if (i >= (int)scene_state.max_lightmaps) { break; } RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform_and_specular_intensity); // Light texture size. Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; scene_state.lightmaps[i].flags = light_storage->lightmap_get_shadowmask_mode(lightmap); if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; } scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = light_storage->lightmap_uses_spherical_harmonics(lightmap); float &specular_intensity = scene_state.lightmaps[i].normal_xform_and_specular_intensity[3]; specular_intensity = light_storage->lightmap_get_specular_intensity(lightmap); scene_state.lightmap_has_specular[i] = scene_state.lightmap_has_sh[i] && (specular_intensity > 0.0f); scene_state.lightmaps_used++; } if (scene_state.lightmaps_used > 0) { RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps); } } /* SDFGI */ void RenderForwardClustered::_update_sdfgi(RenderDataRD *p_render_data) { if (p_render_data->sdfgi_update_data == nullptr) { return; } Ref rb; if (p_render_data && p_render_data->render_buffers.is_valid()) { rb = p_render_data->render_buffers; } if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { RENDER_TIMESTAMP("Render SDFGI"); Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); float exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); } for (int i = 0; i < p_render_data->render_sdfgi_region_count; i++) { sdfgi->render_region(rb, p_render_data->render_sdfgi_regions[i].region, p_render_data->render_sdfgi_regions[i].instances, exposure_normalization); } if (p_render_data->sdfgi_update_data->update_static) { sdfgi->render_static_lights(p_render_data, rb, p_render_data->sdfgi_update_data->static_cascade_count, p_render_data->sdfgi_update_data->static_cascade_indices, p_render_data->sdfgi_update_data->static_positional_lights); } } } /* Debug */ void RenderForwardClustered::_debug_draw_cluster(Ref p_render_buffers) { if (p_render_buffers.is_valid() && current_cluster_builder != nullptr) { RSE::ViewportDebugDraw dd = get_debug_draw_mode(); if (dd == RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS || dd == RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS || dd == RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_AREA_LIGHTS || dd == RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS || dd == RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES) { ClusterBuilderRD::ElementType elem_type = ClusterBuilderRD::ELEMENT_TYPE_MAX; switch (dd) { case RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS: elem_type = ClusterBuilderRD::ELEMENT_TYPE_OMNI_LIGHT; break; case RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS: elem_type = ClusterBuilderRD::ELEMENT_TYPE_SPOT_LIGHT; break; case RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_AREA_LIGHTS: elem_type = ClusterBuilderRD::ELEMENT_TYPE_AREA_LIGHT; break; case RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS: elem_type = ClusterBuilderRD::ELEMENT_TYPE_DECAL; break; case RSE::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES: elem_type = ClusterBuilderRD::ELEMENT_TYPE_REFLECTION_PROBE; break; default: { } } current_cluster_builder->debug(elem_type); } } } //////////////////////////////////////////////////////////////////////////////// // FOG SHADER void RenderForwardClustered::_update_volumetric_fog(Ref p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes) { ERR_FAIL_COND(p_render_buffers.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); ERR_FAIL_COND(!p_render_buffers->has_custom_data(RB_SCOPE_GI)); Ref rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI); Ref sdfgi; if (p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); } Size2i size = p_render_buffers->get_internal_size(); float ratio = float(size.x) / float((size.x + size.y) / 2); uint32_t target_width = uint32_t(float(get_volumetric_fog_size()) * ratio); uint32_t target_height = uint32_t(float(get_volumetric_fog_size()) / ratio); if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { Ref fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); //validate if (p_environment.is_null() || !environment_get_volumetric_fog_enabled(p_environment) || fog->width != target_width || fog->height != target_height || fog->depth != get_volumetric_fog_depth()) { p_render_buffers->set_custom_data(RB_SCOPE_FOG, Ref()); } } if (p_environment.is_null() || !environment_get_volumetric_fog_enabled(p_environment)) { //no reason to enable or update, bye return; } if (p_environment.is_valid() && environment_get_volumetric_fog_enabled(p_environment) && !p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { //required volumetric fog but not existing, create Ref fog; fog.instantiate(); fog->init(Vector3i(target_width, target_height, get_volumetric_fog_depth()), sky.sky_shader.default_shader_rd); p_render_buffers->set_custom_data(RB_SCOPE_FOG, fog); } if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { Ref fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); RendererRD::Fog::VolumetricFogSettings settings; settings.rb_size = size; settings.time = time; settings.is_using_radiance_octmap_array = is_using_radiance_octmap_array(); settings.max_cluster_elements = RendererRD::LightStorage::get_singleton()->get_max_cluster_elements(); settings.volumetric_fog_filter_active = get_volumetric_fog_filter_active(); settings.shadow_sampler = shadow_sampler; settings.shadow_atlas_depth = RendererRD::LightStorage::get_singleton()->owns_shadow_atlas(p_shadow_atlas) ? RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_shadow_atlas) : RID(); settings.voxel_gi_buffer = rbgi->get_voxel_gi_buffer(); settings.omni_light_buffer = RendererRD::LightStorage::get_singleton()->get_omni_light_buffer(); settings.spot_light_buffer = RendererRD::LightStorage::get_singleton()->get_spot_light_buffer(); settings.area_light_buffer = RendererRD::LightStorage::get_singleton()->get_area_light_buffer(); settings.area_light_atlas = RendererRD::TextureStorage::get_singleton()->area_light_atlas_get_texture(); settings.directional_shadow_depth = RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture(); settings.directional_light_buffer = RendererRD::LightStorage::get_singleton()->get_directional_light_buffer(); settings.vfog = fog; settings.cluster_builder = rb_data->cluster_builder; settings.rbgi = rbgi; settings.sdfgi = sdfgi; settings.env = p_environment; settings.sky = &sky; settings.gi = &gi; RendererRD::Fog::get_singleton()->volumetric_fog_update(settings, p_cam_projection, p_cam_transform, p_prev_cam_inv_transform, p_shadow_atlas, p_directional_light_count, p_use_directional_shadows, p_positional_light_count, p_voxel_gi_count, p_fog_volumes); } } /* Lighting */ void RenderForwardClustered::setup_added_reflection_probe(const Transform3D &p_transform, const Vector3 &p_half_size) { if (current_cluster_builder != nullptr) { current_cluster_builder->add_box(ClusterBuilderRD::BOX_TYPE_REFLECTION_PROBE, p_transform, p_half_size); } } void RenderForwardClustered::setup_added_light(const RSE::LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture, const Vector2 &p_area_size) { if (current_cluster_builder != nullptr) { ClusterBuilderRD::LightType type; if (p_type == RSE::LIGHT_SPOT) { type = ClusterBuilderRD::LIGHT_TYPE_SPOT; } else if (p_type == RSE::LIGHT_OMNI) { type = ClusterBuilderRD::LIGHT_TYPE_OMNI; } else { type = ClusterBuilderRD::LIGHT_TYPE_AREA; } current_cluster_builder->add_light(type, p_transform, p_radius, p_spot_aperture, p_area_size); } } void RenderForwardClustered::setup_added_decal(const Transform3D &p_transform, const Vector3 &p_half_size) { if (current_cluster_builder != nullptr) { current_cluster_builder->add_box(ClusterBuilderRD::BOX_TYPE_DECAL, p_transform, p_half_size); } } /* Render scene */ void RenderForwardClustered::_process_ssao(Ref p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_environment.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); RENDER_TIMESTAMP("Process SSAO"); RendererRD::SSEffects::SSAOSettings settings; settings.radius = environment_get_ssao_radius(p_environment); settings.intensity = environment_get_ssao_intensity(p_environment); settings.power = environment_get_ssao_power(p_environment); settings.detail = environment_get_ssao_detail(p_environment); settings.horizon = environment_get_ssao_horizon(p_environment); settings.sharpness = environment_get_ssao_sharpness(p_environment); settings.full_screen_size = p_render_buffers->get_internal_size(); ss_effects->ssao_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssao, settings); for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { ss_effects->generate_ssao(p_render_buffers, rb_data->ss_effects_data.ssao, v, p_normal_buffers[v], p_projections[v], settings); } } void RenderForwardClustered::_process_ssil(Ref p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections, const Transform3D &p_transform) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_environment.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); RENDER_TIMESTAMP("Process SSIL"); RendererRD::SSEffects::SSILSettings settings; settings.radius = environment_get_ssil_radius(p_environment); settings.intensity = environment_get_ssil_intensity(p_environment); settings.sharpness = environment_get_ssil_sharpness(p_environment); settings.normal_rejection = environment_get_ssil_normal_rejection(p_environment); settings.full_screen_size = p_render_buffers->get_internal_size(); ss_effects->ssil_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssil, settings); Transform3D transform = p_transform; transform.set_origin(Vector3(0.0, 0.0, 0.0)); for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_projections[v]; Projection last_frame_projection = rb_data->ss_effects_data.ssil_last_frame_projections[v] * Projection(rb_data->ss_effects_data.ssil_last_frame_transform.affine_inverse()) * Projection(transform) * projection.inverse(); ss_effects->screen_space_indirect_lighting(p_render_buffers, rb_data->ss_effects_data.ssil, v, p_normal_buffers[v], p_projections[v], last_frame_projection, settings); rb_data->ss_effects_data.ssil_last_frame_projections[v] = projection; } rb_data->ss_effects_data.ssil_last_frame_transform = transform; } void RenderForwardClustered::_process_ssr(Ref p_render_buffers, RID p_environment, const RID *p_normal_slices, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_transform) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_render_buffers.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); RENDER_TIMESTAMP("Process SSR"); ss_effects->ssr_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssr, p_render_buffers->get_base_data_format()); Projection reprojections[RendererSceneRender::MAX_RENDER_VIEWS]; for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_projections[v]; reprojections[v] = rb_data->ss_effects_data.ssr_last_frame_projections[v] * Projection(rb_data->ss_effects_data.ssr_last_frame_transform.affine_inverse()) * Projection(p_transform) * projection.inverse(); rb_data->ss_effects_data.ssr_last_frame_projections[v] = projection; } rb_data->ss_effects_data.ssr_last_frame_transform = p_transform; ss_effects->screen_space_reflection(p_render_buffers, rb_data->ss_effects_data.ssr, p_normal_slices, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), p_projections, reprojections, p_eye_offsets, *copy_effects); } void RenderForwardClustered::_process_sscs(Ref p_render_buffers, const Projection *p_projections, const Transform3D &p_transform, const LocalVector &p_contact_shadows, const RenderShadowData *p_render_shadows, const float p_taa_frame_count) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_render_buffers.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); RENDER_TIMESTAMP("Process SSCS"); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::SSEffects::SSCSSettings settings; settings.quality = RSE::ScreenSpaceContactShadowsLength(GLOBAL_GET_CACHED(int, "rendering/lights_and_shadows/contact_shadow/shadow_length")); settings.surface_thickness = GLOBAL_GET_CACHED(float, "rendering/lights_and_shadows/contact_shadow/surface_thickness"); Transform3D inverse_transform = p_transform.affine_inverse(); ss_effects->sscs_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.sscs, p_contact_shadows.size()); for (uint32_t i = 0; i < p_contact_shadows.size(); i++) { RID light_instance = p_render_shadows[p_contact_shadows[i]].light; RID base = light_storage->light_instance_get_base_light(light_instance); if (!light_storage->light_get_allow_contact_shadows(base)) { continue; } Transform3D light_transform = light_storage->light_instance_get_base_transform(light_instance); Vector3 light_direction = inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, 1))).normalized(); float blur = light_storage->light_get_param(base, RSE::LIGHT_PARAM_CONTACT_SHADOW_BLUR); ss_effects->screen_space_contact_shadows(p_render_buffers, rb_data->ss_effects_data.sscs, settings, p_projections, light_direction, i, blur, p_taa_frame_count); } } void RenderForwardClustered::_copy_framebuffer_to_ss_effects(Ref p_render_buffers, bool p_use_ssil, bool p_use_ssr) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_render_buffers.is_null()); ss_effects->copy_internal_texture_to_last_frame(p_render_buffers, *copy_effects); } void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_ssr, bool p_use_sscs, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer) { // Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); Ref rb = p_render_data->render_buffers; Ref rb_data; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { // Our forward clustered custom data buffer will only be available when we're rendering our normal view. // This will not be available when rendering reflection probes. rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } RENDER_TIMESTAMP("Setup Shadows"); if (rb.is_valid() && p_use_gi && rb->has_custom_data(RB_SCOPE_SDFGI)) { Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); sdfgi->store_probes(); } Size2i viewport_size = Size2i(1, 1); if (rb.is_valid()) { viewport_size = rb->get_internal_size(); } p_render_data->cube_shadows.clear(); p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); p_render_data->contact_shadows.clear(); float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier(); { for (int i = 0; i < p_render_data->render_shadow_count; i++) { RID li = p_render_data->render_shadows[i].light; RID base = light_storage->light_instance_get_base_light(li); if (light_storage->light_get_type(base) == RSE::LIGHT_DIRECTIONAL) { p_render_data->directional_shadows.push_back(i); if (rb_data.is_valid() && ss_effects) { // Add contact shadows to be processed if (p_render_data->render_shadows[i].pass == 0 && light_storage->light_get_allow_contact_shadows(base)) { // Contact shadows only need one pass p_render_data->contact_shadows.push_back(i); } } } else if (light_storage->light_get_type(base) == RSE::LIGHT_OMNI && light_storage->light_omni_get_shadow_mode(base) == RSE::LIGHT_OMNI_SHADOW_CUBE) { p_render_data->cube_shadows.push_back(i); } else { p_render_data->shadows.push_back(i); } } if (p_render_data->cube_shadows.size()) { RENDER_TIMESTAMP("Render OmniLight Shadows"); // Cube shadows are rendered in their own way. for (const int &index : p_render_data->cube_shadows) { _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } } if (p_render_data->directional_shadows.size()) { //open the pass for directional shadows light_storage->update_directional_shadow_atlas(); RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::DRAW_CLEAR_DEPTH, Vector(), 0.0f); RD::get_singleton()->draw_list_end(); } } // Render GI bool render_shadows = p_render_data->directional_shadows.size() || p_render_data->shadows.size(); bool render_gi = rb.is_valid() && p_use_gi; if (render_shadows && render_gi) { RENDER_TIMESTAMP("Render GI + Render Directional/SpotLight Shadows (Parallel)"); } else if (render_shadows) { RENDER_TIMESTAMP("Render Directional/SpotLight Shadows"); } else if (render_gi) { RENDER_TIMESTAMP("Render GI"); } //prepare shadow rendering if (render_shadows) { _render_shadow_begin(); //render directional shadows for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) { _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } //render positional shadows for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) { _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } _render_shadow_process(); } if (render_gi) { gi.process_gi(rb, p_normal_roughness_slices, p_voxel_gi_buffer, p_render_data->environment, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances); } if (render_shadows) { _render_shadow_end(); } if (rb_data.is_valid() && ss_effects) { // Note, in multiview we're allocating buffers for each eye/view we're rendering. // This should allow most of the processing to happen in parallel even if we're doing // drawcalls per eye/view. It will all sync up at the barrier. if (p_use_ssil || p_use_ssr) { ss_effects->allocate_last_frame_buffer(rb, p_use_ssil, p_use_ssr); } if (p_use_ssao || p_use_ssil) { RENDER_TIMESTAMP("Prepare Depth for SSAO/SSIL"); // Convert our depth buffer data to linear data in for (uint32_t v = 0; v < rb->get_view_count(); v++) { ss_effects->downsample_depth(rb, v, p_render_data->scene_data->view_projection[v]); } if (p_use_ssao) { _process_ssao(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection); } if (p_use_ssil) { _process_ssil(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform); } } if (p_use_sscs) { _process_sscs(rb, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform, p_render_data->contact_shadows, p_render_data->render_shadows, p_render_data->scene_data->taa_frame_count); } if (p_use_ssr) { _process_ssr(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform); } } RENDER_TIMESTAMP("Pre Opaque Render"); if (current_cluster_builder) { // Note: when rendering stereoscopic (multiview) we are using our combined frustum projection to create // our cluster data. We use reprojection in the shader to adjust for our left/right eye. // This only works as we don't filter our cluster by depth buffer. // If we ever make this optimization we should make it optional and only use it in mono. // What we win by filtering out a few lights, we loose by having to do the work double for stereo. current_cluster_builder->begin(p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, !p_render_data->reflection_probe.is_valid()); } bool using_shadows = true; if (p_render_data->reflection_probe.is_valid()) { if (!RSG::light_storage->reflection_probe_renders_shadows(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe))) { using_shadows = false; } } else { //do not render reflections when rendering a reflection probe light_storage->update_reflection_probe_buffer(p_render_data, *p_render_data->reflection_probes, p_render_data->scene_data->cam_transform.affine_inverse(), p_render_data->environment); } uint32_t directional_light_count = 0; uint32_t positional_light_count = 0; light_storage->update_light_buffers(p_render_data, *p_render_data->lights, p_render_data->scene_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); texture_storage->update_decal_buffer(*p_render_data->decals, p_render_data->scene_data->cam_transform); p_render_data->directional_light_count = directional_light_count; if (current_cluster_builder) { current_cluster_builder->bake_cluster(); } if (rb_data.is_valid()) { RENDER_TIMESTAMP("Update Volumetric Fog"); bool directional_shadows = RendererRD::LightStorage::get_singleton()->has_directional_shadows(directional_light_count); _update_volumetric_fog(rb, p_render_data->environment, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, p_render_data->scene_data->prev_cam_transform.affine_inverse(), p_render_data->shadow_atlas, directional_light_count, directional_shadows, positional_light_count, p_render_data->voxel_gi_count, *p_render_data->fog_volumes); } } void RenderForwardClustered::_process_sss(Ref p_render_buffers, const Projection &p_camera) { ERR_FAIL_COND(p_render_buffers.is_null()); Size2i internal_size = p_render_buffers->get_internal_size(); bool can_use_effects = internal_size.x >= 8 && internal_size.y >= 8; if (!can_use_effects) { //just copy return; } p_render_buffers->allocate_blur_textures(); for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { RID internal_texture = p_render_buffers->get_internal_texture(v); RID depth_texture = p_render_buffers->get_depth_texture(v); ss_effects->sub_surface_scattering(p_render_buffers, internal_texture, depth_texture, p_camera, internal_size); } } void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { scene_state.used_uniform_buffer_count = 0; RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); ERR_FAIL_NULL(p_render_data); Ref rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); Ref rb_data; if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { // Our forward clustered custom data buffer will only be available when we're rendering our normal view. // This will not be available when rendering reflection probes. rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } bool is_reflection_probe = p_render_data->reflection_probe.is_valid(); bool is_multiview = rb->get_view_count() > 1; static const int texture_multisamples[RSE::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8 }; //first of all, make a new render pass //fill up ubo RENDER_TIMESTAMP("Prepare 3D Scene"); // get info about our rendering effects bool ce_needs_motion_vectors = _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_NEEDS_MOTION_VECTORS); bool ce_needs_normal_roughness = _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_NEEDS_ROUGHNESS); bool ce_needs_separate_specular = _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_NEEDS_SEPARATE_SPECULAR); // sdfgi first _update_sdfgi(p_render_data); // assign render indices to voxel_gi_instances for (uint32_t i = 0; i < (uint32_t)p_render_data->voxel_gi_instances->size(); i++) { RID voxel_gi_instance = (*p_render_data->voxel_gi_instances)[i]; gi.voxel_gi_instance_set_render_index(voxel_gi_instance, i); } // obtain cluster builder if (light_storage->owns_reflection_probe_instance(p_render_data->reflection_probe)) { current_cluster_builder = light_storage->reflection_probe_instance_get_cluster_builder(p_render_data->reflection_probe, &cluster_builder_shared); if (p_render_data->camera_attributes.is_valid()) { light_storage->reflection_probe_set_baked_exposure(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe), RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes)); } } else if (rb_data.is_valid()) { current_cluster_builder = rb_data->cluster_builder; p_render_data->voxel_gi_count = 0; if (rb->has_custom_data(RB_SCOPE_SDFGI)) { Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); if (sdfgi.is_valid()) { sdfgi->update_cascades(); sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data); sdfgi->update_light(); } } gi.setup_voxel_gi_instances(p_render_data, p_render_data->render_buffers, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances, p_render_data->voxel_gi_count); } else { ERR_PRINT("No render buffer nor reflection atlas, bug"); // Should never happen! current_cluster_builder = nullptr; return; // No point in continuing, we'll just crash. } ERR_FAIL_NULL(current_cluster_builder); p_render_data->cluster_buffer = current_cluster_builder->get_cluster_buffer(); p_render_data->cluster_size = current_cluster_builder->get_cluster_size(); p_render_data->cluster_max_elements = current_cluster_builder->get_max_cluster_elements(); _update_vrs(rb); RENDER_TIMESTAMP("Setup 3D Scene"); bool using_debug_mvs = get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS; bool using_taa = rb->get_use_taa(); enum { SCALE_NONE, SCALE_FSR2, SCALE_MFX, } scale_type = SCALE_NONE; switch (rb->get_scaling_3d_mode()) { case RSE::VIEWPORT_SCALING_3D_MODE_FSR2: scale_type = SCALE_FSR2; break; case RSE::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL: #ifdef METAL_MFXTEMPORAL_ENABLED scale_type = SCALE_MFX; #else scale_type = SCALE_NONE; #endif break; default: break; } bool using_upscaling = scale_type != SCALE_NONE; // check if we need motion vectors bool motion_vectors_required; if (using_debug_mvs) { motion_vectors_required = true; } else if (ce_needs_motion_vectors) { motion_vectors_required = true; } else if (!is_reflection_probe && using_taa) { motion_vectors_required = true; } else if (!is_reflection_probe && using_upscaling) { motion_vectors_required = true; } else { motion_vectors_required = false; } //p_render_data->scene_data->subsurface_scatter_width = subsurface_scatter_size; p_render_data->scene_data->calculate_motion_vectors = motion_vectors_required; p_render_data->scene_data->directional_light_count = 0; p_render_data->scene_data->opaque_prepass_threshold = 0.99f; Size2i screen_size; RID color_framebuffer; RID color_only_framebuffer; RID depth_framebuffer; RendererRD::MaterialStorage::Samplers samplers; PassMode depth_pass_mode = PASS_MODE_DEPTH; uint32_t color_pass_flags = 0; Vector depth_pass_clear; bool using_separate_specular = false; bool using_ssr = false; bool using_sscs = false; bool using_sdfgi = false; bool using_voxelgi = false; bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_ssil = !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); bool using_motion_pass = rb_data.is_valid() && using_upscaling; if (is_reflection_probe) { uint32_t resolution = light_storage->reflection_probe_instance_get_resolution(p_render_data->reflection_probe); screen_size.x = resolution; screen_size.y = resolution; color_framebuffer = light_storage->reflection_probe_instance_get_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass); color_only_framebuffer = color_framebuffer; depth_framebuffer = light_storage->reflection_probe_instance_get_depth_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass); if (light_storage->reflection_probe_is_interior(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe))) { p_render_data->environment = RID(); //no environment on interiors } reverse_cull = true; // for some reason our views are inverted samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(); // Indicate pipelines for reflection probes are required. global_pipeline_data_required.use_reflection_probes = true; } else { screen_size = rb->get_internal_size(); if (p_render_data->scene_data->calculate_motion_vectors) { color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS; scene_shader.enable_advanced_shader_group(); // Indicate pipelines for motion vectors are required. global_pipeline_data_required.use_motion_vectors = true; } if (p_render_data->voxel_gi_instances->size() > 0) { using_voxelgi = true; } if (p_render_data->environment.is_valid()) { if (environment_get_sdfgi_enabled(p_render_data->environment) && get_debug_draw_mode() != RSE::VIEWPORT_DEBUG_DRAW_UNSHADED) { using_sdfgi = true; } if (environment_get_ssr_enabled(p_render_data->environment)) { if (!p_render_data->transparent_bg) { using_ssr = true; } else { WARN_PRINT_ONCE("Screen-space reflections are not supported in viewports with a transparent background. Disabling SSR in transparent viewport."); } } } if (GLOBAL_GET_CACHED(bool, "rendering/lights_and_shadows/contact_shadow/enabled")) { using_sscs = true; } if (p_render_data->scene_data->view_count > 1) { color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW; // Try enabling here in case is_xr_enabled() returns false. scene_shader.shader.enable_group(SceneShaderForwardClustered::SHADER_GROUP_MULTIVIEW); // Indicate pipelines for multiview are required. global_pipeline_data_required.use_multiview = true; } color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); color_only_framebuffer = rb_data->get_color_only_fb(); samplers = rb->get_samplers(); } p_render_data->scene_data->emissive_exposure_normalization = -1.0; RD::get_singleton()->draw_command_begin_label("Render Setup"); _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); _setup_voxelgis(*p_render_data->voxel_gi_instances); uint32_t depth_prepass_uniform_buffer_index = _setup_environment(p_render_data, is_reflection_probe, screen_size, screen_size, p_default_bg_color, false); // May have changed due to the above (light buffer enlarged, as an example). _update_render_base_uniform_set(); _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi, using_motion_pass); render_list[RENDER_LIST_OPAQUE].sort_by_key(); render_list[RENDER_LIST_MOTION].sort_by_key(); render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); int *render_info = p_render_data->render_info ? p_render_data->render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr; _fill_instance_data(RENDER_LIST_OPAQUE, render_info); _fill_instance_data(RENDER_LIST_MOTION, render_info); _fill_instance_data(RENDER_LIST_ALPHA, render_info); RD::get_singleton()->draw_command_end_label(); if (!is_reflection_probe) { if (using_voxelgi) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI; } else if (p_render_data->environment.is_valid()) { if (using_ssr || using_sdfgi || environment_get_ssao_enabled(p_render_data->environment) || using_ssil || using_sscs || ce_needs_normal_roughness || get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER || scene_state.used_normal_texture) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; } } else if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER || scene_state.used_normal_texture) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; } switch (depth_pass_mode) { case PASS_MODE_DEPTH: { depth_framebuffer = rb_data->get_depth_fb(); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS); depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI); depth_pass_clear.push_back(Color(0, 0, 0, 0)); depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; default: { }; } } bool using_sss = rb_data.is_valid() && !is_reflection_probe && scene_state.used_sss && ss_effects->sss_get_quality() != RSE::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; if (using_sss && p_render_data->transparent_bg) { WARN_PRINT_ONCE("Sub-surface scattering is not supported in viewports with a transparent background. Disabling SSS in transparent viewport."); using_sss = false; } if ((using_sss || ce_needs_separate_specular) && !using_separate_specular) { using_separate_specular = true; color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR; color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); } // Ensure this is allocated so we don't get a stutter the first time an object with SSS appears on screen. if (global_surface_data.sss_used && !is_reflection_probe) { rb_data->ensure_specular(); } if (global_surface_data.normal_texture_used && !is_reflection_probe) { rb_data->ensure_normal_roughness_texture(); } if (using_sss || using_separate_specular || scene_state.used_lightmap || using_voxelgi || global_surface_data.sss_used) { scene_shader.enable_advanced_shader_group(p_render_data->scene_data->view_count > 1); } // Update the global pipeline requirements with all the features found to be in use in this scene. if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || global_surface_data.normal_texture_used) { global_pipeline_data_required.use_normal_and_roughness = true; } if (scene_state.used_lightmap || scene_state.lightmaps_used > 0) { global_pipeline_data_required.use_lightmaps = true; } if (using_voxelgi) { global_pipeline_data_required.use_voxelgi = true; } if (using_separate_specular || global_surface_data.sss_used) { global_pipeline_data_required.use_separate_specular = true; } // Update the compiled pipelines if any of the requirements have changed. _update_dirty_geometry_pipelines(); RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; // We invert luminance_multiplier for sky so that we can combine it with exposure value. float sky_luminance_multiplier = 1.0 / rb->get_luminance_multiplier(); float sky_brightness_multiplier = 1.0; Color clear_color; bool load_color = false; if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (is_environment(p_render_data->environment)) { RSE::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); RSE::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(p_render_data->environment); if (p_render_data->camera_attributes.is_valid()) { bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); } switch (bg_mode) { case RSE::ENV_BG_CLEAR_COLOR: case RSE::ENV_BG_COLOR: { clear_color = bg_mode == RSE::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color)); } clear_color = clear_color.srgb_to_linear(); clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; } break; case RSE::ENV_BG_SKY: { draw_sky = !p_render_data->transparent_bg; } break; case RSE::ENV_BG_CANVAS: { if (!is_reflection_probe) { RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear); } load_color = true; } break; case RSE::ENV_BG_KEEP: { load_color = true; } break; case RSE::ENV_BG_CAMERA_FEED: { } break; default: { } } // setup sky if used for ambient, reflections, or background if (draw_sky || draw_sky_fog_only || (reflection_source == RSE::ENV_REFLECTION_SOURCE_BG && bg_mode == RSE::ENV_BG_SKY) || reflection_source == RSE::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RSE::ENV_AMBIENT_SOURCE_SKY) { RENDER_TIMESTAMP("Setup Sky"); RD::get_singleton()->draw_command_begin_label("Setup Sky"); // Setup our sky render information for this frame/viewport sky.setup_sky(p_render_data, screen_size); sky_brightness_multiplier *= bg_energy_multiplier; RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { sky.update_radiance_buffers(rb, p_render_data->environment, p_render_data->scene_data->cam_transform.origin, time, sky_luminance_multiplier, sky_brightness_multiplier); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid draw_sky = false; } if (draw_sky || draw_sky_fog_only) { // update sky half/quarter res buffers (if required) sky.update_res_buffers(rb, p_render_data->environment, time, sky_luminance_multiplier, sky_brightness_multiplier); } RD::get_singleton()->draw_command_end_label(); } if (bg_mode != RSE::ENV_BG_CLEAR_COLOR && bg_mode != RSE::ENV_BG_COLOR) { clear_color = clear_color.srgb_to_linear(); } } else { clear_color = p_default_bg_color.srgb_to_linear(); } // After this point clear_color has linear encoding. RSE::ViewportMSAA msaa = rb->get_msaa_3d(); bool use_msaa = msaa != RSE::VIEWPORT_MSAA_DISABLED; bool ce_pre_opaque_resolved_color = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_COLOR, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_OPAQUE); bool ce_post_opaque_resolved_color = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_COLOR, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_OPAQUE); bool ce_pre_transparent_resolved_color = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_COLOR, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT); bool ce_pre_opaque_resolved_depth = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_DEPTH, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_OPAQUE); bool ce_post_opaque_resolved_depth = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_DEPTH, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_OPAQUE); bool ce_pre_transparent_resolved_depth = use_msaa && _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_DEPTH, RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT); bool debug_voxelgis = get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_VOXEL_GI_ALBEDO || get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING || get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION; bool debug_sdfgi_probes = get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; bool force_depth_pre_pass = scene_state.used_opaque_stencil; bool depth_pre_pass = (force_depth_pre_pass || scene_shader.depth_prepass_enabled) && depth_framebuffer.is_valid(); SceneShaderForwardClustered::ShaderSpecialization base_specialization = scene_shader.default_specialization; base_specialization.use_depth_fog = p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RSE::EnvironmentFogMode::ENV_FOG_MODE_DEPTH; bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); if (depth_pre_pass) { //depth pre pass bool needs_pre_resolve = _needs_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); if (needs_pre_resolve) { RENDER_TIMESTAMP("GI + Render Depth Pre-Pass (Parallel)"); } else { RENDER_TIMESTAMP("Render Depth Pre-Pass"); } if (needs_pre_resolve) { //pre clear the depth framebuffer, as AMD (and maybe others?) use compute for it, and barrier other compute shaders. RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::DRAW_CLEAR_ALL, depth_pass_clear, 0.0f); RD::get_singleton()->draw_list_end(); //start compute processes here, so they run at the same time as depth pre-pass _post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); } RD::get_singleton()->draw_command_begin_label("Render Depth Pre-Pass"); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, is_multiview, RID(), samplers, depth_prepass_uniform_buffer_index); bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization, !is_reflection_probe); _render_list_with_draw_list(&render_list_params, depth_framebuffer, RD::DrawFlags(needs_pre_resolve ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_ALL), depth_pass_clear, 0.0f, 0u, p_render_data->render_region); RD::get_singleton()->draw_command_end_label(); if (use_msaa) { RENDER_TIMESTAMP("Resolve Depth Pre-Pass (MSAA)"); RD::get_singleton()->draw_command_begin_label("Resolve Depth Pre-Pass (MSAA)"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_gi(rb->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[msaa]); } } else if (finish_depth) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); } } RD::get_singleton()->draw_command_end_label(); } } { if (ce_pre_opaque_resolved_color) { // We haven't rendered color data yet so... WARN_PRINT_ONCE("Pre opaque rendering effects can't access resolved color buffers."); } if (ce_pre_opaque_resolved_depth && !depth_pre_pass) { // We haven't rendered depth data yet so... WARN_PRINT_ONCE("Pre opaque rendering effects can't access resolved depth buffers."); } RENDER_TIMESTAMP("Process Pre Opaque Compositor Effects"); _process_compositor_effects(RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_OPAQUE, p_render_data); } RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS]; if (rb_data.is_valid() && rb_data->has_normal_roughness()) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { normal_roughness_views[v] = rb_data->get_normal_roughness(v); } } _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_ssr, using_sscs, using_sdfgi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID()); if (current_cluster_builder) { base_specialization.cluster_has_area_light = current_cluster_builder->get_cluster_count_by_type(ClusterBuilderRD::ELEMENT_TYPE_AREA_LIGHT) != 0; } RENDER_TIMESTAMP("Render Opaque Pass"); RD::get_singleton()->draw_command_begin_label("Render Opaque Pass"); p_render_data->scene_data->directional_light_count = p_render_data->directional_light_count; p_render_data->scene_data->opaque_prepass_threshold = 0.0f; // Shadow pass can change the base uniform set samplers. _update_render_base_uniform_set(); uint32_t opaque_pass_uniform_buffer_index = _setup_environment(p_render_data, is_reflection_probe, screen_size, screen_size, p_default_bg_color, true, using_motion_pass); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, is_multiview, radiance_texture, samplers, opaque_pass_uniform_buffer_index, true); { bool render_motion_pass = !render_list[RENDER_LIST_MOTION].elements.is_empty(); { Vector c; if (!load_color) { if (using_separate_specular || rb_data.is_valid()) { // Effects that rely on separate specular, like subsurface scattering, must clear the alpha to zero. clear_color.a = 0; } c.push_back(clear_color); if (rb_data.is_valid()) { c.push_back(Color(0, 0, 0, 0)); // Separate specular. c.push_back(Color(0, 0, 0, 0)); // Motion vector. Pushed to the clear color vector even if the framebuffer isn't bound. } } uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~uint32_t(COLOR_PASS_FLAG_MOTION_VECTORS)) : color_pass_flags; RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization, !is_reflection_probe); _render_list_with_draw_list(&render_list_params, opaque_framebuffer, RD::DrawFlags(load_color ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_COLOR_ALL) | (depth_pre_pass ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_DEPTH), c, 0.0f, 0u, p_render_data->render_region); } RD::get_singleton()->draw_command_end_label(); if (using_motion_pass) { if (scale_type == SCALE_MFX) { motion_vectors_store->process(rb, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, p_render_data->scene_data->prev_cam_projection, p_render_data->scene_data->prev_cam_transform); } else { Vector motion_vector_clear_colors; motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0)); RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::DRAW_CLEAR_ALL, motion_vector_clear_colors); RD::get_singleton()->draw_list_end(); } } if (render_motion_pass) { RD::get_singleton()->draw_command_begin_label("Render Motion Pass"); RENDER_TIMESTAMP("Render Motion Pass"); rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, is_multiview, radiance_texture, samplers, opaque_pass_uniform_buffer_index, true); RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization, !is_reflection_probe); _render_list_with_draw_list(&render_list_params, color_framebuffer); RD::get_singleton()->draw_command_end_label(); } } { if (ce_post_opaque_resolved_color) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v)); } } if (ce_post_opaque_resolved_depth) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); } } RENDER_TIMESTAMP("Process Post Opaque Compositor Effects"); _process_compositor_effects(RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_OPAQUE, p_render_data); } if (debug_voxelgis) { Projection dc; dc.set_depth_correction(true); Projection cm = (dc * p_render_data->scene_data->cam_projection) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer); RD::get_singleton()->draw_command_begin_label("Debug VoxelGIs"); for (int i = 0; i < (int)p_render_data->voxel_gi_instances->size(); i++) { gi.debug_voxel_gi((*p_render_data->voxel_gi_instances)[i], draw_list, color_only_framebuffer, cm, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION, 1.0); } RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_list_end(); } if (debug_sdfgi_probes) { Projection dc; dc.set_depth_correction(true); Projection cms[RendererSceneRender::MAX_RENDER_VIEWS]; for (uint32_t v = 0; v < p_render_data->scene_data->view_count; v++) { cms[v] = (dc * p_render_data->scene_data->view_projection[v]) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); } _debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms); } if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); RD::get_singleton()->draw_command_begin_label("Draw Sky"); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0u, p_render_data->render_region); sky.draw_sky(draw_list, rb, p_render_data->environment, color_only_framebuffer, time, sky_luminance_multiplier, sky_brightness_multiplier); RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_command_end_label(); } if (use_msaa) { RENDER_TIMESTAMP("Resolve MSAA"); if (scene_state.used_screen_texture || using_separate_specular || ce_pre_transparent_resolved_color) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v)); } if (using_separate_specular) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb_data->get_specular_msaa(v), rb_data->get_specular(v)); } } } if (scene_state.used_depth_texture || scene_state.used_normal_texture || using_separate_specular || ce_needs_normal_roughness || ce_pre_transparent_resolved_depth) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); } } } { RENDER_TIMESTAMP("Process Post Sky Compositor Effects"); // Don't need to check for depth or color resolve here, we've already triggered it. _process_compositor_effects(RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_SKY, p_render_data); } if (using_separate_specular) { if (using_sss) { RENDER_TIMESTAMP("Sub-Surface Scattering"); RD::get_singleton()->draw_command_begin_label("Process Sub-Surface Scattering"); _process_sss(rb, p_render_data->scene_data->cam_projection); RD::get_singleton()->draw_command_end_label(); } { //just mix specular back RENDER_TIMESTAMP("Merge Specular"); copy_effects->merge_specular(color_only_framebuffer, rb_data->get_specular(), !use_msaa ? RID() : rb->get_internal_texture(), RID(), p_render_data->scene_data->view_count); } } if (using_separate_specular && is_environment(p_render_data->environment) && (environment_get_background(p_render_data->environment) == RSE::ENV_BG_CANVAS)) { // Canvas background mode does not clear the color buffer, but copies over it. If screen-space specular effects are enabled and the background is blank, // this results in ghosting due to the separate specular buffer copy. Need to explicitly clear the specular buffer once we're done with it to fix it. RENDER_TIMESTAMP("Clear Separate Specular (Canvas Background Mode)"); Vector blank_clear_color; blank_clear_color.push_back(Color(0.0, 0.0, 0.0)); RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::DRAW_CLEAR_ALL, blank_clear_color); RD::get_singleton()->draw_list_end(); } if (rb_data.is_valid() && using_upscaling) { // Make sure the upscaled texture is initialized, but not necessarily filled, before running screen copies // so it properly detect if a dedicated copy texture should be used. rb->ensure_upscaled(); } if (scene_state.used_screen_texture || global_surface_data.screen_texture_used) { RENDER_TIMESTAMP("Copy Screen Texture"); _render_buffers_ensure_screen_texture(p_render_data); if (scene_state.used_screen_texture) { // Copy screen texture to backbuffer so we can read from it _render_buffers_copy_screen_texture(p_render_data); } } if (scene_state.used_depth_texture || global_surface_data.depth_texture_used) { RENDER_TIMESTAMP("Copy Depth Texture"); _render_buffers_ensure_depth_texture(p_render_data); if (scene_state.used_depth_texture) { // Copy depth texture to backbuffer so we can read from it _render_buffers_copy_depth_texture(p_render_data); } } { if (using_separate_specular) { // Our specular will be combined back in (and effects, subsurface scattering and/or ssr applied), // so if we've requested this, we need another copy. // Fairly unlikely scenario though. if (ce_pre_transparent_resolved_color) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v)); } } if (ce_pre_transparent_resolved_depth) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); } } } RENDER_TIMESTAMP("Process Pre Transparent Compositor Effects"); _process_compositor_effects(RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT, p_render_data); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); RD::get_singleton()->draw_command_begin_label("Render 3D Transparent Pass"); uint32_t transparent_pass_uniform_buffer_index = _setup_environment(p_render_data, is_reflection_probe, screen_size, screen_size, p_default_bg_color, false); rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, is_multiview, radiance_texture, samplers, transparent_pass_uniform_buffer_index, true); { uint32_t transparent_color_pass_flags = (color_pass_flags | uint32_t(COLOR_PASS_FLAG_TRANSPARENT)) & ~uint32_t(COLOR_PASS_FLAG_SEPARATE_SPECULAR); // Motion vectors should not be overwritten by transparent objects. transparent_color_pass_flags &= ~uint32_t(COLOR_PASS_FLAG_MOTION_VECTORS); RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization, !is_reflection_probe); _render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 0.0f, 0u, p_render_data->render_region); } RD::get_singleton()->draw_command_end_label(); if (rb_data.is_valid() && use_msaa) { RENDER_TIMESTAMP("> Resolve MSAA (Post-Transparent)"); RD::get_singleton()->draw_command_begin_label("Resolve MSAA (Post-Transparent)"); bool compositor_needs_resolved_depth = _compositor_effects_has_flag(p_render_data, RSE::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_DEPTH, RSE::CompositorEffectCallbackType::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_TRANSPARENT); bool using_dof = RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes); bool resolve_transparent_depth = (using_taa || using_upscaling || using_dof || compositor_needs_resolved_depth); bool resolve_velocity_buffer = (using_taa || using_upscaling || ce_needs_motion_vectors) && rb->has_velocity_buffer(true); RENDER_TIMESTAMP("Resolve Color"); for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v)); } if (resolve_transparent_depth) { RENDER_TIMESTAMP("Resolve Depth"); for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); } } if (resolve_velocity_buffer) { RENDER_TIMESTAMP("Resolve Velocity Buffer"); for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_velocity_buffer(true, v), rb->get_velocity_buffer(false, v)); } } RENDER_TIMESTAMP("< Resolve MSAA (Post-Transparent)"); RD::get_singleton()->draw_command_end_label(); } RD::get_singleton()->draw_command_begin_label("Copy Framebuffer for SSIL/SSR"); if (using_ssil || using_ssr) { RENDER_TIMESTAMP("Copy Final Framebuffer (SSIL/SSR)"); _copy_framebuffer_to_ss_effects(rb, using_ssil, using_ssr); } RD::get_singleton()->draw_command_end_label(); { RENDER_TIMESTAMP("Process Post Transparent Compositor Effects"); _process_compositor_effects(RSE::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_TRANSPARENT, p_render_data); } if (rb_data.is_valid() && (using_upscaling || using_taa)) { if (scale_type == SCALE_FSR2) { rb_data->ensure_fsr2(fsr2_effect); RID exposure; if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { exposure = luminance->get_current_luminance_buffer(rb); } RD::get_singleton()->draw_command_begin_label("FSR2"); RENDER_TIMESTAMP("FSR2"); for (uint32_t v = 0; v < rb->get_view_count(); v++) { real_t fov = p_render_data->scene_data->cam_projection.get_fov(); real_t aspect = p_render_data->scene_data->cam_projection.get_aspect(); real_t fovy = p_render_data->scene_data->cam_projection.get_fovy(fov, 1.0 / aspect); Vector2 jitter = p_render_data->scene_data->taa_jitter * Vector2(rb->get_internal_size()) * 0.5f; RendererRD::FSR2Effect::Parameters params; params.context = rb_data->get_fsr2_context(); params.internal_size = rb->get_internal_size(); params.sharpness = CLAMP(1.0f - (rb->get_fsr_sharpness() / 2.0f), 0.0f, 1.0f); params.color = rb->get_internal_texture(v); params.depth = rb->get_depth_texture(v); params.velocity = rb->get_velocity_buffer(false, v); params.reactive = rb->get_internal_texture_reactive(v); params.exposure = exposure; params.output = rb->get_upscaled_texture(v); params.z_near = p_render_data->scene_data->z_near; params.z_far = p_render_data->scene_data->z_far; params.fovy = fovy; params.jitter = jitter; params.delta_time = float(time_step); params.reset_accumulation = false; // FIXME: The engine does not provide a way to reset the accumulation. Projection correction; correction.set_depth_correction(true, true, false); const Projection &prev_proj = p_render_data->scene_data->prev_cam_projection; const Projection &cur_proj = p_render_data->scene_data->cam_projection; const Transform3D &prev_transform = p_render_data->scene_data->prev_cam_transform; const Transform3D &cur_transform = p_render_data->scene_data->cam_transform; params.reprojection = (correction * prev_proj) * prev_transform.affine_inverse() * cur_transform * (correction * cur_proj).inverse(); fsr2_effect->upscale(params); } RD::get_singleton()->draw_command_end_label(); } else if (scale_type == SCALE_MFX) { #ifdef METAL_MFXTEMPORAL_ENABLED bool reset = rb_data->ensure_mfx_temporal(mfx_temporal_effect); RID exposure; if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { exposure = luminance->get_current_luminance_buffer(rb); } RD::get_singleton()->draw_command_begin_label("MetalFX Temporal"); // Scale to ±0.5. Vector2 jitter = p_render_data->scene_data->taa_jitter * 0.5f; jitter *= Vector2(1.0, -1.0); // Flip y-axis as bottom left is origin. for (uint32_t v = 0; v < rb->get_view_count(); v++) { RendererRD::MFXTemporalEffect::Params params; params.src = rb->get_internal_texture(v); params.depth = rb->get_depth_texture(v); params.motion = rb->get_velocity_buffer(false, v); params.exposure = exposure; params.dst = rb->get_upscaled_texture(v); params.jitter_offset = jitter; params.reset = reset; mfx_temporal_effect->process(rb_data->get_mfx_temporal_context(), params); } RD::get_singleton()->draw_command_end_label(); #endif } else if (using_taa) { RD::get_singleton()->draw_command_begin_label("TAA"); RENDER_TIMESTAMP("TAA"); taa->process(rb, rb->get_base_data_format(), p_render_data->scene_data->z_near, p_render_data->scene_data->z_far); RD::get_singleton()->draw_command_end_label(); } } if (rb_data.is_valid()) { _debug_draw_cluster(rb); RENDER_TIMESTAMP("Tonemap"); _render_buffers_post_process_and_tonemap(p_render_data); } if (rb_data.is_valid()) { _render_buffers_debug_draw(p_render_data); if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_SDFGI && rb->has_custom_data(RB_SCOPE_SDFGI)) { Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); Vector view_rids; // SDFGI renders at internal resolution, need to check if our debug correctly supports outputting upscaled. Size2i size = rb->get_internal_size(); RID source_texture = rb->get_internal_texture(); for (uint32_t v = 0; v < rb->get_view_count(); v++) { view_rids.push_back(rb->get_internal_texture(v)); } sdfgi->debug_draw(p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform, size.x, size.y, rb->get_render_target(), source_texture, view_rids); } } } void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_render_data) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); Ref rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); Ref rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); ERR_FAIL_COND(rb_data.is_null()); RendererSceneRenderRD::_render_buffers_debug_draw(p_render_data); RID render_target = rb->get_render_target(); if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_SSAO && rb->has_texture(RB_SCOPE_SSAO, RB_FINAL)) { RID final = rb->get_texture_slice(RB_SCOPE_SSAO, RB_FINAL, 0, 0); Size2i rtsize = texture_storage->render_target_get_size(render_target); copy_effects->copy_to_fb_rect(final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, true); } if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_SSIL && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL)) { RID final = rb->get_texture_slice(RB_SCOPE_SSIL, RB_FINAL, 0, 0); Size2i rtsize = texture_storage->render_target_get_size(render_target); copy_effects->copy_to_fb_rect(final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); } if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) { Size2i rtsize = texture_storage->render_target_get_size(render_target); RID ambient_texture = rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT); RID reflection_texture = rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION); copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, rb->get_view_count() > 1); } } void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingServerTypes::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); ERR_FAIL_COND(!light_storage->owns_light_instance(p_light)); RID base = light_storage->light_instance_get_base_light(p_light); Rect2i atlas_rect; uint32_t atlas_size = 1; RID atlas_fb; bool reverse_cull_face = light_storage->light_get_reverse_cull_face_mode(base); bool using_dual_paraboloid = false; bool using_dual_paraboloid_flip = false; Vector2i dual_paraboloid_offset; RID render_fb; RID render_texture; float zfar; bool use_pancake = false; bool render_cubemap = false; bool finalize_cubemap = false; bool flip_y = false; Projection light_projection; Transform3D light_transform; if (light_storage->light_get_type(base) == RSE::LIGHT_DIRECTIONAL) { //set pssm stuff uint64_t last_scene_shadow_pass = light_storage->light_instance_get_shadow_pass(p_light); if (last_scene_shadow_pass != get_scene_pass()) { light_storage->light_instance_set_directional_rect(p_light, light_storage->get_directional_shadow_rect()); light_storage->directional_shadow_increase_current_light(); light_storage->light_instance_set_shadow_pass(p_light, get_scene_pass()); } use_pancake = light_storage->light_get_param(base, RSE::LIGHT_PARAM_SHADOW_PANCAKE_SIZE) > 0; light_projection = light_storage->light_instance_get_shadow_camera(p_light, p_pass); light_transform = light_storage->light_instance_get_shadow_transform(p_light, p_pass); atlas_rect = light_storage->light_instance_get_directional_rect(p_light); if (light_storage->light_directional_get_shadow_mode(base) == RSE::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { atlas_rect.size.width /= 2; atlas_rect.size.height /= 2; if (p_pass == 1) { atlas_rect.position.x += atlas_rect.size.width; } else if (p_pass == 2) { atlas_rect.position.y += atlas_rect.size.height; } else if (p_pass == 3) { atlas_rect.position += atlas_rect.size; } } else if (light_storage->light_directional_get_shadow_mode(base) == RSE::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { atlas_rect.size.height /= 2; if (p_pass == 0) { } else { atlas_rect.position.y += atlas_rect.size.height; } } float directional_shadow_size = light_storage->directional_shadow_get_size(); Rect2 atlas_rect_norm = atlas_rect; atlas_rect_norm.position /= directional_shadow_size; atlas_rect_norm.size /= directional_shadow_size; light_storage->light_instance_set_directional_shadow_atlas_rect(p_light, p_pass, atlas_rect_norm); zfar = RSG::light_storage->light_get_param(base, RSE::LIGHT_PARAM_RANGE); render_fb = light_storage->direction_shadow_get_fb(); render_texture = RID(); flip_y = true; } else { //set from shadow atlas ERR_FAIL_COND(!light_storage->owns_shadow_atlas(p_shadow_atlas)); ERR_FAIL_COND(!light_storage->shadow_atlas_owns_light_instance(p_shadow_atlas, p_light)); RSG::light_storage->shadow_atlas_update(p_shadow_atlas); uint32_t key = light_storage->shadow_atlas_get_light_instance_key(p_shadow_atlas, p_light); uint32_t quadrant = (key >> RendererRD::LightStorage::QUADRANT_SHIFT) & 0x3; uint32_t shadow = key & RendererRD::LightStorage::SHADOW_INDEX_MASK; uint32_t subdivision = light_storage->shadow_atlas_get_quadrant_subdivision(p_shadow_atlas, quadrant); ERR_FAIL_INDEX((int)shadow, light_storage->shadow_atlas_get_quadrant_shadow_size(p_shadow_atlas, quadrant)); uint32_t shadow_atlas_size = light_storage->shadow_atlas_get_size(p_shadow_atlas); uint32_t quadrant_size = shadow_atlas_size >> 1; atlas_rect.position.x = (quadrant & 1) * quadrant_size; atlas_rect.position.y = (quadrant >> 1) * quadrant_size; uint32_t shadow_size = (quadrant_size / subdivision); atlas_rect.position.x += (shadow % subdivision) * shadow_size; atlas_rect.position.y += (shadow / subdivision) * shadow_size; atlas_rect.size.width = shadow_size; atlas_rect.size.height = shadow_size; zfar = light_storage->light_get_param(base, RSE::LIGHT_PARAM_RANGE); if (light_storage->light_get_type(base) == RSE::LIGHT_OMNI) { bool wrap = (shadow + 1) % subdivision == 0; dual_paraboloid_offset = wrap ? Vector2i(1 - subdivision, 1) : Vector2i(1, 0); if (light_storage->light_omni_get_shadow_mode(base) == RSE::LIGHT_OMNI_SHADOW_CUBE) { render_texture = light_storage->get_cubemap(shadow_size / 2); render_fb = light_storage->get_cubemap_fb(shadow_size / 2, p_pass); light_projection = light_storage->light_instance_get_shadow_camera(p_light, p_pass); light_transform = light_storage->light_instance_get_shadow_transform(p_light, p_pass); render_cubemap = true; finalize_cubemap = p_pass == 5; atlas_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas); atlas_size = shadow_atlas_size; if (p_pass == 0) { _render_shadow_begin(); } } else { atlas_rect.position.x += 1; atlas_rect.position.y += 1; atlas_rect.size.x -= 2; atlas_rect.size.y -= 2; atlas_rect.position += p_pass * atlas_rect.size * dual_paraboloid_offset; light_projection = light_storage->light_instance_get_shadow_camera(p_light, 0); light_transform = light_storage->light_instance_get_shadow_transform(p_light, 0); using_dual_paraboloid = true; using_dual_paraboloid_flip = p_pass == 1; render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas); flip_y = true; } } else if (light_storage->light_get_type(base) == RSE::LIGHT_SPOT) { light_projection = light_storage->light_instance_get_shadow_camera(p_light, 0); light_transform = light_storage->light_instance_get_shadow_transform(p_light, 0); render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas); flip_y = true; } else if (light_storage->light_get_type(base) == RSE::LIGHT_AREA) { Vector2 area_size = light_storage->light_area_get_size(base); zfar = light_storage->light_get_param(base, RSE::LIGHT_PARAM_RANGE) + area_size.length() / 2.0; light_transform = light_storage->light_instance_get_shadow_transform(p_light, 0); light_projection = light_storage->light_instance_get_shadow_camera(p_light, 0); render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas); flip_y = true; using_dual_paraboloid = true; } } if (render_cubemap) { //rendering to cubemap _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size, p_main_cam_transform); if (finalize_cubemap) { _render_shadow_process(); _render_shadow_end(); //reblit Rect2 atlas_rect_norm = atlas_rect; atlas_rect_norm.position /= float(atlas_size); atlas_rect_norm.size /= float(atlas_size); copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), zfar, false); atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size; copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), zfar, true); //restore transform so it can be properly used light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0); } } else { //render shadow _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size, p_main_cam_transform); } } void RenderForwardClustered::_render_shadow_begin() { scene_state.shadow_passes.clear(); RD::get_singleton()->draw_command_begin_label("Shadow Setup"); _update_render_base_uniform_set(); render_list[RENDER_LIST_SECONDARY].clear(); // No need to reset scene_state.curr_gpu_ptr or scene_state.instance_buffer[RENDER_LIST_SECONDARY] // because _fill_instance_data will do that if it detects p_offset == 0u. } void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingServerTypes::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { SceneState::ShadowPass shadow_pass; RenderSceneDataRD scene_data; scene_data.flip_y = !p_flip_y; // Q: Why is this inverted? Do we assume flip in shadow logic? scene_data.cam_projection = p_projection; scene_data.cam_transform = p_transform; scene_data.view_projection[0] = p_projection; scene_data.z_far = p_zfar; scene_data.z_near = 0.0; scene_data.lod_distance_multiplier = p_lod_distance_multiplier; scene_data.dual_paraboloid_side = p_use_dp_flip ? -1 : 1; scene_data.opaque_prepass_threshold = 0.1f; scene_data.time = time; scene_data.time_step = time_step; scene_data.main_cam_transform = p_main_cam_transform; scene_data.shadow_pass = true; RenderDataRD render_data; render_data.scene_data = &scene_data; render_data.cluster_size = 1; render_data.cluster_max_elements = 32; render_data.instances = &p_instances; render_data.render_info = p_render_info; Size2i screen_size = RD::get_singleton()->framebuffer_get_size(p_framebuffer); Size2i viewport_size = p_rect.size; if (viewport_size == Size2()) { viewport_size = screen_size; } uint32_t uniform_buffer_index = _setup_environment(&render_data, true, screen_size, viewport_size, Color(), false, false, p_use_pancake); if (get_debug_draw_mode() == RSE::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { scene_data.screen_mesh_lod_threshold = 0.0; } else { scene_data.screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; } PassMode pass_mode = p_use_dp ? PASS_MODE_SHADOW_DP : PASS_MODE_SHADOW; uint32_t render_list_from = render_list[RENDER_LIST_SECONDARY].elements.size(); _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, false, false, false, true); uint32_t render_list_size = render_list[RENDER_LIST_SECONDARY].elements.size() - render_list_from; render_list[RENDER_LIST_SECONDARY].sort_by_key_range(render_list_from, render_list_size); _fill_instance_data(RENDER_LIST_SECONDARY, p_render_info ? p_render_info->info[RSE::VIEWPORT_RENDER_INFO_TYPE_SHADOW] : (int *)nullptr, render_list_from, render_list_size, false); { //regular forward for now bool flip_cull = p_use_dp_flip; if (p_flip_y) { flip_cull = !flip_cull; } if (p_reverse_cull_face) { flip_cull = !flip_cull; } shadow_pass.element_from = render_list_from; shadow_pass.element_count = render_list_size; shadow_pass.flip_cull = flip_cull; shadow_pass.pass_mode = pass_mode; shadow_pass.rp_uniform_set = RID(); //will be filled later when instance buffer is complete shadow_pass.screen_mesh_lod_threshold = scene_data.screen_mesh_lod_threshold; shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; shadow_pass.framebuffer = p_framebuffer; shadow_pass.clear_depth = p_begin || p_clear_region; shadow_pass.rect = p_rect; shadow_pass.uniform_buffer_index = uniform_buffer_index; scene_state.shadow_passes.push_back(shadow_pass); } } void RenderForwardClustered::_render_shadow_process() { RenderingDevice *rd = RenderingDevice::get_singleton(); if (scene_state.instance_buffer[RENDER_LIST_SECONDARY].get_size(0u) > 0u) { rd->buffer_flush(scene_state.instance_buffer[RENDER_LIST_SECONDARY]._get(0u)); } //render shadows one after the other, so this can be done un-barriered and the driver can optimize (as well as allow us to run compute at the same time) for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { //render passes need to be configured after instance buffer is done, since they need the latest version SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, false, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), shadow_pass.uniform_buffer_index, false); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::_render_shadow_end() { RD::get_singleton()->draw_command_begin_label("Shadow Render"); for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) { RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from); _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, shadow_pass.clear_depth ? RD::DRAW_CLEAR_DEPTH : RD::DRAW_DEFAULT_ALL, Vector(), 0.0f, 0, shadow_pass.rect); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) { RENDER_TIMESTAMP("Setup GPUParticlesCollisionHeightField3D"); RD::get_singleton()->draw_command_begin_label("Render Collider Heightfield"); RenderSceneDataRD scene_data; scene_data.flip_y = true; scene_data.cam_projection = p_cam_projection; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.z_near = 0.0; scene_data.z_far = p_cam_projection.get_z_far(); scene_data.dual_paraboloid_side = 0; scene_data.opaque_prepass_threshold = 0.0; scene_data.time = time; scene_data.time_step = time_step; scene_data.main_cam_transform = p_cam_transform; scene_data.shadow_pass = true; // Not a shadow pass, but should be treated like one. RenderDataRD render_data; render_data.scene_data = &scene_data; render_data.cluster_size = 1; render_data.cluster_max_elements = 32; render_data.instances = &p_instances; _update_render_base_uniform_set(); Size2i screen_size = RD::get_singleton()->framebuffer_get_size(p_fb); uint32_t uniform_buffer_index = _setup_environment(&render_data, true, screen_size, screen_size, Color(), false, false, false); PassMode pass_mode = PASS_MODE_SHADOW; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, false, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), uniform_buffer_index); RENDER_TIMESTAMP("Render Collider Heightfield"); { //regular forward for now RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, 0, true, false, rp_uniform_set); _render_list_with_draw_list(&render_list_params, p_fb, RD::DRAW_CLEAR_ALL); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.dual_paraboloid_side = 0; scene_data.material_uv2_mode = false; scene_data.opaque_prepass_threshold = 0.0f; scene_data.emissive_exposure_normalization = p_exposure_normalization; scene_data.time = time; scene_data.time_step = time_step; scene_data.main_cam_transform = p_cam_transform; RenderDataRD render_data; render_data.scene_data = &scene_data; render_data.cluster_size = 1; render_data.cluster_max_elements = 32; render_data.instances = &p_instances; scene_shader.enable_advanced_shader_group(); _update_render_base_uniform_set(); Size2i screen_size = RD::get_singleton()->framebuffer_get_size(p_framebuffer); Size2i viewport_size = p_region.size; if (viewport_size == Size2()) { viewport_size = screen_size; } uint32_t uniform_buffer_index = _setup_environment(&render_data, true, screen_size, viewport_size, Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, false, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), uniform_buffer_index); RENDER_TIMESTAMP("Render 3D Material"); { RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set); //regular forward for now Vector clear = { Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0) }; RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::_render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) { RENDER_TIMESTAMP("Setup Rendering UV2"); RD::get_singleton()->draw_command_begin_label("Render UV2"); RenderSceneDataRD scene_data; scene_data.dual_paraboloid_side = 0; scene_data.material_uv2_mode = true; scene_data.opaque_prepass_threshold = 0.0; scene_data.emissive_exposure_normalization = -1.0; RenderDataRD render_data; render_data.scene_data = &scene_data; render_data.cluster_size = 1; render_data.cluster_max_elements = 32; render_data.instances = &p_instances; scene_shader.enable_advanced_shader_group(); _update_render_base_uniform_set(); Size2i screen_size = RD::get_singleton()->framebuffer_get_size(p_framebuffer); Size2i viewport_size = p_region.size; if (viewport_size == Size2()) { viewport_size = screen_size; } uint32_t uniform_buffer_index = _setup_environment(&render_data, true, screen_size, viewport_size, Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, false, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), uniform_buffer_index); RENDER_TIMESTAMP("Render 3D Material"); { RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, true); //regular forward for now Vector clear = { Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0) }; RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); const int uv_offset_count = 9; static const Vector2 uv_offsets[uv_offset_count] = { Vector2(-1, 1), Vector2(1, 1), Vector2(1, -1), Vector2(-1, -1), Vector2(-1, 0), Vector2(1, 0), Vector2(0, -1), Vector2(0, 1), Vector2(0, 0), }; for (int i = 0; i < uv_offset_count; i++) { Vector2 ofs = uv_offsets[i]; ofs.x /= p_region.size.width; ofs.y /= p_region.size.height; render_list_params.uv_offset = ofs; _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); //first wireframe, for pseudo conservative } render_list_params.uv_offset = Vector2(); render_list_params.force_wireframe = false; _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); //second regular triangles RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::_render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { RENDER_TIMESTAMP("Render SDFGI"); RD::get_singleton()->draw_command_begin_label("Render SDFGI Voxel"); RenderSceneDataRD scene_data; RenderDataRD render_data; render_data.scene_data = &scene_data; render_data.cluster_size = 1; render_data.cluster_max_elements = 32; render_data.instances = &p_instances; _update_render_base_uniform_set(); // Indicate pipelines for SDFGI are required. global_pipeline_data_required.use_sdfgi = true; PassMode pass_mode = PASS_MODE_SDF; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); Vector3 half_size = p_bounds.size * 0.5; Vector3 center = p_bounds.position + half_size; //print_line("re-render " + p_from + " - " + p_size + " bounds " + p_bounds); for (int i = 0; i < 3; i++) { scene_state.ubo.sdf_offset[i] = p_from[i]; scene_state.ubo.sdf_size[i] = p_size[i]; } for (int i = 0; i < 3; i++) { Vector3 axis; axis[i] = 1.0; Vector3 up, right; int right_axis = (i + 1) % 3; int up_axis = (i + 2) % 3; up[up_axis] = 1.0; right[right_axis] = 1.0; Size2i fb_size; fb_size.x = p_size[right_axis]; fb_size.y = p_size[up_axis]; scene_data.cam_transform.origin = center + axis * half_size; scene_data.cam_transform.basis.set_column(0, right); scene_data.cam_transform.basis.set_column(1, up); scene_data.cam_transform.basis.set_column(2, axis); //print_line("pass: " + itos(i) + " xform " + scene_data.cam_transform); float h_size = half_size[right_axis]; float v_size = half_size[up_axis]; float d_size = half_size[i] * 2.0; scene_data.cam_projection.set_orthogonal(-h_size, h_size, -v_size, v_size, 0, d_size); //print_line("pass: " + itos(i) + " cam hsize: " + rtos(h_size) + " vsize: " + rtos(v_size) + " dsize " + rtos(d_size)); Transform3D to_bounds; to_bounds.origin = p_bounds.position; to_bounds.basis.scale(p_bounds.size); RendererRD::MaterialStorage::store_transform(to_bounds.affine_inverse() * scene_data.cam_transform, scene_state.ubo.sdf_to_bounds); scene_data.emissive_exposure_normalization = p_exposure_normalization; uint32_t uniform_buffer_index = _setup_environment(&render_data, true, fb_size, fb_size, Color()); RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), uniform_buffer_index); HashMap::Iterator E = sdfgi_framebuffer_size_cache.find(fb_size); if (!E) { RID fb = RD::get_singleton()->framebuffer_create_empty(fb_size); E = sdfgi_framebuffer_size_cache.insert(fb_size, fb); } RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false); _render_list_with_draw_list(&render_list_params, E->value); } RD::get_singleton()->draw_command_end_label(); } void RenderForwardClustered::base_uniforms_changed() { if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { RD::get_singleton()->free_rid(render_base_uniform_set); } render_base_uniform_set = RID(); } void RenderForwardClustered::_update_render_base_uniform_set() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != light_storage->lightmap_array_get_version())) { if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { RD::get_singleton()->free_rid(render_base_uniform_set); } lightmap_texture_array_version = light_storage->lightmap_array_get_version(); Vector uniforms; { RD::Uniform u; u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; u.append_id(scene_shader.shadow_sampler); uniforms.push_back(u); } { RD::Uniform u; u.binding = 3; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_omni_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_spot_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_area_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_reflection_probe_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_directional_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_buffer); uniforms.push_back(u); } { RD::Uniform u; u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_capture_buffer); uniforms.push_back(u); } { RD::Uniform u; u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); u.append_id(decal_atlas); uniforms.push_back(u); } { RD::Uniform u; u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_srgb(); u.append_id(decal_atlas); uniforms.push_back(u); } { RD::Uniform u; u.binding = 12; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::TextureStorage::get_singleton()->get_decal_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 13; u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 14; u.append_id(sdfgi_get_ubo()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 15; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; u.append_id(RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RSE::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RSE::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); uniforms.push_back(u); } { RD::Uniform u; u.binding = 16; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(best_fit_normal.texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 17; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(dfg_lut.texture); uniforms.push_back(u); } { // Lookup-table for Area Lights - Linearly transformed cosines (LTC) if (ltc.lut1_texture.is_null() || ltc.lut2_texture.is_null()) { Ref lut1_image; int dimensions = LTC_LUT_DIMENSIONS; int lut1_bytes = 4 * dimensions * dimensions; size_t lut1_size = lut1_bytes * 4; // float Vector lut1_data; lut1_data.resize(lut1_size); memcpy(lut1_data.ptrw(), LTC_LUT1, lut1_size); lut1_image = Image::create_from_data(dimensions, dimensions, false, Image::FORMAT_RGBAF, lut1_data); ltc.lut1_texture = RS::get_singleton()->texture_2d_create(lut1_image); int lut2_bytes = 4 * dimensions * dimensions; size_t lut2_size = lut2_bytes * 4; Ref lut2_image; Vector lut2_data; lut2_data.resize(lut2_size); memcpy(lut2_data.ptrw(), LTC_LUT2, lut2_size); lut2_image = Image::create_from_data(dimensions, dimensions, false, Image::FORMAT_RGBAF, lut2_data); ltc.lut2_texture = RS::get_singleton()->texture_2d_create(lut2_image); } } { RD::Uniform u; u.binding = 18; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(RendererRD::TextureStorage::get_singleton()->texture_get_rd_texture(ltc.lut1_texture)); uniforms.push_back(u); } { RD::Uniform u; u.binding = 19; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(RendererRD::TextureStorage::get_singleton()->texture_get_rd_texture(ltc.lut2_texture)); uniforms.push_back(u); } { RD::Uniform u; u.binding = 20; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID area_light_atlas = RendererRD::TextureStorage::get_singleton()->area_light_atlas_get_texture(); u.append_id(area_light_atlas); uniforms.push_back(u); } render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); } } RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, bool p_is_multiview, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, uint32_t p_uniform_buffer_index, bool p_use_directional_shadow_atlas) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); bool is_multiview = p_is_multiview; Ref rb; // handy for not having to fully type out p_render_data->render_buffers all the time... Ref rb_data; if (p_render_data && p_render_data->render_buffers.is_valid()) { rb = p_render_data->render_buffers; if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { // Our forward clustered custom data buffer will only be available when we're rendering our normal view. // This will not be available when rendering reflection probes. rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } } //default render buffer and scene state uniform set thread_local LocalVector uniforms; uniforms.clear(); { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(scene_state.uniform_buffers[p_uniform_buffer_index]); uniforms.push_back(u); } { RD::Uniform u; u.binding = 1; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(scene_state.implementation_uniform_buffers[p_uniform_buffer_index]); uniforms.push_back(u); } { RD::Uniform u; u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER_DYNAMIC; if (scene_state.instance_buffer[p_render_list].get_size(0u) == 0u) { // Any buffer will do since it's not used, so just create one. // We can't use scene_shader.default_vec4_xform_buffer because it's not dynamic. scene_state.instance_buffer[p_render_list].set_storage_size(0u, INSTANCE_DATA_BUFFER_MIN_SIZE * sizeof(SceneState::InstanceData)); scene_state.instance_buffer[p_render_list].prepare_for_upload(); } RID instance_buffer = scene_state.instance_buffer[p_render_list]._get(0u); u.append_id(instance_buffer); uniforms.push_back(u); } { RID radiance_texture; if (p_radiance_texture.is_valid()) { radiance_texture = p_radiance_texture; } else { radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_octmap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); } RD::Uniform u; u.binding = 3; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(radiance_texture); uniforms.push_back(u); } { RID ref_texture = (p_render_data && p_render_data->reflection_atlas.is_valid()) ? light_storage->reflection_atlas_get_texture(p_render_data->reflection_atlas) : RID(); RD::Uniform u; u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; if (ref_texture.is_valid()) { u.append_id(ref_texture); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK)); } uniforms.push_back(u); } { RD::Uniform u; u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture; if (p_render_data && p_render_data->shadow_atlas.is_valid()) { texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_render_data->shadow_atlas); } if (!texture.is_valid()) { texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); } u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; if (p_use_directional_shadow_atlas && RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture().is_valid()) { u.append_id(RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture()); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH)); } uniforms.push_back(u); } { Vector textures; textures.resize(scene_state.max_lightmaps * 2); RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); for (uint32_t i = 0; i < scene_state.max_lightmaps * 2; i++) { uint32_t current_lightmap_index = i < scene_state.max_lightmaps ? i : i - scene_state.max_lightmaps; if (p_render_data && current_lightmap_index < p_render_data->lightmaps->size()) { RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[current_lightmap_index]); RID texture; if (i < scene_state.max_lightmaps) { // Lightmap texture = light_storage->lightmap_get_texture(base); } else { // Shadowmask texture = light_storage->shadowmask_get_texture(base); } if (texture.is_valid()) { RID rd_texture = texture_storage->texture_get_rd_texture(texture); textures.write[i] = rd_texture; continue; } } textures.write[i] = default_tex; } RD::Uniform u(RD::UNIFORM_TYPE_TEXTURE, 7, textures); uniforms.push_back(u); } { RD::Uniform u; u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); for (int i = 0; i < MAX_VOXEL_GI_INSTANCESS; i++) { if (p_render_data && i < (int)p_render_data->voxel_gi_instances->size()) { RID tex = gi.voxel_gi_instance_get_texture((*p_render_data->voxel_gi_instances)[i]); if (!tex.is_valid()) { tex = default_tex; } u.append_id(tex); } else { u.append_id(default_tex); } } uniforms.push_back(u); } { RD::Uniform u; u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; RID cb = (p_render_data && p_render_data->cluster_buffer.is_valid()) ? p_render_data->cluster_buffer : scene_shader.default_vec4_xform_buffer; u.append_id(cb); uniforms.push_back(u); } { RD::Uniform u; u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; RID sampler; switch (decals_get_filter()) { case RSE::DECAL_FILTER_NEAREST: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_NEAREST_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; } u.append_id(sampler); uniforms.push_back(u); } { RD::Uniform u; u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; RID sampler; switch (light_projectors_get_filter()) { case RSE::LIGHT_PROJECTOR_FILTER_NEAREST: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; } u.append_id(sampler); uniforms.push_back(u); } p_samplers.append_uniforms(uniforms, 12); { RD::Uniform u; u.binding = 24; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture; if (rb.is_valid() && rb->has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH)) { texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH); } else { texture = texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_DEPTH : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); } u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 25; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID bbt = rb_data.is_valid() ? rb->get_back_buffer_texture() : RID(); RID texture = bbt.is_valid() ? bbt : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 26; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb_data->has_normal_roughness() ? rb_data->get_normal_roughness() : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_NORMAL : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_NORMAL); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 27; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID aot = rb.is_valid() && rb->has_texture(RB_SCOPE_SSAO, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSAO, RB_FINAL) : RID(); RID texture = aot.is_valid() ? aot : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 28; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 29; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 30; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); t = sdfgi->lightprobe_texture; } if (t.is_null()) { t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); } u.append_id(t); uniforms.push_back(u); } { RD::Uniform u; u.binding = 31; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); t = sdfgi->occlusion_texture; } if (t.is_null()) { t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); } u.append_id(t); uniforms.push_back(u); } { RD::Uniform u; u.binding = 32; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; RID voxel_gi; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_GI)) { Ref rbgi = rb->get_custom_data(RB_SCOPE_GI); voxel_gi = rbgi->get_voxel_gi_buffer(); } u.append_id(voxel_gi.is_valid() ? voxel_gi : render_buffers_get_default_voxel_gi_buffer()); uniforms.push_back(u); } { RD::Uniform u; u.binding = 33; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID vfog; if (rb_data.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) { Ref fog = rb->get_custom_data(RB_SCOPE_FOG); vfog = fog->fog_map; if (vfog.is_null()) { vfog = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); } } else { vfog = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); } u.append_id(vfog); uniforms.push_back(u); } { RD::Uniform u; u.binding = 34; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssil = rb.is_valid() && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSIL, RB_FINAL) : RID(); RID texture = ssil.is_valid() ? ssil : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 35; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssr; if (rb_data.is_valid()) { if (rb_data->ss_effects_data.ssr.half_size) { if (rb->has_texture(RB_SCOPE_SSR, RB_FINAL)) { ssr = rb->get_texture(RB_SCOPE_SSR, RB_FINAL); } } else { if (rb->has_texture(RB_SCOPE_SSR, RB_SSR)) { ssr = rb->get_texture(RB_SCOPE_SSR, RB_SSR); } } } RID texture = ssr.is_valid() ? ssr : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 36; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssr_mip_level = (rb_data.is_valid() && !rb_data->ss_effects_data.ssr.half_size && rb->has_texture(RB_SCOPE_SSR, RB_MIP_LEVEL)) ? rb->get_texture(RB_SCOPE_SSR, RB_MIP_LEVEL) : RID(); RID texture = ssr_mip_level.is_valid() ? ssr_mip_level : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 37; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID sscs; if (rb_data.is_valid()) { if (rb->has_texture(RB_SCOPE_SSCS, RB_SSCS)) { sscs = rb->get_texture(RB_SCOPE_SSCS, RB_SSCS); } } RID texture = sscs.is_valid() ? sscs : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK); u.append_id(texture); uniforms.push_back(u); } #ifdef MODULE_TEXTURE_STREAMING_ENABLED { RD::Uniform u; u.binding = 38; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; RID instance_buffer = TextureStreaming::get_singleton()->feedback_buffer_get_uniform_rid(); if (instance_buffer.is_null()) { instance_buffer = scene_shader.default_material_feedback_buffer; } u.append_id(instance_buffer); uniforms.push_back(u); } #endif // MODULE_TEXTURE_STREAMING_ENABLED return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.get_default_shader_rd(is_multiview), RENDER_PASS_UNIFORM_SET, uniforms); } RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, uint32_t p_uniform_buffer_index) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); thread_local LocalVector uniforms; uniforms.clear(); { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(scene_state.uniform_buffers[p_uniform_buffer_index]); uniforms.push_back(u); } { RD::Uniform u; u.binding = 1; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(scene_state.implementation_uniform_buffers[p_uniform_buffer_index]); uniforms.push_back(u); } { RD::Uniform u; u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER_DYNAMIC; if (scene_state.instance_buffer[RENDER_LIST_SECONDARY].get_size(0u) == 0u) { // Any buffer will do since it's not used, so just create one. // We can't use scene_shader.default_vec4_xform_buffer because it's not dynamic. scene_state.instance_buffer[RENDER_LIST_SECONDARY].set_storage_size(0u, INSTANCE_DATA_BUFFER_MIN_SIZE * sizeof(SceneState::InstanceData)); scene_state.instance_buffer[RENDER_LIST_SECONDARY].prepare_for_upload(); } RID instance_buffer = scene_state.instance_buffer[RENDER_LIST_SECONDARY]._get(0u); u.append_id(instance_buffer); uniforms.push_back(u); } { // No radiance texture. RID radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_octmap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); RD::Uniform u; u.binding = 3; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(radiance_texture); uniforms.push_back(u); } { // No reflection atlas. RID ref_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK); RD::Uniform u; u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.append_id(ref_texture); uniforms.push_back(u); } { // No shadow atlas. RD::Uniform u; u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); u.append_id(texture); uniforms.push_back(u); } { // No directional shadow atlas. RD::Uniform u; u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); u.append_id(texture); uniforms.push_back(u); } { // No Lightmaps RD::Uniform u; u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); for (uint32_t i = 0; i < scene_state.max_lightmaps * 2; i++) { u.append_id(default_tex); } uniforms.push_back(u); } { // No VoxelGIs RD::Uniform u; u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); for (int i = 0; i < MAX_VOXEL_GI_INSTANCESS; i++) { u.append_id(default_tex); } uniforms.push_back(u); } { RD::Uniform u; u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; RID cb = scene_shader.default_vec4_xform_buffer; u.append_id(cb); uniforms.push_back(u); } { RD::Uniform u; u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; RID sampler; switch (decals_get_filter()) { case RSE::DECAL_FILTER_NEAREST: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_NEAREST_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; } u.append_id(sampler); uniforms.push_back(u); } { RD::Uniform u; u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; RID sampler; switch (light_projectors_get_filter()) { case RSE::LIGHT_PROJECTOR_FILTER_NEAREST: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; case RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = p_samplers.get_sampler(RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RSE::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; } u.append_id(sampler); uniforms.push_back(u); } p_samplers.append_uniforms(uniforms, 12); // actual sdfgi stuff { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 24; u.append_id(p_albedo_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 25; u.append_id(p_emission_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 26; u.append_id(p_emission_aniso_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 27; u.append_id(p_geom_facing_texture); uniforms.push_back(u); } { RD::Uniform u; u.binding = 38; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; RID instance_buffer = scene_shader.default_material_feedback_buffer; u.append_id(instance_buffer); uniforms.push_back(u); } if (scene_shader.default_shader_sdfgi_rd.is_null()) { // The variant for SDF from the default material should only be retrieved when SDFGI is required. ERR_FAIL_NULL_V(scene_shader.default_material_shader_ptr, RID()); scene_shader.enable_advanced_shader_group(); scene_shader.default_shader_sdfgi_rd = scene_shader.default_material_shader_ptr->get_shader_variant(SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF, 0, true); ERR_FAIL_COND_V(scene_shader.default_shader_sdfgi_rd.is_null(), RID()); } return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_sdfgi_rd, RENDER_PASS_UNIFORM_SET, uniforms); } RID RenderForwardClustered::_render_buffers_get_normal_texture(Ref p_render_buffers) { Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); return rb_data->get_normal_roughness(); } RID RenderForwardClustered::_render_buffers_get_velocity_texture(Ref p_render_buffers) { return p_render_buffers->get_velocity_buffer(false); } void RenderForwardClustered::environment_set_ssao_quality(RSE::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_quality < RSE::EnvironmentSSAOQuality::ENV_SSAO_QUALITY_VERY_LOW || p_quality > RSE::EnvironmentSSAOQuality::ENV_SSAO_QUALITY_ULTRA); ss_effects->ssao_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to); } void RenderForwardClustered::environment_set_ssil_quality(RSE::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_quality < RSE::EnvironmentSSILQuality::ENV_SSIL_QUALITY_VERY_LOW || p_quality > RSE::EnvironmentSSILQuality::ENV_SSIL_QUALITY_ULTRA); ss_effects->ssil_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to); } void RenderForwardClustered::environment_set_ssr_half_size(bool p_half_size) { ERR_FAIL_NULL(ss_effects); ss_effects->ssr_set_half_size(p_half_size); } void RenderForwardClustered::environment_set_ssr_roughness_quality(RSE::EnvironmentSSRRoughnessQuality p_quality) { WARN_PRINT_ONCE("environment_set_ssr_roughness_quality has been deprecated and no longer does anything."); } void RenderForwardClustered::sub_surface_scattering_set_quality(RSE::SubSurfaceScatteringQuality p_quality) { ERR_FAIL_NULL(ss_effects); ERR_FAIL_COND(p_quality < RSE::SubSurfaceScatteringQuality::SUB_SURFACE_SCATTERING_QUALITY_DISABLED || p_quality > RSE::SubSurfaceScatteringQuality::SUB_SURFACE_SCATTERING_QUALITY_HIGH); ss_effects->sss_set_quality(p_quality); } void RenderForwardClustered::sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) { ERR_FAIL_NULL(ss_effects); ss_effects->sss_set_scale(p_scale, p_depth_scale); } RenderForwardClustered *RenderForwardClustered::singleton = nullptr; void RenderForwardClustered::sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) { Ref rb = p_render_buffers; ERR_FAIL_COND(rb.is_null()); Ref sdfgi; if (rb->has_custom_data(RB_SCOPE_SDFGI)) { sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); } bool needs_sdfgi = p_environment.is_valid() && environment_get_sdfgi_enabled(p_environment); bool needs_reset = sdfgi.is_valid() ? sdfgi->version != gi.sdfgi_current_version : false; if (!needs_sdfgi || needs_reset) { if (sdfgi.is_valid()) { // delete it sdfgi.unref(); rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } if (!needs_sdfgi) { return; } } // Ensure advanced shaders are available if SDFGI is used. // Call here as this is the first entry point for SDFGI. scene_shader.enable_advanced_shader_group(); static const uint32_t history_frames_to_converge[RSE::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge]; if (sdfgi.is_valid() && (sdfgi->num_cascades != environment_get_sdfgi_cascades(p_environment) || sdfgi->min_cell_size != environment_get_sdfgi_min_cell_size(p_environment) || requested_history_size != sdfgi->history_size || sdfgi->uses_occlusion != environment_get_sdfgi_use_occlusion(p_environment) || sdfgi->y_scale_mode != environment_get_sdfgi_y_scale(p_environment))) { //configuration changed, erase sdfgi.unref(); rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } if (sdfgi.is_null()) { // re-create sdfgi = gi.create_sdfgi(p_environment, p_world_position, requested_history_size); rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } else { //check for updates sdfgi->update(p_environment, p_world_position); } } int RenderForwardClustered::sdfgi_get_pending_region_count(const Ref &p_render_buffers) const { Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), 0); if (!rb->has_custom_data(RB_SCOPE_SDFGI)) { return 0; } Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); int dirty_count = 0; for (const RendererRD::GI::SDFGI::Cascade &c : sdfgi->cascades) { if (c.dirty_regions == RendererRD::GI::SDFGI::Cascade::DIRTY_ALL) { dirty_count++; } else { for (int j = 0; j < 3; j++) { if (c.dirty_regions[j] != 0) { dirty_count++; } } } } return dirty_count; } AABB RenderForwardClustered::sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), AABB()); Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); ERR_FAIL_COND_V(sdfgi.is_null(), AABB()); int c = sdfgi->get_pending_region_data(p_region, from, size, bounds); ERR_FAIL_COND_V(c == -1, AABB()); return bounds; } uint32_t RenderForwardClustered::sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), -1); Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); ERR_FAIL_COND_V(sdfgi.is_null(), -1); return sdfgi->get_pending_region_data(p_region, from, size, bounds); } void RenderForwardClustered::GeometryInstanceForwardClustered::_mark_dirty() { if (dirty_list_element.in_list()) { return; } //clear surface caches GeometryInstanceSurfaceDataCache *surf = surface_caches; while (surf) { GeometryInstanceSurfaceDataCache *next = surf->next; RenderForwardClustered::get_singleton()->geometry_instance_surface_alloc.free(surf); surf = next; } surface_caches = nullptr; RenderForwardClustered::get_singleton()->geometry_instance_dirty_list.add(&dirty_list_element); } void RenderForwardClustered::_update_global_pipeline_data_requirements_from_project() { const int msaa_3d_mode = GLOBAL_GET_CACHED(int, "rendering/anti_aliasing/quality/msaa_3d"); const bool directional_shadow_16_bits = GLOBAL_GET_CACHED(bool, "rendering/lights_and_shadows/directional_shadow/16_bits"); const bool positional_shadow_16_bits = GLOBAL_GET_CACHED(bool, "rendering/lights_and_shadows/positional_shadow/atlas_16_bits"); global_pipeline_data_required.use_16_bit_shadows = directional_shadow_16_bits || positional_shadow_16_bits; global_pipeline_data_required.use_32_bit_shadows = !directional_shadow_16_bits || !positional_shadow_16_bits; global_pipeline_data_required.texture_samples = RenderSceneBuffersRD::msaa_to_samples(RSE::ViewportMSAA(msaa_3d_mode)); } void RenderForwardClustered::_update_global_pipeline_data_requirements_from_light_storage() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); global_pipeline_data_required.use_shadow_cubemaps = light_storage->get_shadow_cubemaps_used(); global_pipeline_data_required.use_shadow_dual_paraboloid = light_storage->get_shadow_dual_paraboloid_used(); } void RenderForwardClustered::_geometry_instance_add_surface_with_material(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); uint32_t flags = 0; if (p_material->shader_data->uses_sss) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING; global_surface_data.sss_used = true; } if (p_material->shader_data->uses_screen_texture) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SCREEN_TEXTURE; global_surface_data.screen_texture_used = true; } if (p_material->shader_data->uses_depth_texture) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DEPTH_TEXTURE; global_surface_data.depth_texture_used = true; } if (p_material->shader_data->uses_normal_texture) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_NORMAL_TEXTURE; global_surface_data.normal_texture_used = true; } if (ginstance->data->cast_double_sided_shadows) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS; } if (p_material->shader_data->stencil_enabled) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_STENCIL; } if (p_material->shader_data->uses_alpha_pass()) { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA; if (p_material->shader_data->uses_depth_in_alpha_pass()) { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH; flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW; } } else { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE; flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH; flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW; } if (p_material->shader_data->uses_particle_trails) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS; } if (p_material->shader_data->is_animated()) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_MOTION_VECTOR; } if (p_material->shader_data->stencil_enabled) { if (p_material->shader_data->stencil_flags & SceneShaderForwardClustered::ShaderData::STENCIL_FLAG_READ) { // Stencil materials which read from the stencil buffer must be in the alpha pass. // This is critical to preserve compatibility once we'll have the compositor. if (!(flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path)); } } } SceneShaderForwardClustered::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (p_material->shader_data->uses_shared_shadow_material()) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL; material_shadow = static_cast(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh); if (shadow_mesh.is_valid()) { surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, p_surface); } } else { material_shadow = p_material; } GeometryInstanceSurfaceDataCache *sdcache = geometry_instance_surface_alloc.alloc(); sdcache->flags = flags; sdcache->shader = p_material->shader_data; sdcache->material = p_material; sdcache->material_uniform_set = p_material->uniform_set; sdcache->surface = mesh_storage->mesh_get_surface(p_mesh, p_surface); sdcache->primitive = mesh_storage->mesh_surface_get_primitive(sdcache->surface); sdcache->surface_index = p_surface; if (ginstance->data->dirty_dependencies) { RSG::utilities->base_update_dependency(p_mesh, &ginstance->data->dependency_tracker); } //shadow sdcache->shader_shadow = material_shadow->shader_data; sdcache->material_uniform_set_shadow = material_shadow->uniform_set; sdcache->surface_shadow = surface_shadow ? surface_shadow : sdcache->surface; sdcache->owner = ginstance; sdcache->next = ginstance->surface_caches; ginstance->surface_caches = sdcache; //sortkey sdcache->sort.sort_key1 = 0; sdcache->sort.sort_key2 = 0; sdcache->sort.surface_index = p_surface; sdcache->sort.material_id_hi = (p_material_id & 0xFF000000) >> 24; sdcache->sort.material_id_lo = (p_material_id & 0x00FFFFFF); sdcache->sort.shader_id = p_shader_id; sdcache->sort.geometry_id = p_mesh.get_local_index(); //only meshes can repeat anyway sdcache->sort.uses_forward_gi = ginstance->can_sdfgi; sdcache->sort.priority = p_material->priority; sdcache->sort.uses_projector = ginstance->using_projectors; sdcache->sort.uses_softshadow = ginstance->using_softshadows; uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); if (p_material->shader_data->uses_tangent && !p_material->shader_data->writes_tangent && !(format & RSE::ARRAY_FORMAT_TANGENT)) { String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; String mesh_path = mesh_storage->mesh_get_path(p_mesh).is_empty() ? "" : "(" + mesh_storage->mesh_get_path(p_mesh) + ")"; WARN_PRINT_ED(vformat("Attempting to use a shader %s that requires tangents with a mesh %s that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool).", shader_path, mesh_path)); } #if PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION if (!sdcache->compilation_dirty_element.in_list()) { geometry_surface_compilation_dirty_list.add(&sdcache->compilation_dirty_element); } if (!sdcache->compilation_all_element.in_list()) { geometry_surface_compilation_all_list.add(&sdcache->compilation_all_element); } #endif } void RenderForwardClustered::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh) { SceneShaderForwardClustered::MaterialData *material = p_material; RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); _geometry_instance_add_surface_with_material(ginstance, p_surface, material, p_mat_src.get_local_index(), material_storage->material_get_shader_id(p_mat_src), p_mesh); while (material->next_pass.is_valid()) { RID next_pass = material->next_pass; material = static_cast(material_storage->material_get_data(next_pass, RendererRD::MaterialStorage::SHADER_TYPE_3D)); if (!material || !material->shader_data->is_valid()) { break; } if (ginstance->data->dirty_dependencies) { material_storage->material_update_dependency(next_pass, &ginstance->data->dependency_tracker); } _geometry_instance_add_surface_with_material(ginstance, p_surface, material, next_pass.get_local_index(), material_storage->material_get_shader_id(next_pass), p_mesh); } } void RenderForwardClustered::_geometry_instance_add_surface(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, RID p_material, RID p_mesh) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RID m_src; m_src = ginstance->data->material_override.is_valid() ? ginstance->data->material_override : p_material; SceneShaderForwardClustered::MaterialData *material = nullptr; if (m_src.is_valid()) { material = static_cast(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D)); if (!material || !material->shader_data->is_valid()) { material = nullptr; } } if (material) { if (ginstance->data->dirty_dependencies) { material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker); } } else { material = static_cast(material_storage->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); m_src = scene_shader.default_material; } ERR_FAIL_NULL(material); _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material, m_src, p_mesh); if (ginstance->data->material_overlay.is_valid()) { m_src = ginstance->data->material_overlay; material = static_cast(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D)); if (material && material->shader_data->is_valid()) { if (ginstance->data->dirty_dependencies) { material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker); } _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material, m_src, p_mesh); } } } void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p_geometry_instance) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); GeometryInstanceForwardClustered *ginstance = static_cast(p_geometry_instance); if (ginstance->data->dirty_dependencies) { ginstance->data->dependency_tracker.update_begin(); } //add geometry for drawing switch (ginstance->data->base_type) { case RSE::INSTANCE_MESH: { const RID *materials = nullptr; uint32_t surface_count; RID mesh = ginstance->data->base; materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); if (materials) { //if no materials, no surfaces. const RID *inst_materials = ginstance->data->surface_materials.ptr(); uint32_t surf_mat_count = ginstance->data->surface_materials.size(); for (uint32_t j = 0; j < surface_count; j++) { RID material = (j < surf_mat_count && inst_materials[j].is_valid()) ? inst_materials[j] : materials[j]; _geometry_instance_add_surface(ginstance, j, material, mesh); } } ginstance->instance_count = 1; } break; case RSE::INSTANCE_MULTIMESH: { RID mesh = mesh_storage->multimesh_get_mesh(ginstance->data->base); if (mesh.is_valid()) { const RID *materials = nullptr; uint32_t surface_count; materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); if (materials) { for (uint32_t j = 0; j < surface_count; j++) { _geometry_instance_add_surface(ginstance, j, materials[j], mesh); } } ginstance->instance_count = mesh_storage->multimesh_get_instances_to_draw(ginstance->data->base); } } break; #if 0 case RSE::INSTANCE_IMMEDIATE: { RasterizerStorageGLES3::Immediate *immediate = storage->immediate_owner.get_or_null(inst->base); ERR_CONTINUE(!immediate); _add_geometry(immediate, inst, nullptr, -1, p_depth_pass, p_shadow_pass); } break; #endif case RSE::INSTANCE_PARTICLES: { int draw_passes = particles_storage->particles_get_draw_passes(ginstance->data->base); for (int j = 0; j < draw_passes; j++) { RID mesh = particles_storage->particles_get_draw_pass_mesh(ginstance->data->base, j); if (!mesh.is_valid()) { continue; } const RID *materials = nullptr; uint32_t surface_count; materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); if (materials) { for (uint32_t k = 0; k < surface_count; k++) { _geometry_instance_add_surface(ginstance, k, materials[k], mesh); } } } ginstance->instance_count = particles_storage->particles_get_amount(ginstance->data->base, ginstance->trail_steps); } break; default: { } } //Fill push constant ginstance->base_flags = 0; bool store_transform = true; if (ginstance->data->base_type == RSE::INSTANCE_MULTIMESH) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RSE::MULTIMESH_TRANSFORM_2D) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; } if (mesh_storage->multimesh_uses_colors(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; } if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; } if (mesh_storage->multimesh_uses_indirect(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT; } ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); } else if (ginstance->data->base_type == RSE::INSTANCE_PARTICLES) { ginstance->base_flags |= INSTANCE_DATA_FLAG_PARTICLES; ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; //for particles, stride is the trail size ginstance->base_flags |= (ginstance->trail_steps << INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT); if (!particles_storage->particles_is_using_local_coords(ginstance->data->base)) { store_transform = false; } if (particles_storage->particles_get_frame_counter(ginstance->data->base) == 0) { // Particles haven't been cleared or updated, update once now to ensure they are ready to render. particles_storage->update_particles(); } ginstance->transforms_uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); if (ginstance->data->dirty_dependencies) { particles_storage->particles_update_dependency(ginstance->data->base, &ginstance->data->dependency_tracker); } } else if (ginstance->data->base_type == RSE::INSTANCE_MESH) { if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); if (ginstance->data->dirty_dependencies) { mesh_storage->skeleton_update_dependency(ginstance->data->skeleton, &ginstance->data->dependency_tracker); } } else { ginstance->transforms_uniform_set = RID(); } } ginstance->store_transform_cache = store_transform; ginstance->can_sdfgi = false; if (!RendererRD::LightStorage::get_singleton()->lightmap_instance_is_valid(ginstance->lightmap_instance)) { if (ginstance->voxel_gi_instances[0].is_null() && (ginstance->data->use_baked_light || ginstance->data->use_dynamic_gi)) { ginstance->can_sdfgi = true; } } if (ginstance->data->dirty_dependencies) { ginstance->data->dependency_tracker.update_end(); ginstance->data->dirty_dependencies = false; } ginstance->dirty_list_element.remove_from_list(); } static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::DataFormat p_color_format, bool p_can_be_storage, RD::TextureSamples p_samples, bool p_specular, bool p_velocity, uint32_t p_view_count) { const bool multisampling = p_samples > RD::TEXTURE_SAMPLES_1; RD::AttachmentFormat attachment; attachment.samples = p_samples; RD::AttachmentFormat unused_attachment; unused_attachment.usage_flags = RD::AttachmentFormat::UNUSED_ATTACHMENT; thread_local Vector attachments; attachments.clear(); // Color attachment. attachment.format = p_color_format; attachment.usage_flags = RenderSceneBuffersRD::get_color_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); if (p_specular) { attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_format(); attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); } else { attachments.push_back(unused_attachment); } if (p_velocity) { attachment.format = RenderSceneBuffersRD::get_velocity_format(); attachment.usage_flags = RenderSceneBuffersRD::get_velocity_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); } else { attachments.push_back(unused_attachment); } // Depth attachment. attachment.format = RenderSceneBuffersRD::get_depth_format(false, multisampling, p_can_be_storage); attachment.usage_flags = RenderSceneBuffersRD::get_depth_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); thread_local Vector passes; passes.resize(1); passes.ptrw()[0].color_attachments.resize(attachments.size() - 1); int *color_attachments = passes.ptrw()[0].color_attachments.ptrw(); for (int64_t i = 0; i < attachments.size() - 1; i++) { color_attachments[i] = (attachments[i].usage_flags == RD::AttachmentFormat::UNUSED_ATTACHMENT) ? RD::ATTACHMENT_UNUSED : i; } passes.ptrw()[0].depth_attachment = attachments.size() - 1; return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count); } static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline(bool p_storage) { RD::AttachmentFormat attachment; thread_local Vector attachments; attachments.clear(); attachment.format = RendererRD::LightStorage::get_reflection_probe_color_format(); attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits(p_storage); attachments.push_back(attachment); attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format(); attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_depth_usage_bits(); attachments.push_back(attachment); return RD::get_singleton()->framebuffer_format_create(attachments); } static RD::FramebufferFormatID _get_depth_framebuffer_format_for_pipeline(bool p_can_be_storage, RD::TextureSamples p_samples, bool p_normal_roughness, bool p_voxelgi) { const bool multisampling = p_samples > RD::TEXTURE_SAMPLES_1; RD::AttachmentFormat attachment; attachment.samples = p_samples; thread_local LocalVector attachments; attachments.clear(); attachment.format = RenderSceneBuffersRD::get_depth_format(false, multisampling, p_can_be_storage); attachment.usage_flags = RenderSceneBuffersRD::get_depth_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); if (p_normal_roughness) { attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_format(); attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); } if (p_voxelgi) { attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_format(); attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_usage_bits(false, multisampling, p_can_be_storage); attachments.push_back(attachment); } thread_local Vector passes; passes.resize(1); passes.ptrw()[0].color_attachments.resize(attachments.size() - 1); int *color_attachments = passes.ptrw()[0].color_attachments.ptrw(); for (int64_t i = 1; i < attachments.size(); i++) { color_attachments[i - 1] = (attachments[i].usage_flags == RD::AttachmentFormat::UNUSED_ATTACHMENT) ? RD::ATTACHMENT_UNUSED : i; } passes.ptrw()[0].depth_attachment = 0; return RD::get_singleton()->framebuffer_format_create_multipass(Vector(attachments), passes); } static RD::FramebufferFormatID _get_shadow_cubemap_framebuffer_format_for_pipeline() { thread_local LocalVector attachments; attachments.clear(); RD::AttachmentFormat attachment; attachment.format = RendererRD::LightStorage::get_cubemap_depth_format(); attachment.usage_flags = RendererRD::LightStorage::get_cubemap_depth_usage_bits(); attachments.push_back(attachment); return RD::get_singleton()->framebuffer_format_create(Vector(attachments)); } static RD::FramebufferFormatID _get_shadow_atlas_framebuffer_format_for_pipeline(bool p_use_16_bits) { thread_local LocalVector attachments; attachments.clear(); RD::AttachmentFormat attachment; attachment.format = RendererRD::LightStorage::get_shadow_atlas_depth_format(p_use_16_bits); attachment.usage_flags = RendererRD::LightStorage::get_shadow_atlas_depth_usage_bits(); attachments.push_back(attachment); return RD::get_singleton()->framebuffer_format_create(Vector(attachments)); } static RD::FramebufferFormatID _get_reflection_probe_depth_framebuffer_format_for_pipeline() { thread_local LocalVector attachments; attachments.clear(); RD::AttachmentFormat attachment; attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format(); attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_depth_usage_bits(); attachments.push_back(attachment); return RD::get_singleton()->framebuffer_format_create(Vector(attachments)); } void RenderForwardClustered::_mesh_compile_pipeline_for_surface(SceneShaderForwardClustered::ShaderData *p_shader, void *p_mesh_surface, bool p_ubershader, bool p_instanced_surface, RSE::PipelineSource p_source, SceneShaderForwardClustered::ShaderData::PipelineKey &r_pipeline_key, Vector *r_pipeline_pairs) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); uint64_t input_mask = p_shader->get_vertex_input_mask(r_pipeline_key.version, r_pipeline_key.color_pass_flags, p_ubershader); bool pipeline_motion_vectors = r_pipeline_key.color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS; bool emulate_point_size = p_shader->uses_point_size && scene_shader.emulate_point_size; r_pipeline_key.vertex_format_id = mesh_storage->mesh_surface_get_vertex_format(p_mesh_surface, input_mask, p_instanced_surface, pipeline_motion_vectors, emulate_point_size); r_pipeline_key.ubershader = p_ubershader; p_shader->pipeline_hash_map.compile_pipeline(r_pipeline_key, r_pipeline_key.hash(), p_source, p_ubershader); if (r_pipeline_pairs != nullptr) { r_pipeline_pairs->push_back({ p_shader, r_pipeline_key }); } } void RenderForwardClustered::_mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RSE::PipelineSource p_source, Vector *r_pipeline_pairs) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); bool octmap_use_storage = !copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP); // Retrieve from the scene shader which groups are currently enabled. const bool multiview_enabled = p_global.use_multiview && scene_shader.is_multiview_shader_group_enabled(); const RD::DataFormat buffers_color_format = _render_buffers_get_preferred_color_format(); const bool buffers_can_be_storage = _render_buffers_can_be_storage(); // Set the attributes common to all pipelines. SceneShaderForwardClustered::ShaderData::PipelineKey pipeline_key; pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED; pipeline_key.primitive_type = mesh_storage->mesh_surface_get_primitive(p_surface.mesh_surface); pipeline_key.wireframe = false; // Grab the shader and surface used for most passes. const uint32_t multiview_iterations = multiview_enabled ? 2 : 1; const uint32_t lightmap_iterations = p_global.use_lightmaps && p_surface.can_use_lightmap ? 2 : 1; const uint32_t alpha_iterations = p_surface.uses_transparent ? 2 : 1; for (uint32_t multiview = 0; multiview < multiview_iterations; multiview++) { for (uint32_t lightmap = 0; lightmap < lightmap_iterations; lightmap++) { for (uint32_t alpha = p_surface.uses_opaque ? 0 : 1; alpha < alpha_iterations; alpha++) { // Generate all the possible variants used during the color pass. pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_COLOR_PASS; pipeline_key.color_pass_flags = 0; if (lightmap) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_LIGHTMAP; } if (alpha) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_TRANSPARENT; } if (multiview) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW; } else if (p_global.use_reflection_probes) { // Reflection probe can't be rendered in multiview. pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline(octmap_use_storage); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } // View count is assumed to be 2 as the configuration is dependent on the viewport. It's likely a safe assumption for stereo rendering. uint32_t view_count = multiview ? 2 : 1; pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false, view_count); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); // Generate all the possible variants used during the advanced color passes. const uint32_t separate_specular_iterations = p_global.use_separate_specular ? 2 : 1; const uint32_t motion_vectors_iterations = p_global.use_motion_vectors ? 2 : 1; uint32_t base_color_pass_flags = pipeline_key.color_pass_flags; for (uint32_t separate_specular = 0; separate_specular < separate_specular_iterations; separate_specular++) { for (uint32_t motion_vectors = 0; motion_vectors < motion_vectors_iterations; motion_vectors++) { if (!separate_specular && !motion_vectors) { // This case was already generated. continue; } pipeline_key.color_pass_flags = base_color_pass_flags; if (separate_specular) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR; } if (motion_vectors) { pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS; } pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), separate_specular, motion_vectors, view_count); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } } } } } if (!p_surface.uses_depth) { return; } // Generate the depth pipelines if the material supports depth or it must be part of the shadow pass. pipeline_key.color_pass_flags = 0; if (p_global.use_normal_and_roughness) { // A lot of different effects rely on normal and roughness being written to during the depth pass. pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS; pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), true, false); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } if (p_global.use_voxelgi) { // Depth pass with VoxelGI support. pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI; pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), true, true); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } if (p_global.use_sdfgi) { // Depth pass with SDFGI support. pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF; pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); // Depth pass with SDFGI support for an empty framebuffer. pipeline_key.framebuffer_format_id = RD::get_singleton()->framebuffer_format_create_empty(); _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } // The dedicated depth passes use a different version of the surface and the shader. pipeline_key.primitive_type = mesh_storage->mesh_surface_get_primitive(p_surface.mesh_surface_shadow); pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS; pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false); _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); if (p_global.use_shadow_dual_paraboloid) { pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP; _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } if (p_global.use_shadow_cubemaps) { pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS; pipeline_key.framebuffer_format_id = _get_shadow_cubemap_framebuffer_format_for_pipeline(); _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } // Atlas shadowmaps (omni lights) can be in both 16-bit and 32-bit versions. const uint32_t use_16_bits_start = p_global.use_32_bit_shadows ? 0 : 1; const uint32_t use_16_bits_iterations = p_global.use_16_bit_shadows ? 2 : 1; for (uint32_t use_16_bits = use_16_bits_start; use_16_bits < use_16_bits_iterations; use_16_bits++) { pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS; pipeline_key.framebuffer_format_id = _get_shadow_atlas_framebuffer_format_for_pipeline(use_16_bits); _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); if (p_global.use_shadow_dual_paraboloid) { pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP; _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } } if (p_global.use_reflection_probes) { // Depth pass for reflection probes. Normally this will be redundant as the format is the exact same as the shadow cubemap. pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS; pipeline_key.framebuffer_format_id = _get_reflection_probe_depth_framebuffer_format_for_pipeline(); _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs); } } void RenderForwardClustered::_mesh_generate_all_pipelines_for_surface_cache(GeometryInstanceSurfaceDataCache *p_surface_cache, const GlobalPipelineData &p_global) { bool uses_alpha_pass = (p_surface_cache->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA) != 0; float multiplied_fade_alpha = p_surface_cache->owner->force_alpha * p_surface_cache->owner->parent_fade_alpha; bool uses_fade = (multiplied_fade_alpha < FADE_ALPHA_PASS_THRESHOLD) || p_surface_cache->owner->fade_near || p_surface_cache->owner->fade_far; SurfacePipelineData surface; surface.mesh_surface = p_surface_cache->surface; surface.mesh_surface_shadow = p_surface_cache->surface_shadow; surface.shader = p_surface_cache->shader; surface.shader_shadow = p_surface_cache->shader_shadow; surface.instanced = p_surface_cache->owner->mesh_instance.is_valid(); surface.uses_opaque = !uses_alpha_pass; surface.uses_transparent = uses_alpha_pass || uses_fade; surface.uses_depth = (p_surface_cache->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW)) != 0; surface.can_use_lightmap = p_surface_cache->owner->lightmap_instance.is_valid() || p_surface_cache->owner->lightmap_sh; _mesh_compile_pipelines_for_surface(surface, p_global, RSE::PIPELINE_SOURCE_SURFACE); } void RenderForwardClustered::_update_dirty_geometry_instances() { while (geometry_instance_dirty_list.first()) { _geometry_instance_update(geometry_instance_dirty_list.first()->self()); } _update_dirty_geometry_pipelines(); } void RenderForwardClustered::_update_dirty_geometry_pipelines() { if (global_pipeline_data_required.key != global_pipeline_data_compiled.key) { // Go through the entire list of surfaces and compile pipelines for everything again. for (GeometryInstanceSurfaceDataCache &surface_cache : geometry_surface_compilation_all_list) { _mesh_generate_all_pipelines_for_surface_cache(&surface_cache, global_pipeline_data_required); if (surface_cache.compilation_dirty_element.in_list()) { // Remove any elements from the dirty list as they don't need to be processed again. geometry_surface_compilation_dirty_list.remove(&surface_cache.compilation_dirty_element); } } global_pipeline_data_compiled.key = global_pipeline_data_required.key; } else { // Compile pipelines only for the dirty list. if (!geometry_surface_compilation_dirty_list.first()) { return; } while (geometry_surface_compilation_dirty_list.first() != nullptr) { GeometryInstanceSurfaceDataCache *surface_cache = geometry_surface_compilation_dirty_list.first()->self(); _mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_compiled); surface_cache->compilation_dirty_element.remove_from_list(); } } } void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) { switch (p_notification) { case Dependency::DEPENDENCY_CHANGED_MATERIAL: case Dependency::DEPENDENCY_CHANGED_MESH: case Dependency::DEPENDENCY_CHANGED_PARTICLES: case Dependency::DEPENDENCY_CHANGED_PARTICLES_INSTANCES: case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast(p_tracker->userdata)->_mark_dirty(); static_cast(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceForwardClustered *ginstance = static_cast(p_tracker->userdata); if (ginstance->data->base_type == RSE::INSTANCE_MULTIMESH) { ginstance->instance_count = RendererRD::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(ginstance->data->base); } } break; default: { //rest of notifications of no interest } break; } } void RenderForwardClustered::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast(p_tracker->userdata)->_mark_dirty(); static_cast(p_tracker->userdata)->data->dirty_dependencies = true; } RenderGeometryInstance *RenderForwardClustered::geometry_instance_create(RID p_base) { RSE::InstanceType type = RSG::utilities->get_base_type(p_base); ERR_FAIL_COND_V(!((1 << type) & RSE::INSTANCE_GEOMETRY_MASK), nullptr); GeometryInstanceForwardClustered *ginstance = geometry_instance_alloc.alloc(); ginstance->data = memnew(GeometryInstanceForwardClustered::Data); ginstance->data->base = p_base; ginstance->data->base_type = type; ginstance->data->dependency_tracker.userdata = ginstance; ginstance->data->dependency_tracker.changed_callback = _geometry_instance_dependency_changed; ginstance->data->dependency_tracker.deleted_callback = _geometry_instance_dependency_deleted; ginstance->_mark_dirty(); return ginstance; } void RenderForwardClustered::GeometryInstanceForwardClustered::set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) { uint64_t frame = RSG::rasterizer->get_frame_number(); if (frame != prev_transform_change_frame) { prev_transform = transform; prev_transform_change_frame = frame; transform_status = TransformStatus::MOVED; } else if (unlikely(transform_status == TransformStatus::TELEPORTED)) { prev_transform = transform; } RenderGeometryInstanceBase::set_transform(p_transform, p_aabb, p_transformed_aabb); } void RenderForwardClustered::GeometryInstanceForwardClustered::reset_motion_vectors() { prev_transform = transform; transform_status = TransformStatus::TELEPORTED; } void RenderForwardClustered::GeometryInstanceForwardClustered::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { lightmap_instance = p_lightmap_instance; lightmap_uv_scale = p_lightmap_uv_scale; lightmap_slice_index = p_lightmap_slice_index; _mark_dirty(); } void RenderForwardClustered::GeometryInstanceForwardClustered::set_lightmap_capture(const Color *p_sh9) { if (p_sh9) { if (lightmap_sh == nullptr) { lightmap_sh = RenderForwardClustered::get_singleton()->geometry_instance_lightmap_sh.alloc(); } memcpy(lightmap_sh->sh, p_sh9, sizeof(Color) * 9); } else { if (lightmap_sh != nullptr) { RenderForwardClustered::get_singleton()->geometry_instance_lightmap_sh.free(lightmap_sh); lightmap_sh = nullptr; } } _mark_dirty(); } void RenderForwardClustered::geometry_instance_free(RenderGeometryInstance *p_geometry_instance) { GeometryInstanceForwardClustered *ginstance = static_cast(p_geometry_instance); ERR_FAIL_NULL(ginstance); if (ginstance->lightmap_sh != nullptr) { geometry_instance_lightmap_sh.free(ginstance->lightmap_sh); } GeometryInstanceSurfaceDataCache *surf = ginstance->surface_caches; while (surf) { GeometryInstanceSurfaceDataCache *next = surf->next; geometry_instance_surface_alloc.free(surf); surf = next; } memdelete(ginstance->data); geometry_instance_alloc.free(ginstance); } uint32_t RenderForwardClustered::geometry_instance_get_pair_mask() { return (1 << RSE::INSTANCE_VOXEL_GI); } void RenderForwardClustered::mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh); uint32_t surface_count = 0; const RID *materials = mesh_storage->mesh_get_surface_count_and_materials(p_mesh, surface_count); Vector pipeline_pairs; for (uint32_t i = 0; i < surface_count; i++) { if (materials[i].is_null()) { continue; } void *mesh_surface = mesh_storage->mesh_get_surface(p_mesh, i); void *mesh_surface_shadow = mesh_surface; SceneShaderForwardClustered::MaterialData *material = static_cast(material_storage->material_get_data(materials[i], RendererRD::MaterialStorage::SHADER_TYPE_3D)); if (material == nullptr || !material->shader_data->is_valid()) { continue; } SceneShaderForwardClustered::ShaderData *shader = material->shader_data; SceneShaderForwardClustered::ShaderData *shader_shadow = shader; if (material->shader_data->uses_shared_shadow_material()) { SceneShaderForwardClustered::MaterialData *material_shadow = static_cast(material_storage->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); if (material_shadow != nullptr) { shader_shadow = material_shadow->shader_data; if (shadow_mesh.is_valid()) { mesh_surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, i); } } } if (!shader->is_valid()) { continue; } SurfacePipelineData surface; surface.mesh_surface = mesh_surface; surface.mesh_surface_shadow = mesh_surface_shadow; surface.shader = shader; surface.shader_shadow = shader_shadow; surface.instanced = mesh_storage->mesh_needs_instance(p_mesh, true); surface.uses_opaque = !material->shader_data->uses_alpha_pass(); surface.uses_transparent = material->shader_data->uses_alpha_pass(); surface.uses_depth = surface.uses_opaque || (surface.uses_transparent && material->shader_data->uses_depth_in_alpha_pass()); surface.can_use_lightmap = mesh_storage->mesh_surface_get_format(mesh_surface) & RSE::ARRAY_FORMAT_TEX_UV2; _mesh_compile_pipelines_for_surface(surface, global_pipeline_data_required, RSE::PIPELINE_SOURCE_MESH, &pipeline_pairs); } // Wait for all the pipelines that were compiled. This will force the loader to wait on all ubershader pipelines to be ready. if (!p_background_compilation && !pipeline_pairs.is_empty()) { for (ShaderPipelinePair pair : pipeline_pairs) { pair.first->pipeline_hash_map.wait_for_pipeline(pair.second.hash()); } } } uint32_t RenderForwardClustered::get_pipeline_compilations(RSE::PipelineSource p_source) { return scene_shader.get_pipeline_compilations(p_source); } void RenderForwardClustered::enable_features(BitField p_feature_bits) { if (p_feature_bits.has_flag(FEATURE_MULTIVIEW_BIT)) { scene_shader.enable_multiview_shader_group(); } if (p_feature_bits.has_flag(FEATURE_ADVANCED_BIT)) { scene_shader.enable_advanced_shader_group(p_feature_bits.has_flag(FEATURE_MULTIVIEW_BIT)); } if (p_feature_bits.has_flag(FEATURE_VRS_BIT)) { gi.enable_vrs_shader_group(); } } String RenderForwardClustered::get_name() const { return "forward_clustered"; } void RenderForwardClustered::GeometryInstanceForwardClustered::pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) { if (p_voxel_gi_instance_count > 0) { voxel_gi_instances[0] = p_voxel_gi_instances[0]; } else { voxel_gi_instances[0] = RID(); } if (p_voxel_gi_instance_count > 1) { voxel_gi_instances[1] = p_voxel_gi_instances[1]; } else { voxel_gi_instances[1] = RID(); } } void RenderForwardClustered::GeometryInstanceForwardClustered::set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) { using_projectors = p_projector; using_softshadows = p_softshadow; _mark_dirty(); } void RenderForwardClustered::_update_shader_quality_settings() { SceneShaderForwardClustered::ShaderSpecialization specialization = {}; specialization.decal_use_mipmaps = decals_get_filter() == RSE::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RSE::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RSE::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || decals_get_filter() == RSE::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; ; specialization.projector_use_mipmaps = light_projectors_get_filter() == RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RSE::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || light_projectors_get_filter() == RSE::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; specialization.soft_shadow_samples = soft_shadow_samples_get(); specialization.penumbra_shadow_samples = penumbra_shadow_samples_get(); specialization.directional_soft_shadow_samples = directional_soft_shadow_samples_get(); specialization.directional_penumbra_shadow_samples = directional_penumbra_shadow_samples_get(); specialization.use_lightmap_bicubic_filter = lightmap_filter_bicubic_get(); specialization.fog_use_legacy_blending = fog_use_legacy_blending_get(); scene_shader.set_default_specialization(specialization); base_uniforms_changed(); //also need this } RenderForwardClustered::RenderForwardClustered() { singleton = this; /* SCENE SHADER */ { String defines; defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n"; if (is_using_radiance_octmap_array()) { defines += "\n#define USE_RADIANCE_OCTMAP_ARRAY \n"; } defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n"; defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading"); if (force_vertex_shading) { defines += "\n#define USE_VERTEX_LIGHTING\n"; } bool multi_bounce_occlusion = GLOBAL_GET("rendering/lights_and_shadows/multi_bounce_occlusion/enabled"); if (multi_bounce_occlusion) { defines += "\n#define MULTI_BOUNCE_OCCLUSION_ENABLED\n"; } bool specular_occlusion = GLOBAL_GET("rendering/reflections/specular_occlusion/enabled"); if (!specular_occlusion) { defines += "\n#define SPECULAR_OCCLUSION_DISABLED\n"; } #ifdef MODULE_TEXTURE_STREAMING_ENABLED bool texture_streaming = GLOBAL_GET("rendering/textures/streaming/enabled"); if (texture_streaming) { defines += "\n#define TEXTURE_STREAMING\n"; bool prepass_enabled = GLOBAL_GET("rendering/driver/depth_prepass/enable"); if (prepass_enabled) { defines += "\n#define TEXTURE_STREAMING_DEPTH_PREPASS\n"; } } #endif { //lightmaps scene_state.max_lightmaps = MAX_LIGHTMAPS; defines += "\n#define MAX_LIGHTMAP_TEXTURES " + itos(scene_state.max_lightmaps) + "\n"; defines += "\n#define MAX_LIGHTMAPS " + itos(scene_state.max_lightmaps) + "\n"; scene_state.lightmap_buffer = RD::get_singleton()->storage_buffer_create(sizeof(LightmapData) * scene_state.max_lightmaps); } { //captures scene_state.max_lightmap_captures = 2048; scene_state.lightmap_captures = memnew_arr(LightmapCaptureData, scene_state.max_lightmap_captures); scene_state.lightmap_capture_buffer = RD::get_singleton()->storage_buffer_create(sizeof(LightmapCaptureData) * scene_state.max_lightmap_captures); } { defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n"; } #ifdef REAL_T_IS_DOUBLE { defines += "\n#define USE_DOUBLE_PRECISION \n"; } #endif scene_shader.init(defines); } /* shadow sampler */ { RD::SamplerState sampler; sampler.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler.min_filter = RD::SAMPLER_FILTER_NEAREST; sampler.enable_compare = true; sampler.compare_op = RD::COMPARE_OP_GREATER; shadow_sampler = RD::get_singleton()->sampler_create(sampler); } { Vector modes; modes.push_back("\n"); best_fit_normal.shader.initialize(modes); best_fit_normal.shader_version = best_fit_normal.shader.version_create(); best_fit_normal.pipeline = RD::get_singleton()->compute_pipeline_create(best_fit_normal.shader.version_get_shader(best_fit_normal.shader_version, 0)); RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8_UNORM; tformat.width = 1024; tformat.height = 1024; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; best_fit_normal.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView()); RID shader = best_fit_normal.shader.version_get_shader(best_fit_normal.shader_version, 0); ERR_FAIL_COND(shader.is_null()); Vector uniforms; { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.append_id(best_fit_normal.texture); uniforms.push_back(u); } RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader, 0); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, best_fit_normal.pipeline); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); RD::get_singleton()->compute_list_dispatch_threads(compute_list, tformat.width, tformat.height, 1); RD::get_singleton()->compute_list_end(); } /* DFG LUT */ { Vector modes; modes.push_back("\n"); dfg_lut.shader.initialize(modes); dfg_lut.shader_version = dfg_lut.shader.version_create(); dfg_lut.pipeline = RD::get_singleton()->compute_pipeline_create(dfg_lut.shader.version_get_shader(dfg_lut.shader_version, 0)); RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R16G16_SFLOAT; tformat.width = 128; tformat.height = 128; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; dfg_lut.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView()); RID shader = dfg_lut.shader.version_get_shader(dfg_lut.shader_version, 0); ERR_FAIL_COND(shader.is_null()); Vector uniforms; { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.append_id(dfg_lut.texture); uniforms.push_back(u); } RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader, 0); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, dfg_lut.pipeline); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); RD::get_singleton()->compute_list_dispatch_threads(compute_list, tformat.width, tformat.height, 1); RD::get_singleton()->compute_list_end(); } _update_shader_quality_settings(); _update_global_pipeline_data_requirements_from_project(); taa = memnew(RendererRD::TAA); fsr2_effect = memnew(RendererRD::FSR2Effect); ss_effects = memnew(RendererRD::SSEffects); #ifdef METAL_MFXTEMPORAL_ENABLED motion_vectors_store = memnew(RendererRD::MotionVectorsStore); mfx_temporal_effect = memnew(RendererRD::MFXTemporalEffect); #endif } RenderForwardClustered::~RenderForwardClustered() { if (ss_effects != nullptr) { memdelete(ss_effects); ss_effects = nullptr; } if (taa != nullptr) { memdelete(taa); taa = nullptr; } if (fsr2_effect) { memdelete(fsr2_effect); fsr2_effect = nullptr; } #ifdef METAL_MFXTEMPORAL_ENABLED if (mfx_temporal_effect) { memdelete(mfx_temporal_effect); mfx_temporal_effect = nullptr; } if (motion_vectors_store) { memdelete(motion_vectors_store); motion_vectors_store = nullptr; } #endif RD::get_singleton()->free_rid(shadow_sampler); RSG::light_storage->directional_shadow_atlas_set_size(0); RD::get_singleton()->free_rid(best_fit_normal.pipeline); RD::get_singleton()->free_rid(best_fit_normal.texture); best_fit_normal.shader.version_free(best_fit_normal.shader_version); RD::get_singleton()->free_rid(dfg_lut.pipeline); RD::get_singleton()->free_rid(dfg_lut.texture); dfg_lut.shader.version_free(dfg_lut.shader_version); if (ltc.lut1_texture.is_valid()) { RS::get_singleton()->free_rid(ltc.lut1_texture); } if (ltc.lut2_texture.is_valid()) { RS::get_singleton()->free_rid(ltc.lut2_texture); } { for (const RID &rid : scene_state.uniform_buffers) { RD::get_singleton()->free_rid(rid); } for (const RID &rid : scene_state.implementation_uniform_buffers) { RD::get_singleton()->free_rid(rid); } RD::get_singleton()->free_rid(scene_state.lightmap_buffer); RD::get_singleton()->free_rid(scene_state.lightmap_capture_buffer); for (uint32_t i = 0; i < RENDER_LIST_MAX; i++) { scene_state.instance_buffer[i].uninit(); } memdelete_arr(scene_state.lightmap_captures); } while (sdfgi_framebuffer_size_cache.begin()) { RD::get_singleton()->free_rid(sdfgi_framebuffer_size_cache.begin()->value); sdfgi_framebuffer_size_cache.remove(sdfgi_framebuffer_size_cache.begin()); } }