浏览代码

Several GI related optimizations and fixes

-SDFGI direct light is done over many frames
-SDFGI Changed settings for rays/frame
-SDFGI Misc optimizations
-SDFGI Bug fix on probe scroll

-GIProbe was not working, got it to work again
-GIProbe dynamic objects were not working, fixed

-Added a half size GI option.
reduz 4 年之前
父节点
当前提交
6fe342478b

+ 3 - 0
editor/editor_node.cpp

@@ -516,6 +516,9 @@ void EditorNode::_notification(int p_what) {
 				RS::get_singleton()->environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink"));
 				RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/quality/2d_shadow_atlas/size"));
 
+				bool use_half_res_gi = GLOBAL_DEF("rendering/quality/gi/use_half_resolution", false);
+				RS::get_singleton()->gi_set_use_half_resolution(use_half_res_gi);
+
 				bool snap_2d_transforms = GLOBAL_GET("rendering/quality/2d/snap_2d_transforms_to_pixel");
 				scene_root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
 				bool snap_2d_vertices = GLOBAL_GET("rendering/quality/2d/snap_2d_vertices_to_pixel");

+ 9 - 0
main/main.cpp

@@ -171,6 +171,8 @@ static bool disable_render_loop = false;
 static int fixed_fps = -1;
 static bool print_fps = false;
 
+bool profile_gpu = false;
+
 /* Helper methods */
 
 // Used by Mono module, should likely be registered in Engine singleton instead
@@ -357,6 +359,7 @@ void Main::print_help(const char *p_binary) {
 	OS::get_singleton()->print("  --disable-crash-handler          Disable crash handler when supported by the platform code.\n");
 	OS::get_singleton()->print("  --fixed-fps <fps>                Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
 	OS::get_singleton()->print("  --print-fps                      Print the frames per second to the stdout.\n");
+	OS::get_singleton()->print("  --profile-gpu                    Show a simple profile of the tasks that took more time during frame rendering.\n");
 	OS::get_singleton()->print("\n");
 
 	OS::get_singleton()->print("Standalone tools:\n");
@@ -1006,6 +1009,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			}
 		} else if (I->get() == "--print-fps") {
 			print_fps = true;
+		} else if (I->get() == "--profile-gpu") {
+			profile_gpu = true;
 		} else if (I->get() == "--disable-crash-handler") {
 			OS::get_singleton()->disable_crash_handler();
 		} else if (I->get() == "--skip-breakpoints") {
@@ -1574,6 +1579,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
 	rendering_server->init();
 	rendering_server->set_render_loop_enabled(!disable_render_loop);
 
+	if (profile_gpu) {
+		rendering_server->set_print_gpu_profile(true);
+	}
+
 	OS::get_singleton()->initialize_joypads();
 
 	/* Initialize Audio Driver */

+ 3 - 0
scene/3d/gi_probe.cpp

@@ -520,7 +520,10 @@ String GIProbe::get_configuration_warning() const {
 			warning += "\n\n";
 		}
 		warning += TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead.");
+	} else if (probe_data.is_null()) {
+		warning += TTR("No GIProbe data set, so this node is disabled. Bake static objects to enable GI.");
 	}
+
 	return warning;
 }
 

+ 7 - 41
servers/rendering/renderer_rd/renderer_scene_render_forward.cpp

@@ -583,19 +583,6 @@ void RendererSceneRenderForward::RenderBufferDataForward::ensure_specular() {
 	}
 }
 
-void RendererSceneRenderForward::RenderBufferDataForward::ensure_gi() {
-	if (!reflection_buffer.is_valid()) {
-		RD::TextureFormat tf;
-		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
-		tf.width = width;
-		tf.height = height;
-		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
-
-		reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
-		ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
-	}
-}
-
 void RendererSceneRenderForward::RenderBufferDataForward::ensure_giprobe() {
 	if (!giprobe_buffer.is_valid()) {
 		RD::TextureFormat tf;
@@ -633,16 +620,6 @@ void RendererSceneRenderForward::RenderBufferDataForward::ensure_giprobe() {
 }
 
 void RendererSceneRenderForward::RenderBufferDataForward::clear() {
-	if (ambient_buffer != RID() && ambient_buffer != color) {
-		RD::get_singleton()->free(ambient_buffer);
-		ambient_buffer = RID();
-	}
-
-	if (reflection_buffer != RID() && reflection_buffer != specular) {
-		RD::get_singleton()->free(reflection_buffer);
-		reflection_buffer = RID();
-	}
-
 	if (giprobe_buffer != RID()) {
 		RD::get_singleton()->free(giprobe_buffer);
 		giprobe_buffer = RID();
@@ -1403,6 +1380,7 @@ void RendererSceneRenderForward::_fill_render_list(const PagedArray<GeometryInst
 					}
 
 					inst->push_constant.gi_offset = probe0_index | (probe1_index << 16);
+					flags |= INSTANCE_DATA_FLAG_USE_GIPROBE;
 					uses_gi = true;
 				} else {
 					if (p_using_sdfgi && inst->can_sdfgi) {
@@ -1553,7 +1531,6 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 
 		if (!low_end && p_gi_probes.size() > 0) {
 			using_giprobe = true;
-			render_buffer->ensure_gi();
 		}
 
 		if (!p_environment.is_valid() && using_giprobe) {
@@ -1563,7 +1540,6 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 			if (environment_is_sdfgi_enabled(p_environment)) {
 				depth_pass_mode = using_giprobe ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; // also giprobe
 				using_sdfgi = true;
-				render_buffer->ensure_gi();
 			} else {
 				depth_pass_mode = using_giprobe ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE : PASS_MODE_DEPTH_NORMAL_ROUGHNESS;
 			}
@@ -1739,7 +1715,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 	}
 
 	if (using_sdfgi || using_giprobe) {
-		_process_gi(p_render_buffer, render_buffer->normal_roughness_buffer, render_buffer->ambient_buffer, render_buffer->reflection_buffer, render_buffer->giprobe_buffer, p_environment, p_cam_projection, p_cam_transform, p_gi_probes);
+		_process_gi(p_render_buffer, render_buffer->normal_roughness_buffer, render_buffer->giprobe_buffer, p_environment, p_cam_projection, p_cam_transform, p_gi_probes);
 	}
 
 	_setup_environment(p_environment, p_render_buffer, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_size, p_cluster_size, p_max_cluster_elements, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), p_render_buffer.is_valid());
@@ -1930,7 +1906,7 @@ void RendererSceneRenderForward::_render_material(const Transform &p_cam_transfo
 	render_pass++;
 
 	scene_state.ubo.dual_paraboloid_side = 0;
-	scene_state.ubo.material_uv2_mode = true;
+	scene_state.ubo.material_uv2_mode = false;
 
 	_setup_environment(RID(), RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), 1, 32, RID(), false, Color(), 0, 0);
 
@@ -2395,7 +2371,8 @@ RID RendererSceneRenderForward::_setup_render_pass_uniform_set(RID p_render_buff
 			RD::Uniform u;
 			u.binding = 10;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = rb && rb->ambient_buffer.is_valid() ? rb->ambient_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			RID ambient_buffer = p_render_buffers.is_valid() ? render_buffers_get_gi_ambient_texture(p_render_buffers) : RID();
+			RID texture = ambient_buffer.is_valid() ? ambient_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
 		}
@@ -2404,7 +2381,8 @@ RID RendererSceneRenderForward::_setup_render_pass_uniform_set(RID p_render_buff
 			RD::Uniform u;
 			u.binding = 11;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = rb && rb->reflection_buffer.is_valid() ? rb->reflection_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			RID reflection_buffer = p_render_buffers.is_valid() ? render_buffers_get_gi_reflection_texture(p_render_buffers) : RID();
+			RID texture = reflection_buffer.is_valid() ? reflection_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
 		}
@@ -2585,18 +2563,6 @@ RID RendererSceneRenderForward::_render_buffers_get_normal_texture(RID p_render_
 	return rb->normal_roughness_buffer;
 }
 
-RID RendererSceneRenderForward::_render_buffers_get_ambient_texture(RID p_render_buffers) {
-	RenderBufferDataForward *rb = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
-
-	return rb->ambient_buffer;
-}
-
-RID RendererSceneRenderForward::_render_buffers_get_reflection_texture(RID p_render_buffers) {
-	RenderBufferDataForward *rb = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
-
-	return rb->reflection_buffer;
-}
-
 RendererSceneRenderForward *RendererSceneRenderForward::singleton = nullptr;
 
 void RendererSceneRenderForward::set_time(double p_time, double p_step) {

+ 0 - 6
servers/rendering/renderer_rd/renderer_scene_render_forward.h

@@ -213,9 +213,6 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 		RID normal_roughness_buffer;
 		RID giprobe_buffer;
 
-		RID ambient_buffer;
-		RID reflection_buffer;
-
 		RS::ViewportMSAA msaa;
 		RD::TextureSamples texture_samples;
 
@@ -236,7 +233,6 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 
 		RID render_sdfgi_uniform_set;
 		void ensure_specular();
-		void ensure_gi();
 		void ensure_giprobe();
 		void clear();
 		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa);
@@ -258,8 +254,6 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 	void _render_buffers_clear_uniform_set(RenderBufferDataForward *rb);
 	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
 	virtual RID _render_buffers_get_normal_texture(RID p_render_buffers);
-	virtual RID _render_buffers_get_ambient_texture(RID p_render_buffers);
-	virtual RID _render_buffers_get_reflection_texture(RID p_render_buffers);
 
 	void _update_render_base_uniform_set();
 	RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture);

+ 142 - 34
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -1276,14 +1276,26 @@ void RendererSceneRenderRD::sdfgi_update_probes(RID p_render_buffers, RID p_envi
 		push_constant.multibounce = rb->sdfgi->uses_multibounce;
 		push_constant.y_mult = rb->sdfgi->y_mult;
 
-		push_constant.process_offset = 0;
-		push_constant.process_increment = 1;
-
 		for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
 			SDFGI::Cascade &cascade = rb->sdfgi->cascades[i];
 			push_constant.light_count = cascade_light_count[i];
 			push_constant.cascade = i;
 
+			if (rb->sdfgi->cascades[i].all_dynamic_lights_dirty || sdfgi_frames_to_update_light == RS::ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME) {
+				push_constant.process_offset = 0;
+				push_constant.process_increment = 1;
+			} else {
+				static uint32_t frames_to_update_table[RS::ENV_SDFGI_UPDATE_LIGHT_MAX] = {
+					1, 2, 4, 8, 16
+				};
+
+				uint32_t frames_to_update = frames_to_update_table[sdfgi_frames_to_update_light];
+
+				push_constant.process_offset = RSG::rasterizer->get_frame_number() % frames_to_update;
+				push_constant.process_increment = frames_to_update;
+			}
+			rb->sdfgi->cascades[i].all_dynamic_lights_dirty = false;
+
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_uniform_set, 0);
 			RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DirectLightPushConstant));
 			RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer, 0);
@@ -1301,7 +1313,7 @@ void RendererSceneRenderRD::sdfgi_update_probes(RID p_render_buffers, RID p_envi
 	push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
 	push_constant.history_index = rb->sdfgi->render_pass % rb->sdfgi->history_size;
 	push_constant.history_size = rb->sdfgi->history_size;
-	static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 8, 16, 32, 64, 96, 128 };
+	static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 };
 	push_constant.ray_count = ray_count[sdfgi_ray_count];
 	push_constant.ray_bias = rb->sdfgi->probe_bias;
 	push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count;
@@ -1492,13 +1504,35 @@ void RendererSceneRenderRD::_setup_giprobes(RID p_render_buffers, const Transfor
 	}
 }
 
-void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes) {
+void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes) {
 	RENDER_TIMESTAMP("Render GI");
 
 	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
 	ERR_FAIL_COND(rb == nullptr);
 	Environment *env = environment_owner.getornull(p_environment);
 
+	if (rb->ambient_buffer.is_null() || rb->using_half_size_gi != gi.half_resolution) {
+		if (rb->ambient_buffer.is_valid()) {
+			RD::get_singleton()->free(rb->ambient_buffer);
+			RD::get_singleton()->free(rb->reflection_buffer);
+		}
+
+		RD::TextureFormat tf;
+		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+		tf.width = rb->width;
+		tf.height = rb->height;
+		if (gi.half_resolution) {
+			tf.width >>= 1;
+			tf.height >>= 1;
+		}
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+		rb->reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+		rb->ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+		rb->using_half_size_gi = gi.half_resolution;
+
+		_render_buffers_uniform_set_changed(p_render_buffers);
+	}
+
 	GI::PushConstant push_constant;
 
 	push_constant.screen_size[0] = rb->width;
@@ -1693,7 +1727,7 @@ void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_rough
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
 			u.binding = 9;
-			u.ids.push_back(p_ambient_buffer);
+			u.ids.push_back(rb->ambient_buffer);
 			uniforms.push_back(u);
 		}
 
@@ -1701,7 +1735,7 @@ void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_rough
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
 			u.binding = 10;
-			u.ids.push_back(p_reflection_buffer);
+			u.ids.push_back(rb->reflection_buffer);
 			uniforms.push_back(u);
 		}
 
@@ -1765,12 +1799,23 @@ void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_rough
 		rb->gi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi.shader.version_get_shader(gi.shader_version, 0), 0);
 	}
 
-	GI::Mode mode = (use_sdfgi && use_giprobes) ? GI::MODE_COMBINED : (use_sdfgi ? GI::MODE_SDFGI : GI::MODE_GIPROBE);
+	GI::Mode mode;
+
+	if (rb->using_half_size_gi) {
+		mode = (use_sdfgi && use_giprobes) ? GI::MODE_HALF_RES_COMBINED : (use_sdfgi ? GI::MODE_HALF_RES_SDFGI : GI::MODE_HALF_RES_GIPROBE);
+	} else {
+		mode = (use_sdfgi && use_giprobes) ? GI::MODE_COMBINED : (use_sdfgi ? GI::MODE_SDFGI : GI::MODE_GIPROBE);
+	}
 	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi.pipelines[mode]);
 	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->gi_uniform_set, 0);
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GI::PushConstant));
-	RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1, 8, 8, 1);
+
+	if (rb->using_half_size_gi) {
+		RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width >> 1, rb->height >> 1, 1, 8, 8, 1);
+	} else {
+		RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1, 8, 8, 1);
+	}
 	RD::get_singleton()->compute_list_end();
 }
 
@@ -3105,6 +3150,9 @@ void RendererSceneRenderRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGI
 void RendererSceneRenderRD::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) {
 	sdfgi_frames_to_converge = p_frames;
 }
+void RendererSceneRenderRD::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) {
+	sdfgi_frames_to_update_light = p_update;
+}
 
 void RendererSceneRenderRD::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) {
 	Environment *env = environment_owner.getornull(p_env);
@@ -5159,6 +5207,13 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
 		RD::get_singleton()->free(rb->ssr.normal_scaled);
 		rb->ssr.normal_scaled = RID();
 	}
+
+	if (rb->ambient_buffer.is_valid()) {
+		RD::get_singleton()->free(rb->ambient_buffer);
+		RD::get_singleton()->free(rb->reflection_buffer);
+		rb->ambient_buffer = RID();
+		rb->reflection_buffer = RID();
+	}
 }
 
 void RendererSceneRenderRD::_process_sss(RID p_render_buffers, const CameraMatrix &p_camera) {
@@ -5558,10 +5613,10 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
 		effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
 	}
 
-	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && _render_buffers_get_ambient_texture(p_render_buffers).is_valid()) {
+	if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->ambient_buffer.is_valid()) {
 		Size2 rtsize = storage->render_target_get_size(rb->render_target);
-		RID ambient_texture = _render_buffers_get_ambient_texture(p_render_buffers);
-		RID reflection_texture = _render_buffers_get_reflection_texture(p_render_buffers);
+		RID ambient_texture = rb->ambient_buffer;
+		RID reflection_texture = rb->reflection_buffer;
 		effects->copy_to_fb_rect(ambient_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture);
 	}
 }
@@ -5753,6 +5808,17 @@ RID RendererSceneRenderRD::render_buffers_get_default_gi_probe_buffer() {
 	return default_giprobe_buffer;
 }
 
+RID RendererSceneRenderRD::render_buffers_get_gi_ambient_texture(RID p_render_buffers) {
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+	ERR_FAIL_COND_V(!rb, RID());
+	return rb->ambient_buffer;
+}
+RID RendererSceneRenderRD::render_buffers_get_gi_reflection_texture(RID p_render_buffers) {
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+	ERR_FAIL_COND_V(!rb, RID());
+	return rb->reflection_buffer;
+}
+
 uint32_t RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const {
 	const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
 	ERR_FAIL_COND_V(!rb, 0);
@@ -5939,6 +6005,10 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
 	rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture);
 }
 
+void RendererSceneRenderRD::gi_set_use_half_resolution(bool p_enable) {
+	gi.half_resolution = p_enable;
+}
+
 void RendererSceneRenderRD::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) {
 	sss_quality = p_quality;
 }
@@ -7609,6 +7679,9 @@ void RendererSceneRenderRD::render_sdfgi(RID p_render_buffers, int p_region, con
 			push_constant.scroll[1] = 0;
 			push_constant.scroll[2] = 0;
 		}
+
+		rb->sdfgi->cascades[cascade].all_dynamic_lights_dirty = true;
+
 		push_constant.grid_size = rb->sdfgi->cascade_size;
 		push_constant.cascade = cascade;
 
@@ -7684,6 +7757,23 @@ void RendererSceneRenderRD::render_sdfgi(RID p_render_buffers, int p_region, con
 				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
 				RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
 				RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1, 8, 8, 1);
+
+				RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+				if (rb->sdfgi->uses_multibounce) {
+					//multibounce requires this to be stored so direct light can read from it
+
+					RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]);
+
+					//convert to octahedral to store
+					ipush_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+					ipush_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+
+					RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0);
+					RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
+					RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
+					RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1, 8, 8, 1);
+				}
 			}
 
 			//ok finally barrier
@@ -7922,25 +8012,8 @@ void RendererSceneRenderRD::render_sdfgi_static_lights(RID p_render_buffers, uin
 
 	_sdfgi_update_cascades(p_render_buffers); //need cascades updated for this
 
-	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
-	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_STATIC]);
-
-	SDGIShader::DirectLightPushConstant dl_push_constant;
-
-	dl_push_constant.grid_size[0] = rb->sdfgi->cascade_size;
-	dl_push_constant.grid_size[1] = rb->sdfgi->cascade_size;
-	dl_push_constant.grid_size[2] = rb->sdfgi->cascade_size;
-	dl_push_constant.max_cascades = rb->sdfgi->cascades.size();
-	dl_push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
-	dl_push_constant.multibounce = false; // this is static light, do not multibounce yet
-	dl_push_constant.y_mult = rb->sdfgi->y_mult;
-
-	//all must be processed
-	dl_push_constant.process_offset = 0;
-	dl_push_constant.process_increment = 1;
-
 	SDGIShader::Light lights[SDFGI::MAX_STATIC_LIGHTS];
+	uint32_t light_count[SDFGI::MAX_STATIC_LIGHTS];
 
 	for (uint32_t i = 0; i < p_cascade_count; i++) {
 		ERR_CONTINUE(p_cascade_indices[i] >= rb->sdfgi->cascades.size());
@@ -8005,9 +8078,36 @@ void RendererSceneRenderRD::render_sdfgi_static_lights(RID p_render_buffers, uin
 			if (idx > 0) {
 				RD::get_singleton()->buffer_update(cc.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, true);
 			}
-			dl_push_constant.light_count = idx;
+
+			light_count[i] = idx;
 		}
+	}
+
+	/* Static Lights */
+	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_STATIC]);
+
+	SDGIShader::DirectLightPushConstant dl_push_constant;
+
+	dl_push_constant.grid_size[0] = rb->sdfgi->cascade_size;
+	dl_push_constant.grid_size[1] = rb->sdfgi->cascade_size;
+	dl_push_constant.grid_size[2] = rb->sdfgi->cascade_size;
+	dl_push_constant.max_cascades = rb->sdfgi->cascades.size();
+	dl_push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
+	dl_push_constant.multibounce = false; // this is static light, do not multibounce yet
+	dl_push_constant.y_mult = rb->sdfgi->y_mult;
+
+	//all must be processed
+	dl_push_constant.process_offset = 0;
+	dl_push_constant.process_increment = 1;
+
+	for (uint32_t i = 0; i < p_cascade_count; i++) {
+		ERR_CONTINUE(p_cascade_indices[i] >= rb->sdfgi->cascades.size());
+
+		SDFGI::Cascade &cc = rb->sdfgi->cascades[p_cascade_indices[i]];
+
+		dl_push_constant.light_count = light_count[i];
 		dl_push_constant.cascade = p_cascade_indices[i];
 
 		if (dl_push_constant.light_count > 0) {
@@ -8299,7 +8399,10 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 	roughness_layers = GLOBAL_GET("rendering/quality/reflections/roughness_layers");
 	sky_ggx_samples_quality = GLOBAL_GET("rendering/quality/reflections/ggx_samples");
 	sky_use_cubemap_array = GLOBAL_GET("rendering/quality/reflections/texture_array_reflections");
-	//	sky_use_cubemap_array = false;
+
+	sdfgi_ray_count = RS::EnvironmentSDFGIRayCount(CLAMP(int32_t(GLOBAL_GET("rendering/sdfgi/probe_ray_count")), 0, int32_t(RS::ENV_SDFGI_RAY_COUNT_MAX - 1)));
+	sdfgi_frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/sdfgi/frames_to_converge")), 0, int32_t(RS::ENV_SDFGI_CONVERGE_MAX - 1)));
+	sdfgi_frames_to_update_light = RS::EnvironmentSDFGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/sdfgi/frames_to_update_lights")), 0, int32_t(RS::ENV_SDFGI_UPDATE_LIGHT_MAX - 1)));
 
 	uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
 
@@ -8632,9 +8735,12 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 			//calculate tables
 			String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
 			Vector<String> gi_modes;
-			gi_modes.push_back("\n#define USE_GIPROBE\n");
+			gi_modes.push_back("\n#define USE_GIPROBES\n");
 			gi_modes.push_back("\n#define USE_SDFGI\n");
-			gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_GIPROBE\n");
+			gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
+			gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_GIPROBES\n");
+			gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n");
+			gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
 
 			gi.shader.initialize(gi_modes, defines);
 			gi.shader_version = gi.shader.version_create();
@@ -8766,6 +8872,8 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 	environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink"));
 
 	cull_argument.set_page_pool(&cull_argument_pool);
+
+	gi.half_resolution = GLOBAL_GET("rendering/quality/gi/use_half_resolution");
 }
 
 RendererSceneRenderRD::~RendererSceneRenderRD() {

+ 26 - 8
servers/rendering/renderer_rd/renderer_scene_render_rd.h

@@ -124,8 +124,6 @@ protected:
 	virtual void _base_uniforms_changed() = 0;
 	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers) = 0;
 	virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) = 0;
-	virtual RID _render_buffers_get_ambient_texture(RID p_render_buffers) = 0;
-	virtual RID _render_buffers_get_reflection_texture(RID p_render_buffers) = 0;
 
 	void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection);
 	void _process_ssr(RID p_render_buffers, RID p_dest_framebuffer, RID p_normal_buffer, RID p_specular_buffer, RID p_metallic, const Color &p_metallic_mask, RID p_environment, const CameraMatrix &p_projection, bool p_use_additive);
@@ -134,7 +132,7 @@ protected:
 	void _setup_sky(RID p_environment, RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform, const Size2i p_screen_size);
 	void _update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
 	void _draw_sky(bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
-	void _process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes);
+	void _process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes);
 
 	// needed for a single argument calls (material and uv2)
 	PagedArrayPool<GeometryInstance *> cull_argument_pool;
@@ -904,6 +902,16 @@ private:
 
 		RID giprobe_textures[MAX_GIPROBES];
 		RID giprobe_buffer;
+
+		RID ambient_buffer;
+		RID reflection_buffer;
+		bool using_half_size_gi = false;
+
+		struct GI {
+			RID full_buffer;
+			RID full_dispatch;
+			RID full_mask;
+		} gi;
 	};
 
 	RID default_giprobe_buffer;
@@ -965,6 +973,8 @@ private:
 			RID scroll_occlusion_uniform_set;
 			RID integrate_uniform_set;
 			RID lights_buffer;
+
+			bool all_dynamic_lights_dirty = true;
 		};
 
 		//used for rendering (voxelization)
@@ -1026,6 +1036,8 @@ private:
 
 	RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16;
 	RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_10_FRAMES;
+	RS::EnvironmentSDFGIFramesToUpdateLight sdfgi_frames_to_update_light = RS::ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES;
+
 	float sdfgi_solid_cell_ratio = 0.25;
 	Vector3 sdfgi_debug_probe_pos;
 	Vector3 sdfgi_debug_probe_dir;
@@ -1263,14 +1275,12 @@ private:
 			float z_far;
 
 			float proj_info[4];
-
+			float ao_color[3];
 			uint32_t max_giprobes;
+
 			uint32_t high_quality_vct;
-			uint32_t pad2;
 			uint32_t orthogonal;
-
-			float ao_color[3];
-			uint32_t pad;
+			uint32_t pad[2];
 
 			float cam_rotation[12];
 		};
@@ -1280,9 +1290,13 @@ private:
 			MODE_GIPROBE,
 			MODE_SDFGI,
 			MODE_COMBINED,
+			MODE_HALF_RES_GIPROBE,
+			MODE_HALF_RES_SDFGI,
+			MODE_HALF_RES_COMBINED,
 			MODE_MAX
 		};
 
+		bool half_resolution = false;
 		GiShaderRD shader;
 		RID shader_version;
 		RID pipelines[MODE_MAX];
@@ -1657,6 +1671,7 @@ public:
 	virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, bool p_use_multibounce, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias);
 	virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count);
 	virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames);
+	virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update);
 
 	void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality);
 	RS::EnvironmentSSRRoughnessQuality environment_get_ssr_roughness_quality() const;
@@ -1955,11 +1970,14 @@ public:
 */
 	RID render_buffers_create();
 	void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding);
+	void gi_set_use_half_resolution(bool p_enable);
 
 	RID render_buffers_get_ao_texture(RID p_render_buffers);
 	RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
 	RID render_buffers_get_gi_probe_buffer(RID p_render_buffers);
 	RID render_buffers_get_default_gi_probe_buffer();
+	RID render_buffers_get_gi_ambient_texture(RID p_render_buffers);
+	RID render_buffers_get_gi_reflection_texture(RID p_render_buffers);
 
 	uint32_t render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const;
 	bool render_buffers_is_sdfgi_enabled(RID p_render_buffers) const;

+ 34 - 25
servers/rendering/renderer_rd/shaders/gi.glsl

@@ -97,13 +97,12 @@ layout(push_constant, binding = 0, std430) uniform Params {
 
 	vec4 proj_info;
 
+	vec3 ao_color;
 	uint max_giprobes;
+
 	bool high_quality_vct;
-	uint pad2;
 	bool orthogonal;
-
-	vec3 ao_color;
-	uint pad;
+	uint pad[2];
 
 	mat3x4 cam_rotation;
 }
@@ -594,28 +593,16 @@ vec4 fetch_normal_and_roughness(ivec2 pos) {
 	return normal_roughness;
 }
 
-void main() {
-	// Pixel being shaded
-	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
-	if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
-		return;
-	}
-
-	vec3 vertex = reconstruct_position(pos);
-	vertex.y = -vertex.y;
-
+void process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 reflection_light) {
 	vec4 normal_roughness = fetch_normal_and_roughness(pos);
-	vec3 normal = normal_roughness.xyz;
 
-	vec4 ambient_light = vec4(0.0), reflection_light = vec4(0.0);
+	vec3 normal = normal_roughness.xyz;
 
 	if (normal.length() > 0.5) {
 		//valid normal, can do GI
 		float roughness = normal_roughness.w;
-
 		vertex = mat3(params.cam_rotation) * vertex;
 		normal = normalize(mat3(params.cam_rotation) * normal);
-
 		vec3 reflection = normalize(reflect(normalize(vertex), normal));
 
 #ifdef USE_SDFGI
@@ -646,16 +633,38 @@ void main() {
 				spec_accum /= blend_accum;
 			}
 
-			if (params.use_sdfgi) {
-				reflection_light = blend_color(spec_accum, reflection_light);
-				ambient_light = blend_color(amb_accum, ambient_light);
-			} else {
-				reflection_light = spec_accum;
-				ambient_light = amb_accum;
-			}
+#ifdef USE_SDFGI
+			reflection_light = blend_color(spec_accum, reflection_light);
+			ambient_light = blend_color(amb_accum, ambient_light);
+#else
+			reflection_light = spec_accum;
+			ambient_light = amb_accum;
+#endif
 		}
 #endif
 	}
+}
+
+void main() {
+	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+
+#ifdef MODE_HALF_RES
+	pos <<= 1;
+#endif
+	if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+	vec4 ambient_light = vec4(0.0), reflection_light = vec4(0.0);
+
+	vec3 vertex = reconstruct_position(pos);
+	vertex.y = -vertex.y;
+
+	process_gi(pos, vertex, ambient_light, reflection_light);
+
+#ifdef MODE_HALF_RES
+	pos >>= 1;
+#endif
 
 	imageStore(ambient_buffer, pos, ambient_light);
 	imageStore(reflection_buffer, pos, reflection_light);

+ 112 - 1
servers/rendering/renderer_rd/shaders/resolve.glsl

@@ -58,6 +58,117 @@ void main() {
 
 #else
 
+#if 1
+
+	vec4 group1;
+	vec4 group2;
+	vec4 group3;
+	vec4 group4;
+	int best_index = 0;
+
+	//2X
+	group1.x = texelFetch(source_depth, pos, 0).r;
+	group1.y = texelFetch(source_depth, pos, 1).r;
+
+	//4X
+	if (params.sample_count <= 4) {
+		group1.z = texelFetch(source_depth, pos, 2).r;
+		group1.w = texelFetch(source_depth, pos, 3).r;
+	}
+	//8X
+	if (params.sample_count <= 8) {
+		group2.x = texelFetch(source_depth, pos, 4).r;
+		group2.y = texelFetch(source_depth, pos, 5).r;
+		group2.z = texelFetch(source_depth, pos, 6).r;
+		group2.w = texelFetch(source_depth, pos, 7).r;
+	}
+	//16X
+	if (params.sample_count <= 16) {
+		group3.x = texelFetch(source_depth, pos, 8).r;
+		group3.y = texelFetch(source_depth, pos, 9).r;
+		group3.z = texelFetch(source_depth, pos, 10).r;
+		group3.w = texelFetch(source_depth, pos, 11).r;
+
+		group4.x = texelFetch(source_depth, pos, 12).r;
+		group4.y = texelFetch(source_depth, pos, 13).r;
+		group4.z = texelFetch(source_depth, pos, 14).r;
+		group4.w = texelFetch(source_depth, pos, 15).r;
+	}
+
+	if (params.sample_count == 2) {
+		best_index = (pos.x & 1) ^ ((pos.y >> 1) & 1); //not much can be done here
+	} else if (params.sample_count == 4) {
+		vec4 freq = vec4(equal(group1, vec4(group1.x)));
+		freq += vec4(equal(group1, vec4(group1.y)));
+		freq += vec4(equal(group1, vec4(group1.z)));
+		freq += vec4(equal(group1, vec4(group1.w)));
+
+		float min_f = freq.x;
+		best_index = 0;
+		if (freq.y < min_f) {
+			best_index = 1;
+			min_f = freq.y;
+		}
+		if (freq.z < min_f) {
+			best_index = 2;
+			min_f = freq.z;
+		}
+		if (freq.w < min_f) {
+			best_index = 3;
+			min_f = freq.w;
+		}
+	} else if (params.sample_count == 8) {
+		vec4 freq0 = vec4(equal(group1, vec4(group1.x)));
+		vec4 freq1 = vec4(equal(group2, vec4(group1.x)));
+		freq0 += vec4(equal(group1, vec4(group1.y)));
+		freq1 += vec4(equal(group2, vec4(group1.y)));
+		freq0 += vec4(equal(group1, vec4(group1.z)));
+		freq1 += vec4(equal(group2, vec4(group1.z)));
+		freq0 += vec4(equal(group1, vec4(group1.w)));
+		freq1 += vec4(equal(group2, vec4(group1.w)));
+		freq0 += vec4(equal(group1, vec4(group2.x)));
+		freq1 += vec4(equal(group2, vec4(group2.x)));
+		freq0 += vec4(equal(group1, vec4(group2.y)));
+		freq1 += vec4(equal(group2, vec4(group2.y)));
+		freq0 += vec4(equal(group1, vec4(group2.z)));
+		freq1 += vec4(equal(group2, vec4(group2.z)));
+		freq0 += vec4(equal(group1, vec4(group2.w)));
+		freq1 += vec4(equal(group2, vec4(group2.w)));
+
+		float min_f0 = freq0.x;
+		int best_index0 = 0;
+		if (freq0.y < min_f0) {
+			best_index0 = 1;
+			min_f0 = freq0.y;
+		}
+		if (freq0.z < min_f0) {
+			best_index0 = 2;
+			min_f0 = freq0.z;
+		}
+		if (freq0.w < min_f0) {
+			best_index0 = 3;
+			min_f0 = freq0.w;
+		}
+
+		float min_f1 = freq1.x;
+		int best_index1 = 4;
+		if (freq1.y < min_f1) {
+			best_index1 = 5;
+			min_f1 = freq1.y;
+		}
+		if (freq1.z < min_f1) {
+			best_index1 = 6;
+			min_f1 = freq1.z;
+		}
+		if (freq1.w < min_f1) {
+			best_index1 = 7;
+			min_f1 = freq1.w;
+		}
+
+		best_index = mix(best_index0, best_index1, min_f0 < min_f1);
+	}
+
+#else
 	float depths[16];
 	int depth_indices[16];
 	int depth_amount[16];
@@ -91,7 +202,7 @@ void main() {
 			depth_least = depth_amount[j];
 		}
 	}
-
+#endif
 	best_depth = texelFetch(source_depth, pos, best_index).r;
 	best_normal_roughness = texelFetch(source_normal_roughness, pos, best_index);
 #ifdef GIPROBE_RESOLVE

+ 10 - 10
servers/rendering/renderer_rd/shaders/scene_forward.glsl

@@ -2371,17 +2371,17 @@ FRAGMENT_SHADER_CODE
 
 	if (bool(draw_call.flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
 
-		ivec2 coord;
+		vec2 coord;
 
 		if (scene_data.gi_upscale_for_msaa) {
-			ivec2 base_coord = ivec2(gl_FragCoord.xy);
-			ivec2 closest_coord = base_coord;
-			float closest_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0).xyz * 2.0 - 1.0);
+			vec2 base_coord = screen_uv;
+			vec2 closest_coord = base_coord;
+			float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0.0).xyz * 2.0 - 1.0);
 
 			for (int i = 0; i < 4; i++) {
-				const ivec2 neighbours[4] = ivec2[](ivec2(-1, 0), ivec2(1, 0), ivec2(0, -1), ivec2(0, 1));
-				ivec2 neighbour_coord = base_coord + neighbours[i];
-				float neighbour_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0).xyz * 2.0 - 1.0);
+				const vec2 neighbours[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1));
+				vec2 neighbour_coord = base_coord + neighbours[i] * scene_data.screen_pixel_size;
+				float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0.0).xyz * 2.0 - 1.0);
 				if (neighbour_ang > closest_ang) {
 					closest_ang = neighbour_ang;
 					closest_coord = neighbour_coord;
@@ -2391,11 +2391,11 @@ FRAGMENT_SHADER_CODE
 			coord = closest_coord;
 
 		} else {
-			coord = ivec2(gl_FragCoord.xy);
+			coord = screen_uv;
 		}
 
-		vec4 buffer_ambient = texelFetch(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
-		vec4 buffer_reflection = texelFetch(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
+		vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
+		vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
 
 		ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
 		specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a);

+ 3 - 104
servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl

@@ -97,6 +97,8 @@ void main() {
 	float blend = 0.0;
 
 #if 1
+	// No interpolation
+
 	vec3 inv_dir = 1.0 / ray_dir;
 
 	float rough = 0.5;
@@ -161,114 +163,11 @@ void main() {
 
 		hit_light *= (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
 
-		if (blend > 0.0) {
-			light = mix(light, hit_light, blend);
-			blend = 0.0;
-		} else {
-			light = hit_light;
-
-			//process blend
-			float blend_from = (float(params.probe_axis_size - 1) / 2.0) - 2.5;
-			float blend_to = blend_from + 2.0;
-
-			vec3 cam_pos = params.cam_transform[3].xyz - cascades.data[i].offset;
-			cam_pos *= cascades.data[i].to_cell;
-
-			pos += ray_dir * min(advance, max_advance);
-			vec3 inner_pos = pos - cam_pos;
-
-			inner_pos = inner_pos * float(params.probe_axis_size - 1) / params.grid_size.x;
-
-			float len = length(inner_pos);
-
-			inner_pos = abs(normalize(inner_pos));
-			len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
-
-			if (len >= blend_from) {
-				blend = smoothstep(blend_from, blend_to, len);
-
-				pos /= cascades.data[i].to_cell;
-				pos += cascades.data[i].offset;
-				ray_pos = pos;
-				hit = false; //continue trace for blend
-
-				continue;
-			}
-		}
+		light = hit_light;
 
 		break;
 	}
 
-	light = mix(light, vec3(0.0), blend);
-
-#else
-
-	vec3 inv_dir = 1.0 / ray_dir;
-
-	bool hit = false;
-	vec4 light_accum = vec4(0.0);
-
-	float blend_size = (params.grid_size.x / float(params.probe_axis_size - 1)) * 0.5;
-
-	float radius_sizes[MAX_CASCADES];
-	for (uint i = 0; i < params.max_cascades; i++) {
-		radius_sizes[i] = (1.0 / cascades.data[i].to_cell) * (params.grid_size.x * 0.5 - blend_size);
-	}
-
-	float max_distance = radius_sizes[params.max_cascades - 1];
-	float advance = 0;
-	while (advance < max_distance) {
-		for (uint i = 0; i < params.max_cascades; i++) {
-			if (advance < radius_sizes[i]) {
-				vec3 pos = (ray_pos + ray_dir * advance) - cascades.data[i].offset;
-				pos *= cascades.data[i].to_cell * pos_to_uvw;
-
-				float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.0;
-
-				vec4 hit_light = vec4(0.0);
-				if (distance < 1.0) {
-					hit_light.a = max(0.0, 1.0 - distance);
-					hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
-					hit_light.rgb *= hit_light.a;
-				}
-
-				distance /= cascades.data[i].to_cell;
-
-				if (i < (params.max_cascades - 1)) {
-					pos = (ray_pos + ray_dir * advance) - cascades.data[i + 1].offset;
-					pos *= cascades.data[i + 1].to_cell * pos_to_uvw;
-
-					float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.0;
-
-					vec4 hit_light2 = vec4(0.0);
-					if (distance2 < 1.0) {
-						hit_light2.a = max(0.0, 1.0 - distance2);
-						hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
-						hit_light2.rgb *= hit_light2.a;
-					}
-
-					float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
-					float blend = (advance - prev_radius) / (radius_sizes[i] - prev_radius);
-
-					distance2 /= cascades.data[i + 1].to_cell;
-
-					hit_light = mix(hit_light, hit_light2, blend);
-					distance = mix(distance, distance2, blend);
-				}
-
-				light_accum += hit_light;
-				advance += distance;
-				break;
-			}
-		}
-
-		if (light_accum.a > 0.98) {
-			break;
-		}
-	}
-
-	light = light_accum.rgb / light_accum.a;
-
 #endif
 
 	imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0));

+ 4 - 1
servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl

@@ -125,7 +125,10 @@ void main() {
 	uint voxel_index = uint(gl_GlobalInvocationID.x);
 
 	//used for skipping voxels every N frames
-	voxel_index = params.process_offset + voxel_index * params.process_increment;
+	if (params.process_increment > 1) {
+		voxel_index *= params.process_increment;
+		voxel_index += params.process_offset;
+	}
 
 	if (voxel_index >= dispatch_data.total_count) {
 		return;

+ 33 - 26
servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl

@@ -189,14 +189,12 @@ void main() {
 		vec3 inv_dir = 1.0 / ray_dir;
 
 		bool hit = false;
-		vec3 hit_normal;
-		vec3 hit_light;
-		vec3 hit_aniso0;
-		vec3 hit_aniso1;
+		uint hit_cascade;
 
 		float bias = params.ray_bias;
 		vec3 abs_ray_dir = abs(ray_dir);
 		ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell;
+		vec3 uvw;
 
 		for (uint j = params.cascade; j < params.max_cascades; j++) {
 			//convert to local bounds
@@ -215,14 +213,12 @@ void main() {
 
 			float advance = 0.0;
 
-			vec3 uvw;
-
 			while (advance < max_advance) {
 				//read how much to advance from SDF
 				uvw = (pos + ray_dir * advance) * pos_to_uvw;
 
 				float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
-				if (distance < 0.001) {
+				if (distance < 0.05) {
 					//consider hit
 					hit = true;
 					break;
@@ -232,17 +228,7 @@ void main() {
 			}
 
 			if (hit) {
-				const float EPSILON = 0.001;
-				hit_normal = normalize(vec3(
-						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
-						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
-						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
-
-				hit_light = texture(sampler3D(light_cascades[j], linear_sampler), uvw).rgb;
-				vec4 aniso0 = texture(sampler3D(aniso0_cascades[j], linear_sampler), uvw);
-				hit_aniso0 = aniso0.rgb;
-				hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[j], linear_sampler), uvw).rg);
-
+				hit_cascade = j;
 				break;
 			}
 
@@ -255,6 +241,17 @@ void main() {
 
 		vec4 light;
 		if (hit) {
+			const float EPSILON = 0.001;
+			vec3 hit_normal = normalize(vec3(
+					texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
+					texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
+					texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
+
+			vec3 hit_light = texture(sampler3D(light_cascades[hit_cascade], linear_sampler), uvw).rgb;
+			vec4 aniso0 = texture(sampler3D(aniso0_cascades[hit_cascade], linear_sampler), uvw);
+			vec3 hit_aniso0 = aniso0.rgb;
+			vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[hit_cascade], linear_sampler), uvw).rg);
+
 			//one liner magic
 			light.rgb = hit_light * (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
 			light.a = 1.0;
@@ -490,13 +487,15 @@ void main() {
 		//can't scroll, must look for position in parent cascade
 
 		//to global coords
-		float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+		float cell_to_probe = float(params.grid_size.x / float(params.probe_axis_size - 1));
+
+		float probe_cell_size = cell_to_probe / cascades.data[params.cascade].to_cell;
 		vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
 
 		//to parent local coords
+		float probe_cell_size_next = cell_to_probe / cascades.data[params.cascade + 1].to_cell;
 		probe_pos -= cascades.data[params.cascade + 1].offset;
-		probe_pos *= cascades.data[params.cascade + 1].to_cell;
-		probe_pos = probe_pos * float(params.probe_axis_size - 1) / float(params.grid_size.x);
+		probe_pos /= probe_cell_size_next;
 
 		ivec3 probe_posi = ivec3(probe_pos);
 		//add up all light, no need to use occlusion here, since occlusion will do its work afterwards
@@ -549,20 +548,28 @@ void main() {
 		}
 
 	} else {
-		// clear and let it re-raytrace, only for the last cascade, which happens very un-often
-		//scroll
+		//scroll at the edge of the highest cascade, just copy what is there,
+		//since its the closest we have anyway
+
 		for (uint j = 0; j < params.history_size; j++) {
+			ivec2 tex_pos;
+			tex_pos = probe_cell.xy;
+			tex_pos.x += probe_cell.z * int(params.probe_axis_size);
+
 			for (int i = 0; i < SH_SIZE; i++) {
 				// copy from history texture
+				ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j));
 				ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
-				imageStore(lightprobe_history_scroll_texture, dst_pos, ivec4(0));
+				ivec4 value = imageLoad(lightprobe_history_texture, dst_pos);
+				imageStore(lightprobe_history_scroll_texture, dst_pos, value);
 			}
 		}
 
 		for (int i = 0; i < SH_SIZE; i++) {
 			// copy from average texture
-			ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
-			imageStore(lightprobe_average_scroll_texture, dst_pos, ivec4(0));
+			ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i);
+			ivec4 average = imageLoad(lightprobe_average_texture, spos);
+			imageStore(lightprobe_average_scroll_texture, spos, average);
 		}
 	}
 

+ 3 - 0
servers/rendering/renderer_scene.h

@@ -140,6 +140,7 @@ public:
 
 	virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0;
 	virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0;
+	virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0;
 
 	virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0;
 
@@ -180,6 +181,8 @@ public:
 	virtual RID render_buffers_create() = 0;
 	virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0;
 
+	virtual void gi_set_use_half_resolution(bool p_enable) = 0;
+
 	virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0;
 
 	virtual TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) = 0;

+ 3 - 0
servers/rendering/renderer_scene_cull.h

@@ -795,6 +795,7 @@ public:
 			lightmaps.set_page_pool(p_rid_pool);
 			reflections.set_page_pool(p_rid_pool);
 			decals.set_page_pool(p_rid_pool);
+			gi_probes.set_page_pool(p_rid_pool);
 			mesh_instances.set_page_pool(p_rid_pool);
 			for (int i = 0; i < RendererSceneRender::MAX_DIRECTIONAL_LIGHTS; i++) {
 				for (int j = 0; j < RendererSceneRender::MAX_DIRECTIONAL_LIGHT_CASCADES; j++) {
@@ -995,6 +996,7 @@ public:
 	PASS11(environment_set_sdfgi, RID, bool, RS::EnvironmentSDFGICascades, float, RS::EnvironmentSDFGIYScale, bool, bool, bool, float, float, float)
 	PASS1(environment_set_sdfgi_ray_count, RS::EnvironmentSDFGIRayCount)
 	PASS1(environment_set_sdfgi_frames_to_converge, RS::EnvironmentSDFGIFramesToConverge)
+	PASS1(environment_set_sdfgi_frames_to_update_light, RS::EnvironmentSDFGIFramesToUpdateLight)
 
 	PASS1RC(RS::EnvironmentBG, environment_get_background, RID)
 	PASS1RC(int, environment_get_canvas_max_layer, RID)
@@ -1024,6 +1026,7 @@ public:
 
 	PASS0R(RID, render_buffers_create)
 	PASS7(render_buffers_configure, RID, RID, int, int, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool)
+	PASS1(gi_set_use_half_resolution, bool)
 
 	/* Shadow Atlas */
 	PASS0R(RID, shadow_atlas_create)

+ 2 - 0
servers/rendering/renderer_scene_render.h

@@ -136,6 +136,7 @@ public:
 
 	virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0;
 	virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0;
+	virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0;
 
 	virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0;
 
@@ -208,6 +209,7 @@ public:
 
 	virtual RID render_buffers_create() = 0;
 	virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0;
+	virtual void gi_set_use_half_resolution(bool p_enable) = 0;
 
 	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0;
 	virtual bool screen_space_roughness_limiter_is_active() const = 0;

+ 50 - 0
servers/rendering/rendering_server_default.cpp

@@ -162,6 +162,51 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
 	}
 
 	frame_profile_frame = RSG::storage->get_captured_timestamps_frame();
+
+	if (print_gpu_profile) {
+		if (print_frame_profile_ticks_from == 0) {
+			print_frame_profile_ticks_from = OS::get_singleton()->get_ticks_usec();
+		}
+		float total_time = 0.0;
+
+		for (int i = 0; i < frame_profile.size() - 1; i++) {
+			String name = frame_profile[i].name;
+			if (name[0] == '<' || name[0] == '>') {
+				continue;
+			}
+
+			float time = frame_profile[i + 1].gpu_msec - frame_profile[i].gpu_msec;
+
+			if (name[0] != '<' && name[0] != '>') {
+				if (print_gpu_profile_task_time.has(name)) {
+					print_gpu_profile_task_time[name] += time;
+				} else {
+					print_gpu_profile_task_time[name] = time;
+				}
+			}
+		}
+
+		if (frame_profile.size()) {
+			total_time = frame_profile[frame_profile.size() - 1].gpu_msec;
+		}
+
+		uint64_t ticks_elapsed = OS::get_singleton()->get_ticks_usec() - print_frame_profile_ticks_from;
+		print_frame_profile_frame_count++;
+		if (ticks_elapsed > 1000000) {
+			print_line("GPU PROFILE (total " + rtos(total_time) + "ms): ");
+
+			float print_threshold = 0.01;
+			for (OrderedHashMap<String, float>::Element E = print_gpu_profile_task_time.front(); E; E = E.next()) {
+				float time = E.value() / float(print_frame_profile_frame_count);
+				if (time > print_threshold) {
+					print_line("\t-" + E.key() + ": " + rtos(time) + "ms");
+				}
+			}
+			print_gpu_profile_task_time.clear();
+			print_frame_profile_ticks_from = OS::get_singleton()->get_ticks_usec();
+			print_frame_profile_frame_count = 0;
+		}
+	}
 }
 
 float RenderingServerDefault::get_frame_setup_time_cpu() const {
@@ -232,6 +277,11 @@ void RenderingServerDefault::sdfgi_set_debug_probe_select(const Vector3 &p_posit
 	RSG::scene->sdfgi_set_debug_probe_select(p_position, p_dir);
 }
 
+void RenderingServerDefault::set_print_gpu_profile(bool p_enable) {
+	RSG::storage->capturing_timestamps = p_enable;
+	print_gpu_profile = p_enable;
+}
+
 RID RenderingServerDefault::get_test_cube() {
 	if (!test_cube.is_valid()) {
 		test_cube = _make_test_cube();

+ 12 - 0
servers/rendering/rendering_server_default.h

@@ -32,6 +32,7 @@
 #define RENDERING_SERVER_DEFAULT_H
 
 #include "core/math/octree.h"
+#include "core/templates/ordered_hash_map.h"
 #include "renderer_canvas_cull.h"
 #include "renderer_scene_cull.h"
 #include "renderer_viewport.h"
@@ -74,6 +75,12 @@ class RenderingServerDefault : public RenderingServer {
 
 	float frame_setup_time = 0;
 
+	//for printing
+	bool print_gpu_profile = false;
+	OrderedHashMap<String, float> print_gpu_profile_task_time;
+	uint64_t print_frame_profile_ticks_from = 0;
+	uint32_t print_frame_profile_frame_count = 0;
+
 public:
 	//if editor is redrawing when it shouldn't, enable this and put a breakpoint in _changes_changed()
 	//#define DEBUG_CHANGES
@@ -616,6 +623,7 @@ public:
 	BIND11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, float, float, float)
 	BIND1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount)
 	BIND1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge)
+	BIND1(environment_set_sdfgi_frames_to_update_light, EnvironmentSDFGIFramesToUpdateLight)
 
 	BIND3R(Ref<Image>, environment_bake_panorama, RID, bool, const Size2i &)
 
@@ -688,6 +696,8 @@ public:
 
 	BIND3R(TypedArray<Image>, bake_render_uv2, RID, const Vector<RID> &, const Size2i &)
 
+	BIND1(gi_set_use_half_resolution, bool)
+
 #undef BINDBASE
 //from now on, calls forwarded to this singleton
 #define BINDBASE RSG::canvas
@@ -865,6 +875,8 @@ public:
 
 	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir);
 
+	virtual void set_print_gpu_profile(bool p_enable);
+
 	RenderingServerDefault();
 	~RenderingServerDefault();
 

+ 7 - 0
servers/rendering/rendering_server_wrap_mt.h

@@ -507,6 +507,7 @@ public:
 	FUNC11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, float, float, float)
 	FUNC1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount)
 	FUNC1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge)
+	FUNC1(environment_set_sdfgi_frames_to_update_light, EnvironmentSDFGIFramesToUpdateLight)
 
 	FUNC11(environment_set_glow, RID, bool, Vector<float>, float, float, float, float, EnvironmentGlowBlendMode, float, float, float)
 	FUNC1(environment_glow_set_use_bicubic_upscale, bool)
@@ -744,6 +745,8 @@ public:
 		return rendering_server->get_video_adapter_vendor();
 	}
 
+	FUNC1(gi_set_use_half_resolution, bool)
+
 	FUNC4(set_boot_image, const Ref<Image> &, const Color &, bool, bool)
 	FUNC1(set_default_clear_color, const Color &)
 
@@ -786,6 +789,10 @@ public:
 		rendering_server->sdfgi_set_debug_probe_select(p_position, p_dir);
 	}
 
+	virtual void set_print_gpu_profile(bool p_enable) {
+		rendering_server->set_print_gpu_profile(p_enable);
+	}
+
 	RenderingServerWrapMT(RenderingServer *p_contained, bool p_create_thread);
 	~RenderingServerWrapMT();
 

+ 6 - 2
servers/rendering_server.cpp

@@ -2304,6 +2304,8 @@ RenderingServer::RenderingServer() {
 	GLOBAL_DEF("rendering/quality/reflection_atlas/reflection_size.mobile", 128);
 	GLOBAL_DEF("rendering/quality/reflection_atlas/reflection_count", 64);
 
+	GLOBAL_DEF("rendering/quality/gi/use_half_resolution", false);
+
 	GLOBAL_DEF("rendering/quality/gi_probes/anisotropic", false);
 	GLOBAL_DEF("rendering/quality/gi_probes/quality", 1);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/gi_probes/quality", PropertyInfo(Variant::INT, "rendering/quality/gi_probes/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"));
@@ -2367,10 +2369,12 @@ RenderingServer::RenderingServer() {
 	GLOBAL_DEF("rendering/lightmapper/probe_capture_update_speed", 15);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/lightmapper/probe_capture_update_speed", PropertyInfo(Variant::FLOAT, "rendering/lightmapper/probe_capture_update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"));
 
-	GLOBAL_DEF("rendering/sdfgi/probe_ray_count", 2);
+	GLOBAL_DEF("rendering/sdfgi/probe_ray_count", 1);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/probe_ray_count", PropertyInfo(Variant::INT, "rendering/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"));
-	GLOBAL_DEF("rendering/sdfgi/frames_to_converge", 1);
+	GLOBAL_DEF("rendering/sdfgi/frames_to_converge", 4);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/frames_to_converge", PropertyInfo(Variant::INT, "rendering/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"));
+	GLOBAL_DEF("rendering/sdfgi/frames_to_update_lights", 2);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/frames_to_update_lights", PropertyInfo(Variant::INT, "rendering/sdfgi/frames_to_update_lights", PROPERTY_HINT_ENUM, "1 (Slower),2,4,8,16 (Faster)"));
 
 	GLOBAL_DEF("rendering/volumetric_fog/volume_size", 64);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/volume_size", PropertyInfo(Variant::INT, "rendering/volumetric_fog/volume_size", PROPERTY_HINT_RANGE, "16,512,1"));

+ 16 - 0
servers/rendering_server.h

@@ -986,6 +986,7 @@ public:
 	virtual void environment_set_sdfgi(RID p_env, bool p_enable, EnvironmentSDFGICascades p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, bool p_use_multibounce, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
 
 	enum EnvironmentSDFGIRayCount {
+		ENV_SDFGI_RAY_COUNT_4,
 		ENV_SDFGI_RAY_COUNT_8,
 		ENV_SDFGI_RAY_COUNT_16,
 		ENV_SDFGI_RAY_COUNT_32,
@@ -1009,6 +1010,17 @@ public:
 
 	virtual void environment_set_sdfgi_frames_to_converge(EnvironmentSDFGIFramesToConverge p_frames) = 0;
 
+	enum EnvironmentSDFGIFramesToUpdateLight {
+		ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME,
+		ENV_SDFGI_UPDATE_LIGHT_IN_2_FRAMES,
+		ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES,
+		ENV_SDFGI_UPDATE_LIGHT_IN_8_FRAMES,
+		ENV_SDFGI_UPDATE_LIGHT_IN_16_FRAMES,
+		ENV_SDFGI_UPDATE_LIGHT_MAX,
+	};
+
+	virtual void environment_set_sdfgi_frames_to_update_light(EnvironmentSDFGIFramesToUpdateLight p_update) = 0;
+
 	virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) = 0;
 
 	enum EnvVolumetricFogShadowFilter {
@@ -1430,6 +1442,8 @@ public:
 
 	virtual float get_frame_setup_time_cpu() const = 0;
 
+	virtual void gi_set_use_half_resolution(bool p_enable) = 0;
+
 	/* TESTING */
 
 	virtual RID get_test_cube() = 0;
@@ -1462,6 +1476,8 @@ public:
 
 	virtual bool is_low_end() const = 0;
 
+	virtual void set_print_gpu_profile(bool p_enable) = 0;
+
 	RenderingDevice *create_local_rendering_device() const;
 
 	bool is_render_loop_enabled() const;