Browse Source

Implemented raster versions of bokeh shaders to replace broken gaussian implementation

Bastiaan Olij 4 years ago
parent
commit
07fd559478

+ 220 - 101
servers/rendering/renderer_rd/effects_rd.cpp

@@ -928,7 +928,7 @@ void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_
 	}
 }
 
-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) {
+void EffectsRD::bokeh_dof(const BokehBuffers &p_buffers, 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;
@@ -957,22 +957,22 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 	// The alpha channel of the source color texture is filled with the expected circle size
 	// If used for DOF far, the size is positive, if used for near, its negative.
 
-	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BLUR_SIZE]);
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
 
-	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
-	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_texture), 1);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.depth_texture), 1);
 
-	bokeh.push_constant.size[0] = p_base_texture_size.x;
-	bokeh.push_constant.size[1] = p_base_texture_size.y;
+	bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+	bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
 
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
 
-	RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+	RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
 	RD::get_singleton()->compute_list_add_barrier(compute_list);
 
 	if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
 		//second pass
-		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]);
+		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]);
 
 		static const int quality_samples[4] = { 6, 12, 12, 24 };
 
@@ -981,18 +981,18 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 		if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
 			//box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
 
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
 
-			bokeh.push_constant.size[0] = p_base_texture_size.x >> 1;
-			bokeh.push_constant.size[1] = p_base_texture_size.y >> 1;
+			bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+			bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
 			bokeh.push_constant.half_size = true;
 			bokeh.push_constant.blur_size *= 0.5;
 
 		} else {
 			//medium and high quality use full size
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_secondary_texture), 0);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.secondary_texture), 0);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
 		}
 
 		RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1004,11 +1004,11 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 		bokeh.push_constant.second_pass = true;
 
 		if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture2), 0);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[1]), 0);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
 		} else {
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_secondary_texture), 1);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.secondary_texture), 1);
 		}
 
 		RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1019,25 +1019,25 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 		if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
 			//forth pass, upscale for low quality
 
-			RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]);
+			RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
 
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture2), 1);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[1]), 1);
 
-			bokeh.push_constant.size[0] = p_base_texture_size.x;
-			bokeh.push_constant.size[1] = p_base_texture_size.y;
+			bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+			bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
 			bokeh.push_constant.half_size = false;
 			bokeh.push_constant.second_pass = false;
 
 			RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
 
-			RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+			RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
 		}
 	} else {
 		//circle
 
 		//second pass
-		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
+		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
 
 		static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
 
@@ -1046,11 +1046,11 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 
 		//circle always runs in half size, otherwise too expensive
 
-		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0);
-		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
 
-		bokeh.push_constant.size[0] = p_base_texture_size.x >> 1;
-		bokeh.push_constant.size[1] = p_base_texture_size.y >> 1;
+		bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+		bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
 		bokeh.push_constant.half_size = true;
 
 		RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1062,93 +1062,195 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
 
 		// upscale
 
-		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]);
+		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
 
-		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
-		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
 
-		bokeh.push_constant.size[0] = p_base_texture_size.x;
-		bokeh.push_constant.size[1] = p_base_texture_size.y;
+		bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+		bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
 		bokeh.push_constant.half_size = false;
 		bokeh.push_constant.second_pass = false;
 
 		RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
 
-		RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+		RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
 	}
 
 	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) {
+void EffectsRD::bokeh_dof_raster(const BokehBuffers &p_buffers, 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, RenderingServer::DOFBokehShape p_bokeh_shape, 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;
+	memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
 
-	Vector2 pixel_size = Vector2(1.0 / p_base_texture_size.width, 1.0 / p_base_texture_size.height);
+	bokeh.push_constant.orthogonal = p_cam_orthogonal;
+	bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
+	bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
+	bokeh.push_constant.z_far = p_cam_zfar;
+	bokeh.push_constant.z_near = p_cam_znear;
 
-	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;
+	bokeh.push_constant.second_pass = false;
+	bokeh.push_constant.half_size = false;
+	bokeh.push_constant.blur_size = p_dof_blur_amount;
 
 	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;
+			bokeh.push_constant.blur_far_active = true;
+			bokeh.push_constant.blur_far_begin = p_dof_far_begin;
+			bokeh.push_constant.blur_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;
+			bokeh.push_constant.blur_near_active = true;
+			bokeh.push_constant.blur_near_begin = p_dof_near_begin;
+			bokeh.push_constant.blur_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);
+		{
+			// generate our depth data
+			RID framebuffer = p_buffers.base_weight_fb;
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.depth_texture), 0);
+			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, &bokeh.push_constant, sizeof(BokehPushConstant));
 
-		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();
+		}
 
-		RD::get_singleton()->draw_list_draw(draw_list, true);
-		RD::get_singleton()->draw_list_end();
+		if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+			// double pass approach
+			BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
 
-		//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);
+			if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+				//box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
+				bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+				bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+				bokeh.push_constant.half_size = true;
+				bokeh.push_constant.blur_size *= 0.5;
+			}
 
-		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;
+			static const int quality_samples[4] = { 6, 12, 12, 24 };
+			bokeh.push_constant.blur_scale = 0.5;
+			bokeh.push_constant.steps = quality_samples[p_quality];
 
-		RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+			RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
 
-		RD::get_singleton()->draw_list_draw(draw_list, true);
-		RD::get_singleton()->draw_list_end();
+			// Pass 1
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
+			RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+			RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+			RD::get_singleton()->draw_list_draw(draw_list, true);
+			RD::get_singleton()->draw_list_end();
+
+			// Pass 2
+			if (!bokeh.push_constant.half_size) {
+				// do not output weight, we're writing back into our base buffer
+				mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
+			}
+			bokeh.push_constant.second_pass = true;
+
+			framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
+			RID texture = bokeh.push_constant.half_size ? p_buffers.half_texture[0] : p_buffers.secondary_texture;
+			RID weight = bokeh.push_constant.half_size ? p_buffers.weight_texture[2] : p_buffers.weight_texture[1];
+
+			draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(texture), 0);
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(weight), 1);
+			RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+			RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+			RD::get_singleton()->draw_list_draw(draw_list, true);
+			RD::get_singleton()->draw_list_end();
+
+			if (bokeh.push_constant.half_size) {
+				// Compose pass
+				mode = BOKEH_COMPOSITE;
+				framebuffer = p_buffers.base_fb;
+
+				draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[1]), 0);
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[3]), 1);
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
+				RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+				RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+				RD::get_singleton()->draw_list_draw(draw_list, true);
+				RD::get_singleton()->draw_list_end();
+			}
+
+		} else {
+			// circular is a single pass approach
+			BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
+
+			{
+				// circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
+				bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+				bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+				bokeh.push_constant.half_size = true;
+				// bokeh.push_constant.blur_size *= 0.5;
+			}
+
+			static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
+			bokeh.push_constant.blur_scale = quality_scale[p_quality];
+			bokeh.push_constant.steps = 0.0;
+
+			RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
+
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
+			RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+			RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+			RD::get_singleton()->draw_list_draw(draw_list, true);
+			RD::get_singleton()->draw_list_end();
+
+			if (bokeh.push_constant.half_size) {
+				// Compose
+				mode = BOKEH_COMPOSITE;
+				framebuffer = p_buffers.base_fb;
+
+				draw_list = RD::get_singleton()->draw_list_begin(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, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[0]), 0);
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[2]), 1);
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
+				RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+				RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+				RD::get_singleton()->draw_list_draw(draw_list, true);
+				RD::get_singleton()->draw_list_end();
+			} else {
+				// Just copy it back (we use our blur raster shader here)..
+				draw_list = RD::get_singleton()->draw_list_begin(p_buffers.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_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_buffers.base_fb)));
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.secondary_texture), 0);
+				RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+				memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+				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();
+			}
+		}
 	}
 }
 
@@ -1774,9 +1876,7 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
 		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_modes.push_back("\n#define MODE_COPY\n"); // BLUR_MODE_COPY
 
 		blur_raster.shader.initialize(blur_modes);
 		memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
@@ -1956,23 +2056,40 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
 		cube_to_dp.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
 	}
 
+	// Initialize bokeh
+	Vector<String> bokeh_modes;
+	bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
+	bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
+	bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
+	bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
+	bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
+	bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
+	bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
 	if (prefer_raster_effects) {
-		// not supported
+		bokeh.raster_shader.initialize(bokeh_modes);
+
+		bokeh.shader_version = bokeh.raster_shader.version_create();
+
+		const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
+		for (int i = 0; i < BOKEH_MAX; i++) {
+			RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
+			bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
+		}
 	} else {
-		// Initialize bokeh
-		Vector<String> bokeh_modes;
-		bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
-		bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
-		bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
-		bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n");
-		bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
+		bokeh.compute_shader.initialize(bokeh_modes);
 
-		bokeh.shader.initialize(bokeh_modes);
+		bokeh.shader_version = bokeh.compute_shader.version_create();
+		bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
+		bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
 
-		bokeh.shader_version = bokeh.shader.version_create();
+		for (int i = 0; i < BOKEH_MAX; i++) {
+			if (bokeh.compute_shader.is_variant_enabled(i)) {
+				bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
+			}
+		}
 
 		for (int i = 0; i < BOKEH_MAX; i++) {
-			bokeh.pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.shader.version_get_shader(bokeh.shader_version, i));
+			bokeh.raster_pipelines[i].clear();
 		}
 	}
 
@@ -2173,12 +2290,13 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
 
 		if (prefer_raster_effects) {
 			filter.raster_shader.initialize(cubemap_filter_modes);
-			filter.shader_version = filter.raster_shader.version_create();
 
 			// array variants are not supported in raster
 			filter.raster_shader.set_variant_enabled(FILTER_MODE_HIGH_QUALITY_ARRAY, false);
 			filter.raster_shader.set_variant_enabled(FILTER_MODE_LOW_QUALITY_ARRAY, false);
 
+			filter.shader_version = filter.raster_shader.version_create();
+
 			for (int i = 0; i < FILTER_MODE_MAX; i++) {
 				if (filter.raster_shader.is_variant_enabled(i)) {
 					filter.raster_pipelines[i].setup(filter.raster_shader.version_get_shader(filter.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
@@ -2390,12 +2508,13 @@ EffectsRD::~EffectsRD() {
 
 	if (prefer_raster_effects) {
 		blur_raster.shader.version_free(blur_raster.shader_version);
+		bokeh.raster_shader.version_free(blur_raster.shader_version);
 		luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
 		roughness.raster_shader.version_free(roughness.shader_version);
 		cubemap_downsampler.raster_shader.version_free(cubemap_downsampler.shader_version);
 		filter.raster_shader.version_free(filter.shader_version);
 	} else {
-		bokeh.shader.version_free(bokeh.shader_version);
+		bokeh.compute_shader.version_free(bokeh.shader_version);
 		luminance_reduce.shader.version_free(luminance_reduce.shader_version);
 		roughness.compute_shader.version_free(roughness.shader_version);
 		cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version);

+ 46 - 41
servers/rendering/renderer_rd/effects_rd.h

@@ -35,6 +35,7 @@
 #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/bokeh_dof_raster.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"
 #include "servers/rendering/renderer_rd/shaders/cube_to_dp.glsl.gen.h"
@@ -72,10 +73,7 @@ private:
 		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_COPY,
 
 		BLUR_MODE_MAX
 	};
@@ -84,8 +82,6 @@ private:
 		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 {
@@ -103,19 +99,6 @@ private:
 		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 {
@@ -252,29 +235,29 @@ private:
 	};
 
 	struct TonemapPushConstant {
-		float bcs[3];
-		uint32_t use_bcs;
+		float bcs[3]; // 12 - 12
+		uint32_t use_bcs; //  4 - 16
 
-		uint32_t use_glow;
-		uint32_t use_auto_exposure;
-		uint32_t use_color_correction;
-		uint32_t tonemapper;
+		uint32_t use_glow; //  4 - 20
+		uint32_t use_auto_exposure; //  4 - 24
+		uint32_t use_color_correction; //  4 - 28
+		uint32_t tonemapper; //  4 - 32
 
-		uint32_t glow_texture_size[2];
-		float glow_intensity;
-		uint32_t pad3;
+		uint32_t glow_texture_size[2]; //  8 - 40
+		float glow_intensity; //  4 - 44
+		uint32_t pad3; //  4 - 48
 
-		uint32_t glow_mode;
-		float glow_levels[7];
+		uint32_t glow_mode; //  4 - 52
+		float glow_levels[7]; // 28 - 80
 
-		float exposure;
-		float white;
-		float auto_exposure_grey;
-		uint32_t pad2;
+		float exposure; //  4 - 84
+		float white; //  4 - 88
+		float auto_exposure_grey; //  4 - 92
+		uint32_t pad2; //  4 - 96
 
-		float pixel_size[2];
-		uint32_t use_fxaa;
-		uint32_t use_debanding;
+		float pixel_size[2]; //  8 - 104
+		uint32_t use_fxaa; //  4 - 108
+		uint32_t use_debanding; //  4 - 112
 	};
 
 	/* tonemap actually writes to a framebuffer, which is
@@ -375,7 +358,9 @@ private:
 	enum BokehMode {
 		BOKEH_GEN_BLUR_SIZE,
 		BOKEH_GEN_BOKEH_BOX,
+		BOKEH_GEN_BOKEH_BOX_NOWEIGHT,
 		BOKEH_GEN_BOKEH_HEXAGONAL,
+		BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT,
 		BOKEH_GEN_BOKEH_CIRCULAR,
 		BOKEH_COMPOSITE,
 		BOKEH_MAX
@@ -383,9 +368,11 @@ private:
 
 	struct Bokeh {
 		BokehPushConstant push_constant;
-		BokehDofShaderRD shader;
+		BokehDofShaderRD compute_shader;
+		BokehDofRasterShaderRD raster_shader;
 		RID shader_version;
-		RID pipelines[BOKEH_MAX];
+		RID compute_pipelines[BOKEH_MAX];
+		PipelineCacheRD raster_pipelines[BOKEH_MAX];
 	} bokeh;
 
 	enum SSAOMode {
@@ -784,8 +771,26 @@ public:
 	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 BokehBuffers {
+		// bokeh buffers
+
+		// textures
+		Size2i base_texture_size;
+		RID base_texture;
+		RID depth_texture;
+		RID secondary_texture;
+		RID half_texture[2];
+
+		// raster only
+		RID base_fb;
+		RID secondary_fb; // with weights
+		RID half_fb[2]; // with weights
+		RID base_weight_fb;
+		RID weight_texture[4];
+	};
+
+	void bokeh_dof(const BokehBuffers &p_buffers, 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 bokeh_dof_raster(const BokehBuffers &p_buffers, 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, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
 
 	struct TonemapSettings {
 		bool use_glow = false;

+ 73 - 3
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -1464,6 +1464,53 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
 		base_width = MAX(1, base_width >> 1);
 		base_height = MAX(1, base_height >> 1);
 	}
+
+	if (!_render_buffers_can_be_storage()) {
+		// create 4 weight textures, 2 full size, 2 half size
+
+		tf.format = RD::DATA_FORMAT_R16_SFLOAT; // We could probably use DATA_FORMAT_R8_SNORM if we don't pre-multiply by blur_size but that depends on whether we can remove DEPTH_GAP
+		tf.width = rb->width;
+		tf.height = rb->height;
+		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 | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+		tf.mipmaps = 1;
+		for (uint32_t i = 0; i < 4; i++) {
+			// associated blur texture
+			RID texture;
+			if (i == 0) {
+				texture = rb->texture;
+			} else if (i == 1) {
+				texture = rb->blur[0].mipmaps[0].texture;
+			} else if (i == 2) {
+				texture = rb->blur[1].mipmaps[0].texture;
+			} else if (i == 3) {
+				texture = rb->blur[0].mipmaps[1].texture;
+			}
+
+			// create weight texture
+			rb->weight_buffers[i].weight = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+			// create frame buffer
+			Vector<RID> fb;
+			fb.push_back(texture);
+			fb.push_back(rb->weight_buffers[i].weight);
+			rb->weight_buffers[i].fb = RD::get_singleton()->framebuffer_create(fb);
+
+			if (i == 1) {
+				// next 2 are half size
+				tf.width = MAX(1, tf.width >> 1);
+				tf.height = MAX(1, tf.height >> 1);
+			}
+		}
+
+		{
+			// and finally an FB for just our base weights
+			Vector<RID> fb;
+			fb.push_back(rb->weight_buffers[0].weight);
+			rb->base_weight_fb = RD::get_singleton()->framebuffer_create(fb);
+		}
+	}
 }
 
 void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) {
@@ -1851,11 +1898,34 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 			_allocate_blur_textures(rb);
 		}
 
+		EffectsRD::BokehBuffers buffers;
+
+		// textures we use
+		buffers.base_texture_size = Size2i(rb->width, rb->height);
+		buffers.base_texture = rb->texture;
+		buffers.depth_texture = rb->depth_texture;
+		buffers.secondary_texture = rb->blur[0].mipmaps[0].texture;
+		buffers.half_texture[0] = rb->blur[1].mipmaps[0].texture;
+		buffers.half_texture[1] = rb->blur[0].mipmaps[1].texture;
+
+		float bokeh_size = camfx->dof_blur_amount * 64.0;
 		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);
+			storage->get_effects()->bokeh_dof(buffers, 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);
+			// set framebuffers
+			buffers.base_fb = rb->texture_fb;
+			buffers.secondary_fb = rb->weight_buffers[1].fb;
+			buffers.half_fb[0] = rb->weight_buffers[2].fb;
+			buffers.half_fb[1] = rb->weight_buffers[3].fb;
+			buffers.weight_texture[0] = rb->weight_buffers[0].weight;
+			buffers.weight_texture[1] = rb->weight_buffers[1].weight;
+			buffers.weight_texture[2] = rb->weight_buffers[2].weight;
+			buffers.weight_texture[3] = rb->weight_buffers[3].weight;
+
+			// set weight buffers
+			buffers.base_weight_fb = rb->base_weight_fb;
+
+			storage->get_effects()->bokeh_dof_raster(buffers, 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, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
 		}
 		RD::get_singleton()->draw_command_end_label();
 	}

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

@@ -483,6 +483,15 @@ private:
 
 		Blur blur[2]; //the second one starts from the first mipmap
 
+		struct WeightBuffers {
+			RID weight;
+			RID fb; // FB with both texture and weight
+		};
+
+		// 2 full size, 2 half size
+		WeightBuffers weight_buffers[4]; // Only used in raster
+		RID base_weight_fb; // base buffer for weight
+
 		struct Luminance {
 			Vector<RID> reduce;
 			RID current;

+ 3 - 95
servers/rendering/renderer_rd/shaders/blur_raster.glsl

@@ -37,32 +37,6 @@ layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
 
 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
 
@@ -155,74 +129,8 @@ void main() {
 
 #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);
-	}
-
+#ifdef MODE_COPY
+	vec4 color = textureLod(source_color, uv_interp, 0.0);
+	frag_color = color;
 #endif
 }

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

@@ -1,8 +1,6 @@
 #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;
@@ -19,18 +17,5 @@ layout(push_constant, binding = 1, std430) uniform Blur {
 	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;

+ 1 - 37
servers/rendering/renderer_rd/shaders/bokeh_dof.glsl

@@ -25,34 +25,7 @@ layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
 
 // based on https://www.shadertoy.com/view/Xd3GDl
 
-layout(push_constant, binding = 1, std430) uniform Params {
-	ivec2 size;
-	float z_far;
-	float z_near;
-
-	bool orthogonal;
-	float blur_size;
-	float blur_scale;
-	int blur_steps;
-
-	bool blur_near_active;
-	float blur_near_begin;
-	float blur_near_end;
-	bool blur_far_active;
-
-	float blur_far_begin;
-	float blur_far_end;
-	bool second_pass;
-	bool half_size;
-
-	bool use_jitter;
-	float jitter_seed;
-	uint pad[2];
-}
-params;
-
-//used to work around downsampling filter
-#define DEPTH_GAP 0.0
+#include "bokeh_dof_inc.glsl"
 
 #ifdef MODE_GEN_BLUR_SIZE
 
@@ -80,15 +53,6 @@ float get_blur_size(float depth) {
 
 #endif
 
-const float GOLDEN_ANGLE = 2.39996323;
-
-//note: uniform pdf rand [0;1[
-float hash12n(vec2 p) {
-	p = fract(p * vec2(5.3987, 5.4421));
-	p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
-	return fract(p.x * p.y * 95.4307);
-}
-
 #if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
 
 vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {

+ 37 - 0
servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl

@@ -0,0 +1,37 @@
+layout(push_constant, binding = 1, std430) uniform Params {
+	ivec2 size;
+	float z_far;
+	float z_near;
+
+	bool orthogonal;
+	float blur_size;
+	float blur_scale;
+	int blur_steps;
+
+	bool blur_near_active;
+	float blur_near_begin;
+	float blur_near_end;
+	bool blur_far_active;
+
+	float blur_far_begin;
+	float blur_far_end;
+	bool second_pass;
+	bool half_size;
+
+	bool use_jitter;
+	float jitter_seed;
+	uint pad[2];
+}
+params;
+
+//used to work around downsampling filter
+#define DEPTH_GAP 0.0
+
+const float GOLDEN_ANGLE = 2.39996323;
+
+//note: uniform pdf rand [0;1[
+float hash12n(vec2 p) {
+	p = fract(p * vec2(5.3987, 5.4421));
+	p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
+	return fract(p.x * p.y * 95.4307);
+}

+ 253 - 0
servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl

@@ -0,0 +1,253 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "bokeh_dof_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 "bokeh_dof_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+#ifdef MODE_GEN_BLUR_SIZE
+layout(location = 0) out float weight;
+
+layout(set = 0, binding = 0) uniform sampler2D source_depth;
+#else
+layout(location = 0) out vec4 frag_color;
+#ifdef OUTPUT_WEIGHT
+layout(location = 1) out float weight;
+#endif
+
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+layout(set = 1, binding = 0) uniform sampler2D source_weight;
+#ifdef MODE_COMPOSITE_BOKEH
+layout(set = 2, binding = 0) uniform sampler2D original_weight;
+#endif
+#endif
+
+//DOF
+// Bokeh single pass implementation based on http://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
+
+#ifdef MODE_GEN_BLUR_SIZE
+
+float get_depth_at_pos(vec2 uv) {
+	float depth = textureLod(source_depth, uv, 0.0).x;
+	if (params.orthogonal) {
+		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+	}
+	return depth;
+}
+
+float get_blur_size(float depth) {
+	if (params.blur_near_active && depth < params.blur_near_begin) {
+		return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
+	}
+
+	if (params.blur_far_active && depth > params.blur_far_begin) {
+		return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
+	}
+
+	return 0.0;
+}
+
+#endif
+
+#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
+
+vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
+	dir *= pixel_size;
+	vec4 color = texture(source_color, uv);
+	color.a = texture(source_weight, uv).r;
+
+	vec4 accum = color;
+	float total = 1.0;
+
+	float blur_scale = params.blur_size / float(params.blur_steps);
+
+	if (params.use_jitter) {
+		uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
+	}
+
+	for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
+		if (i == 0) {
+			continue;
+		}
+		float radius = float(i) * blur_scale;
+		vec2 suv = uv + dir * radius;
+		radius = abs(radius);
+
+		vec4 sample_color = texture(source_color, suv);
+		sample_color.a = texture(source_weight, suv).r;
+		float limit;
+
+		if (sample_color.a < color.a) {
+			limit = abs(sample_color.a);
+		} else {
+			limit = abs(color.a);
+		}
+
+		limit -= DEPTH_GAP;
+
+		float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+
+		accum += mix(color, sample_color, m);
+
+		total += 1.0;
+	}
+
+	return accum / total;
+}
+
+#endif
+
+void main() {
+	vec2 pixel_size = 1.0 / vec2(params.size);
+	vec2 uv = uv_interp;
+
+#ifdef MODE_GEN_BLUR_SIZE
+	uv += pixel_size * 0.5;
+	float center_depth = get_depth_at_pos(uv);
+	weight = get_blur_size(center_depth);
+#endif
+
+#ifdef MODE_BOKEH_BOX
+	//pixel_size*=0.5; //resolution is doubled
+	if (params.second_pass || !params.half_size) {
+		uv += pixel_size * 0.5; //half pixel to read centers
+	} else {
+		uv += pixel_size * 0.25; //half pixel to read centers from full res
+	}
+
+	float alpha = texture(source_color, uv).a; // retain this
+	vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
+
+	vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+	frag_color = color;
+	frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+	weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_HEXAGONAL
+
+	//pixel_size*=0.5; //resolution is doubled
+	if (params.second_pass || !params.half_size) {
+		uv += pixel_size * 0.5; //half pixel to read centers
+	} else {
+		uv += pixel_size * 0.25; //half pixel to read centers from full res
+	}
+
+	float alpha = texture(source_color, uv).a; // retain this
+
+	vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
+
+	vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+	if (params.second_pass) {
+		dir = normalize(vec2(-1.0, 0.577350269189626));
+
+		vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
+
+		color.rgb = min(color.rgb, color2.rgb);
+		color.a = (color.a + color2.a) * 0.5;
+	}
+
+	frag_color = color;
+	frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+	weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_CIRCULAR
+	if (params.half_size) {
+		pixel_size *= 0.5; //resolution is doubled
+	}
+
+	uv += pixel_size * 0.5; //half pixel to read centers
+
+	vec4 color = texture(source_color, uv);
+	float alpha = color.a; // retain this
+	color.a = texture(source_weight, uv).r;
+
+	vec4 color_accum = color;
+	float accum = 1.0;
+
+	float radius = params.blur_scale;
+	for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
+		vec2 uv_adj = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
+
+		vec4 sample_color = texture(source_color, uv_adj);
+		sample_color.a = texture(source_weight, uv_adj).r;
+
+		float limit;
+
+		if (sample_color.a < color.a) {
+			limit = abs(sample_color.a);
+		} else {
+			limit = abs(color.a);
+		}
+
+		limit -= DEPTH_GAP;
+
+		float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+		color_accum += mix(color_accum / accum, sample_color, m);
+		accum += 1.0;
+
+		radius += params.blur_scale / radius;
+	}
+
+	color_accum = color_accum / accum;
+
+	frag_color.rgb = color_accum.rgb;
+	frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+	weight = color_accum.a;
+#endif
+
+#endif
+
+#ifdef MODE_COMPOSITE_BOKEH
+	frag_color.rgb = texture(source_color, uv).rgb;
+
+	float center_weigth = texture(source_weight, uv).r;
+	float sample_weight = texture(original_weight, uv).r;
+
+	float mix_amount;
+	if (sample_weight < center_weigth) {
+		mix_amount = min(1.0, max(0.0, max(abs(center_weigth), abs(sample_weight)) - DEPTH_GAP));
+	} else {
+		mix_amount = min(1.0, max(0.0, abs(center_weigth) - DEPTH_GAP));
+	}
+
+	// let alpha blending take care of mixing
+	frag_color.a = mix_amount;
+#endif
+}