summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Software/EfbInterface.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2018-05-01 19:31:54 +1000
committerGitHub <noreply@github.com>2018-05-01 19:31:54 +1000
commitc46882142bffbfc18103d4c977dd2551408e229c (patch)
tree5c27fdafb147e9cdea441dc192049c243f415496 /Source/Core/VideoBackends/Software/EfbInterface.cpp
parent42d73deaed9e79e8d2e45c1a0ebfa69d19a9c9b3 (diff)
parentea4090d92c51a53ee7aedcd1fe7871861a78550c (diff)
Merge pull request #6369 from stenzek/xfb-copy-filter
Implement copy filter (deflickering/brightness) and XFB gamma
Diffstat (limited to 'Source/Core/VideoBackends/Software/EfbInterface.cpp')
-rw-r--r--Source/Core/VideoBackends/Software/EfbInterface.cpp89
1 files changed, 80 insertions, 9 deletions
diff --git a/Source/Core/VideoBackends/Software/EfbInterface.cpp b/Source/Core/VideoBackends/Software/EfbInterface.cpp
index a94ac467b8..a13feb6c50 100644
--- a/Source/Core/VideoBackends/Software/EfbInterface.cpp
+++ b/Source/Core/VideoBackends/Software/EfbInterface.cpp
@@ -469,10 +469,59 @@ u32 GetColor(u16 x, u16 y)
return GetPixelColor(offset);
}
+static u32 VerticalFilter(const std::array<u32, 3>& colors,
+ const std::array<u8, 7>& 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;
+}
+
+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<u8>(
+ 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.
-yuv444 GetColorYUV(u16 x, u16 y)
+static yuv444 ConvertColorToYUV(u32 color)
{
- const u32 color = GetColor(x, y);
const u8 red = static_cast<u8>(color >> 24);
const u8 green = static_cast<u8>(color >> 16);
const u8 blue = static_cast<u8>(color >> 8);
@@ -497,7 +546,8 @@ 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,
+ float gamma)
{
if (!xfb_in_ram)
{
@@ -505,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.
@@ -523,13 +577,30 @@ 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<u16>(std::max(clamp_top ? source_rect.top : 0, y - 1));
+ u16 y_next = static_cast<u16>(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<u32, 3> colors = {{GetColor(x, y_prev), GetColor(x, y), GetColor(x, y_next)}};
+
+ // Vertical Filter (Multisampling resolve, deflicker, brightness)
+ u32 filtered = VerticalFilter(colors, filter_coefficients);
+
+ // Gamma correction happens here.
+ filtered = GammaCorrection(filtered, gamma_rcp);
+
+ scanline[i] = ConvertColorToYUV(filtered);
}
// Flipper clamps the border colors
@@ -549,7 +620,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;
}