From fc96479f122d3b2fdd9a5191c67ebce811ff569d Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Wed, 18 May 2016 08:21:02 +1200 Subject: VideoSoftware: Implement xfb copy filter (Deflickering/Brightness) --- .../Core/VideoBackends/Software/EfbInterface.cpp | 62 +++++++++++++++++++--- 1 file changed, 56 insertions(+), 6 deletions(-) (limited to 'Source/Core/VideoBackends/Software/EfbInterface.cpp') diff --git a/Source/Core/VideoBackends/Software/EfbInterface.cpp b/Source/Core/VideoBackends/Software/EfbInterface.cpp index a94ac467b8..12c208f70c 100644 --- a/Source/Core/VideoBackends/Software/EfbInterface.cpp +++ b/Source/Core/VideoBackends/Software/EfbInterface.cpp @@ -469,10 +469,42 @@ u32 GetColor(u16 x, u16 y) return GetPixelColor(offset); } +static u32 VerticalFilter(const std::array& colors, + const std::array& filterCoefficients) +{ + u8 in_colors[3][4]; + std::memcpy(&in_colors, colors.data(), sizeof(in_colors)); + + // Alpha channel is not used + u8 out_color[4]; + out_color[ALP_C] = 0; + + // All Coefficients should sum to 64, otherwise the total brightness will change, which many games + // do on purpose to implement a brightness filter across the whole copy. + for (int i = BLU_C; i <= RED_C; i++) + { + // TODO: implement support for multisampling. + // In non-multisampling mode: + // * Coefficients 2, 3 and 4 sample from the current pixel. + // * Coefficients 0 and 1 sample from the pixel above this one + // * Coefficients 5 and 6 sample from the pixel below this one + int sum = + in_colors[0][i] * (filterCoefficients[0] + filterCoefficients[1]) + + in_colors[1][i] * (filterCoefficients[2] + filterCoefficients[3] + filterCoefficients[4]) + + in_colors[2][i] * (filterCoefficients[5] + filterCoefficients[6]); + + // TODO: this clamping behavior appears to be correct, but isn't confirmed on hardware. + out_color[i] = std::min(255, sum >> 6); // clamp larger values to 255 + } + + u32 out_color32; + std::memcpy(&out_color32, out_color, sizeof(out_color32)); + return out_color32; +} + // For internal used only, return a non-normalized value, which saves work later. -yuv444 GetColorYUV(u16 x, u16 y) +static yuv444 ConvertColorToYUV(u32 color) { - const u32 color = GetColor(x, y); const u8 red = static_cast(color >> 24); const u8 green = static_cast(color >> 16); const u8 blue = static_cast(color >> 8); @@ -497,7 +529,9 @@ u8* GetPixelPointer(u16 x, u16 y, bool depth) return &efb[GetColorOffset(x, y)]; } -void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rect, float y_scale) +void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rect, float y_scale, + bool clamp_top, bool clamp_bottom, float Gamma, + const std::array& filterCoefficients) { if (!xfb_in_ram) { @@ -523,13 +557,29 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec source.resize(EFB_WIDTH * EFB_HEIGHT); yuv422_packed* src_ptr = &source[0]; - for (float y = source_rect.top; y < source_rect.bottom; y++) + for (int y = source_rect.top; y < source_rect.bottom; y++) { - // Get a scanline of YUV pixels in 4:4:4 format + // Clamping behavior + // NOTE: when the clamp bits aren't set, the hardware will happily read beyond the EFB, + // which returns random garbage from the empty bus (confirmed by hardware tests). + // + // In our implementation, the garbage just so happens to be the top or bottom row. + // Statistically, that could happen. + u16 y_prev = static_cast(std::max(clamp_top ? source_rect.top : 0, y - 1)); + u16 y_next = static_cast(std::min(clamp_bottom ? source_rect.bottom : EFB_HEIGHT, y + 1)); + // Get a scanline of YUV pixels in 4:4:4 format for (int i = 1, x = left; x < right; i++, x++) { - scanline[i] = GetColorYUV(x, y); + // Get RGB colors + std::array colors = {{GetColor(x, y_prev), GetColor(x, y), GetColor(x, y_next)}}; + + // Vertical Filter (Multisampling resolve, deflicker, brightness) + u32 filtered = VerticalFilter(colors, filterCoefficients); + + // TODO: Gamma correction happens here. + + scanline[i] = ConvertColorToYUV(filtered); } // Flipper clamps the border colors -- cgit v1.2.3 From a192a3bb3043c921ec897186c2a927b33050dffc Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Thu, 19 May 2016 10:16:26 +1200 Subject: While I'm here, fix some chroma sub-sampling bugs. RE4's brightness screen is actually very good for spotting these. Bug 1: Colors at the end of the scanlines are clamped, instead of a black border Bug 2: U and V color channels share coordinates, instead of being offset by a pixel. --- Source/Core/VideoBackends/Software/EfbInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoBackends/Software/EfbInterface.cpp') diff --git a/Source/Core/VideoBackends/Software/EfbInterface.cpp b/Source/Core/VideoBackends/Software/EfbInterface.cpp index 12c208f70c..5d852e0ff9 100644 --- a/Source/Core/VideoBackends/Software/EfbInterface.cpp +++ b/Source/Core/VideoBackends/Software/EfbInterface.cpp @@ -599,7 +599,7 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec src_ptr[x + 1].Y = scanline[i + 1].Y + 16; // V[i] = 1/4 * V[i-1] + 1/2 * V[i] + 1/4 * V[i+1] src_ptr[x + 1].UV = - 128 + ((scanline[i].V + (scanline[i + 1].V << 1) + scanline[i + 2].V) >> 2); + 128 + ((scanline[i - 1].V + (scanline[i].V << 1) + scanline[i + 1].V) >> 2); } src_ptr += memory_stride; } -- cgit v1.2.3 From 9e798eec94a8ebe94c1ef8270b504b5698b2a0f5 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 29 Apr 2018 18:52:30 +1000 Subject: Implement EFB copy filter and gamma in hardware backends Also makes y_scale a dynamic parameter for EFB copies, as it doesn't make sense to keep it as part of the uid, otherwise we're generating redundant shaders. --- .../Core/VideoBackends/Software/EfbInterface.cpp | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'Source/Core/VideoBackends/Software/EfbInterface.cpp') diff --git a/Source/Core/VideoBackends/Software/EfbInterface.cpp b/Source/Core/VideoBackends/Software/EfbInterface.cpp index 5d852e0ff9..a13feb6c50 100644 --- a/Source/Core/VideoBackends/Software/EfbInterface.cpp +++ b/Source/Core/VideoBackends/Software/EfbInterface.cpp @@ -502,6 +502,23 @@ static u32 VerticalFilter(const std::array& colors, return out_color32; } +static u32 GammaCorrection(u32 color, const float gamma_rcp) +{ + u8 in_colors[4]; + std::memcpy(&in_colors, &color, sizeof(in_colors)); + + u8 out_color[4]; + for (int i = BLU_C; i <= RED_C; i++) + { + out_color[i] = static_cast( + MathUtil::Clamp(std::pow(in_colors[i] / 255.0f, gamma_rcp) * 255.0f, 0.0f, 255.0f)); + } + + u32 out_color32; + std::memcpy(&out_color32, out_color, sizeof(out_color32)); + return out_color32; +} + // For internal used only, return a non-normalized value, which saves work later. static yuv444 ConvertColorToYUV(u32 color) { @@ -530,8 +547,7 @@ u8* GetPixelPointer(u16 x, u16 y, bool depth) } void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rect, float y_scale, - bool clamp_top, bool clamp_bottom, float Gamma, - const std::array& filterCoefficients) + float gamma) { if (!xfb_in_ram) { @@ -539,8 +555,12 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec return; } - int left = source_rect.left; - int right = source_rect.right; + const int left = source_rect.left; + const int right = source_rect.right; + const bool clamp_top = bpmem.triggerEFBCopy.clamp_top; + const bool clamp_bottom = bpmem.triggerEFBCopy.clamp_bottom; + const float gamma_rcp = 1.0f / gamma; + const auto filter_coefficients = bpmem.copyfilter.GetCoefficients(); // this assumes copies will always start on an even (YU) pixel and the // copy always has an even width, which might not be true. @@ -575,9 +595,10 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec std::array colors = {{GetColor(x, y_prev), GetColor(x, y), GetColor(x, y_next)}}; // Vertical Filter (Multisampling resolve, deflicker, brightness) - u32 filtered = VerticalFilter(colors, filterCoefficients); + u32 filtered = VerticalFilter(colors, filter_coefficients); - // TODO: Gamma correction happens here. + // Gamma correction happens here. + filtered = GammaCorrection(filtered, gamma_rcp); scanline[i] = ConvertColorToYUV(filtered); } -- cgit v1.2.3