Browse Source

Add support for low-end 3D rendering.

-Reduce number of uniform sets from 6 to 4.
-Remove features in low end mode, in order to reduce the number of texture units fit to 16.
reduz 4 years ago
parent
commit
2748b9a10d

+ 239 - 227
servers/rendering/renderer_rd/renderer_scene_render_forward.cpp

@@ -257,6 +257,9 @@ void RendererSceneRenderForward::ShaderData::set_code(const String &p_code) {
 			RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j];
 
 			for (int k = 0; k < SHADER_VERSION_MAX; k++) {
+				if (!static_cast<RendererSceneRenderForward *>(singleton)->shader.scene_shader.is_variant_enabled(k)) {
+					continue;
+				}
 				RD::PipelineRasterizationState raster_state;
 				raster_state.cull_mode = cull_mode_rd;
 				raster_state.wireframe = wireframe;
@@ -516,11 +519,11 @@ RendererStorageRD::MaterialData *RendererSceneRenderForward::_create_material_fu
 	return material_data;
 }
 
-RendererSceneRenderForward::RenderBufferDataHighEnd::~RenderBufferDataHighEnd() {
+RendererSceneRenderForward::RenderBufferDataForward::~RenderBufferDataForward() {
 	clear();
 }
 
-void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_specular() {
+void RendererSceneRenderForward::RenderBufferDataForward::ensure_specular() {
 	if (!specular.is_valid()) {
 		RD::TextureFormat tf;
 		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
@@ -574,7 +577,7 @@ void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_specular() {
 	}
 }
 
-void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_gi() {
+void RendererSceneRenderForward::RenderBufferDataForward::ensure_gi() {
 	if (!reflection_buffer.is_valid()) {
 		RD::TextureFormat tf;
 		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
@@ -587,7 +590,7 @@ void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_gi() {
 	}
 }
 
-void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_giprobe() {
+void RendererSceneRenderForward::RenderBufferDataForward::ensure_giprobe() {
 	if (!giprobe_buffer.is_valid()) {
 		RD::TextureFormat tf;
 		tf.format = RD::DATA_FORMAT_R8G8_UINT;
@@ -623,7 +626,7 @@ void RendererSceneRenderForward::RenderBufferDataHighEnd::ensure_giprobe() {
 	}
 }
 
-void RendererSceneRenderForward::RenderBufferDataHighEnd::clear() {
+void RendererSceneRenderForward::RenderBufferDataForward::clear() {
 	if (ambient_buffer != RID() && ambient_buffer != color) {
 		RD::get_singleton()->free(ambient_buffer);
 		ambient_buffer = RID();
@@ -687,7 +690,7 @@ void RendererSceneRenderForward::RenderBufferDataHighEnd::clear() {
 	}
 }
 
-void RendererSceneRenderForward::RenderBufferDataHighEnd::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) {
+void RendererSceneRenderForward::RenderBufferDataForward::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) {
 	clear();
 
 	msaa = p_msaa;
@@ -754,7 +757,7 @@ void RendererSceneRenderForward::RenderBufferDataHighEnd::configure(RID p_color_
 	}
 }
 
-void RendererSceneRenderForward::_allocate_normal_roughness_texture(RenderBufferDataHighEnd *rb) {
+void RendererSceneRenderForward::_allocate_normal_roughness_texture(RenderBufferDataForward *rb) {
 	if (rb->normal_roughness_buffer.is_valid()) {
 		return;
 	}
@@ -793,7 +796,7 @@ void RendererSceneRenderForward::_allocate_normal_roughness_texture(RenderBuffer
 }
 
 RendererSceneRenderRD::RenderBufferData *RendererSceneRenderForward::_create_render_buffer_data() {
-	return memnew(RenderBufferDataHighEnd);
+	return memnew(RenderBufferDataForward);
 }
 
 bool RendererSceneRenderForward::free(RID p_rid) {
@@ -911,7 +914,7 @@ void RendererSceneRenderForward::_fill_instances(RenderList::Element **p_element
 				id.flags |= INSTANCE_DATA_FLAG_USE_GI_BUFFERS;
 			}
 
-			if (!e->instance->gi_probe_instances.empty()) {
+			if (!low_end && !e->instance->gi_probe_instances.empty()) {
 				uint32_t written = 0;
 				for (int j = 0; j < e->instance->gi_probe_instances.size(); j++) {
 					RID probe = e->instance->gi_probe_instances[j];
@@ -950,23 +953,13 @@ void RendererSceneRenderForward::_fill_instances(RenderList::Element **p_element
 
 /// RENDERING ///
 
-void RendererSceneRenderForward::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set, bool p_force_wireframe, const Vector2 &p_uv_offset) {
+void RendererSceneRenderForward::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_render_pass_uniform_set, bool p_force_wireframe, const Vector2 &p_uv_offset) {
 	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);
-	if (p_radiance_uniform_set.is_valid()) {
-		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_radiance_uniform_set, RADIANCE_UNIFORM_SET);
-	} else {
-		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_radiance_uniform_set, RADIANCE_UNIFORM_SET);
-	}
-	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, view_dependant_uniform_set, VIEW_DEPENDANT_UNIFORM_SET);
-	if (p_render_buffers_uniform_set.is_valid()) {
-		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET);
-	} else {
-		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET);
-	}
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_render_pass_uniform_set, RENDER_PASS_UNIFORM_SET);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_vec4_xform_uniform_set, TRANSFORMS_UNIFORM_SET);
 
 	MaterialData *prev_material = nullptr;
@@ -1214,7 +1207,7 @@ void RendererSceneRenderForward::_setup_environment(RID p_environment, RID p_ren
 	scene_state.ubo.fog_enabled = false;
 
 	if (p_render_buffers.is_valid()) {
-		RenderBufferDataHighEnd *render_buffers = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
+		RenderBufferDataForward *render_buffers = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
 		if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) {
 			scene_state.ubo.gi_upscale_for_msaa = true;
 		}
@@ -1639,9 +1632,9 @@ void RendererSceneRenderForward::_setup_lightmaps(InstanceBase **p_lightmap_cull
 }
 
 void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, int p_directional_light_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) {
-	RenderBufferDataHighEnd *render_buffer = nullptr;
+	RenderBufferDataForward *render_buffer = nullptr;
 	if (p_render_buffer.is_valid()) {
-		render_buffer = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffer);
+		render_buffer = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffer);
 	}
 
 	//first of all, make a new render pass
@@ -1686,7 +1679,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 
 		opaque_framebuffer = render_buffer->color_fb;
 
-		if (p_gi_probe_cull_count > 0) {
+		if (!low_end && p_gi_probe_cull_count > 0) {
 			using_giprobe = true;
 			render_buffer->ensure_gi();
 		}
@@ -1761,7 +1754,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 	render_list.clear();
 	_fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, using_sdfgi);
 
-	bool using_sss = render_buffer && scene_state.used_sss && sub_surface_scattering_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED;
+	bool using_sss = !low_end && render_buffer && scene_state.used_sss && sub_surface_scattering_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED;
 
 	if (using_sss) {
 		using_separate_specular = true;
@@ -1769,7 +1762,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 		using_separate_specular = true;
 		opaque_specular_framebuffer = render_buffer->color_specular_fb;
 	}
-	RID radiance_uniform_set;
+	RID radiance_texture;
 	bool draw_sky = false;
 	bool draw_sky_fog_only = false;
 
@@ -1831,7 +1824,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 			RID sky = environment_get_sky(p_environment);
 			if (sky.is_valid()) {
 				_update_sky(p_environment, projection, p_cam_transform);
-				radiance_uniform_set = sky_get_radiance_uniform_set_rd(sky, default_shader_rd, RADIANCE_UNIFORM_SET);
+				radiance_texture = sky_get_radiance_texture_rd(sky);
 			} else {
 				// do not try to draw sky if invalid
 				draw_sky = false;
@@ -1841,7 +1834,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 		clear_color = p_default_bg_color;
 	}
 
-	_setup_view_dependant_uniform_set(p_shadow_atlas, p_reflection_atlas, p_gi_probe_cull_result, p_gi_probe_cull_count);
+	RID rp_uniform_set = _setup_render_pass_uniform_set(p_render_buffer, radiance_texture, p_shadow_atlas, p_reflection_atlas, p_gi_probe_cull_result, p_gi_probe_cull_count);
 
 	render_list.sort_by_key(false);
 
@@ -1850,8 +1843,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 	bool debug_giprobes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION;
 	bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES;
 
-	bool depth_pre_pass = depth_framebuffer.is_valid();
-	RID render_buffers_uniform_set;
+	bool depth_pre_pass = !low_end && depth_framebuffer.is_valid();
 
 	bool using_ssao = depth_pre_pass && p_render_buffer.is_valid() && p_environment.is_valid() && environment_is_ssao_enabled(p_environment);
 	bool continue_depth = false;
@@ -1860,7 +1852,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 
 		bool finish_depth = using_ssao || using_sdfgi || using_giprobe;
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, depth_pass_clear);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID(), get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 
 		if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
@@ -1884,12 +1876,6 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 		_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_probe_cull_result, p_gi_probe_cull_count);
 	}
 
-	if (p_render_buffer.is_valid()) {
-		//update the render buffers uniform set in case it changed
-		_update_render_buffers_uniform_set(p_render_buffer);
-		render_buffers_uniform_set = render_buffer->uniform_set;
-	}
-
 	_setup_environment(p_environment, p_render_buffer, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, 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());
 
 	RENDER_TIMESTAMP("Render Opaque Pass");
@@ -1914,7 +1900,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 
 		RID framebuffer = using_separate_specular ? opaque_specular_framebuffer : opaque_framebuffer;
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(framebuffer), render_list.elements, render_list.element_count, false, using_separate_specular ? PASS_MODE_COLOR_SPECULAR : PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(framebuffer), render_list.elements, render_list.element_count, false, using_separate_specular ? PASS_MODE_COLOR_SPECULAR : PASS_MODE_COLOR, render_buffer == nullptr, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 
 		if (will_continue_color && using_separate_specular) {
@@ -2002,7 +1988,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
 
 	{
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 	}
 
@@ -2028,7 +2014,7 @@ void RendererSceneRenderForward::_render_shadow(RID p_framebuffer, InstanceBase
 
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode);
 
-	_setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+	RID rp_uniform_set = _setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), nullptr, 0);
 
 	RENDER_TIMESTAMP("Render Shadow");
 
@@ -2039,7 +2025,7 @@ void RendererSceneRenderForward::_render_shadow(RID p_framebuffer, InstanceBase
 	{
 		//regular forward for now
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, p_use_dp_flip, pass_mode, true, RID(), RID());
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, p_use_dp_flip, pass_mode, true, rp_uniform_set);
 		RD::get_singleton()->draw_list_end();
 	}
 }
@@ -2061,7 +2047,7 @@ void RendererSceneRenderForward::_render_particle_collider_heightfield(RID p_fb,
 
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode);
 
-	_setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+	RID rp_uniform_set = _setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), nullptr, 0);
 
 	RENDER_TIMESTAMP("Render Collider Heightield");
 
@@ -2072,7 +2058,7 @@ void RendererSceneRenderForward::_render_particle_collider_heightfield(RID p_fb,
 	{
 		//regular forward for now
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_fb), render_list.elements, render_list.element_count, false, pass_mode, true, RID(), RID());
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_fb), render_list.elements, render_list.element_count, false, pass_mode, true, rp_uniform_set);
 		RD::get_singleton()->draw_list_end();
 	}
 }
@@ -2094,7 +2080,7 @@ void RendererSceneRenderForward::_render_material(const Transform &p_cam_transfo
 	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode);
 
-	_setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+	RID rp_uniform_set = _setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), nullptr, 0);
 
 	RENDER_TIMESTAMP("Render Material");
 
@@ -2111,7 +2097,7 @@ void RendererSceneRenderForward::_render_material(const Transform &p_cam_transfo
 		clear.push_back(Color(0, 0, 0, 0));
 		clear.push_back(Color(0, 0, 0, 0));
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID());
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, rp_uniform_set);
 		RD::get_singleton()->draw_list_end();
 	}
 }
@@ -2133,7 +2119,7 @@ void RendererSceneRenderForward::_render_uv2(InstanceBase **p_cull_result, int p
 	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode);
 
-	_setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+	RID rp_uniform_set = _setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), nullptr, 0);
 
 	RENDER_TIMESTAMP("Render Material");
 
@@ -2169,9 +2155,9 @@ void RendererSceneRenderForward::_render_uv2(InstanceBase **p_cull_result, int p
 			Vector2 ofs = uv_offsets[i];
 			ofs.x /= p_region.size.width;
 			ofs.y /= p_region.size.height;
-			_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID(), true, ofs); //first wireframe, for pseudo conservative
+			_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, rp_uniform_set, true, ofs); //first wireframe, for pseudo conservative
 		}
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID(), false); //second regular triangles
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, rp_uniform_set, false); //second regular triangles
 
 		RD::get_singleton()->draw_list_end();
 	}
@@ -2182,7 +2168,7 @@ void RendererSceneRenderForward::_render_sdfgi(RID p_render_buffers, const Vecto
 
 	_update_render_base_uniform_set();
 
-	RenderBufferDataHighEnd *render_buffer = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
+	RenderBufferDataForward *render_buffer = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
 	ERR_FAIL_COND(!render_buffer);
 
 	render_pass++;
@@ -2193,45 +2179,11 @@ void RendererSceneRenderForward::_render_sdfgi(RID p_render_buffers, const Vecto
 	render_list.sort_by_key(false);
 	_fill_instances(render_list.elements, render_list.element_count, true);
 
-	_setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+	RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture);
 
 	Vector3 half_extents = p_bounds.size * 0.5;
 	Vector3 center = p_bounds.position + half_extents;
 
-	if (render_buffer->render_sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_buffer->render_sdfgi_uniform_set)) {
-		Vector<RD::Uniform> uniforms;
-		{
-			RD::Uniform u;
-			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
-			u.binding = 0;
-			u.ids.push_back(p_albedo_texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
-			u.binding = 1;
-			u.ids.push_back(p_emission_texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
-			u.binding = 2;
-			u.ids.push_back(p_emission_aniso_texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
-			u.binding = 3;
-			u.ids.push_back(p_geom_facing_texture);
-			uniforms.push_back(u);
-		}
-
-		render_buffer->render_sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_sdfgi_rd, RENDER_BUFFERS_UNIFORM_SET);
-	}
-
 	Vector<RID> sbs;
 	sbs.push_back(p_albedo_texture);
 	sbs.push_back(p_emission_texture);
@@ -2287,7 +2239,7 @@ void RendererSceneRenderForward::_render_sdfgi(RID p_render_buffers, const Vecto
 		}
 
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(E->get(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), sbs);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(E->get()), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), render_buffer->render_sdfgi_uniform_set, false); //second regular triangles
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(E->get()), render_list.elements, render_list.element_count, true, pass_mode, true, rp_uniform_set, false); //second regular triangles
 		RD::get_singleton()->draw_list_end();
 	}
 }
@@ -2455,7 +2407,7 @@ void RendererSceneRenderForward::_update_render_base_uniform_set() {
 			uniforms.push_back(u);
 		}
 
-		{
+		if (!low_end) {
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
 			u.binding = 20;
@@ -2467,9 +2419,14 @@ void RendererSceneRenderForward::_update_render_base_uniform_set() {
 	}
 }
 
-void RendererSceneRenderForward::_setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count) {
-	if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) {
-		RD::get_singleton()->free(view_dependant_uniform_set);
+RID RendererSceneRenderForward::_setup_render_pass_uniform_set(RID p_render_buffers, RID p_radiance_texture, RID p_shadow_atlas, RID p_reflection_atlas, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count) {
+	if (render_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_pass_uniform_set)) {
+		RD::get_singleton()->free(render_pass_uniform_set);
+	}
+
+	RenderBufferDataForward *rb = nullptr;
+	if (p_render_buffers.is_valid()) {
+		rb = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
 	}
 
 	//default render buffer and scene state uniform set
@@ -2477,10 +2434,24 @@ void RendererSceneRenderForward::_setup_view_dependant_uniform_set(RID p_shadow_
 	Vector<RD::Uniform> uniforms;
 
 	{
-		RID ref_texture = p_reflection_atlas.is_valid() ? reflection_atlas_get_texture(p_reflection_atlas) : RID();
+		RID radiance_texture;
+		if (p_radiance_texture.is_valid()) {
+			radiance_texture = p_radiance_texture;
+		} else {
+			radiance_texture = storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
+		}
 		RD::Uniform u;
 		u.binding = 0;
 		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		u.ids.push_back(radiance_texture);
+		uniforms.push_back(u);
+	}
+
+	{
+		RID ref_texture = p_reflection_atlas.is_valid() ? reflection_atlas_get_texture(p_reflection_atlas) : RID();
+		RD::Uniform u;
+		u.binding = 1;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 		if (ref_texture.is_valid()) {
 			u.ids.push_back(ref_texture);
 		} else {
@@ -2491,7 +2462,7 @@ void RendererSceneRenderForward::_setup_view_dependant_uniform_set(RID p_shadow_
 
 	{
 		RD::Uniform u;
-		u.binding = 1;
+		u.binding = 2;
 		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 		RID texture;
 		if (p_shadow_atlas.is_valid()) {
@@ -2506,8 +2477,9 @@ void RendererSceneRenderForward::_setup_view_dependant_uniform_set(RID p_shadow_
 
 	{
 		RD::Uniform u;
-		u.binding = 2;
+		u.binding = 3;
 		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		u.ids.resize(MAX_GI_PROBES);
 		RID default_tex = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
 		for (int i = 0; i < MAX_GI_PROBES; i++) {
 			if (i < p_gi_probe_cull_count) {
@@ -2515,84 +2487,47 @@ void RendererSceneRenderForward::_setup_view_dependant_uniform_set(RID p_shadow_
 				if (!tex.is_valid()) {
 					tex = default_tex;
 				}
-				u.ids.push_back(tex);
+				u.ids.write[i] = tex;
 			} else {
-				u.ids.push_back(default_tex);
+				u.ids.write[i] = default_tex;
 			}
 		}
 
 		uniforms.push_back(u);
 	}
-	view_dependant_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, VIEW_DEPENDANT_UNIFORM_SET);
-}
 
-void RendererSceneRenderForward::_render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb) {
-	if (!rb->uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) {
-		RD::get_singleton()->free(rb->uniform_set);
+	{
+		RD::Uniform u;
+		u.binding = 4;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		RID texture = false && rb && rb->depth.is_valid() ? rb->depth : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+		u.ids.push_back(texture);
+		uniforms.push_back(u);
 	}
-	rb->uniform_set = RID();
-}
-
-void RendererSceneRenderForward::_render_buffers_uniform_set_changed(RID p_render_buffers) {
-	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
-
-	_render_buffers_clear_uniform_set(rb);
-}
-
-RID RendererSceneRenderForward::_render_buffers_get_normal_texture(RID p_render_buffers) {
-	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
-
-	return rb->normal_roughness_buffer;
-}
-
-RID RendererSceneRenderForward::_render_buffers_get_ambient_texture(RID p_render_buffers) {
-	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
-
-	return rb->ambient_buffer;
-}
-
-RID RendererSceneRenderForward::_render_buffers_get_reflection_texture(RID p_render_buffers) {
-	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
-
-	return rb->reflection_buffer;
-}
-
-void RendererSceneRenderForward::_update_render_buffers_uniform_set(RID p_render_buffers) {
-	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
-
-	if (rb->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) {
-		Vector<RD::Uniform> uniforms;
-		{
-			RD::Uniform u;
-			u.binding = 0;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = false && rb->depth.is_valid() ? rb->depth : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
-			u.ids.push_back(texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.binding = 1;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID bbt = render_buffers_get_back_buffer_texture(p_render_buffers);
-			RID texture = bbt.is_valid() ? bbt : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
-			u.ids.push_back(texture);
-			uniforms.push_back(u);
-		}
+	{
+		RD::Uniform u;
+		u.binding = 5;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		RID bbt = rb ? render_buffers_get_back_buffer_texture(p_render_buffers) : RID();
+		RID texture = bbt.is_valid() ? bbt : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+		u.ids.push_back(texture);
+		uniforms.push_back(u);
+	}
+	if (!low_end) {
 		{
 			RD::Uniform u;
-			u.binding = 2;
+			u.binding = 6;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = rb->normal_roughness_buffer.is_valid() ? rb->normal_roughness_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_NORMAL);
+			RID texture = rb && rb->normal_roughness_buffer.is_valid() ? rb->normal_roughness_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_NORMAL);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
 		}
 
 		{
 			RD::Uniform u;
-			u.binding = 4;
+			u.binding = 7;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID aot = render_buffers_get_ao_texture(p_render_buffers);
+			RID aot = rb ? render_buffers_get_ao_texture(p_render_buffers) : RID();
 			RID texture = aot.is_valid() ? aot : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
@@ -2600,27 +2535,27 @@ void RendererSceneRenderForward::_update_render_buffers_uniform_set(RID p_render
 
 		{
 			RD::Uniform u;
-			u.binding = 5;
+			u.binding = 8;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = rb->ambient_buffer.is_valid() ? rb->ambient_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			RID texture = rb && rb->ambient_buffer.is_valid() ? rb->ambient_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
 		}
 
 		{
 			RD::Uniform u;
-			u.binding = 6;
+			u.binding = 9;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = rb->reflection_buffer.is_valid() ? rb->reflection_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			RID texture = rb && rb->reflection_buffer.is_valid() ? rb->reflection_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
 			u.ids.push_back(texture);
 			uniforms.push_back(u);
 		}
 		{
 			RD::Uniform u;
-			u.binding = 7;
+			u.binding = 10;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 			RID t;
-			if (render_buffers_is_sdfgi_enabled(p_render_buffers)) {
+			if (rb && render_buffers_is_sdfgi_enabled(p_render_buffers)) {
 				t = render_buffers_get_sdfgi_irradiance_probes(p_render_buffers);
 			} else {
 				t = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE);
@@ -2630,9 +2565,9 @@ void RendererSceneRenderForward::_update_render_buffers_uniform_set(RID p_render
 		}
 		{
 			RD::Uniform u;
-			u.binding = 8;
+			u.binding = 11;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			if (render_buffers_is_sdfgi_enabled(p_render_buffers)) {
+			if (rb && render_buffers_is_sdfgi_enabled(p_render_buffers)) {
 				u.ids.push_back(render_buffers_get_sdfgi_occlusion_texture(p_render_buffers));
 			} else {
 				u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
@@ -2641,17 +2576,17 @@ void RendererSceneRenderForward::_update_render_buffers_uniform_set(RID p_render
 		}
 		{
 			RD::Uniform u;
-			u.binding = 9;
+			u.binding = 12;
 			u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
-			u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers));
+			u.ids.push_back(rb ? render_buffers_get_default_gi_probe_buffer() : render_buffers_get_gi_probe_buffer(p_render_buffers));
 			uniforms.push_back(u);
 		}
 		{
 			RD::Uniform u;
-			u.binding = 10;
+			u.binding = 13;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 			RID vfog = RID();
-			if (p_render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_buffers)) {
+			if (rb && render_buffers_has_volumetric_fog(p_render_buffers)) {
 				vfog = render_buffers_get_volumetric_fog_texture(p_render_buffers);
 				if (vfog.is_null()) {
 					vfog = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
@@ -2662,8 +2597,122 @@ void RendererSceneRenderForward::_update_render_buffers_uniform_set(RID p_render
 			u.ids.push_back(vfog);
 			uniforms.push_back(u);
 		}
-		rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET);
 	}
+
+	render_pass_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_PASS_UNIFORM_SET);
+	return render_pass_uniform_set;
+}
+
+RID RendererSceneRenderForward::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture) {
+	if (sdfgi_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sdfgi_pass_uniform_set)) {
+		RD::get_singleton()->free(sdfgi_pass_uniform_set);
+	}
+
+	Vector<RD::Uniform> uniforms;
+
+	{
+		// No radiance texture.
+		RID radiance_texture = storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
+		RD::Uniform u;
+		u.binding = 0;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		u.ids.push_back(radiance_texture);
+		uniforms.push_back(u);
+	}
+
+	{
+		// No reflection atlas.
+		RID ref_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK);
+		RD::Uniform u;
+		u.binding = 1;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		u.ids.push_back(ref_texture);
+		uniforms.push_back(u);
+	}
+
+	{
+		// No shadow atlas.
+		RD::Uniform u;
+		u.binding = 2;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		RID texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+		u.ids.push_back(texture);
+		uniforms.push_back(u);
+	}
+
+	{
+		// No GIProbes
+		RD::Uniform u;
+		u.binding = 3;
+		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+		u.ids.resize(MAX_GI_PROBES);
+		RID default_tex = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
+		for (int i = 0; i < MAX_GI_PROBES; i++) {
+			u.ids.write[i] = default_tex;
+		}
+
+		uniforms.push_back(u);
+	}
+	// actual sdfgi stuff
+
+	{
+		RD::Uniform u;
+		u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+		u.binding = 4;
+		u.ids.push_back(p_albedo_texture);
+		uniforms.push_back(u);
+	}
+	{
+		RD::Uniform u;
+		u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+		u.binding = 5;
+		u.ids.push_back(p_emission_texture);
+		uniforms.push_back(u);
+	}
+	{
+		RD::Uniform u;
+		u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+		u.binding = 6;
+		u.ids.push_back(p_emission_aniso_texture);
+		uniforms.push_back(u);
+	}
+	{
+		RD::Uniform u;
+		u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+		u.binding = 7;
+		u.ids.push_back(p_geom_facing_texture);
+		uniforms.push_back(u);
+	}
+
+	sdfgi_pass_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_PASS_UNIFORM_SET);
+	return sdfgi_pass_uniform_set;
+}
+
+void RendererSceneRenderForward::_render_buffers_clear_uniform_set(RenderBufferDataForward *rb) {
+}
+
+void RendererSceneRenderForward::_render_buffers_uniform_set_changed(RID p_render_buffers) {
+	RenderBufferDataForward *rb = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
+
+	_render_buffers_clear_uniform_set(rb);
+}
+
+RID RendererSceneRenderForward::_render_buffers_get_normal_texture(RID p_render_buffers) {
+	RenderBufferDataForward *rb = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffers);
+
+	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;
@@ -2676,12 +2725,17 @@ void RendererSceneRenderForward::set_time(double p_time, double p_step) {
 RendererSceneRenderForward::RendererSceneRenderForward(RendererStorageRD *p_storage) :
 		RendererSceneRenderRD(p_storage) {
 	singleton = this;
+	low_end = is_low_end();
 	storage = p_storage;
 
 	/* SCENE SHADER */
 
 	{
 		String defines;
+		if (low_end) {
+			defines += "\n#define LOW_END_MODE \n";
+		}
+
 		defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n";
 		if (is_using_radiance_cubemap_array()) {
 			defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n";
@@ -2721,6 +2775,16 @@ RendererSceneRenderForward::RendererSceneRenderForward(RendererStorageRD *p_stor
 		shader_versions.push_back("\n#define USE_LIGHTMAP\n");
 		shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n");
 		shader.scene_shader.initialize(shader_versions, defines);
+
+		if (is_low_end()) {
+			//disable the high end versions
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS, false);
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE, false);
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_SDF, false);
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI, false);
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR, false);
+			shader.scene_shader.set_variant_enabled(SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR, false);
+		}
 	}
 
 	storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_3D, _create_shader_funcs);
@@ -2924,7 +2988,9 @@ RendererSceneRenderForward::RendererSceneRenderForward(RendererStorageRD *p_stor
 
 		MaterialData *md = (MaterialData *)storage->material_get_data(default_material, RendererStorageRD::SHADER_TYPE_3D);
 		default_shader_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS);
-		default_shader_sdfgi_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF);
+		if (!low_end) {
+			default_shader_sdfgi_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF);
+		}
 	}
 
 	{
@@ -2958,74 +3024,20 @@ RendererSceneRenderForward::RendererSceneRenderForward(RendererStorageRD *p_stor
 		sampler.compare_op = RD::COMPARE_OP_LESS;
 		shadow_sampler = RD::get_singleton()->sampler_create(sampler);
 	}
-
-	{
-		Vector<RD::Uniform> uniforms;
-
-		RD::Uniform u;
-		u.binding = 0;
-		u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-
-		default_radiance_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RADIANCE_UNIFORM_SET);
-	}
-
-	{ //render buffers
-		Vector<RD::Uniform> uniforms;
-		for (int i = 0; i < 7; i++) {
-			RD::Uniform u;
-			u.binding = i;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = storage->texture_rd_get_default(i == 0 ? RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE : (i == 2 ? RendererStorageRD::DEFAULT_RD_TEXTURE_NORMAL : RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK));
-			u.ids.push_back(texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.binding = 7;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			RID texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE);
-			u.ids.push_back(texture);
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.binding = 8;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.binding = 9;
-			u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
-			u.ids.push_back(render_buffers_get_default_gi_probe_buffer());
-			uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.binding = 10;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
-			uniforms.push_back(u);
-		}
-
-		default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET);
-	}
 }
 
 RendererSceneRenderForward::~RendererSceneRenderForward() {
 	directional_shadow_atlas_set_size(0);
 
 	//clear base uniform set if still valid
-	if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) {
-		RD::get_singleton()->free(view_dependant_uniform_set);
+	if (render_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_pass_uniform_set)) {
+		RD::get_singleton()->free(render_pass_uniform_set);
+	}
+
+	if (sdfgi_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sdfgi_pass_uniform_set)) {
+		RD::get_singleton()->free(sdfgi_pass_uniform_set);
 	}
 
-	RD::get_singleton()->free(default_render_buffers_uniform_set);
-	RD::get_singleton()->free(default_radiance_uniform_set);
 	RD::get_singleton()->free(default_vec4_xform_buffer);
 	RD::get_singleton()->free(shadow_sampler);
 

+ 16 - 19
servers/rendering/renderer_rd/renderer_scene_render_forward.h

@@ -34,16 +34,14 @@
 #include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
 #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
 #include "servers/rendering/renderer_rd/renderer_storage_rd.h"
-#include "servers/rendering/renderer_rd/shaders/scene_high_end.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/scene_forward.glsl.gen.h"
 
 class RendererSceneRenderForward : public RendererSceneRenderRD {
 	enum {
 		SCENE_UNIFORM_SET = 0,
-		RADIANCE_UNIFORM_SET = 1,
-		VIEW_DEPENDANT_UNIFORM_SET = 2,
-		RENDER_BUFFERS_UNIFORM_SET = 3,
-		TRANSFORMS_UNIFORM_SET = 4,
-		MATERIAL_UNIFORM_SET = 5
+		RENDER_PASS_UNIFORM_SET = 1,
+		TRANSFORMS_UNIFORM_SET = 2,
+		MATERIAL_UNIFORM_SET = 3
 	};
 
 	enum {
@@ -69,7 +67,7 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 	};
 
 	struct {
-		SceneHighEndShaderRD scene_shader;
+		SceneForwardShaderRD scene_shader;
 		ShaderCompilerRD compiler;
 	} shader;
 
@@ -209,7 +207,7 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 
 	/* Framebuffer */
 
-	struct RenderBufferDataHighEnd : public RenderBufferData {
+	struct RenderBufferDataForward : public RenderBufferData {
 		//for rendering, may be MSAAd
 
 		RID color;
@@ -246,30 +244,29 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 		void clear();
 		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa);
 
-		RID uniform_set;
-
-		~RenderBufferDataHighEnd();
+		~RenderBufferDataForward();
 	};
 
 	virtual RenderBufferData *_create_render_buffer_data();
-	void _allocate_normal_roughness_texture(RenderBufferDataHighEnd *rb);
+	void _allocate_normal_roughness_texture(RenderBufferDataForward *rb);
 
 	RID shadow_sampler;
 	RID render_base_uniform_set;
-	RID view_dependant_uniform_set;
+	RID render_pass_uniform_set;
+	RID sdfgi_pass_uniform_set;
 
 	uint64_t lightmap_texture_array_version = 0xFFFFFFFF;
 
 	virtual void _base_uniforms_changed();
-	void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
+	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();
-	void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count);
-	void _update_render_buffers_uniform_set(RID p_render_buffers);
+	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);
+	RID _setup_render_pass_uniform_set(RID p_render_buffers, RID p_radiance_texture, RID p_shadow_atlas, RID p_reflection_atlas, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count);
 
 	struct LightmapData {
 		float normal_xform[12];
@@ -552,8 +549,6 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 	RID wireframe_material;
 	RID default_shader_rd;
 	RID default_shader_sdfgi_rd;
-	RID default_radiance_uniform_set;
-	RID default_render_buffers_uniform_set;
 
 	RID default_vec4_xform_buffer;
 	RID default_vec4_xform_uniform_set;
@@ -575,7 +570,7 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 	void _setup_lightmaps(InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, const Transform &p_cam_transform);
 
 	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth, bool p_has_sdfgi = false, bool p_has_opaque_gi = false);
-	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2());
+	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2());
 	_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index, bool p_using_sdfgi = false);
 	_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index, bool p_using_sdfgi = false);
 
@@ -583,6 +578,8 @@ class RendererSceneRenderForward : public RendererSceneRenderRD {
 
 	Map<Size2i, RID> sdfgi_framebuffer_size_cache;
 
+	bool low_end = false;
+
 protected:
 	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, int p_directional_light_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake);

+ 154 - 114
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -2971,6 +2971,10 @@ void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::
 	Environment *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
 
+	if (low_end) {
+		return;
+	}
+
 	env->sdfgi_enabled = p_enable;
 	env->sdfgi_cascades = p_cascades;
 	env->sdfgi_min_cell_size = p_min_cell_size;
@@ -3045,6 +3049,10 @@ void RendererSceneRenderRD::environment_set_volumetric_fog(RID p_env, bool p_ena
 	Environment *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
 
+	if (low_end) {
+		return;
+	}
+
 	env->volumetric_fog_enabled = p_enable;
 	env->volumetric_fog_density = p_density;
 	env->volumetric_fog_light = p_light;
@@ -3095,6 +3103,10 @@ void RendererSceneRenderRD::environment_set_ssr(RID p_env, bool p_enable, int p_
 	Environment *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
 
+	if (low_end) {
+		return;
+	}
+
 	env->ssr_enabled = p_enable;
 	env->ssr_max_steps = p_max_steps;
 	env->ssr_fade_in = p_fade_int;
@@ -3114,6 +3126,10 @@ void RendererSceneRenderRD::environment_set_ssao(RID p_env, bool p_enable, float
 	Environment *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
 
+	if (low_end) {
+		return;
+	}
+
 	env->ssao_enabled = p_enable;
 	env->ssao_radius = p_radius;
 	env->ssao_intensity = p_intensity;
@@ -4024,6 +4040,10 @@ bool RendererSceneRenderRD::gi_probe_needs_update(RID p_probe) const {
 	GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
 	ERR_FAIL_COND_V(!gi_probe, false);
 
+	if (low_end) {
+		return false;
+	}
+
 	//return true;
 	return gi_probe->last_probe_version != storage->gi_probe_get_version(gi_probe->probe);
 }
@@ -4032,6 +4052,10 @@ void RendererSceneRenderRD::gi_probe_update(RID p_probe, bool p_update_light_ins
 	GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
 	ERR_FAIL_COND(!gi_probe);
 
+	if (low_end) {
+		return;
+	}
+
 	uint32_t data_version = storage->gi_probe_get_data_version(gi_probe->probe);
 
 	// (RE)CREATE IF NEEDED
@@ -7952,6 +7976,10 @@ int RendererSceneRenderRD::get_max_directional_lights() const {
 	return cluster.max_directional_lights;
 }
 
+bool RendererSceneRenderRD::is_low_end() const {
+	return low_end;
+}
+
 RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 	storage = p_storage;
 	singleton = this;
@@ -7961,9 +7989,15 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 	sky_use_cubemap_array = GLOBAL_GET("rendering/quality/reflections/texture_array_reflections");
 	//	sky_use_cubemap_array = false;
 
-	//uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
+	uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
 
-	{
+	low_end = GLOBAL_GET("rendering/quality/rd_renderer/use_low_end_renderer");
+
+	if (textures_per_stage < 48) {
+		low_end = true;
+	}
+
+	if (!low_end) {
 		//kinda complicated to compute the amount of slots, we try to use as many as we can
 
 		gi_probe_max_lights = 32;
@@ -7992,7 +8026,7 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 		}
 	}
 
-	{
+	if (!low_end) {
 		String defines;
 		Vector<String> versions;
 		versions.push_back("\n#define MODE_DEBUG_COLOR\n");
@@ -8208,121 +8242,125 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 		sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
 	}
 
-	{
-		Vector<String> preprocess_modes;
-		preprocess_modes.push_back("\n#define MODE_SCROLL\n");
-		preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n");
-		preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n");
-		preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n");
-		preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n");
-		preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n");
-		preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n");
-		preprocess_modes.push_back("\n#define MODE_OCCLUSION\n");
-		preprocess_modes.push_back("\n#define MODE_STORE\n");
-		String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n";
-		sdfgi_shader.preprocess.initialize(preprocess_modes, defines);
-		sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create();
-		for (int i = 0; i < SDGIShader::PRE_PROCESS_MAX; i++) {
-			sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i));
+	if (!low_end) {
+		//SDFGI
+		{
+			Vector<String> preprocess_modes;
+			preprocess_modes.push_back("\n#define MODE_SCROLL\n");
+			preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n");
+			preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n");
+			preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n");
+			preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n");
+			preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n");
+			preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n");
+			preprocess_modes.push_back("\n#define MODE_OCCLUSION\n");
+			preprocess_modes.push_back("\n#define MODE_STORE\n");
+			String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n";
+			sdfgi_shader.preprocess.initialize(preprocess_modes, defines);
+			sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create();
+			for (int i = 0; i < SDGIShader::PRE_PROCESS_MAX; i++) {
+				sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i));
+			}
 		}
-	}
 
-	{
-		//calculate tables
-		String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+		{
+			//calculate tables
+			String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
 
-		Vector<String> direct_light_modes;
-		direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n");
-		direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n");
-		sdfgi_shader.direct_light.initialize(direct_light_modes, defines);
-		sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create();
-		for (int i = 0; i < SDGIShader::DIRECT_LIGHT_MODE_MAX; i++) {
-			sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i));
+			Vector<String> direct_light_modes;
+			direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n");
+			direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n");
+			sdfgi_shader.direct_light.initialize(direct_light_modes, defines);
+			sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create();
+			for (int i = 0; i < SDGIShader::DIRECT_LIGHT_MODE_MAX; i++) {
+				sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i));
+			}
 		}
-	}
 
-	{
-		//calculate tables
-		String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-		defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n";
-
-		Vector<String> integrate_modes;
-		integrate_modes.push_back("\n#define MODE_PROCESS\n");
-		integrate_modes.push_back("\n#define MODE_STORE\n");
-		integrate_modes.push_back("\n#define MODE_SCROLL\n");
-		integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n");
-		sdfgi_shader.integrate.initialize(integrate_modes, defines);
-		sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create();
+		{
+			//calculate tables
+			String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+			defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n";
 
-		for (int i = 0; i < SDGIShader::INTEGRATE_MODE_MAX; i++) {
-			sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i));
-		}
+			Vector<String> integrate_modes;
+			integrate_modes.push_back("\n#define MODE_PROCESS\n");
+			integrate_modes.push_back("\n#define MODE_STORE\n");
+			integrate_modes.push_back("\n#define MODE_SCROLL\n");
+			integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n");
+			sdfgi_shader.integrate.initialize(integrate_modes, defines);
+			sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create();
 
-		{
-			Vector<RD::Uniform> uniforms;
+			for (int i = 0; i < SDGIShader::INTEGRATE_MODE_MAX; i++) {
+				sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i));
+			}
 
 			{
-				RD::Uniform u;
-				u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-				u.binding = 0;
-				u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE));
-				uniforms.push_back(u);
+				Vector<RD::Uniform> uniforms;
+
+				{
+					RD::Uniform u;
+					u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+					u.binding = 0;
+					u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE));
+					uniforms.push_back(u);
+				}
+				{
+					RD::Uniform u;
+					u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+					u.binding = 1;
+					u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+					uniforms.push_back(u);
+				}
+
+				sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1);
 			}
-			{
-				RD::Uniform u;
-				u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
-				u.binding = 1;
-				u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
-				uniforms.push_back(u);
+		}
+		{
+			//calculate tables
+			String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+			Vector<String> gi_modes;
+			gi_modes.push_back("");
+			gi.shader.initialize(gi_modes, defines);
+			gi.shader_version = gi.shader.version_create();
+			for (int i = 0; i < GI::MODE_MAX; i++) {
+				gi.pipelines[i] = RD::get_singleton()->compute_pipeline_create(gi.shader.version_get_shader(gi.shader_version, i));
 			}
 
-			sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1);
+			gi.sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(GI::SDFGIData));
 		}
-	}
-	{
-		//calculate tables
-		String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-		Vector<String> gi_modes;
-		gi_modes.push_back("");
-		gi.shader.initialize(gi_modes, defines);
-		gi.shader_version = gi.shader.version_create();
-		for (int i = 0; i < GI::MODE_MAX; i++) {
-			gi.pipelines[i] = RD::get_singleton()->compute_pipeline_create(gi.shader.version_get_shader(gi.shader_version, i));
+		{
+			String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+			Vector<String> debug_modes;
+			debug_modes.push_back("");
+			sdfgi_shader.debug.initialize(debug_modes, defines);
+			sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create();
+			sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0);
+			sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version);
 		}
+		{
+			String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
 
-		gi.sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(GI::SDFGIData));
-	}
-	{
-		String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-		Vector<String> debug_modes;
-		debug_modes.push_back("");
-		sdfgi_shader.debug.initialize(debug_modes, defines);
-		sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create();
-		sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0);
-		sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version);
-	}
-	{
-		String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-
-		Vector<String> versions;
-		versions.push_back("\n#define MODE_PROBES\n");
-		versions.push_back("\n#define MODE_VISIBILITY\n");
+			Vector<String> versions;
+			versions.push_back("\n#define MODE_PROBES\n");
+			versions.push_back("\n#define MODE_VISIBILITY\n");
 
-		sdfgi_shader.debug_probes.initialize(versions, defines);
-		sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create();
+			sdfgi_shader.debug_probes.initialize(versions, defines);
+			sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create();
 
-		{
-			RD::PipelineRasterizationState rs;
-			rs.cull_mode = RD::POLYGON_CULL_DISABLED;
-			RD::PipelineDepthStencilState ds;
-			ds.enable_depth_test = true;
-			ds.enable_depth_write = true;
-			ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
-			for (int i = 0; i < SDGIShader::PROBE_DEBUG_MAX; i++) {
-				RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i);
-				sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
+			{
+				RD::PipelineRasterizationState rs;
+				rs.cull_mode = RD::POLYGON_CULL_DISABLED;
+				RD::PipelineDepthStencilState ds;
+				ds.enable_depth_test = true;
+				ds.enable_depth_write = true;
+				ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+				for (int i = 0; i < SDGIShader::PROBE_DEBUG_MAX; i++) {
+					RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i);
+					sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
+				}
 			}
 		}
+		default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES);
 	}
 
 	//cluster setup
@@ -8366,7 +8404,7 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 
 	cluster.builder.setup(16, 8, 24);
 
-	{
+	if (!low_end) {
 		String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(cluster.max_directional_lights) + "\n";
 		Vector<String> volumetric_fog_modes;
 		volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n");
@@ -8379,7 +8417,6 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
 			volumetric_fog.pipelines[i] = RD::get_singleton()->compute_pipeline_create(volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, i));
 		}
 	}
-	default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES);
 
 	{
 		RD::SamplerState sampler;
@@ -8427,22 +8464,25 @@ RendererSceneRenderRD::~RendererSceneRenderRD() {
 		RD::get_singleton()->free(sky_scene_state.uniform_set);
 	}
 
-	RD::get_singleton()->free(default_giprobe_buffer);
-	RD::get_singleton()->free(gi_probe_lights_uniform);
-	RD::get_singleton()->free(gi.sdfgi_ubo);
+	if (!low_end) {
+		RD::get_singleton()->free(default_giprobe_buffer);
+		RD::get_singleton()->free(gi_probe_lights_uniform);
+		RD::get_singleton()->free(gi.sdfgi_ubo);
 
-	giprobe_debug_shader.version_free(giprobe_debug_shader_version);
-	giprobe_shader.version_free(giprobe_lighting_shader_version);
-	gi.shader.version_free(gi.shader_version);
-	sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader);
-	sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader);
-	sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
-	sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader);
-	sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader);
+		giprobe_debug_shader.version_free(giprobe_debug_shader_version);
+		giprobe_shader.version_free(giprobe_lighting_shader_version);
+		gi.shader.version_free(gi.shader_version);
+		sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader);
+		sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader);
+		sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
+		sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader);
+		sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader);
 
-	volumetric_fog.shader.version_free(volumetric_fog.shader_version);
+		volumetric_fog.shader.version_free(volumetric_fog.shader_version);
+
+		memdelete_arr(gi_probe_lights);
+	}
 
-	memdelete_arr(gi_probe_lights);
 	SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RendererStorageRD::SHADER_TYPE_SKY);
 	sky_shader.shader.version_free(md->shader_data->version);
 	RD::get_singleton()->free(sky_scene_state.directional_light_buffer);

+ 4 - 0
servers/rendering/renderer_rd/renderer_scene_render_rd.h

@@ -1456,6 +1456,8 @@ private:
 		float weight;
 	};
 
+	bool low_end = false;
+
 public:
 	/* SHADOW ATLAS API */
 
@@ -1952,6 +1954,8 @@ public:
 
 	void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir);
 
+	bool is_low_end() const;
+
 	RendererSceneRenderRD(RendererStorageRD *p_storage);
 	~RendererSceneRenderRD();
 };

+ 24 - 0
servers/rendering/renderer_rd/shader_rd.cpp

@@ -199,6 +199,10 @@ void ShaderRD::_clear_version(Version *p_version) {
 }
 
 void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
+	if (!variants_enabled[p_variant]) {
+		return; //variant is disabled, return
+	}
+
 	Vector<RD::ShaderStageData> stages;
 
 	String error;
@@ -365,6 +369,9 @@ void ShaderRD::_compile_version(Version *p_version) {
 
 	bool all_valid = true;
 	for (int i = 0; i < variant_defines.size(); i++) {
+		if (!variants_enabled[i]) {
+			continue; //disabled
+		}
 		if (p_version->variants[i].is_null()) {
 			all_valid = false;
 			break;
@@ -374,6 +381,9 @@ void ShaderRD::_compile_version(Version *p_version) {
 	if (!all_valid) {
 		//clear versions if they exist
 		for (int i = 0; i < variant_defines.size(); i++) {
+			if (!variants_enabled[i]) {
+				continue; //disabled
+			}
 			if (!p_version->variants[i].is_null()) {
 				RD::get_singleton()->free(p_version->variants[i]);
 			}
@@ -454,12 +464,26 @@ bool ShaderRD::version_free(RID p_version) {
 	return true;
 }
 
+void ShaderRD::set_variant_enabled(int p_variant, bool p_enabled) {
+	ERR_FAIL_COND(version_owner.get_rid_count() > 0); //versions exist
+	ERR_FAIL_INDEX(p_variant, variants_enabled.size());
+	variants_enabled.write[p_variant] = p_enabled;
+}
+
+bool ShaderRD::is_variant_enabled(int p_variant) const {
+	ERR_FAIL_INDEX_V(p_variant, variants_enabled.size(), false);
+	return variants_enabled[p_variant];
+}
+
 void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String &p_general_defines) {
 	ERR_FAIL_COND(variant_defines.size());
 	ERR_FAIL_COND(p_variant_defines.size() == 0);
+
 	general_defines = p_general_defines.utf8();
+
 	for (int i = 0; i < p_variant_defines.size(); i++) {
 		variant_defines.push_back(p_variant_defines[i].utf8());
+		variants_enabled.push_back(true);
 	}
 }
 

+ 5 - 0
servers/rendering/renderer_rd/shader_rd.h

@@ -46,6 +46,7 @@ class ShaderRD {
 	//versions
 	CharString general_defines;
 	Vector<CharString> variant_defines;
+	Vector<bool> variants_enabled;
 
 	struct Version {
 		CharString uniforms;
@@ -109,6 +110,7 @@ public:
 
 	_FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) {
 		ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID());
+		ERR_FAIL_COND_V(!variants_enabled[p_variant], RID());
 
 		Version *version = version_owner.getornull(p_version);
 		ERR_FAIL_COND_V(!version, RID());
@@ -128,6 +130,9 @@ public:
 
 	bool version_free(RID p_version);
 
+	void set_variant_enabled(int p_variant, bool p_enabled);
+	bool is_variant_enabled(int p_variant) const;
+
 	void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
 	virtual ~ShaderRD();
 };

+ 1 - 1
servers/rendering/renderer_rd/shaders/SCsub

@@ -11,7 +11,7 @@ if "RD_GLSL" in env["BUILDERS"]:
     env.RD_GLSL("cubemap_roughness.glsl")
     env.RD_GLSL("cubemap_downsampler.glsl")
     env.RD_GLSL("cubemap_filter.glsl")
-    env.RD_GLSL("scene_high_end.glsl")
+    env.RD_GLSL("scene_forward.glsl")
     env.RD_GLSL("sky.glsl")
     env.RD_GLSL("tonemap.glsl")
     env.RD_GLSL("cube_to_dp.glsl")

+ 17 - 24
servers/rendering/renderer_rd/shaders/scene_high_end.glsl → servers/rendering/renderer_rd/shaders/scene_forward.glsl

@@ -4,7 +4,7 @@
 
 VERSION_DEFINES
 
-#include "scene_high_end_inc.glsl"
+#include "scene_forward_inc.glsl"
 
 /* INPUT ATTRIBS */
 
@@ -296,7 +296,7 @@ VERTEX_SHADER_CODE
 
 VERSION_DEFINES
 
-#include "scene_high_end_inc.glsl"
+#include "scene_forward_inc.glsl"
 
 /* Varyings */
 
@@ -1548,8 +1548,6 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 	out_spec += vec4(irr_light.rgb * blend, blend);
 }
 
-#endif //USE_FORWARD_GI
-
 vec2 octahedron_wrap(vec2 v) {
 	vec2 signVal;
 	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
@@ -1683,10 +1681,14 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal
 	}
 }
 
+#endif //USE_FORWARD_GI
+
 #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 #ifndef MODE_RENDER_DEPTH
 
+#ifndef LOW_END_MODE
+
 vec4 volumetric_fog_process(vec2 screen_uv, float z) {
 	vec3 fog_pos = vec3(screen_uv, z * scene_data.volumetric_fog_inv_length);
 	if (fog_pos.z < 0.0) {
@@ -1697,6 +1699,7 @@ vec4 volumetric_fog_process(vec2 screen_uv, float z) {
 
 	return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos);
 }
+#endif
 
 vec4 fog_process(vec3 vertex) {
 	vec3 fog_color = scene_data.fog_light_color;
@@ -2221,30 +2224,13 @@ FRAGMENT_SHADER_CODE
 		specular_light = spec_accum.rgb;
 		ambient_light = amb_accum.rgb;
 	}
-#else
+#elif !defined(LOW_END_MODE)
+
 	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
 
 		ivec2 coord;
 
 		if (scene_data.gi_upscale_for_msaa) {
-			/*
-			//find the closest depth to upscale from, based on neighbours
-			ivec2 base_coord = ivec2(gl_FragCoord.xy);
-			float z_dist = gl_FragCoord.z;
-			ivec2 closest_coord = base_coord;
-			float closest_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord,0).r-z_dist);
-
-			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_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord,0).r-z_dist);
-				if (neighbour_z_dist < closest_z_dist) {
-					closest_z_dist = neighbour_z_dist;
-					closest_coord = neighbour_coord;
-				}
-			}
-
-*/
 			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);
@@ -2823,11 +2809,13 @@ FRAGMENT_SHADER_CODE
 //ambient occlusion
 #if defined(AO_USED)
 
+#ifndef LOW_END_MODE
 	if (scene_data.ssao_enabled && scene_data.ssao_ao_affect > 0.0) {
 		float ssao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv).r;
 		ao = mix(ao, min(ao, ssao), scene_data.ssao_ao_affect);
 		ao_light_affect = mix(ao_light_affect, max(ao_light_affect, scene_data.ssao_light_affect), scene_data.ssao_ao_affect);
 	}
+#endif //LOW_END_MODE
 
 	ambient_light = mix(scene_data.ao_color.rgb, ambient_light, ao);
 	ao_light_affect = mix(1.0, ao, ao_light_affect);
@@ -2835,6 +2823,7 @@ FRAGMENT_SHADER_CODE
 	diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect);
 #else
 
+#ifndef LOW_END_MODE
 	if (scene_data.ssao_enabled) {
 		float ao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv).r;
 		ambient_light = mix(scene_data.ao_color.rgb, ambient_light, ao);
@@ -2842,6 +2831,7 @@ FRAGMENT_SHADER_CODE
 		specular_light = mix(scene_data.ao_color.rgb, specular_light, ao_light_affect);
 		diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect);
 	}
+#endif //LOW_END_MODE
 
 #endif // AO_USED
 
@@ -2871,11 +2861,13 @@ FRAGMENT_SHADER_CODE
 		specular_buffer.rgb = mix(specular_buffer.rgb, vec3(0.0), fog.a);
 	}
 
+#ifndef LOW_END_MODE
 	if (scene_data.volumetric_fog_enabled) {
 		vec4 fog = volumetric_fog_process(screen_uv, -vertex.z);
 		diffuse_buffer.rgb = mix(diffuse_buffer.rgb, fog.rgb, fog.a);
 		specular_buffer.rgb = mix(specular_buffer.rgb, vec3(0.0), fog.a);
 	}
+#endif // LOW_END_MODE
 
 #if defined(CUSTOM_FOG_USED)
 	diffuse_buffer.rgb = mix(diffuse_buffer.rgb, custom_fog.rgb, custom_fog.a);
@@ -2896,11 +2888,12 @@ FRAGMENT_SHADER_CODE
 		vec4 fog = fog_process(vertex);
 		frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a);
 	}
-
+#ifndef LOW_END_MODE
 	if (scene_data.volumetric_fog_enabled) {
 		vec4 fog = volumetric_fog_process(screen_uv, -vertex.z);
 		frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a);
 	}
+#endif
 
 #if defined(CUSTOM_FOG_USED)
 	frag_color.rgb = mix(frag_color.rgb, custom_fog.rgb, custom_fog.a);

+ 29 - 18
servers/rendering/renderer_rd/shaders/scene_high_end_inc.glsl → servers/rendering/renderer_rd/shaders/scene_forward_inc.glsl

@@ -204,6 +204,8 @@ layout(set = 0, binding = 19, std430) restrict readonly buffer GlobalVariableDat
 }
 global_variables;
 
+#ifndef LOW_END_MODE
+
 struct SDFGIProbeCascadeData {
 	vec3 position;
 	float to_probe;
@@ -239,6 +241,8 @@ layout(set = 0, binding = 20, std140) uniform SDFGI {
 }
 sdfgi;
 
+#endif //LOW_END_MODE
+
 // decal atlas
 
 /* Set 1, Radiance */
@@ -255,20 +259,22 @@ layout(set = 1, binding = 0) uniform textureCube radiance_cubemap;
 
 /* Set 2, Reflection and Shadow Atlases (view dependent) */
 
-layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
+layout(set = 1, binding = 1) uniform textureCubeArray reflection_atlas;
 
-layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
+layout(set = 1, binding = 2) uniform texture2D shadow_atlas;
 
-layout(set = 2, binding = 2) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
+#ifndef LOW_END_MODE
+layout(set = 1, binding = 3) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
+#endif
 
 /* Set 3, Render Buffers */
 
 #ifdef MODE_RENDER_SDF
 
-layout(r16ui, set = 3, binding = 0) uniform restrict writeonly uimage3D albedo_volume_grid;
-layout(r32ui, set = 3, binding = 1) uniform restrict writeonly uimage3D emission_grid;
-layout(r32ui, set = 3, binding = 2) uniform restrict writeonly uimage3D emission_aniso_grid;
-layout(r32ui, set = 3, binding = 3) uniform restrict uimage3D geom_facing_grid;
+layout(r16ui, set = 1, binding = 4) uniform restrict writeonly uimage3D albedo_volume_grid;
+layout(r32ui, set = 1, binding = 5) uniform restrict writeonly uimage3D emission_grid;
+layout(r32ui, set = 1, binding = 6) uniform restrict writeonly uimage3D emission_aniso_grid;
+layout(r32ui, set = 1, binding = 7) uniform restrict uimage3D geom_facing_grid;
 
 //still need to be present for shaders that use it, so remap them to something
 #define depth_buffer shadow_atlas
@@ -277,14 +283,17 @@ layout(r32ui, set = 3, binding = 3) uniform restrict uimage3D geom_facing_grid;
 
 #else
 
-layout(set = 3, binding = 0) uniform texture2D depth_buffer;
-layout(set = 3, binding = 1) uniform texture2D color_buffer;
-layout(set = 3, binding = 2) uniform texture2D normal_roughness_buffer;
-layout(set = 3, binding = 4) uniform texture2D ao_buffer;
-layout(set = 3, binding = 5) uniform texture2D ambient_buffer;
-layout(set = 3, binding = 6) uniform texture2D reflection_buffer;
-layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture;
-layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades;
+layout(set = 1, binding = 4) uniform texture2D depth_buffer;
+layout(set = 1, binding = 5) uniform texture2D color_buffer;
+
+#ifndef LOW_END_MODE
+
+layout(set = 1, binding = 6) uniform texture2D normal_roughness_buffer;
+layout(set = 1, binding = 7) uniform texture2D ao_buffer;
+layout(set = 1, binding = 8) uniform texture2D ambient_buffer;
+layout(set = 1, binding = 9) uniform texture2D reflection_buffer;
+layout(set = 1, binding = 10) uniform texture2DArray sdfgi_lightprobe_texture;
+layout(set = 1, binding = 11) uniform texture3D sdfgi_occlusion_cascades;
 
 struct GIProbeData {
 	mat4 xform;
@@ -302,18 +311,20 @@ struct GIProbeData {
 	uint mipmaps;
 };
 
-layout(set = 3, binding = 9, std140) uniform GIProbes {
+layout(set = 1, binding = 12, std140) uniform GIProbes {
 	GIProbeData data[MAX_GI_PROBES];
 }
 gi_probes;
 
-layout(set = 3, binding = 10) uniform texture3D volumetric_fog_texture;
+layout(set = 1, binding = 13) uniform texture3D volumetric_fog_texture;
+
+#endif // LOW_END_MODE
 
 #endif
 
 /* Set 4 Skeleton & Instancing (Multimesh) */
 
-layout(set = 4, binding = 0, std430) restrict readonly buffer Transforms {
+layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
 	vec4 data[];
 }
 transforms;

+ 2 - 0
servers/rendering/renderer_scene_render.h

@@ -260,6 +260,8 @@ public:
 
 	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0;
 
+	virtual bool is_low_end() const = 0;
+
 	virtual void update() = 0;
 	virtual ~RendererSceneRender() {}
 };

+ 3 - 0
servers/rendering_server.cpp

@@ -2268,6 +2268,9 @@ RenderingServer::RenderingServer() {
 
 	GLOBAL_DEF("rendering/quality/2d_shadow_atlas/size", 2048);
 
+	GLOBAL_DEF("rendering/quality/rd_renderer/use_low_end_renderer", false);
+	GLOBAL_DEF("rendering/quality/rd_renderer/use_low_end_renderer.mobile", true);
+
 	GLOBAL_DEF("rendering/quality/shadow_atlas/size", 4096);
 	GLOBAL_DEF("rendering/quality/shadow_atlas/size.mobile", 2048);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadow_atlas/size", PropertyInfo(Variant::INT, "rendering/quality/shadow_atlas/size", PROPERTY_HINT_RANGE, "256,16384"));