Browse Source

Merge pull request #50644 from BastiaanOlij/mobile_compute_to_fragment

Rémi Verschelde 4 years ago
parent
commit
2a8a59eac0

+ 232 - 6
servers/rendering/renderer_rd/effects_rd.cpp

@@ -383,6 +383,8 @@ void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i
 }
 
 void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) {
+	ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
+
 	memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
 
 	uint32_t base_flags = 0;
@@ -416,6 +418,8 @@ void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back
 }
 
 void EffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_treshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+	ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer.");
+
 	memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
 
 	CopyMode copy_mode = p_first_pass && p_auto_exposure.is_valid() ? COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : COPY_MODE_GAUSSIAN_GLOW;
@@ -449,6 +453,57 @@ void EffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const
 	RD::get_singleton()->compute_list_end();
 }
 
+void EffectsRD::gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_treshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+	ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the fragment version of the gaussian glow with the clustered renderer.");
+
+	memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+	BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW;
+	uint32_t base_flags = 0;
+
+	blur_raster.push_constant.pixel_size[0] = p_pixel_size.x;
+	blur_raster.push_constant.pixel_size[1] = p_pixel_size.y;
+
+	blur_raster.push_constant.glow_strength = p_strength;
+	blur_raster.push_constant.glow_bloom = p_bloom;
+	blur_raster.push_constant.glow_hdr_threshold = p_hdr_bleed_treshold;
+	blur_raster.push_constant.glow_hdr_scale = p_hdr_bleed_scale;
+	blur_raster.push_constant.glow_exposure = p_exposure;
+	blur_raster.push_constant.glow_white = 0; //actually unused
+	blur_raster.push_constant.glow_luminance_cap = p_luminance_cap;
+
+	blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
+
+	//HORIZONTAL
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half)));
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
+	if (p_auto_exposure.is_valid() && p_first_pass) {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_auto_exposure), 1);
+	}
+	RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+	blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0);
+	RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+	RD::get_singleton()->draw_list_draw(draw_list, true);
+	RD::get_singleton()->draw_list_end();
+
+	blur_mode = BLUR_MODE_GAUSSIAN_GLOW;
+
+	//VERTICAL
+	draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_rd_texture_half), 0);
+	RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+	blur_raster.push_constant.flags = base_flags;
+	RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+	RD::get_singleton()->draw_list_draw(draw_list, true);
+	RD::get_singleton()->draw_list_end();
+}
+
 void EffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera) {
 	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 
@@ -736,6 +791,8 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
 }
 
 void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
+	ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of luminance reduction with the mobile renderer.");
+
 	luminance_reduce.push_constant.source_size[0] = p_source_size.x;
 	luminance_reduce.push_constant.source_size[1] = p_source_size.y;
 	luminance_reduce.push_constant.max_luminance = p_max_luminance;
@@ -774,7 +831,41 @@ void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_
 	RD::get_singleton()->compute_list_end();
 }
 
+void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
+	ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use fragment version of luminance reduction with the clustered renderer.");
+	ERR_FAIL_COND_MSG(p_reduce.size() != p_fb.size(), "Incorrect frame buffer account for luminance reduction.");
+
+	luminance_reduce_raster.push_constant.max_luminance = p_max_luminance;
+	luminance_reduce_raster.push_constant.min_luminance = p_min_luminance;
+	luminance_reduce_raster.push_constant.exposure_adjust = p_adjust;
+
+	for (int i = 0; i < p_reduce.size(); i++) {
+		luminance_reduce_raster.push_constant.source_size[0] = i == 0 ? p_source_size.x : luminance_reduce_raster.push_constant.dest_size[0];
+		luminance_reduce_raster.push_constant.source_size[1] = i == 0 ? p_source_size.y : luminance_reduce_raster.push_constant.dest_size[1];
+		luminance_reduce_raster.push_constant.dest_size[0] = MAX(luminance_reduce_raster.push_constant.source_size[0] / 8, 1);
+		luminance_reduce_raster.push_constant.dest_size[1] = MAX(luminance_reduce_raster.push_constant.source_size[1] / 8, 1);
+
+		bool final = !p_set && (luminance_reduce_raster.push_constant.dest_size[0] == 1) && (luminance_reduce_raster.push_constant.dest_size[1] == 1);
+		LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT);
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_fb[i])));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(i == 0 ? p_source_texture : p_reduce[i - 1]), 0);
+		if (final) {
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_prev_luminance), 1);
+		}
+		RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &luminance_reduce_raster.push_constant, sizeof(LuminanceReduceRasterPushConstant));
+
+		RD::get_singleton()->draw_list_draw(draw_list, true);
+		RD::get_singleton()->draw_list_end();
+	}
+}
+
 void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_halfsize_texture1, RID p_halfsize_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+	ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of BOKEH DOF with the mobile renderer.");
+
 	bokeh.push_constant.blur_far_active = p_dof_far;
 	bokeh.push_constant.blur_far_begin = p_dof_far_begin;
 	bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
@@ -924,6 +1015,78 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 	RD::get_singleton()->compute_list_end();
 }
 
+void EffectsRD::blur_dof_raster(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_base_fb, RID p_secondary_texture, RID p_secondary_fb, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+	ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use blur DOF with the clustered renderer.");
+
+	memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+	BlurRasterMode blur_mode;
+	int qsteps[4] = { 4, 4, 10, 20 };
+	uint32_t base_flags = p_cam_orthogonal ? BLUR_FLAG_USE_ORTHOGONAL_PROJECTION : 0;
+
+	Vector2 pixel_size = Vector2(1.0 / p_base_texture_size.width, 1.0 / p_base_texture_size.height);
+
+	blur_raster.push_constant.dof_radius = (p_dof_blur_amount * p_dof_blur_amount) / qsteps[p_quality];
+	blur_raster.push_constant.pixel_size[0] = pixel_size.x;
+	blur_raster.push_constant.pixel_size[1] = pixel_size.y;
+	blur_raster.push_constant.camera_z_far = p_cam_zfar;
+	blur_raster.push_constant.camera_z_near = p_cam_znear;
+
+	if (p_dof_far || p_dof_near) {
+		if (p_quality == RS::DOF_BLUR_QUALITY_HIGH) {
+			blur_mode = BLUR_MODE_DOF_HIGH;
+		} else if (p_quality == RS::DOF_BLUR_QUALITY_MEDIUM) {
+			blur_mode = BLUR_MODE_DOF_MEDIUM;
+		} else { // for LOW or VERYLOW we use LOW
+			blur_mode = BLUR_MODE_DOF_LOW;
+		}
+
+		if (p_dof_far) {
+			base_flags |= BLUR_FLAG_DOF_FAR;
+			blur_raster.push_constant.dof_far_begin = p_dof_far_begin;
+			blur_raster.push_constant.dof_far_end = p_dof_far_begin + p_dof_far_size;
+		}
+
+		if (p_dof_near) {
+			base_flags |= BLUR_FLAG_DOF_NEAR;
+			blur_raster.push_constant.dof_near_begin = p_dof_near_begin;
+			blur_raster.push_constant.dof_near_end = p_dof_near_begin - p_dof_near_size;
+		}
+
+		//HORIZONTAL
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_secondary_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_secondary_fb)));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_base_texture), 0);
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_depth_texture), 1);
+		RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+		blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL;
+		blur_raster.push_constant.dof_dir[0] = 1.0;
+		blur_raster.push_constant.dof_dir[1] = 0.0;
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+		RD::get_singleton()->draw_list_draw(draw_list, true);
+		RD::get_singleton()->draw_list_end();
+
+		//VERTICAL
+		draw_list = RD::get_singleton()->draw_list_begin(p_base_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_base_fb)));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary_texture), 0);
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_depth_texture), 1);
+		RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+		blur_raster.push_constant.flags = base_flags;
+		blur_raster.push_constant.dof_dir[0] = 0.0;
+		blur_raster.push_constant.dof_dir[1] = 1.0;
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+		RD::get_singleton()->draw_list_draw(draw_list, true);
+		RD::get_singleton()->draw_list_end();
+	}
+}
+
 void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set) {
 	RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_gather_uniform_set, 0);
 	if ((p_settings.quality == RS::ENV_SSAO_QUALITY_ULTRA) && !p_adaptive_base_pass) {
@@ -1464,7 +1627,35 @@ void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) {
 	RD::get_singleton()->compute_list_end();
 }
 
-EffectsRD::EffectsRD() {
+EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
+	prefer_raster_effects = p_prefer_raster_effects;
+
+	if (prefer_raster_effects) {
+		// init blur shader (on compute use copy shader)
+
+		Vector<String> blur_modes;
+		blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); // BLUR_MODE_GAUSSIAN_BLUR
+		blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); // BLUR_MODE_GAUSSIAN_GLOW
+		blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); // BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE
+		blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_LOW\n"); // BLUR_MODE_DOF_LOW
+		blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_MEDIUM\n"); // BLUR_MODE_DOF_MEDIUM
+		blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_HIGH\n"); // BLUR_MODE_DOF_HIGH
+
+		blur_raster.shader.initialize(blur_modes);
+		memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+		blur_raster.shader_version = blur_raster.shader.version_create();
+
+		for (int i = 0; i < BLUR_MODE_MAX; i++) {
+			blur_raster.pipelines[i].setup(blur_raster.shader.version_get_shader(blur_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+		}
+
+	} else {
+		// not used in clustered
+		for (int i = 0; i < BLUR_MODE_MAX; i++) {
+			blur_raster.pipelines[i].clear();
+		}
+	}
+
 	{ // Initialize copy
 		Vector<String> copy_modes;
 		copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n");
@@ -1483,10 +1674,21 @@ EffectsRD::EffectsRD() {
 
 		copy.shader.initialize(copy_modes);
 		memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+		if (prefer_raster_effects) {
+			// disable shaders we can't use
+			copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_COPY, false);
+			copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_COPY_8BIT, false);
+			copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_GLOW, false);
+			copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE, false);
+		}
+
 		copy.shader_version = copy.shader.version_create();
 
 		for (int i = 0; i < COPY_MODE_MAX; i++) {
-			copy.pipelines[i] = RD::get_singleton()->compute_pipeline_create(copy.shader.version_get_shader(copy.shader_version, i));
+			if (copy.shader.is_variant_enabled(i)) {
+				copy.pipelines[i] = RD::get_singleton()->compute_pipeline_create(copy.shader.version_get_shader(copy.shader_version, i));
+			}
 		}
 	}
 	{
@@ -1551,7 +1753,20 @@ EffectsRD::EffectsRD() {
 		}
 	}
 
-	{
+	if (prefer_raster_effects) {
+		Vector<String> luminance_reduce_modes;
+		luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST
+		luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT
+		luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL
+
+		luminance_reduce_raster.shader.initialize(luminance_reduce_modes);
+		memset(&luminance_reduce_raster.push_constant, 0, sizeof(LuminanceReduceRasterPushConstant));
+		luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create();
+
+		for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
+			luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+		}
+	} else {
 		// Initialize luminance_reduce
 		Vector<String> luminance_reduce_modes;
 		luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n");
@@ -1565,6 +1780,10 @@ EffectsRD::EffectsRD() {
 		for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) {
 			luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i));
 		}
+
+		for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
+			luminance_reduce_raster.pipelines[i].clear();
+		}
 	}
 
 	{
@@ -1583,7 +1802,9 @@ EffectsRD::EffectsRD() {
 		cube_to_dp.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
 	}
 
-	{
+	if (prefer_raster_effects) {
+		// not supported
+	} else {
 		// Initialize bokeh
 		Vector<String> bokeh_modes;
 		bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
@@ -1974,13 +2195,18 @@ EffectsRD::~EffectsRD() {
 	RD::get_singleton()->free(ssao.gather_constants_buffer);
 	RD::get_singleton()->free(ssao.importance_map_load_counter);
 
-	bokeh.shader.version_free(bokeh.shader_version);
+	if (prefer_raster_effects) {
+		blur_raster.shader.version_free(blur_raster.shader_version);
+		luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
+	} else {
+		bokeh.shader.version_free(bokeh.shader_version);
+		luminance_reduce.shader.version_free(luminance_reduce.shader_version);
+	}
 	copy.shader.version_free(copy.shader_version);
 	copy_to_fb.shader.version_free(copy_to_fb.shader_version);
 	cube_to_dp.shader.version_free(cube_to_dp.shader_version);
 	cubemap_downsampler.shader.version_free(cubemap_downsampler.shader_version);
 	filter.shader.version_free(filter.shader_version);
-	luminance_reduce.shader.version_free(luminance_reduce.shader_version);
 	resolve.shader.version_free(resolve.shader_version);
 	roughness.shader.version_free(roughness.shader_version);
 	roughness_limiter.shader.version_free(roughness_limiter.shader_version);

+ 89 - 1
servers/rendering/renderer_rd/effects_rd.h

@@ -33,6 +33,7 @@
 
 #include "core/math/camera_matrix.h"
 #include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/blur_raster.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/bokeh_dof.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/copy.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/copy_to_fb.glsl.gen.h"
@@ -41,6 +42,7 @@
 #include "servers/rendering/renderer_rd/shaders/cubemap_filter.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/resolve.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl.gen.h"
@@ -60,6 +62,63 @@
 #include "servers/rendering_server.h"
 
 class EffectsRD {
+	enum BlurRasterMode {
+		BLUR_MODE_GAUSSIAN_BLUR,
+		BLUR_MODE_GAUSSIAN_GLOW,
+		BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
+
+		BLUR_MODE_DOF_LOW,
+		BLUR_MODE_DOF_MEDIUM,
+		BLUR_MODE_DOF_HIGH,
+
+		BLUR_MODE_MAX
+	};
+
+	enum {
+		BLUR_FLAG_HORIZONTAL = (1 << 0),
+		BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
+		BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
+		BLUR_FLAG_DOF_FAR = (1 << 3),
+		BLUR_FLAG_DOF_NEAR = (1 << 4),
+	};
+
+	struct BlurRasterPushConstant {
+		float pixel_size[2];
+		uint32_t flags;
+		uint32_t pad;
+
+		//glow
+		float glow_strength;
+		float glow_bloom;
+		float glow_hdr_threshold;
+		float glow_hdr_scale;
+
+		float glow_exposure;
+		float glow_white;
+		float glow_luminance_cap;
+		float glow_auto_exposure_grey;
+
+		//dof
+		float dof_far_begin;
+		float dof_far_end;
+		float dof_near_begin;
+		float dof_near_end;
+
+		float dof_radius;
+		float dof_pad[3];
+
+		float dof_dir[2];
+		float camera_z_far;
+		float camera_z_near;
+	};
+
+	struct BlurRaster {
+		BlurRasterPushConstant push_constant;
+		BlurRasterShaderRD shader;
+		RID shader_version;
+		PipelineCacheRD pipelines[BLUR_MODE_MAX];
+	} blur_raster;
+
 	enum CopyMode {
 		COPY_MODE_GAUSSIAN_COPY,
 		COPY_MODE_GAUSSIAN_COPY_8BIT,
@@ -239,6 +298,29 @@ class EffectsRD {
 		RID pipelines[LUMINANCE_REDUCE_MAX];
 	} luminance_reduce;
 
+	enum LuminanceReduceRasterMode {
+		LUMINANCE_REDUCE_FRAGMENT_FIRST,
+		LUMINANCE_REDUCE_FRAGMENT,
+		LUMINANCE_REDUCE_FRAGMENT_FINAL,
+		LUMINANCE_REDUCE_FRAGMENT_MAX
+	};
+
+	struct LuminanceReduceRasterPushConstant {
+		int32_t source_size[2];
+		int32_t dest_size[2];
+		float exposure_adjust;
+		float min_luminance;
+		float max_luminance;
+		float pad[1];
+	};
+
+	struct LuminanceReduceFragment {
+		LuminanceReduceRasterPushConstant push_constant;
+		LuminanceReduceRasterShaderRD shader;
+		RID shader_version;
+		PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX];
+	} luminance_reduce_raster;
+
 	struct CopyToDPPushConstant {
 		float z_far;
 		float z_near;
@@ -656,6 +738,8 @@ class EffectsRD {
 	RID _get_compute_uniform_set_from_texture_pair(RID p_texture, RID p_texture2, bool p_use_mipmaps = false);
 	RID _get_compute_uniform_set_from_image_pair(RID p_texture, RID p_texture2);
 
+	bool prefer_raster_effects;
+
 public:
 	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID());
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
@@ -666,12 +750,16 @@ public:
 	void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
 	void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
 	void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
+	void gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 
 	void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
 	void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
 	void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_texture, const Rect2 &p_rect, float p_z_near, float p_z_far, bool p_dp_flip);
 	void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
+	void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
+
 	void bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_bokeh_texture1, RID p_bokeh_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+	void blur_dof_raster(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_base_fb, RID p_secondary_texture, RID p_secondary_fb, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
 
 	struct TonemapSettings {
 		bool use_glow = false;
@@ -751,7 +839,7 @@ public:
 
 	void sort_buffer(RID p_uniform_set, int p_size);
 
-	EffectsRD();
+	EffectsRD(bool p_prefer_raster_effects);
 	~EffectsRD();
 };
 

+ 1 - 8
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

@@ -96,10 +96,6 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b
 	RD::DataFormat color_format = RenderForwardMobile::singleton->_render_buffers_get_color_format();
 
 	if (p_msaa == RS::VIEWPORT_MSAA_DISABLED) {
-		if (color_format == RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32) {
-			// @TODO add a second color buffer for alpha as this format is RGB only
-		}
-
 		Vector<RID> fb;
 		fb.push_back(p_color_buffer);
 		fb.push_back(depth);
@@ -165,15 +161,12 @@ bool RenderForwardMobile::free(RID p_rid) {
 
 RD::DataFormat RenderForwardMobile::_render_buffers_get_color_format() {
 	// Using 32bit buffers enables AFBC on mobile devices which should have a definate performance improvement (MALI G710 and newer support this on 64bit RTs)
-	// NO ALPHA and unsigned float.
-	// @TODO No alpha is an issue, recommendation here is to add a second RT for alpha
 	return RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32;
 }
 
 bool RenderForwardMobile::_render_buffers_can_be_storage() {
 	// Using 32bit buffers enables AFBC on mobile devices which should have a definate performance improvement (MALI G710 and newer support this on 64bit RTs)
-	// NO ALPHA and unsigned float.
-	// @TODO No alpha is an issue, recommendation here is to add a second RT for alpha
+	// Doesn't support storage
 	return false;
 }
 

+ 3 - 0
servers/rendering/renderer_rd/renderer_compositor_rd.cpp

@@ -280,6 +280,9 @@ RendererCompositorRD::RendererCompositorRD() {
 		// default to our high end renderer
 		scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered(storage));
 	}
+
+	// now we're ready to create our effects,
+	storage->init_effects(!scene->_render_buffers_can_be_storage());
 }
 
 RendererCompositorRD::~RendererCompositorRD() {

+ 139 - 14
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -1383,12 +1383,20 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
 
 	uint32_t mipmaps_required = Image::get_image_required_mipmaps(rb->width, rb->height, Image::FORMAT_RGBAH);
 
+	// TODO make sure texture_create_shared_from_slice works for multiview
+
 	RD::TextureFormat tf;
-	tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+	tf.format = _render_buffers_get_color_format(); // RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
 	tf.width = rb->width;
 	tf.height = rb->height;
-	tf.texture_type = RD::TEXTURE_TYPE_2D;
-	tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+	tf.texture_type = rb->view_count > 1 ? RD::TEXTURE_TYPE_2D_ARRAY : RD::TEXTURE_TYPE_2D;
+	tf.array_layers = rb->view_count;
+	tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+	if (_render_buffers_can_be_storage()) {
+		tf.usage_bits += RD::TEXTURE_USAGE_STORAGE_BIT;
+	} else {
+		tf.usage_bits += RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+	}
 	tf.mipmaps = mipmaps_required;
 
 	rb->blur[0].texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
@@ -1408,11 +1416,40 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
 		mm.width = base_width;
 		mm.height = base_height;
 
+		if (!_render_buffers_can_be_storage()) {
+			Vector<RID> fb;
+			fb.push_back(mm.texture);
+
+			mm.fb = RD::get_singleton()->framebuffer_create(fb);
+		}
+
+		if (!_render_buffers_can_be_storage()) {
+			// and half texture, this is an intermediate result so just allocate a texture, is this good enough?
+			tf.width = MAX(1, base_width >> 1);
+			tf.height = base_height;
+			tf.mipmaps = 1; // 1 or 0?
+
+			mm.half_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+			Vector<RID> half_fb;
+			half_fb.push_back(mm.half_texture);
+			mm.half_fb = RD::get_singleton()->framebuffer_create(half_fb);
+		}
+
 		rb->blur[0].mipmaps.push_back(mm);
 
 		if (i > 0) {
 			mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[1].texture, 0, i - 1);
 
+			if (!_render_buffers_can_be_storage()) {
+				Vector<RID> fb;
+				fb.push_back(mm.texture);
+
+				mm.fb = RD::get_singleton()->framebuffer_create(fb);
+
+				// We can re-use the half texture here as it is an intermediate result
+			}
+
 			rb->blur[1].mipmaps.push_back(mm);
 		}
 
@@ -1435,26 +1472,48 @@ void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) {
 		tf.format = RD::DATA_FORMAT_R32_SFLOAT;
 		tf.width = w;
 		tf.height = h;
-		tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
 
 		bool final = w == 1 && h == 1;
 
-		if (final) {
-			tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
+		if (_render_buffers_can_be_storage()) {
+			tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
+			if (final) {
+				tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
+			}
+		} else {
+			tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
 		}
 
 		RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
 
 		rb->luminance.reduce.push_back(texture);
+		if (!_render_buffers_can_be_storage()) {
+			Vector<RID> fb;
+			fb.push_back(texture);
+
+			rb->luminance.fb.push_back(RD::get_singleton()->framebuffer_create(fb));
+		}
 
 		if (final) {
 			rb->luminance.current = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+			if (!_render_buffers_can_be_storage()) {
+				Vector<RID> fb;
+				fb.push_back(rb->luminance.current);
+
+				rb->luminance.current_fb = RD::get_singleton()->framebuffer_create(fb);
+			}
 			break;
 		}
 	}
 }
 
 void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
+	if (rb->texture_fb.is_valid()) {
+		RD::get_singleton()->free(rb->texture_fb);
+		rb->texture_fb = RID();
+	}
+
 	if (rb->texture.is_valid()) {
 		RD::get_singleton()->free(rb->texture);
 		rb->texture = RID();
@@ -1466,19 +1525,43 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
 	}
 
 	for (int i = 0; i < 2; i++) {
+		for (int m = 0; m < rb->blur[i].mipmaps.size(); m++) {
+			// do we free the texture slice here? or is it enough to free the main texture?
+
+			// do free the mobile extra stuff
+			if (rb->blur[i].mipmaps[m].fb.is_valid()) {
+				RD::get_singleton()->free(rb->blur[i].mipmaps[m].fb);
+			}
+			if (rb->blur[i].mipmaps[m].half_fb.is_valid()) {
+				RD::get_singleton()->free(rb->blur[i].mipmaps[m].half_fb);
+			}
+			if (rb->blur[i].mipmaps[m].half_texture.is_valid()) {
+				RD::get_singleton()->free(rb->blur[i].mipmaps[m].half_texture);
+			}
+		}
+		rb->blur[i].mipmaps.clear();
+
 		if (rb->blur[i].texture.is_valid()) {
 			RD::get_singleton()->free(rb->blur[i].texture);
 			rb->blur[i].texture = RID();
-			rb->blur[i].mipmaps.clear();
 		}
 	}
 
+	for (int i = 0; i < rb->luminance.fb.size(); i++) {
+		RD::get_singleton()->free(rb->luminance.fb[i]);
+	}
+	rb->luminance.fb.clear();
+
 	for (int i = 0; i < rb->luminance.reduce.size(); i++) {
 		RD::get_singleton()->free(rb->luminance.reduce[i]);
 	}
-
 	rb->luminance.reduce.clear();
 
+	if (rb->luminance.current_fb.is_valid()) {
+		RD::get_singleton()->free(rb->luminance.current_fb);
+		rb->luminance.current_fb = RID();
+	}
+
 	if (rb->luminance.current.is_valid()) {
 		RD::get_singleton()->free(rb->luminance.current);
 		rb->luminance.current = RID();
@@ -1750,17 +1833,27 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 	CameraEffects *camfx = camera_effects_owner.getornull(p_render_data->camera_effects);
 
 	bool can_use_effects = rb->width >= 8 && rb->height >= 8;
+	bool can_use_storage = _render_buffers_can_be_storage();
+
+	// @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye
 
 	if (can_use_effects && camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0) {
+		RD::get_singleton()->draw_command_begin_label("DOF");
 		if (rb->blur[0].texture.is_null()) {
 			_allocate_blur_textures(rb);
 		}
 
-		float bokeh_size = camfx->dof_blur_amount * 64.0;
-		storage->get_effects()->bokeh_dof(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->blur[0].mipmaps[0].texture, rb->blur[1].mipmaps[0].texture, rb->blur[0].mipmaps[1].texture, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
+		if (can_use_storage) {
+			float bokeh_size = camfx->dof_blur_amount * 64.0;
+			storage->get_effects()->bokeh_dof(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->blur[0].mipmaps[0].texture, rb->blur[1].mipmaps[0].texture, rb->blur[0].mipmaps[1].texture, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
+		} else {
+			storage->get_effects()->blur_dof_raster(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->texture_fb, rb->blur[0].mipmaps[0].texture, rb->blur[0].mipmaps[0].fb, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, camfx->dof_blur_amount, dof_blur_quality, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
+		}
+		RD::get_singleton()->draw_command_end_label();
 	}
 
 	if (can_use_effects && env && env->auto_exposure) {
+		RD::get_singleton()->draw_command_begin_label("Auto exposure");
 		if (rb->luminance.current.is_null()) {
 			_allocate_luminance_textures(rb);
 		}
@@ -1769,16 +1862,26 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		rb->auto_exposure_version = env->auto_exposure_version;
 
 		double step = env->auto_exp_speed * time_step;
-		storage->get_effects()->luminance_reduction(rb->texture, Size2i(rb->width, rb->height), rb->luminance.reduce, rb->luminance.current, env->min_luminance, env->max_luminance, step, set_immediate);
-
+		if (can_use_storage) {
+			storage->get_effects()->luminance_reduction(rb->texture, Size2i(rb->width, rb->height), rb->luminance.reduce, rb->luminance.current, env->min_luminance, env->max_luminance, step, set_immediate);
+		} else {
+			storage->get_effects()->luminance_reduction_raster(rb->texture, Size2i(rb->width, rb->height), rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, env->min_luminance, env->max_luminance, step, set_immediate);
+		}
 		//swap final reduce with prev luminance
 		SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]);
+		if (!can_use_storage) {
+			SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]);
+		}
+
 		RenderingServerDefault::redraw_request(); //redraw all the time if auto exposure rendering is on
+		RD::get_singleton()->draw_command_end_label();
 	}
 
 	int max_glow_level = -1;
 
 	if (can_use_effects && env && env->glow_enabled) {
+		RD::get_singleton()->draw_command_begin_label("Gaussian Glow");
+
 		/* see that blur textures are allocated */
 
 		if (rb->blur[1].texture.is_null()) {
@@ -1804,14 +1907,26 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 				if (env->auto_exposure && rb->luminance.current.is_valid()) {
 					luminance_texture = rb->luminance.current;
 				}
-				storage->get_effects()->gaussian_glow(rb->texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
+				if (can_use_storage) {
+					storage->get_effects()->gaussian_glow(rb->texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
+				} else {
+					storage->get_effects()->gaussian_glow_raster(rb->texture, rb->blur[1].mipmaps[i].half_fb, rb->blur[1].mipmaps[i].half_texture, rb->blur[1].mipmaps[i].fb, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
+				}
 			} else {
-				storage->get_effects()->gaussian_glow(rb->blur[1].mipmaps[i - 1].texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality);
+				if (can_use_storage) {
+					storage->get_effects()->gaussian_glow(rb->blur[1].mipmaps[i - 1].texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality);
+				} else {
+					storage->get_effects()->gaussian_glow_raster(rb->blur[1].mipmaps[i - 1].texture, rb->blur[1].mipmaps[i].half_fb, rb->blur[1].mipmaps[i].half_texture, rb->blur[1].mipmaps[i].fb, Vector2(1.0 / vp_w, 1.0 / vp_h), env->glow_strength, glow_high_quality);
+				}
 			}
 		}
+
+		RD::get_singleton()->draw_command_end_label();
 	}
 
 	{
+		RD::get_singleton()->draw_command_begin_label("Tonemap");
+
 		//tonemap
 		EffectsRD::TonemapSettings tonemap;
 
@@ -1870,6 +1985,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		tonemap.view_count = p_render_data->view_count;
 
 		storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap);
+
+		RD::get_singleton()->draw_command_end_label();
 	}
 
 	storage->render_target_disable_clear_request(rb->render_target);
@@ -2197,6 +2314,14 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
 		rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
 	}
 
+	if (!_render_buffers_can_be_storage()) {
+		// ONLY USED ON MOBILE RENDERER, ONLY USED FOR POST EFFECTS!
+		Vector<RID> fb;
+		fb.push_back(rb->texture);
+
+		rb->texture_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, rb->view_count);
+	}
+
 	rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa, p_view_count);
 
 	if (is_clustered_enabled()) {

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

@@ -450,6 +450,7 @@ private:
 
 		RID texture; //main texture for rendering to, must be filled after done rendering
 		RID depth_texture; //main depth texture
+		RID texture_fb; // framebuffer for the main texture, ONLY USED FOR MOBILE RENDERER POST EFFECTS, DO NOT USE FOR RENDERING 3D!!!
 
 		RendererSceneGIRD::SDFGI *sdfgi = nullptr;
 		VolumetricFog *volumetric_fog = nullptr;
@@ -465,6 +466,11 @@ private:
 				RID texture;
 				int width;
 				int height;
+
+				// only used on mobile renderer
+				RID fb;
+				RID half_texture;
+				RID half_fb;
 			};
 
 			Vector<Mipmap> mipmaps;
@@ -475,6 +481,10 @@ private:
 		struct Luminance {
 			Vector<RID> reduce;
 			RID current;
+
+			// used only on mobile renderer
+			Vector<RID> fb;
+			RID current_fb;
 		} luminance;
 
 		struct SSAO {

+ 18 - 8
servers/rendering/renderer_rd/renderer_storage_rd.cpp

@@ -4959,7 +4959,7 @@ void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &
 		RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1);
 
 		RD::get_singleton()->compute_list_end();
-		effects.sort_buffer(particles->particles_sort_uniform_set, particles->amount);
+		effects->sort_buffer(particles->particles_sort_uniform_set, particles->amount);
 	}
 
 	copy_push_constant.total_particles *= copy_push_constant.total_particles;
@@ -7535,7 +7535,7 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c
 
 	//single texture copy for backbuffer
 	//RD::get_singleton()->texture_copy(rt->color, rt->backbuffer_mipmap0, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true);
-	effects.copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+	effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
 
 	if (!p_gen_mipmaps) {
 		return;
@@ -7551,7 +7551,7 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c
 		region.size.y = MAX(1, region.size.y >> 1);
 
 		const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
-		effects.gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
+		effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
 		prev_texture = mm.mipmap;
 	}
 }
@@ -7574,7 +7574,7 @@ void RendererStorageRD::render_target_clear_back_buffer(RID p_render_target, con
 	}
 
 	//single texture copy for backbuffer
-	effects.set_color(rt->backbuffer_mipmap0, p_color, region, true);
+	effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
 }
 
 void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) {
@@ -7604,7 +7604,7 @@ void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_targe
 		region.size.y = MAX(1, region.size.y >> 1);
 
 		const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
-		effects.gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
+		effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
 		prev_texture = mm.mipmap;
 	}
 }
@@ -7925,14 +7925,14 @@ void RendererStorageRD::_update_decal_atlas() {
 				while ((K = decal_atlas.textures.next(K))) {
 					DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
 					Texture *src_tex = texture_owner.getornull(*K);
-					effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
+					effects->copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
 				}
 
 				RD::get_singleton()->draw_list_end();
 
 				prev_texture = mm.texture;
 			} else {
-				effects.copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
+				effects->copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
 				prev_texture = mm.texture;
 			}
 		} else {
@@ -8804,8 +8804,13 @@ bool RendererStorageRD::free(RID p_rid) {
 	return true;
 }
 
+void RendererStorageRD::init_effects(bool p_prefer_raster_effects) {
+	effects = memnew(EffectsRD(p_prefer_raster_effects));
+}
+
 EffectsRD *RendererStorageRD::get_effects() {
-	return &effects;
+	ERR_FAIL_NULL_V_MSG(effects, nullptr, "Effects haven't been initialised yet.");
+	return effects;
 }
 
 void RendererStorageRD::capture_timestamps_begin() {
@@ -9535,4 +9540,9 @@ RendererStorageRD::~RendererStorageRD() {
 	if (decal_atlas.texture.is_valid()) {
 		RD::get_singleton()->free(decal_atlas.texture);
 	}
+
+	if (effects) {
+		memdelete(effects);
+		effects = NULL;
+	}
 }

+ 2 - 1
servers/rendering/renderer_rd/renderer_storage_rd.h

@@ -1290,7 +1290,7 @@ private:
 	void _update_global_variables();
 	/* EFFECTS */
 
-	EffectsRD effects;
+	EffectsRD *effects = NULL;
 
 public:
 	virtual bool can_create_resources_async() const;
@@ -2374,6 +2374,7 @@ public:
 
 	static RendererStorageRD *base_singleton;
 
+	void init_effects(bool p_prefer_raster_effects);
 	EffectsRD *get_effects();
 
 	RendererStorageRD();

+ 228 - 0
servers/rendering/renderer_rd/shaders/blur_raster.glsl

@@ -0,0 +1,228 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "blur_raster_inc.glsl"
+
+layout(location = 0) out vec2 uv_interp;
+/* clang-format on */
+
+void main() {
+	vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+	uv_interp = base_arr[gl_VertexIndex];
+
+	gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "blur_raster_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+
+#ifdef GLOW_USE_AUTO_EXPOSURE
+layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
+#endif
+
+layout(location = 0) out vec4 frag_color;
+
+//DOF
+#ifdef MODE_DOF_BLUR
+
+layout(set = 1, binding = 0) uniform sampler2D dof_source_depth;
+
+#ifdef DOF_QUALITY_LOW
+const int dof_kernel_size = 5;
+const int dof_kernel_from = 2;
+const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
+#endif
+
+#ifdef DOF_QUALITY_MEDIUM
+const int dof_kernel_size = 11;
+const int dof_kernel_from = 5;
+const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
+
+#endif
+
+#ifdef DOF_QUALITY_HIGH
+const int dof_kernel_size = 21;
+const int dof_kernel_from = 10;
+const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
+#endif
+
+#endif
+
+void main() {
+#ifdef MODE_MIPMAP
+
+	vec2 pix_size = blur.pixel_size;
+	vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size);
+	color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size);
+	color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size);
+	color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size);
+	frag_color = color / 4.0;
+
+#endif
+
+#ifdef MODE_GAUSSIAN_BLUR
+
+	//Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
+
+	if (bool(blur.flags & FLAG_HORIZONTAL)) {
+		vec2 pix_size = blur.pixel_size;
+		pix_size *= 0.5; //reading from larger buffer, so use more samples
+		vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.214607;
+		color += texture(source_color, uv_interp + vec2(1.0, 0.0) * pix_size) * 0.189879;
+		color += texture(source_color, uv_interp + vec2(2.0, 0.0) * pix_size) * 0.131514;
+		color += texture(source_color, uv_interp + vec2(3.0, 0.0) * pix_size) * 0.071303;
+		color += texture(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size) * 0.189879;
+		color += texture(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size) * 0.131514;
+		color += texture(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size) * 0.071303;
+		frag_color = color;
+	} else {
+		vec2 pix_size = blur.pixel_size;
+		vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.38774;
+		color += texture(source_color, uv_interp + vec2(0.0, 1.0) * pix_size) * 0.24477;
+		color += texture(source_color, uv_interp + vec2(0.0, 2.0) * pix_size) * 0.06136;
+		color += texture(source_color, uv_interp + vec2(0.0, -1.0) * pix_size) * 0.24477;
+		color += texture(source_color, uv_interp + vec2(0.0, -2.0) * pix_size) * 0.06136;
+		frag_color = color;
+	}
+#endif
+
+#ifdef MODE_GAUSSIAN_GLOW
+
+	//Glow uses larger sigma 1 for a more rounded blur effect
+
+#define GLOW_ADD(m_ofs, m_mult)                                                  \
+	{                                                                            \
+		vec2 ofs = uv_interp + m_ofs * pix_size;                                 \
+		vec4 c = texture(source_color, ofs) * m_mult;                            \
+		if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \
+			c *= 0.0;                                                            \
+		}                                                                        \
+		color += c;                                                              \
+	}
+
+	if (bool(blur.flags & FLAG_HORIZONTAL)) {
+		vec2 pix_size = blur.pixel_size;
+		pix_size *= 0.5; //reading from larger buffer, so use more samples
+		vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
+		GLOW_ADD(vec2(1.0, 0.0), 0.165569);
+		GLOW_ADD(vec2(2.0, 0.0), 0.140367);
+		GLOW_ADD(vec2(3.0, 0.0), 0.106595);
+		GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
+		GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
+		GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
+		color *= blur.glow_strength;
+		frag_color = color;
+	} else {
+		vec2 pix_size = blur.pixel_size;
+		vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713;
+		GLOW_ADD(vec2(0.0, 1.0), 0.233062);
+		GLOW_ADD(vec2(0.0, 2.0), 0.122581);
+		GLOW_ADD(vec2(0.0, -1.0), 0.233062);
+		GLOW_ADD(vec2(0.0, -2.0), 0.122581);
+		color *= blur.glow_strength;
+		frag_color = color;
+	}
+
+#undef GLOW_ADD
+
+	if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
+#ifdef GLOW_USE_AUTO_EXPOSURE
+
+		frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey;
+#endif
+		frag_color *= blur.glow_exposure;
+
+		float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
+		float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
+
+		frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap));
+	}
+
+#endif
+
+#ifdef MODE_DOF_BLUR
+
+	vec4 color_accum = vec4(0.0);
+
+	float depth = texture(dof_source_depth, uv_interp, 0.0).r;
+	depth = depth * 2.0 - 1.0;
+
+	if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
+		depth = ((depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
+	} else {
+		depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - depth * (blur.camera_z_far - blur.camera_z_near));
+	}
+
+	// mix near and far blur amount
+	float amount = 1.0;
+	if (bool(blur.flags & FLAG_DOF_FAR)) {
+		amount *= 1.0 - smoothstep(blur.dof_far_begin, blur.dof_far_end, depth);
+	}
+	if (bool(blur.flags & FLAG_DOF_NEAR)) {
+		amount *= smoothstep(blur.dof_near_end, blur.dof_near_begin, depth);
+	}
+	amount = 1.0 - amount;
+
+	if (amount > 0.0) {
+		float k_accum = 0.0;
+
+		for (int i = 0; i < dof_kernel_size; i++) {
+			int int_ofs = i - dof_kernel_from;
+			vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * amount * blur.dof_radius;
+
+			float tap_k = dof_kernel[i];
+
+			float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
+			tap_depth = tap_depth * 2.0 - 1.0;
+
+			if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
+				tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
+			} else {
+				tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near));
+			}
+
+			// mix near and far blur amount
+			float tap_amount = 1.0;
+			if (bool(blur.flags & FLAG_DOF_FAR)) {
+				tap_amount *= mix(1.0 - smoothstep(blur.dof_far_begin, blur.dof_far_end, tap_depth), 0.0, int_ofs == 0);
+			}
+			if (bool(blur.flags & FLAG_DOF_NEAR)) {
+				tap_amount *= mix(smoothstep(blur.dof_near_end, blur.dof_near_begin, tap_depth), 0.0, int_ofs == 0);
+			}
+			tap_amount = 1.0 - tap_amount;
+
+			tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+
+			vec4 tap_color = texture(source_color, tap_uv, 0.0) * tap_k;
+
+			k_accum += tap_k * tap_amount;
+			color_accum += tap_color * tap_amount;
+		}
+
+		if (k_accum > 0.0) {
+			color_accum /= k_accum;
+		}
+
+		frag_color = color_accum; ///k_accum;
+	} else {
+		// we are in focus, don't waste time
+		frag_color = texture(source_color, uv_interp, 0.0);
+	}
+
+#endif
+}

+ 36 - 0
servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl

@@ -0,0 +1,36 @@
+#define FLAG_HORIZONTAL (1 << 0)
+#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
+#define FLAG_GLOW_FIRST_PASS (1 << 2)
+#define FLAG_DOF_FAR (1 << 3)
+#define FLAG_DOF_NEAR (1 << 4)
+
+layout(push_constant, binding = 1, std430) uniform Blur {
+	vec2 pixel_size;
+	uint flags;
+	uint pad;
+
+	// Glow.
+	float glow_strength;
+	float glow_bloom;
+	float glow_hdr_threshold;
+	float glow_hdr_scale;
+
+	float glow_exposure;
+	float glow_white;
+	float glow_luminance_cap;
+	float glow_auto_exposure_grey;
+
+	// DOF.
+	float dof_far_begin;
+	float dof_far_end;
+	float dof_near_begin;
+	float dof_near_end;
+
+	float dof_radius;
+	float dof_pad[3];
+
+	vec2 dof_dir;
+	float camera_z_far;
+	float camera_z_near;
+}
+blur;

+ 74 - 0
servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl

@@ -0,0 +1,74 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "luminance_reduce_raster_inc.glsl"
+
+layout(location = 0) out vec2 uv_interp;
+/* clang-format on */
+
+void main() {
+	vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+	uv_interp = base_arr[gl_VertexIndex];
+
+	gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "luminance_reduce_raster_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+layout(set = 0, binding = 0) uniform sampler2D source_exposure;
+
+#ifdef FINAL_PASS
+layout(set = 1, binding = 0) uniform sampler2D prev_luminance;
+#endif
+
+layout(location = 0) out highp float luminance;
+
+void main() {
+	ivec2 dest_pos = ivec2(uv_interp * settings.dest_size);
+	ivec2 src_pos = ivec2(uv_interp * settings.source_size);
+
+	ivec2 next_pos = (dest_pos + ivec2(1)) * settings.source_size / settings.dest_size;
+	next_pos = max(next_pos, src_pos + ivec2(1)); //so it at least reads one pixel
+
+	highp vec3 source_color = vec3(0.0);
+	for (int i = src_pos.x; i < next_pos.x; i++) {
+		for (int j = src_pos.y; j < next_pos.y; j++) {
+			source_color += texelFetch(source_exposure, ivec2(i, j), 0).rgb;
+		}
+	}
+
+	source_color /= float((next_pos.x - src_pos.x) * (next_pos.y - src_pos.y));
+
+#ifdef FIRST_PASS
+	luminance = max(source_color.r, max(source_color.g, source_color.b));
+
+	// This formula should be more "accurate" but gave an overexposed result when testing.
+	// Leaving it here so we can revisit it if we want.
+	// luminance = source_color.r * 0.21 + source_color.g * 0.71 + source_color.b * 0.07;
+#else
+	luminance = source_color.r;
+#endif
+
+#ifdef FINAL_PASS
+	// Obtain our target luminance
+	luminance = clamp(luminance, settings.min_luminance, settings.max_luminance);
+
+	// Now smooth to our transition
+	highp float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous luminance
+	luminance = prev_lum + (luminance - prev_lum) * clamp(settings.exposure_adjust, 0.0, 1.0);
+#endif
+}

+ 11 - 0
servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl

@@ -0,0 +1,11 @@
+
+layout(push_constant, binding = 1, std430) uniform PushConstant {
+	ivec2 source_size;
+	ivec2 dest_size;
+
+	float exposure_adjust;
+	float min_luminance;
+	float max_luminance;
+	float pad;
+}
+settings;