Pārlūkot izejas kodu

Merge pull request #108761 from allenwp/108757-fix-mobile-hdr2d-debanding

Fix debanding for Mobile rendering method with HDR 2D.
Thaddeus Crews 2 dienas atpakaļ
vecāks
revīzija
967e2d499a

+ 10 - 4
servers/rendering/renderer_rd/effects/tone_mapper.cpp

@@ -123,8 +123,11 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
 	tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
 
 	tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
-	// When convert_to_srgb is false: postpone debanding until convert_to_srgb is true (usually during blit).
-	tonemap.push_constant.flags |= (p_settings.use_debanding && p_settings.convert_to_srgb) ? TONEMAP_FLAG_USE_DEBANDING : 0;
+	if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
+		tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
+	} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
+		tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
+	}
 	tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
 	tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
 
@@ -208,8 +211,11 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
 	tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
 
 	tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
-	// When convert_to_srgb is false: postpone debanding until convert_to_srgb is true (usually during blit).
-	tonemap.push_constant.flags |= (p_settings.use_debanding && p_settings.convert_to_srgb) ? TONEMAP_FLAG_USE_DEBANDING : 0;
+	if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
+		tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
+	} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
+		tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
+	}
 	tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
 
 	tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;

+ 9 - 3
servers/rendering/renderer_rd/effects/tone_mapper.h

@@ -63,8 +63,9 @@ private:
 		TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2),
 		TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3),
 		TONEMAP_FLAG_USE_FXAA = (1 << 4),
-		TONEMAP_FLAG_USE_DEBANDING = (1 << 5),
-		TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 6),
+		TONEMAP_FLAG_USE_8_BIT_DEBANDING = (1 << 5),
+		TONEMAP_FLAG_USE_10_BIT_DEBANDING = (1 << 6),
+		TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 7),
 	};
 
 	struct TonemapPushConstant {
@@ -141,7 +142,12 @@ public:
 		RID color_correction_texture;
 
 		bool use_fxaa = false;
-		bool use_debanding = false;
+		enum DebandingMode {
+			DEBANDING_MODE_DISABLED,
+			DEBANDING_MODE_8_BIT,
+			DEBANDING_MODE_10_BIT,
+		};
+		DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
 		Vector2i texture_size;
 		uint32_t view_count = 1;
 

+ 1 - 2
servers/rendering/renderer_rd/renderer_compositor_rd.cpp

@@ -96,8 +96,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
 		blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
 		blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
 		blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
-		// If convert_to_srgb is false, debanding was applied earlier (usually in tonemapping).
-		blit.push_constant.use_debanding = uint32_t(blit.push_constant.convert_to_srgb && texture_storage->render_target_is_using_debanding(p_render_targets[i].render_target));
+		blit.push_constant.use_debanding = texture_storage->render_target_is_using_debanding(p_render_targets[i].render_target);
 
 		RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
 		RD::get_singleton()->draw_list_draw(draw_list, true);

+ 41 - 5
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -615,6 +615,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 
 		RendererRD::ToneMapper::TonemapSettings tonemap;
 
+		bool using_hdr = texture_storage->render_target_is_using_hdr(render_target);
+
 		tonemap.exposure_texture = luminance->get_current_luminance_buffer(rb);
 		if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && tonemap.exposure_texture.is_valid()) {
 			tonemap.use_auto_exposure = true;
@@ -653,7 +655,6 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 			tonemap.use_fxaa = true;
 		}
 
-		tonemap.use_debanding = rb->get_use_debanding();
 		tonemap.texture_size = Vector2i(color_size.x, color_size.y);
 
 		if (p_render_data->environment.is_valid()) {
@@ -665,7 +666,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		tonemap.use_color_correction = false;
 		tonemap.use_1d_color_correction = false;
 		tonemap.color_correction_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE);
-		tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
+		tonemap.convert_to_srgb = !using_hdr;
 
 		if (can_use_effects && p_render_data->environment.is_valid()) {
 			tonemap.use_bcs = environment_get_adjustments_enabled(p_render_data->environment);
@@ -683,10 +684,12 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		tonemap.view_count = rb->get_view_count();
 
 		RID dest_fb;
+		RD::DataFormat dest_fb_format;
 		if (spatial_upscaler != nullptr || use_smaa) {
 			// If we use a spatial upscaler to upscale or SMAA to antialias we need to write our result into an intermediate buffer.
 			// Note that this is cached so we only create the texture the first time.
-			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
+			dest_fb_format = _render_buffers_get_color_format();
+			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), dest_fb_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
 			dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
 		} else {
 			// If we do a bilinear upscale we just render into our render target and our shader will upscale automatically.
@@ -694,13 +697,30 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 			// Bit nasty but...
 
 			if (dest_is_msaa_2d) {
+				// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
+				dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
 				dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
 				texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
 			} else {
+				// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
+				dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
 				dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
 			}
 		}
 
+		if (rb->get_use_debanding()) {
+			if (dest_fb_format >= RD::DATA_FORMAT_R8_UNORM && dest_fb_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32) {
+				tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
+			} else if (dest_fb_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && dest_fb_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32) {
+				tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_10_BIT;
+			} else {
+				// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
+				tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
+			}
+		} else {
+			tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
+		}
+
 		tone_mapper->tonemapper(color_texture, dest_fb, tonemap);
 
 		RD::get_singleton()->draw_command_end_label();
@@ -814,6 +834,8 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
 		ERR_FAIL_MSG("Auto Exposure is not supported when using subpasses.");
 	}
 
+	bool using_hdr = texture_storage->render_target_is_using_hdr(rb->get_render_target());
+
 	tonemap.use_glow = false;
 	tonemap.glow_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
 	tonemap.glow_map = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE);
@@ -823,7 +845,7 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
 	tonemap.use_color_correction = false;
 	tonemap.use_1d_color_correction = false;
 	tonemap.color_correction_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE);
-	tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(rb->get_render_target());
+	tonemap.convert_to_srgb = !using_hdr;
 
 	if (can_use_effects && p_render_data->environment.is_valid()) {
 		tonemap.use_bcs = environment_get_adjustments_enabled(p_render_data->environment);
@@ -837,12 +859,26 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
 		}
 	}
 
-	tonemap.use_debanding = rb->get_use_debanding();
 	tonemap.texture_size = Vector2i(target_size.x, target_size.y);
 
 	tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier();
 	tonemap.view_count = rb->get_view_count();
 
+	if (rb->get_use_debanding()) {
+		// Assume that the DataFormat of p_framebuffer is the same as render_target_get_color_format.
+		RD::DataFormat dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
+		if (dest_fb_format >= RD::DATA_FORMAT_R8_UNORM && dest_fb_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32) {
+			tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
+		} else if (dest_fb_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && dest_fb_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32) {
+			tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_10_BIT;
+		} else {
+			// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
+			tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
+		}
+	} else {
+		tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
+	}
+
 	tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
 
 	RD::get_singleton()->draw_command_end_label();

+ 8 - 1
servers/rendering/renderer_rd/shaders/blit.glsl

@@ -135,10 +135,17 @@ void main() {
 
 	if (data.convert_to_srgb) {
 		color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
-		// When convert_to_srgb is true, debanding was skipped in tonemap.glsl.
+
+		// Even if debanding was applied earlier in the rendering process, it must
+		// be reapplied after the linear_to_srgb floating point operations.
+		// When the linear_to_srgb operation was not performed, the source is
+		// already an 8-bit format and debanding cannot be effective. In this
+		// case, GPU driver rounding error can add noise so debanding should be
+		// skipped entirely.
 		if (data.use_debanding) {
 			color.rgb += screen_space_dither(gl_FragCoord.xy);
 		}
+
 		color.rgb = clamp(color.rgb, vec3(0.0), vec3(1.0));
 	}
 }

+ 14 - 9
servers/rendering/renderer_rd/shaders/effects/tonemap.glsl

@@ -65,8 +65,9 @@ layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
 #define FLAG_USE_AUTO_EXPOSURE (1 << 2)
 #define FLAG_USE_COLOR_CORRECTION (1 << 3)
 #define FLAG_USE_FXAA (1 << 4)
-#define FLAG_USE_DEBANDING (1 << 5)
-#define FLAG_CONVERT_TO_SRGB (1 << 6)
+#define FLAG_USE_8_BIT_DEBANDING (1 << 5)
+#define FLAG_USE_10_BIT_DEBANDING (1 << 6)
+#define FLAG_CONVERT_TO_SRGB (1 << 7)
 
 layout(push_constant, std430) uniform Params {
 	vec3 bcs;
@@ -819,7 +820,7 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
 // NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
 // This dithering must be applied after encoding changes (linear/nonlinear) have been applied
 // as the final step before quantization from floating point to integer values.
-vec3 screen_space_dither(vec2 frag_coord) {
+vec3 screen_space_dither(vec2 frag_coord, float bit_alignment_diviser) {
 	// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
 	// Removed the time component to avoid passing time into this shader.
 	vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
@@ -827,8 +828,7 @@ vec3 screen_space_dither(vec2 frag_coord) {
 
 	// Subtract 0.5 to avoid slightly brightening the whole viewport.
 	// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
-	// Divide by 255 to align to 8-bit quantization.
-	return (dither.rgb - 0.5) / 255.0;
+	return (dither.rgb - 0.5) / bit_alignment_diviser;
 }
 
 void main() {
@@ -910,10 +910,15 @@ void main() {
 		// linear because the color correction texture sampling does this for us.
 	}
 
-	if (bool(params.flags & FLAG_USE_DEBANDING)) {
-		// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
-		// Otherwise, we're adding noise to an already-quantized image.
-		color.rgb += screen_space_dither(gl_FragCoord.xy);
+	// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
+	// Otherwise, we're adding noise to an already-quantized image.
+
+	if (bool(params.flags & FLAG_USE_8_BIT_DEBANDING)) {
+		// Divide by 255 to align to 8-bit quantization.
+		color.rgb += screen_space_dither(gl_FragCoord.xy, 255.0);
+	} else if (bool(params.flags & FLAG_USE_10_BIT_DEBANDING)) {
+		// Divide by 1023 to align to 10-bit quantization.
+		color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
 	}
 
 	frag_color = color;