Skip to content

Commit aa0c7bb

Browse files
committed
Merge pull request #109970 from allenwp/add-debanding-smaa-before-spatial-upscaler
Add debanding to SMAA and apply debanding before spatial upscalers.
2 parents 27b0135 + 5a3e69d commit aa0c7bb

File tree

5 files changed

+125
-11
lines changed

5 files changed

+125
-11
lines changed

servers/rendering/renderer_rd/effects/smaa.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_colo
181181

182182
smaa.blend_push_constant.inv_size[0] = inv_size.x;
183183
smaa.blend_push_constant.inv_size[1] = inv_size.y;
184+
if (debanding_mode == DEBANDING_MODE_8_BIT) {
185+
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_8_BIT_DEBANDING;
186+
} else if (debanding_mode == DEBANDING_MODE_10_BIT) {
187+
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_10_BIT_DEBANDING;
188+
}
184189

185190
RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
186191

servers/rendering/renderer_rd/effects/smaa.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class SMAA {
5959
struct SMAAEdgePushConstant {
6060
float inv_size[2];
6161
float threshold;
62-
float reserved;
62+
float pad;
6363
};
6464

6565
struct SMAAWeightPushConstant {
@@ -71,7 +71,13 @@ class SMAA {
7171

7272
struct SMAABlendPushConstant {
7373
float inv_size[2];
74-
float reserved[2];
74+
uint32_t flags;
75+
float pad;
76+
};
77+
78+
enum SMAABlendFlags {
79+
SMAA_BLEND_FLAG_USE_8_BIT_DEBANDING = (1 << 0),
80+
SMAA_BLEND_FLAG_USE_10_BIT_DEBANDING = (1 << 1),
7581
};
7682

7783
struct SMAAEffect {
@@ -103,6 +109,13 @@ class SMAA {
103109

104110
void allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers);
105111
void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer);
112+
113+
enum DebandingMode {
114+
DEBANDING_MODE_DISABLED,
115+
DEBANDING_MODE_8_BIT,
116+
DEBANDING_MODE_10_BIT,
117+
};
118+
DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
106119
};
107120

108121
} // namespace RendererRD

servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -685,36 +685,48 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
685685

686686
RID dest_fb;
687687
RD::DataFormat dest_fb_format;
688+
RD::DataFormat format_for_debanding;
688689
if (spatial_upscaler != nullptr || use_smaa) {
689690
// If we use a spatial upscaler to upscale or SMAA to antialias we need to write our result into an intermediate buffer.
690691
// Note that this is cached so we only create the texture the first time.
691692
dest_fb_format = _render_buffers_get_color_format();
692693
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);
693694
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
695+
if (use_smaa) {
696+
format_for_debanding = dest_fb_format;
697+
} else {
698+
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
699+
// This produces suboptimal results because the image will be modified by spatial upscaling after
700+
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
701+
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
702+
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
703+
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
704+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
705+
}
694706
} else {
695707
// If we do a bilinear upscale we just render into our render target and our shader will upscale automatically.
696708
// Target size in this case is lying as we never get our real target size communicated.
697709
// Bit nasty but...
698710

699711
if (dest_is_msaa_2d) {
700-
// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
701-
dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
702712
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
713+
// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
714+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
703715
texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
704716
} else {
705-
// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
706-
dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
707717
dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
718+
// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
719+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
708720
}
709721
}
710722

711723
if (rb->get_use_debanding()) {
712-
if (dest_fb_format >= RD::DATA_FORMAT_R8_UNORM && dest_fb_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32) {
724+
if (_is_8bit_data_format(format_for_debanding)) {
713725
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
714-
} else if (dest_fb_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && dest_fb_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32) {
726+
} else if (_is_10bit_data_format(format_for_debanding)) {
715727
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_10_BIT;
716728
} else {
717-
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
729+
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit or SMAA, for example.)
718730
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
719731
}
720732
} else {
@@ -730,6 +742,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
730742
RENDER_TIMESTAMP("SMAA");
731743
RD::get_singleton()->draw_command_begin_label("SMAA");
732744

745+
bool using_hdr = texture_storage->render_target_is_using_hdr(render_target);
733746
RID dest_fb;
734747
if (spatial_upscaler) {
735748
rb->create_texture(SNAME("SMAA"), 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);
@@ -739,30 +752,78 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
739752
RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0);
740753

741754
RID dest_texture;
755+
RD::DataFormat format_for_debanding;
742756
if (spatial_upscaler) {
743757
dest_texture = rb->get_texture_slice(SNAME("SMAA"), SNAME("destination"), v, 0);
758+
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
759+
// This produces suboptimal results because the image will be modified by spatial upscaling after
760+
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
761+
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
762+
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
763+
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
764+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
744765
} else {
745766
dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v);
767+
// Assume that the DataFormat is the same as render_target_get_color_format.
768+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
746769
}
747770
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
748771

772+
if (rb->get_use_debanding()) {
773+
if (_is_8bit_data_format(format_for_debanding)) {
774+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_8_BIT;
775+
} else if (_is_10bit_data_format(format_for_debanding)) {
776+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_10_BIT;
777+
} else {
778+
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
779+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
780+
}
781+
} else {
782+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
783+
}
784+
749785
smaa->process(rb, source_texture, dest_fb);
750786
}
751787
} else {
752788
RID source_texture = rb->get_texture(SNAME("Tonemapper"), SNAME("destination"));
789+
RD::DataFormat format_for_debanding;
753790

754791
if (spatial_upscaler) {
755792
RID dest_texture = rb->create_texture(SNAME("SMAA"), 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);
756793
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
794+
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
795+
// This produces suboptimal results because the image will be modified by spatial upscaling after
796+
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
797+
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
798+
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
799+
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
800+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
757801
} else {
758802
if (dest_is_msaa_2d) {
759803
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
804+
// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
805+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
760806
texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
761807
} else {
762808
dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
809+
// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
810+
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
763811
}
764812
}
765813

814+
if (rb->get_use_debanding()) {
815+
if (_is_8bit_data_format(format_for_debanding)) {
816+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_8_BIT;
817+
} else if (_is_10bit_data_format(format_for_debanding)) {
818+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_10_BIT;
819+
} else {
820+
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
821+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
822+
}
823+
} else {
824+
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
825+
}
826+
766827
smaa->process(rb, source_texture, dest_fb);
767828
}
768829

servers/rendering/renderer_rd/renderer_scene_render_rd.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ class RendererSceneRenderRD : public RendererSceneRender, public RenderingShader
113113
void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data);
114114
void _disable_clear_request(const RenderDataRD *p_render_data);
115115

116+
_FORCE_INLINE_ bool _is_8bit_data_format(RD::DataFormat p_data_format) {
117+
return p_data_format >= RD::DATA_FORMAT_R8_UNORM && p_data_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32;
118+
}
119+
120+
_FORCE_INLINE_ bool _is_10bit_data_format(RD::DataFormat p_data_format) {
121+
return p_data_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && p_data_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32;
122+
}
123+
116124
// needed for a single argument calls (material and uv2)
117125
PagedArrayPool<RenderGeometryInstance *> cull_argument_pool;
118126
PagedArray<RenderGeometryInstance *> cull_argument; //need this to exist

servers/rendering/renderer_rd/shaders/effects/smaa_blending.glsl

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ layout(location = 1) out vec4 offset;
3434

3535
layout(push_constant, std430) uniform Params {
3636
vec2 inv_size;
37-
vec2 reserved;
37+
vec2 pad;
3838
}
3939
params;
4040

@@ -62,9 +62,13 @@ layout(set = 1, binding = 0) uniform sampler2D blend_tex;
6262

6363
layout(location = 0) out vec4 out_color;
6464

65+
#define FLAG_USE_8_BIT_DEBANDING (1 << 0)
66+
#define FLAG_USE_10_BIT_DEBANDING (1 << 1)
67+
6568
layout(push_constant, std430) uniform Params {
6669
vec2 inv_size;
67-
vec2 reserved;
70+
uint flags;
71+
float pad;
6872
}
6973
params;
7074

@@ -95,6 +99,22 @@ void SMAAMovc(bvec4 cond, inout vec4 variable, vec4 value) {
9599
SMAAMovc(cond.zw, variable.zw, value.zw);
96100
}
97101

102+
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
103+
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
104+
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
105+
// This dithering must be applied after encoding changes (linear/nonlinear) have been applied
106+
// as the final step before quantization from floating point to integer values.
107+
vec3 screen_space_dither(vec2 frag_coord, float bit_alignment_diviser) {
108+
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
109+
// Removed the time component to avoid passing time into this shader.
110+
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
111+
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
112+
113+
// Subtract 0.5 to avoid slightly brightening the whole viewport.
114+
// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
115+
return (dither.rgb - 0.5) / bit_alignment_diviser;
116+
}
117+
98118
void main() {
99119
vec4 a;
100120
a.x = texture(blend_tex, offset.xy).a;
@@ -120,4 +140,11 @@ void main() {
120140
out_color.rgb = linear_to_srgb(out_color.rgb);
121141
out_color.a = texture(color_tex, tex_coord).a;
122142
}
143+
if (bool(params.flags & FLAG_USE_8_BIT_DEBANDING)) {
144+
// Divide by 255 to align to 8-bit quantization.
145+
out_color.rgb += screen_space_dither(gl_FragCoord.xy, 255.0);
146+
} else if (bool(params.flags & FLAG_USE_10_BIT_DEBANDING)) {
147+
// Divide by 1023 to align to 10-bit quantization.
148+
out_color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
149+
}
123150
}

0 commit comments

Comments
 (0)