Pārlūkot izejas kodu

Merge pull request #49924 from BastiaanOlij/mobile_render_subpass

Use subpasses to do 3D rendering and resolve in mobile renderer
Rémi Verschelde 4 gadi atpakaļ
vecāks
revīzija
ed15d2c413

+ 1 - 1
doc/classes/RenderingDevice.xml

@@ -1148,7 +1148,7 @@
 		</constant>
 		<constant name="TEXTURE_USAGE_CAN_COPY_TO_BIT" value="256" enum="TextureUsageBits">
 		</constant>
-		<constant name="TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT" value="512" enum="TextureUsageBits">
+		<constant name="TEXTURE_USAGE_INPUT_ATTACHMENT_BIT" value="512" enum="TextureUsageBits">
 		</constant>
 		<constant name="TEXTURE_SWIZZLE_IDENTITY" value="0" enum="TextureSwizzle">
 		</constant>

+ 29 - 9
drivers/vulkan/rendering_device_vulkan.cpp

@@ -1804,6 +1804,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
 		image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 	}
 
+	if (p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) {
+		image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+	}
+
 	if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) {
 		image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 	}
@@ -2134,6 +2138,10 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID
 			}
 		}
 
+		if (texture.usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) {
+			usage_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+		}
+
 		if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
 			usage_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 		}
@@ -3275,8 +3283,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
 	for (int i = 0; i < p_attachments.size(); i++) {
 		ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE);
 		ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE);
-		ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)),
-				VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set.");
+		ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)),
+				VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set.");
 
 		VkAttachmentDescription description = {};
 		description.flags = 0;
@@ -3473,7 +3481,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
 				reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
 			} else {
 				ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ").");
-				ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as input attachment.");
+				ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture.");
 				ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass.");
 				reference.attachment = attachment;
 				reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@@ -3497,12 +3505,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
 			} else {
 				ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ").");
 				ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused.");
-				ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as resolve attachment.");
+				ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color texture.");
 				ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass.");
 				bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1;
 				ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample.");
 				reference.attachment = attachment;
-				reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+				reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 				attachment_last_pass[attachment] = i;
 			}
 			resolve_references.push_back(reference);
@@ -3632,8 +3640,10 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
 		render_pass_create_info.pDependencies = nullptr;
 	}
 
+	// These are only used if we use multiview but we need to define them in scope.
 	const uint32_t view_mask = (1 << p_view_count) - 1;
 	const uint32_t correlation_mask = (1 << p_view_count) - 1;
+	Vector<uint32_t> view_masks;
 	VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info;
 
 	if (p_view_count > 1) {
@@ -3645,10 +3655,15 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
 		// Make sure we limit this to the number of views we support.
 		ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass");
 
+		// Set view masks for each subpass
+		for (uint32_t i = 0; i < subpasses.size(); i++) {
+			view_masks.push_back(view_mask);
+		};
+
 		render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
 		render_pass_multiview_create_info.pNext = nullptr;
-		render_pass_multiview_create_info.subpassCount = 1;
-		render_pass_multiview_create_info.pViewMasks = &view_mask;
+		render_pass_multiview_create_info.subpassCount = subpasses.size();
+		render_pass_multiview_create_info.pViewMasks = view_masks.ptr();
 		render_pass_multiview_create_info.dependencyCount = 0;
 		render_pass_multiview_create_info.pViewOffsets = nullptr;
 		render_pass_multiview_create_info.correlationMaskCount = 1;
@@ -4506,6 +4521,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve
 						} break;
 						case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: {
 							info.type = UNIFORM_TYPE_INPUT_ATTACHMENT;
+							need_array_dimensions = true;
 						} break;
 						case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: {
 							ERR_PRINT("Acceleration structure not supported.");
@@ -5490,7 +5506,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
 					img_info.sampler = *sampler;
 					img_info.imageView = texture->view;
 
-					if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) {
+					if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) {
 						UniformSet::AttachableTexture attachable_texture;
 						attachable_texture.bind = set_uniform.binding;
 						attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j + 1];
@@ -5543,7 +5559,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
 					img_info.sampler = VK_NULL_HANDLE;
 					img_info.imageView = texture->view;
 
-					if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) {
+					if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) {
 						UniformSet::AttachableTexture attachable_texture;
 						attachable_texture.bind = set_uniform.binding;
 						attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j];
@@ -7416,6 +7432,10 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) {
 	vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor);
 }
 
+uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() {
+	return draw_list_current_subpass;
+}
+
 RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() {
 	ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID);
 	ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID);

+ 1 - 0
drivers/vulkan/rendering_device_vulkan.h

@@ -1149,6 +1149,7 @@ public:
 	virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect);
 	virtual void draw_list_disable_scissor(DrawListID p_list);
 
+	virtual uint32_t draw_list_get_current_pass();
 	virtual DrawListID draw_list_switch_to_next_pass();
 	virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids);
 

+ 70 - 3
servers/rendering/renderer_rd/effects_rd.cpp

@@ -70,6 +70,28 @@ RID EffectsRD::_get_uniform_set_from_image(RID p_image) {
 	return uniform_set;
 }
 
+RID EffectsRD::_get_uniform_set_for_input(RID p_texture) {
+	if (input_to_uniform_set_cache.has(p_texture)) {
+		RID uniform_set = input_to_uniform_set_cache[p_texture];
+		if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+			return uniform_set;
+		}
+	}
+
+	Vector<RD::Uniform> uniforms;
+	RD::Uniform u;
+	u.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT;
+	u.binding = 0;
+	u.ids.push_back(p_texture);
+	uniforms.push_back(u);
+	// This is specific to our subpass shader
+	RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, TONEMAP_MODE_SUBPASS), 0);
+
+	input_to_uniform_set_cache[p_texture] = uniform_set;
+
+	return uniform_set;
+}
+
 RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) {
 	if (texture_to_uniform_set_cache.has(p_texture)) {
 		RID uniform_set = texture_to_uniform_set_cache[p_texture];
@@ -85,7 +107,7 @@ RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps)
 	u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler);
 	u.ids.push_back(p_texture);
 	uniforms.push_back(u);
-	//anything with the same configuration (one texture in binding 0 for set 0), is good
+	// anything with the same configuration (one texture in binding 0 for set 0), is good
 	RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, 0), 0);
 
 	texture_to_uniform_set_cache[p_texture] = uniform_set;
@@ -778,11 +800,11 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
 
 	if (p_settings.view_count > 1) {
 		// Use MULTIVIEW versions
-		mode += 4;
+		mode += 6;
 	}
 
 	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD);
-	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2);
@@ -794,6 +816,45 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
 	RD::get_singleton()->draw_list_end();
 }
 
+void EffectsRD::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings) {
+	memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
+
+	tonemap.push_constant.use_bcs = p_settings.use_bcs;
+	tonemap.push_constant.bcs[0] = p_settings.brightness;
+	tonemap.push_constant.bcs[1] = p_settings.contrast;
+	tonemap.push_constant.bcs[2] = p_settings.saturation;
+
+	ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
+	tonemap.push_constant.use_glow = p_settings.use_glow;
+
+	int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS;
+	if (p_settings.view_count > 1) {
+		// Use MULTIVIEW versions
+		mode += 6;
+	}
+
+	tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
+	tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
+	tonemap.push_constant.exposure = p_settings.exposure;
+	tonemap.push_constant.white = p_settings.white;
+	tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey;
+
+	tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
+
+	tonemap.push_constant.use_debanding = p_settings.use_debanding;
+
+	RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass()));
+	RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_for_input(p_source_color), 0);
+	RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1); // should be set to a default texture, it's ignored
+	RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2); // should be set to a default texture, it's ignored
+	RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.color_correction_texture), 3);
+
+	RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, index_array);
+
+	RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
+	RD::get_singleton()->draw_list_draw(p_subpass_draw_list, true);
+}
+
 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.");
 
@@ -1813,12 +1874,16 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
 		tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n");
 		tonemap_modes.push_back("\n#define USE_1D_LUT\n");
 		tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
+		tonemap_modes.push_back("\n#define SUBPASS\n");
+		tonemap_modes.push_back("\n#define SUBPASS\n#define USE_1D_LUT\n");
 
 		// multiview versions of our shaders
 		tonemap_modes.push_back("\n#define MULTIVIEW\n");
 		tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n");
 		tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_1D_LUT\n");
 		tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
+		tonemap_modes.push_back("\n#define MULTIVIEW\n#define SUBPASS\n");
+		tonemap_modes.push_back("\n#define MULTIVIEW\n#define SUBPASS\n#define USE_1D_LUT\n");
 
 		tonemap.shader.initialize(tonemap_modes);
 
@@ -1827,6 +1892,8 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
 			tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false);
 			tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false);
 			tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false);
+			tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_MULTIVIEW, false);
+			tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW, false);
 		}
 
 		tonemap.shader_version = tonemap.shader.version_create();

+ 7 - 0
servers/rendering/renderer_rd/effects_rd.h

@@ -238,11 +238,15 @@ private:
 		TONEMAP_MODE_BICUBIC_GLOW_FILTER,
 		TONEMAP_MODE_1D_LUT,
 		TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT,
+		TONEMAP_MODE_SUBPASS,
+		TONEMAP_MODE_SUBPASS_1D_LUT,
 
 		TONEMAP_MODE_NORMAL_MULTIVIEW,
 		TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW,
 		TONEMAP_MODE_1D_LUT_MULTIVIEW,
 		TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW,
+		TONEMAP_MODE_SUBPASS_MULTIVIEW,
+		TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW,
 
 		TONEMAP_MODE_MAX
 	};
@@ -718,6 +722,7 @@ private:
 	RID index_array;
 
 	Map<RID, RID> texture_to_uniform_set_cache;
+	Map<RID, RID> input_to_uniform_set_cache;
 
 	Map<RID, RID> image_to_uniform_set_cache;
 
@@ -751,6 +756,7 @@ private:
 	Map<TextureSamplerPair, RID> texture_sampler_to_compute_uniform_set_cache;
 
 	RID _get_uniform_set_from_image(RID p_texture);
+	RID _get_uniform_set_for_input(RID p_texture);
 	RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
 	RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
 	RID _get_compute_uniform_set_from_texture_and_sampler(RID p_texture, RID p_sampler);
@@ -842,6 +848,7 @@ public:
 	};
 
 	void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
+	void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings);
 
 	void 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);
 	void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set);

+ 9 - 1
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -183,7 +183,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() {
 	}
 }
 
-void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
+void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
 	clear();
 
 	ERR_FAIL_COND_MSG(p_view_count != 1, "Multiple views is currently not supported in this renderer, please use the mobile renderer for VR support");
@@ -1555,6 +1555,14 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 	}
 
 	RD::get_singleton()->draw_command_end_label();
+
+	if (p_render_data->render_buffers.is_valid()) {
+		_debug_draw_cluster(p_render_data->render_buffers);
+
+		RENDER_TIMESTAMP("Tonemap");
+
+		_render_buffers_post_process_and_tonemap(p_render_data);
+	}
 }
 
 void RenderForwardClustered::_render_shadow_begin() {

+ 1 - 1
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h

@@ -112,7 +112,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
 		void ensure_specular();
 		void ensure_voxelgi();
 		void clear();
-		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
+		virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
 
 		~RenderBufferDataForwardClustered();
 	};

+ 332 - 92
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

@@ -78,12 +78,16 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::clear() {
 
 	color = RID();
 	depth = RID();
-	color_fb = RID();
+	for (int i = 0; i < FB_CONFIG_MAX; i++) {
+		color_fbs[i] = RID();
+	}
 }
 
-void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
+void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
 	clear();
 
+	bool is_half_resolution = false; // Set this once we support this feature.
+
 	msaa = p_msaa;
 
 	width = p_width;
@@ -93,15 +97,50 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b
 	color = p_color_buffer;
 	depth = p_depth_buffer;
 
-	RD::DataFormat color_format = RenderForwardMobile::singleton->_render_buffers_get_color_format();
+	// We are creating 4 configurations here for our framebuffers.
 
 	if (p_msaa == RS::VIEWPORT_MSAA_DISABLED) {
 		Vector<RID> fb;
-		fb.push_back(p_color_buffer);
-		fb.push_back(depth);
+		fb.push_back(p_color_buffer); // 0 - color buffer
+		fb.push_back(depth); // 1 - depth buffer
+
+		// Now define our subpasses
+		Vector<RD::FramebufferPass> passes;
+		RD::FramebufferPass pass;
+
+		// re-using the same attachments
+		pass.color_attachments.push_back(0);
+		pass.depth_attachment = 1;
+
+		// - opaque pass
+		passes.push_back(pass);
+		color_fbs[FB_CONFIG_ONE_PASS] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
+
+		// - add sky pass
+		passes.push_back(pass);
+		color_fbs[FB_CONFIG_TWO_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
+
+		// - add alpha pass
+		passes.push_back(pass);
+		color_fbs[FB_CONFIG_THREE_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
 
-		color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count);
+		if (!is_half_resolution) {
+			// - add blit to 2D pass
+			fb.push_back(p_target_buffer); // 2 - target buffer
+
+			RD::FramebufferPass blit_pass;
+			blit_pass.color_attachments.push_back(2);
+			blit_pass.input_attachments.push_back(0);
+			passes.push_back(blit_pass);
+
+			color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
+		} else {
+			// can't do our blit pass if resolutions don't match
+			color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RID();
+		}
 	} else {
+		RD::DataFormat color_format = RenderForwardMobile::singleton->_render_buffers_get_color_format();
+
 		RD::TextureFormat tf;
 		if (view_count > 1) {
 			tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
@@ -134,14 +173,87 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b
 
 		{
 			Vector<RID> fb;
-			fb.push_back(color_msaa);
-			fb.push_back(depth_msaa);
+			fb.push_back(color_msaa); // 0 - msaa color buffer
+			fb.push_back(depth_msaa); // 1 - msaa depth buffer
+
+			// Now define our subpasses
+			Vector<RD::FramebufferPass> passes;
+			RD::FramebufferPass pass;
+
+			// re-using the same attachments
+			pass.color_attachments.push_back(0);
+			pass.depth_attachment = 1;
+
+			// - opaque pass
+			passes.push_back(pass);
+
+			// - add sky pass
+			fb.push_back(color); // 2 - color buffer
+			passes.push_back(pass); // without resolve for our 3 + 4 subpass config
+			{
+				// but with resolve for our 2 subpass config
+				Vector<RD::FramebufferPass> two_passes;
+				two_passes.push_back(pass); // opaque subpass without resolve
+				pass.resolve_attachments.push_back(2);
+				two_passes.push_back(pass); // sky subpass with resolve
+
+				color_fbs[FB_CONFIG_TWO_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, two_passes, RenderingDevice::INVALID_ID, view_count);
+			}
+
+			// - add alpha pass (with resolve, we just added that above)
+			passes.push_back(pass);
+			color_fbs[FB_CONFIG_THREE_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
 
-			color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count);
+			{
+				// we also need our one pass with resolve
+				Vector<RD::FramebufferPass> one_pass_with_resolve;
+				one_pass_with_resolve.push_back(pass); // note our pass configuration already has resolve..
+				color_fbs[FB_CONFIG_ONE_PASS] = RD::get_singleton()->framebuffer_create_multipass(fb, one_pass_with_resolve, RenderingDevice::INVALID_ID, view_count);
+			}
+
+			if (!is_half_resolution) {
+				// - add blit to 2D pass
+				fb.push_back(p_target_buffer); // 3 - target buffer
+				RD::FramebufferPass blit_pass;
+				blit_pass.color_attachments.push_back(3);
+				blit_pass.input_attachments.push_back(2);
+				passes.push_back(blit_pass);
+
+				color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count);
+			} else {
+				// can't do our blit pass if resolutions don't match
+				color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RID();
+			}
 		}
 	}
 }
 
+RID RenderForwardMobile::reflection_probe_create_framebuffer(RID p_color, RID p_depth) {
+	// Our attachments
+	Vector<RID> fb;
+	fb.push_back(p_color); // 0
+	fb.push_back(p_depth); // 1
+
+	// Now define our subpasses
+	Vector<RD::FramebufferPass> passes;
+	RD::FramebufferPass pass;
+
+	// re-using the same attachments
+	pass.color_attachments.push_back(0);
+	pass.depth_attachment = 1;
+
+	// - opaque pass
+	passes.push_back(pass);
+
+	// - sky pass
+	passes.push_back(pass);
+
+	// - alpha pass
+	passes.push_back(pass);
+
+	return RD::get_singleton()->framebuffer_create_multipass(fb, passes);
+}
+
 RenderForwardMobile::RenderBufferDataForwardMobile::~RenderBufferDataForwardMobile() {
 	clear();
 }
@@ -365,14 +477,29 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 	scene_state.ubo.viewport_size[1] = vp_he.y;
 	scene_state.ubo.directional_light_count = 0;
 
+	// We can only use our full subpass approach if we're:
+	// - not reading from SCREEN_TEXTURE/DEPTH_TEXTURE
+	// - not using ssr/sss (currently not supported)
+	// - not using glow or other post effects (can't do 4th subpass)
+	// - rendering to a half sized render buffer (can't do 4th subpass)
+	// We'll need to restrict how far we're going with subpasses based on this.
+
 	Size2i screen_size;
-	RID opaque_framebuffer;
-	RID alpha_framebuffer;
+	RID framebuffer;
 	bool reverse_cull = false;
+	bool using_subpass_transparent = true;
+	bool using_subpass_post_process = true;
 
-	// I don't think we support either of these in our mobile renderer so probably should phase them out
-	bool using_ssr = false;
-	bool using_sss = false;
+	bool is_half_resolution = false; // Set this once we support this feature.
+	bool using_ssr = false; // I don't think we support this in our mobile renderer so probably should phase it out
+	bool using_sss = false; // I don't think we support this in our mobile renderer so probably should phase it out
+
+	// fill our render lists early so we can find out if we use various features
+	_fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR);
+	render_list[RENDER_LIST_OPAQUE].sort_by_key();
+	render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority();
+	_fill_element_info(RENDER_LIST_OPAQUE);
+	_fill_element_info(RENDER_LIST_ALPHA);
 
 	if (p_render_data->render_info) {
 		p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = p_render_data->instances->size();
@@ -384,15 +511,36 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 		screen_size.x = render_buffer->width;
 		screen_size.y = render_buffer->height;
 
-		opaque_framebuffer = render_buffer->color_fb;
-		alpha_framebuffer = opaque_framebuffer;
+		if (is_half_resolution) {
+			// can't do blit subpass
+			using_subpass_post_process = false;
+		} else if (env && (env->glow_enabled || env->auto_exposure || camera_effects_uses_dof(p_render_data->camera_effects))) {
+			// can't do blit subpass
+			using_subpass_post_process = false;
+		}
+
+		if (scene_state.used_screen_texture || scene_state.used_depth_texture) {
+			// can't use our last two subpasses
+			using_subpass_transparent = false;
+			using_subpass_post_process = false;
+		}
+
+		if (using_subpass_post_process) {
+			// all as subpasses
+			framebuffer = render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES];
+		} else if (using_subpass_transparent) {
+			// our tonemap pass is separate
+			framebuffer = render_buffer->color_fbs[FB_CONFIG_THREE_SUBPASSES];
+		} else {
+			// only opaque and sky as subpasses
+			framebuffer = render_buffer->color_fbs[FB_CONFIG_TWO_SUBPASSES];
+		}
 	} else if (p_render_data->reflection_probe.is_valid()) {
 		uint32_t resolution = reflection_probe_instance_get_resolution(p_render_data->reflection_probe);
 		screen_size.x = resolution;
 		screen_size.y = resolution;
 
-		opaque_framebuffer = reflection_probe_instance_get_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass);
-		alpha_framebuffer = opaque_framebuffer;
+		framebuffer = reflection_probe_instance_get_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass);
 
 		if (storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_render_data->reflection_probe))) {
 			p_render_data->environment = RID(); //no environment on interiors
@@ -400,6 +548,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 		}
 
 		reverse_cull = true;
+		using_subpass_transparent = true; // we ignore our screen/depth texture here
+		using_subpass_post_process = false; // not applicable at all for reflection probes.
 	} else {
 		ERR_FAIL(); //bug?
 	}
@@ -411,17 +561,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 
 	_update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example)
 
-	_fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR);
-	render_list[RENDER_LIST_OPAQUE].sort_by_key();
-	render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority();
-
-	// we no longer use this...
-	_fill_instance_data(RENDER_LIST_OPAQUE);
-	_fill_instance_data(RENDER_LIST_ALPHA);
-
-	RD::get_singleton()->draw_command_end_label();
-
-	// note, no depth prepass here!
+	RD::get_singleton()->draw_command_end_label(); // Render Setup
 
 	// setup environment
 	RID radiance_texture;
@@ -496,97 +636,199 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 				// do not try to draw sky if invalid
 				draw_sky = false;
 			}
-			RD::get_singleton()->draw_command_end_label();
+			RD::get_singleton()->draw_command_end_label(); // Setup Sky
 		}
 	} else {
 		clear_color = p_default_bg_color;
 	}
 
-	// opaque pass
+	// update sky buffers (if required)
+	if (draw_sky || draw_sky_fog_only) {
+		// !BAS! @TODO See if we can limit doing some things double and maybe even move this into _pre_opaque_render
+		// and change Forward Clustered in the same way as we have here (but without using subpasses)
+		RENDER_TIMESTAMP("Setup Sky resolution buffers");
+
+		RD::get_singleton()->draw_command_begin_label("Setup Sky resolution buffers");
+
+		if (p_render_data->reflection_probe.is_valid()) {
+			CameraMatrix correction;
+			correction.set_depth_correction(true);
+			CameraMatrix projection = correction * p_render_data->cam_projection;
+			sky.update_res_buffers(env, 1, &projection, p_render_data->cam_transform, time);
+		} else {
+			sky.update_res_buffers(env, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time);
+		}
+
+		RD::get_singleton()->draw_command_end_label(); // Setup Sky resolution buffers
+	}
 
-	// !BAS! Look into this, seems most of the code in here related to clustered only, may want to move this code into ForwardClustered/RenderForwardMobile before calling it from here
-	// does trigger shadow map rendering so kinda important
 	_pre_opaque_render(p_render_data, false, false, RID(), RID());
 
-	RD::get_singleton()->draw_command_begin_label("Render Opaque Pass");
+	{
+		if (render_buffer) {
+			RD::get_singleton()->draw_command_begin_label("Render 3D Pass");
+		} else {
+			RD::get_singleton()->draw_command_begin_label("Render Reflection Probe Pass");
+		}
 
-	scene_state.ubo.directional_light_count = p_render_data->directional_light_count;
+		// opaque pass
 
-	_setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, p_render_data->render_buffers.is_valid());
+		RD::get_singleton()->draw_command_begin_label("Render Opaque Subpass");
 
-	RENDER_TIMESTAMP("Render Opaque Pass");
+		scene_state.ubo.directional_light_count = p_render_data->directional_light_count;
 
-	RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true);
+		_setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, p_render_data->render_buffers.is_valid());
 
-	bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss;
-	bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss;
+		RENDER_TIMESTAMP("Render Opaque Subpass");
 
-	{
-		bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only);
-		bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only);
+		RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true);
 
-		// regular forward for now
-		Vector<Color> c;
-		c.push_back(clear_color.to_linear());
+		bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss;
+		bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss;
 
-		RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
-		_render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
-	}
+		{
+			bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only);
+			bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only);
+
+			// regular forward for now
+			Vector<Color> c;
+			c.push_back(clear_color.to_linear()); // our render buffer
+			if (render_buffer) {
+				if (render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+					c.push_back(clear_color.to_linear()); // our resolve buffer
+				}
+				if (using_subpass_post_process) {
+					c.push_back(Color()); // our 2D buffer we're copying into
+				}
+			}
 
-	RD::get_singleton()->draw_command_end_label();
+			RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
+			RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+			render_list_params.framebuffer_format = fb_format;
+			if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
+				// secondary command buffers need more testing at this time
+				//multi threaded
+				thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count());
+				RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
+				RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params);
+			} else {
+				//single threaded
+				RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
+				_render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count);
+			}
+		}
 
-	if (draw_sky || draw_sky_fog_only) {
-		RENDER_TIMESTAMP("Render Sky");
+		RD::get_singleton()->draw_command_end_label(); //Render Opaque Subpass
 
-		RD::get_singleton()->draw_command_begin_label("Draw Sky");
+		if (draw_sky || draw_sky_fog_only) {
+			RENDER_TIMESTAMP("Render Sky Subpass");
 
-		if (p_render_data->reflection_probe.is_valid()) {
-			CameraMatrix correction;
-			correction.set_depth_correction(true);
-			CameraMatrix projection = correction * p_render_data->cam_projection;
-			sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time);
+			RD::get_singleton()->draw_command_begin_label("Draw Sky Subpass");
+
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass();
+
+			if (p_render_data->reflection_probe.is_valid()) {
+				CameraMatrix correction;
+				correction.set_depth_correction(true);
+				CameraMatrix projection = correction * p_render_data->cam_projection;
+				sky.draw(draw_list, env, framebuffer, 1, &projection, p_render_data->cam_transform, time);
+			} else {
+				sky.draw(draw_list, env, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time);
+			}
+
+			RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass
+
+			// note, if MSAA is used in 2-subpass approach we should get an automatic resolve here
 		} else {
-			sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time);
+			// switch to subpass but we do nothing here so basically we skip (though this should trigger resolve with 2-subpass MSAA).
+			RD::get_singleton()->draw_list_switch_to_next_pass();
 		}
-		RD::get_singleton()->draw_command_end_label();
-	}
 
-	if (render_buffer && !can_continue_color && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
-		RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color);
-		/*
-		if (using_separate_specular) {
-			RD::get_singleton()->texture_resolve_multisample(render_buffer->specular_msaa, render_buffer->specular);
+		if (!using_subpass_transparent) {
+			// We're done with our subpasses so end our container pass
+			RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL);
+
+			RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass
 		}
-		*/
-	}
 
-	if (render_buffer && !can_continue_depth && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
-		RD::get_singleton()->texture_resolve_multisample(render_buffer->depth_msaa, render_buffer->depth);
-	}
+		// transparent pass
+		RENDER_TIMESTAMP("Render Transparent Subpass");
 
-	// transparent pass
-	RENDER_TIMESTAMP("Render Transparent Pass");
+		RD::get_singleton()->draw_command_begin_label("Render Transparent Subpass");
 
-	RD::get_singleton()->draw_command_begin_label("Render Transparent Pass");
+		rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true);
 
-	rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true);
+		if (using_subpass_transparent) {
+			RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
+			RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+			render_list_params.framebuffer_format = fb_format;
+			if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
+				// secondary command buffers need more testing at this time
+				//multi threaded
+				thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count());
+				RD::get_singleton()->draw_list_switch_to_next_pass_split(thread_draw_lists.size(), thread_draw_lists.ptr());
+				render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass();
+				RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params);
+			} else {
+				//single threaded
+				RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass();
+				render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass();
+				_render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count);
+			}
 
-	_setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false);
+			RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass
 
-	{
-		RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
-		_render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
+			// note if we are using MSAA we should get an automatic resolve through our subpass configuration.
+
+			// blit to tonemap
+			if (render_buffer && using_subpass_post_process) {
+				_post_process_subpass(render_buffer->color, framebuffer, p_render_data);
+			}
+
+			RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass
+
+			RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL);
+		} else {
+			framebuffer = render_buffer->color_fbs[FB_CONFIG_ONE_PASS];
+
+			// this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation
+			// _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false);
+
+			RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
+			RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+			render_list_params.framebuffer_format = fb_format;
+			if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
+				// secondary command buffers need more testing at this time
+				//multi threaded
+				thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count());
+				RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
+				RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params);
+				RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL);
+			} else {
+				//single threaded
+				RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
+				_render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count);
+				RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL);
+			}
+
+			RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass
+		}
 	}
 
-	RD::get_singleton()->draw_command_end_label();
+	if (render_buffer && !using_subpass_post_process) {
+		RD::get_singleton()->draw_command_begin_label("Post process pass");
+
+		// If we need extra effects we do this in its own pass
+		RENDER_TIMESTAMP("Tonemap");
 
-	RD::get_singleton()->draw_command_begin_label("Resolve");
+		_render_buffers_post_process_and_tonemap(p_render_data);
 
-	if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
-		RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color);
+		RD::get_singleton()->draw_command_end_label(); // Post process pass
 	}
 
-	RD::get_singleton()->draw_command_end_label();
+	if (render_buffer) {
+		_disable_clear_request(p_render_data);
+	}
 }
 
 /* these are being called from RendererSceneRenderRD::_pre_opaque_render */
@@ -635,7 +877,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
 	_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, true);
 	uint32_t render_list_size = render_list[RENDER_LIST_SECONDARY].elements.size() - render_list_from;
 	render_list[RENDER_LIST_SECONDARY].sort_by_key_range(render_list_from, render_list_size);
-	_fill_instance_data(RENDER_LIST_SECONDARY, render_list_from, render_list_size, false);
+	_fill_element_info(RENDER_LIST_SECONDARY, render_list_from, render_list_size);
 
 	{
 		//regular forward for now
@@ -713,7 +955,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
 	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
 	_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
 	render_list[RENDER_LIST_SECONDARY].sort_by_key();
-	_fill_instance_data(RENDER_LIST_SECONDARY);
+	_fill_element_info(RENDER_LIST_SECONDARY);
 
 	RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
 
@@ -754,7 +996,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<GeometryInstance *> &p_in
 	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
 	_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
 	render_list[RENDER_LIST_SECONDARY].sort_by_key();
-	_fill_instance_data(RENDER_LIST_SECONDARY);
+	_fill_element_info(RENDER_LIST_SECONDARY);
 
 	RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
 
@@ -827,7 +1069,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
 
 	_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
 	render_list[RENDER_LIST_SECONDARY].sort_by_key();
-	_fill_instance_data(RENDER_LIST_SECONDARY);
+	_fill_element_info(RENDER_LIST_SECONDARY);
 
 	RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
 
@@ -1413,9 +1655,7 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data,
 	RD::get_singleton()->buffer_update(scene_state.uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo, RD::BARRIER_MASK_RASTER);
 }
 
-void RenderForwardMobile::_fill_instance_data(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) {
-	// !BAS! Rename this to make clear this is not the same as with the forward renderer and remove p_update_buffer?
-
+void RenderForwardMobile::_fill_element_info(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements) {
 	RenderList *rl = &render_list[p_render_list];
 	uint32_t element_total = p_max_elements >= 0 ? uint32_t(p_max_elements) : rl->elements.size();
 
@@ -1669,7 +1909,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
 			prev_index_array_rd = index_array_rd;
 		}
 
-		RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe);
+		RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass);
 
 		if (pipeline_rd != prev_pipeline_rd) {
 			// checking with prev shader does not make so much sense, as

+ 18 - 3
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h

@@ -92,6 +92,18 @@ protected:
 
 	/* Render Buffer */
 
+	// We can have:
+	// - 4 subpasses combining the full render cycle
+	// - 3 subpasses + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer)
+	// - 2 subpasses + 1 normal pass for transparent + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer)
+	enum RenderBufferMobileFramebufferConfigType {
+		FB_CONFIG_ONE_PASS, // Single pass frame buffer for alpha pass
+		FB_CONFIG_TWO_SUBPASSES, // Opaque + Sky sub pass
+		FB_CONFIG_THREE_SUBPASSES, // Opaque + Sky + Alpha sub pass
+		FB_CONFIG_FOUR_SUBPASSES, // Opaque + Sky + Alpha sub pass + Tonemap pass
+		FB_CONFIG_MAX
+	};
+
 	struct RenderBufferDataForwardMobile : public RenderBufferData {
 		RID color;
 		RID depth;
@@ -104,12 +116,12 @@ protected:
 		RID depth_msaa;
 		// RID normal_roughness_buffer_msaa;
 
-		RID color_fb;
+		RID color_fbs[FB_CONFIG_MAX];
 		int width, height;
 		uint32_t view_count;
 
 		void clear();
-		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
+		virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
 
 		~RenderBufferDataForwardMobile();
 	};
@@ -152,6 +164,7 @@ protected:
 		RD::FramebufferFormatID framebuffer_format = 0;
 		uint32_t element_offset = 0;
 		uint32_t barrier = RD::BARRIER_MASK_ALL;
+		uint32_t subpass = 0;
 
 		RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) {
 			elements = p_elements;
@@ -195,7 +208,7 @@ protected:
 	virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) override;
 
 	void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_append = false);
-	void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
+	void _fill_element_info(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1);
 	// void _update_instance_data_buffer(RenderListType p_render_list);
 
 	static RenderForwardMobile *singleton;
@@ -576,6 +589,8 @@ protected:
 	void _update_shader_quality_settings() override;
 
 public:
+	virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth) override;
+
 	static void _geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker);
 	static void _geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker);
 

+ 104 - 22
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -679,10 +679,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc
 		for (int i = 0; i < atlas->count; i++) {
 			atlas->reflections.write[i].data.update_reflection_data(storage, atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, sky.roughness_layers, _render_buffers_get_color_format());
 			for (int j = 0; j < 6; j++) {
-				Vector<RID> fb;
-				fb.push_back(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j]);
-				fb.push_back(atlas->depth_buffer);
-				atlas->reflections.write[i].fbs[j] = RD::get_singleton()->framebuffer_create(fb);
+				atlas->reflections.write[i].fbs[j] = reflection_probe_create_framebuffer(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j], atlas->depth_buffer);
 			}
 		}
 
@@ -728,6 +725,13 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc
 	return true;
 }
 
+RID RendererSceneRenderRD::reflection_probe_create_framebuffer(RID p_color, RID p_depth) {
+	Vector<RID> fb;
+	fb.push_back(p_color);
+	fb.push_back(p_depth);
+	return RD::get_singleton()->framebuffer_create(fb);
+}
+
 bool RendererSceneRenderRD::reflection_probe_instance_postprocess_step(RID p_instance) {
 	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
 	ERR_FAIL_COND_V(!rpi, false);
@@ -1996,6 +2000,75 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 	storage->render_target_disable_clear_request(rb->render_target);
 }
 
+void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data) {
+	RD::get_singleton()->draw_command_begin_label("Post Process Subpass");
+
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers);
+	ERR_FAIL_COND(!rb);
+
+	RendererSceneEnvironmentRD *env = environment_owner.getornull(p_render_data->environment);
+
+	bool can_use_effects = rb->width >= 8 && rb->height >= 8;
+
+	RENDER_TIMESTAMP("Tonemap");
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass();
+
+	EffectsRD::TonemapSettings tonemap;
+
+	if (env) {
+		tonemap.tonemap_mode = env->tone_mapper;
+		tonemap.exposure = env->exposure;
+		tonemap.white = env->white;
+	}
+
+	// We don't support glow or auto exposure here, if they are needed, don't use subpasses!
+	// The problem is that we need to use the result so far and process them before we can
+	// apply this to our results.
+	if (can_use_effects && env && env->glow_enabled) {
+		ERR_FAIL_MSG("Glow is not supported when using subpasses.");
+	}
+	if (can_use_effects && env && env->auto_exposure) {
+		ERR_FAIL_MSG("Glow is not supported when using subpasses.");
+	}
+
+	tonemap.use_glow = false;
+	tonemap.glow_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+	tonemap.use_auto_exposure = false;
+	tonemap.exposure_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+
+	tonemap.use_color_correction = false;
+	tonemap.use_1d_color_correction = false;
+	tonemap.color_correction_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
+
+	if (can_use_effects && env) {
+		tonemap.use_bcs = env->adjustments_enabled;
+		tonemap.brightness = env->adjustments_brightness;
+		tonemap.contrast = env->adjustments_contrast;
+		tonemap.saturation = env->adjustments_saturation;
+		if (env->adjustments_enabled && env->color_correction.is_valid()) {
+			tonemap.use_color_correction = true;
+			tonemap.use_1d_color_correction = env->use_1d_color_correction;
+			tonemap.color_correction_texture = storage->texture_get_rd_texture(env->color_correction);
+		}
+	}
+
+	tonemap.use_debanding = rb->use_debanding;
+	tonemap.texture_size = Vector2i(rb->width, rb->height);
+
+	tonemap.view_count = p_render_data->view_count;
+
+	storage->get_effects()->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
+
+	RD::get_singleton()->draw_command_end_label();
+}
+
+void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_data) {
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers);
+	ERR_FAIL_COND(!rb);
+
+	storage->render_target_disable_clear_request(rb->render_target);
+}
+
 void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) {
 	EffectsRD *effects = storage->get_effects();
 
@@ -2283,12 +2356,11 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
 		tf.width = rb->width;
 		tf.height = rb->height;
 		tf.array_layers = rb->view_count; // create a layer for every view
-		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0) | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 		if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
-			tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
-		} else {
-			tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
 		}
+		tf.usage_bits |= RD::TEXTURE_USAGE_INPUT_ATTACHMENT_BIT; // only needed when using subpasses in the mobile renderer
 
 		rb->texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
 	}
@@ -2326,7 +2398,8 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
 		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);
+	RID target_texture = storage->render_target_get_rd_texture(rb->render_target);
+	rb->data->configure(rb->texture, rb->depth_texture, target_texture, p_width, p_height, p_msaa, p_view_count);
 
 	if (is_clustered_enabled()) {
 		rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture);
@@ -3846,9 +3919,28 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData
 	_render_scene(&render_data, clear_color);
 
 	if (p_render_buffers.is_valid()) {
-		if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES) {
+		/*
+		_debug_draw_cluster(p_render_buffers);
+
+		RENDER_TIMESTAMP("Tonemap");
+
+		_render_buffers_post_process_and_tonemap(&render_data);
+		*/
+
+		_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex);
+		if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) {
+			rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture);
+		}
+	}
+}
+
+void RendererSceneRenderRD::_debug_draw_cluster(RID p_render_buffers) {
+	if (p_render_buffers.is_valid() && current_cluster_builder != nullptr) {
+		RS::ViewportDebugDraw dd = get_debug_draw_mode();
+
+		if (dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES) {
 			ClusterBuilderRD::ElementType elem_type = ClusterBuilderRD::ELEMENT_TYPE_MAX;
-			switch (debug_draw) {
+			switch (dd) {
 				case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS:
 					elem_type = ClusterBuilderRD::ELEMENT_TYPE_OMNI_LIGHT;
 					break;
@@ -3864,17 +3956,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData
 				default: {
 				}
 			}
-			if (current_cluster_builder != nullptr) {
-				current_cluster_builder->debug(elem_type);
-			}
-		}
-
-		RENDER_TIMESTAMP("Tonemap");
-
-		_render_buffers_post_process_and_tonemap(&render_data);
-		_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex);
-		if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) {
-			rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture);
+			current_cluster_builder->debug(elem_type);
 		}
 	}
 }

+ 14 - 3
servers/rendering/renderer_rd/renderer_scene_render_rd.h

@@ -95,7 +95,7 @@ protected:
 	double time_step = 0;
 
 	struct RenderBufferData {
-		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0;
+		virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0;
 		virtual ~RenderBufferData() {}
 	};
 	virtual RenderBufferData *_create_render_buffer_data() = 0;
@@ -117,6 +117,7 @@ protected:
 	virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, const PagedArray<GeometryInstance *> &p_instances) = 0;
 
 	void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
+	void _debug_draw_cluster(RID p_render_buffers);
 
 	RenderBufferData *render_buffers_get_data(RID p_render_buffers);
 
@@ -133,6 +134,10 @@ protected:
 
 	void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_gi, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer);
 
+	void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data);
+	void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data);
+	void _disable_clear_request(const RenderDataRD *p_render_data);
+
 	// needed for a single argument calls (material and uv2)
 	PagedArrayPool<GeometryInstance *> cull_argument_pool;
 	PagedArray<GeometryInstance *> cull_argument; //need this to exist
@@ -146,7 +151,7 @@ protected:
 		} else {
 			return nullptr;
 		}
-	}
+	};
 
 	//used for mobile renderer mostly
 
@@ -524,7 +529,6 @@ private:
 	void _allocate_luminance_textures(RenderBuffers *rb);
 
 	void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer);
-	void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data);
 
 	/* Cluster */
 
@@ -923,6 +927,12 @@ public:
 	virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override;
 	virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override;
 
+	bool camera_effects_uses_dof(RID p_camera_effects) {
+		CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects);
+
+		return camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0;
+	}
+
 	virtual RID light_instance_create(RID p_light) override;
 	virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override;
 	virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override;
@@ -1065,6 +1075,7 @@ public:
 	virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override;
 	virtual bool reflection_probe_instance_has_reflection(RID p_instance) override;
 	virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
+	virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth);
 	virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
 
 	uint32_t reflection_probe_instance_get_resolution(RID p_instance);

+ 174 - 1
servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp

@@ -283,7 +283,7 @@ void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_
 
 	RD::DrawListID draw_list = p_list;
 
-	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format));
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format, false, RD::get_singleton()->draw_list_get_current_pass()));
 
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0);
 	if (p_uniform_set.is_valid()) { //material may not have uniform set
@@ -1467,6 +1467,179 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
 	RD::get_singleton()->draw_list_end();
 }
 
+void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) {
+	ERR_FAIL_COND(!p_env);
+
+	ERR_FAIL_COND(p_view_count == 0);
+	ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
+
+	Sky *sky = get_sky(p_env->sky);
+	ERR_FAIL_COND(!sky);
+
+	SkyMaterialData *material = nullptr;
+	RID sky_material;
+
+	RS::EnvironmentBG background = p_env->background;
+
+	if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
+		ERR_FAIL_COND(!sky);
+		sky_material = sky_get_material(p_env->sky);
+
+		if (sky_material.is_valid()) {
+			material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+			if (!material || !material->shader_data->valid) {
+				material = nullptr;
+			}
+		}
+
+		if (!material) {
+			sky_material = sky_shader.default_material;
+			material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+		}
+	}
+
+	if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
+		sky_material = sky_scene_state.fog_material;
+		material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+	}
+
+	ERR_FAIL_COND(!material);
+
+	SkyShaderData *shader_data = material->shader_data;
+
+	ERR_FAIL_COND(!shader_data);
+
+	Basis sky_transform = p_env->sky_orientation;
+	sky_transform.invert();
+
+	float multiplier = p_env->bg_energy;
+	float custom_fov = p_env->sky_custom_fov;
+
+	// Camera
+	CameraMatrix camera;
+	uint32_t view_count = p_view_count;
+	const CameraMatrix *projections = p_projections;
+
+	if (custom_fov) {
+		// With custom fov we don't support stereo...
+		float near_plane = p_projections[0].get_z_near();
+		float far_plane = p_projections[0].get_z_far();
+		float aspect = p_projections[0].get_aspect();
+
+		camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
+
+		view_count = 1;
+		projections = &camera;
+	}
+
+	sky_transform = p_transform.basis * sky_transform;
+
+	if (shader_data->uses_quarter_res) {
+		PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES];
+
+		RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd);
+
+		Vector<Color> clear_colors;
+		clear_colors.push_back(Color(0.0, 0.0, 0.0));
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+		_render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
+		RD::get_singleton()->draw_list_end();
+	}
+
+	if (shader_data->uses_half_res) {
+		PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES];
+
+		RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd);
+
+		Vector<Color> clear_colors;
+		clear_colors.push_back(Color(0.0, 0.0, 0.0));
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+		_render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
+		RD::get_singleton()->draw_list_end();
+	}
+}
+
+void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) {
+	ERR_FAIL_COND(!p_env);
+
+	ERR_FAIL_COND(p_view_count == 0);
+	ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
+
+	Sky *sky = get_sky(p_env->sky);
+	ERR_FAIL_COND(!sky);
+
+	SkyMaterialData *material = nullptr;
+	RID sky_material;
+
+	RS::EnvironmentBG background = p_env->background;
+
+	if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
+		ERR_FAIL_COND(!sky);
+		sky_material = sky_get_material(p_env->sky);
+
+		if (sky_material.is_valid()) {
+			material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+			if (!material || !material->shader_data->valid) {
+				material = nullptr;
+			}
+		}
+
+		if (!material) {
+			sky_material = sky_shader.default_material;
+			material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+		}
+	}
+
+	if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
+		sky_material = sky_scene_state.fog_material;
+		material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+	}
+
+	ERR_FAIL_COND(!material);
+
+	SkyShaderData *shader_data = material->shader_data;
+
+	ERR_FAIL_COND(!shader_data);
+
+	Basis sky_transform = p_env->sky_orientation;
+	sky_transform.invert();
+
+	float multiplier = p_env->bg_energy;
+	float custom_fov = p_env->sky_custom_fov;
+
+	// Camera
+	CameraMatrix camera;
+	uint32_t view_count = p_view_count;
+	const CameraMatrix *projections = p_projections;
+
+	if (custom_fov) {
+		// With custom fov we don't support stereo...
+		float near_plane = p_projections[0].get_z_near();
+		float far_plane = p_projections[0].get_z_far();
+		float aspect = p_projections[0].get_aspect();
+
+		camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
+
+		view_count = 1;
+		projections = &camera;
+	}
+
+	sky_transform = p_transform.basis * sky_transform;
+
+	PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND];
+
+	RID texture_uniform_set;
+	if (sky) {
+		texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_BACKGROUND, sky_shader.default_shader_rd);
+	} else {
+		texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set;
+	}
+
+	_render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
+}
+
 void RendererSceneSkyRD::invalidate_sky(Sky *p_sky) {
 	if (!p_sky->dirty) {
 		p_sky->dirty = true;

+ 2 - 0
servers/rendering/renderer_rd/renderer_scene_sky_rd.h

@@ -295,6 +295,8 @@ public:
 	void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render);
 	void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time);
 	void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time);
+	void update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time);
+	void draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time);
 
 	void invalidate_sky(Sky *p_sky);
 	void update_dirty_skys();

+ 19 - 3
servers/rendering/renderer_rd/shaders/tonemap.glsl

@@ -35,11 +35,15 @@ void main() {
 
 layout(location = 0) in vec2 uv_interp;
 
-#ifdef MULTIVIEW
+#ifdef SUBPASS
+layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput input_color;
+#else
+#if MULTIVIEW
 layout(set = 0, binding = 0) uniform sampler2DArray source_color;
 #else
 layout(set = 0, binding = 0) uniform sampler2D source_color;
 #endif
+#endif
 layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
 layout(set = 2, binding = 0) uniform sampler2D source_glow;
 #ifdef USE_1D_LUT
@@ -291,6 +295,7 @@ vec3 apply_color_correction(vec3 color) {
 }
 #endif
 
+#ifndef SUBPASS
 vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
 	const float FXAA_REDUCE_MIN = (1.0 / 128.0);
 	const float FXAA_REDUCE_MUL = (1.0 / 8.0);
@@ -346,6 +351,7 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
 		return rgbB;
 	}
 }
+#endif // !SUBPASS
 
 // From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
 // and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
@@ -360,7 +366,10 @@ vec3 screen_space_dither(vec2 frag_coord) {
 }
 
 void main() {
-#ifdef MULTIVIEW
+#ifdef SUBPASS
+	// SUBPASS and MULTIVIEW can be combined but in that case we're already reading from the correct layer
+	vec3 color = subpassLoad(input_color).rgb;
+#elif MULTIVIEW
 	vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb;
 #else
 	vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb;
@@ -370,14 +379,16 @@ void main() {
 
 	float exposure = params.exposure;
 
+#ifndef SUBPASS
 	if (params.use_auto_exposure) {
 		exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey);
 	}
+#endif
 
 	color *= exposure;
 
 	// Early Tonemap & SRGB Conversion
-
+#ifndef SUBPASS
 	if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
 		vec3 glow = gather_glow(source_glow, uv_interp);
 		color.rgb = mix(color.rgb, glow, params.glow_intensity);
@@ -386,15 +397,19 @@ void main() {
 	if (params.use_fxaa) {
 		color = do_fxaa(color, exposure, uv_interp);
 	}
+#endif
+
 	if (params.use_debanding) {
 		// For best results, debanding should be done before tonemapping.
 		// Otherwise, we're adding noise to an already-quantized image.
 		color += screen_space_dither(gl_FragCoord.xy);
 	}
+
 	color = apply_tonemapping(color, params.white);
 
 	color = linear_to_srgb(color); // regular linear -> SRGB conversion
 
+#ifndef SUBPASS
 	// Glow
 
 	if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
@@ -406,6 +421,7 @@ void main() {
 
 		color = apply_glow(color, glow);
 	}
+#endif
 
 	// Additional effects
 

+ 1 - 1
servers/rendering/rendering_device.cpp

@@ -739,7 +739,7 @@ void RenderingDevice::_bind_methods() {
 	BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_UPDATE_BIT);
 	BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_COPY_FROM_BIT);
 	BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_COPY_TO_BIT);
-	BIND_ENUM_CONSTANT(TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT);
+	BIND_ENUM_CONSTANT(TEXTURE_USAGE_INPUT_ATTACHMENT_BIT);
 
 	BIND_ENUM_CONSTANT(TEXTURE_SWIZZLE_IDENTITY);
 	BIND_ENUM_CONSTANT(TEXTURE_SWIZZLE_ZERO);

+ 2 - 1
servers/rendering/rendering_device.h

@@ -422,7 +422,7 @@ public:
 		TEXTURE_USAGE_CAN_UPDATE_BIT = (1 << 6),
 		TEXTURE_USAGE_CAN_COPY_FROM_BIT = (1 << 7),
 		TEXTURE_USAGE_CAN_COPY_TO_BIT = (1 << 8),
-		TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT = (1 << 9),
+		TEXTURE_USAGE_INPUT_ATTACHMENT_BIT = (1 << 9),
 	};
 
 	enum TextureSwizzle {
@@ -1065,6 +1065,7 @@ public:
 	virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0;
 	virtual void draw_list_disable_scissor(DrawListID p_list) = 0;
 
+	virtual uint32_t draw_list_get_current_pass() = 0;
 	virtual DrawListID draw_list_switch_to_next_pass() = 0;
 	virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) = 0;