Skip to content

Commit

Permalink
Make intensity/YUV EFB copies hardware accurate
Browse files Browse the repository at this point in the history
I haven't fully hardware-tested it yet, but at least YUV8A exists (when intensity formats are used with RGBA8), and probably all formats work this way.  However, with YUV8A, Dolphin matches exactly for all values.
  • Loading branch information
Pokechu22 committed Feb 20, 2022
1 parent bc2bf4e commit be26203
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 181 deletions.
6 changes: 3 additions & 3 deletions Source/Core/VideoBackends/Software/EfbInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,9 @@ static yuv444 ConvertColorToYUV(u32 color)

// GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
return {static_cast<u8>(0.257f * red + 0.504f * green + 0.098f * blue),
static_cast<s8>(-0.148f * red + -0.291f * green + 0.439f * blue),
static_cast<s8>(0.439f * red + -0.368f * green + -0.071f * blue)};
return {static_cast<u8>(0.2578125f * red + 0.50390625f * green + 0.09765625f * blue),
static_cast<s8>(-0.1484375f * red + -0.2890625f * green + 0.4375f * blue),
static_cast<s8>(0.4375f * red + -0.3671875f * green + -0.0703125f * blue)};
}

u32 GetDepth(u16 x, u16 y)
Expand Down
170 changes: 23 additions & 147 deletions Source/Core/VideoCommon/TextureConversionShader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

namespace TextureConversionShaderTiled
{
static bool IntensityConstantAdded = false;

u16 GetEncodedSampleCount(EFBCopyFormat format)
{
switch (format)
Expand Down Expand Up @@ -213,6 +211,15 @@ static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, A
" float4(gamma_rcp, gamma_rcp, gamma_rcp, 1.0)) * 255.0));\n");
}

if (params.yuv)
{
code.Write(
" // Add .00001 to ensure that round rounds up\n"
" texcol_raw.rgb = uint3(round(float3(dot(y_const, texcol_raw.rgb) + 16.00001,\n"
" dot(u_const, texcol_raw.rgb) + 128.00001,\n"
" dot(v_const, texcol_raw.rgb) + 128.00001)));\n");
}

code.Write(" return float4(texcol_raw) / {};\n", max_value);
code.Write("}}\n");
}
Expand Down Expand Up @@ -295,16 +302,12 @@ static void WriteSampleColor(ShaderCode& code, std::string_view color_comp, std:
code.Write(" {} = SampleEFB(uv0, pixel_size, {}).{};\n", dest, x_offset, color_comp);
}

static void WriteColorToIntensity(ShaderCode& code, std::string_view src, std::string_view dest)
static void WriteColorToIntensity(ShaderCode& code)
{
if (!IntensityConstantAdded)
{
code.Write(" float4 IntensityConst = float4(0.257f,0.504f,0.098f,0.0625f);\n");
IntensityConstantAdded = true;
}
code.Write(" {} = dot(IntensityConst.rgb, {}.rgb);\n", dest, src);
// don't add IntensityConst.a yet, because doing it later is faster and uses less instructions,
// due to vectorization
code.Write(" // Intensity/YUV format conversion\n"
" const float3 y_const = float3(0.2578125, 0.50390625, 0.09765625);\n"
" const float3 u_const = float3(-0.1484375, -0.2890625, 0.4375);\n"
" const float3 v_const = float3(0.4375, -0.3671875, -0.0703125);\n");
}

static void WriteToBitDepth(ShaderCode& code, u8 depth, std::string_view src, std::string_view dest)
Expand All @@ -315,121 +318,6 @@ static void WriteToBitDepth(ShaderCode& code, u8 depth, std::string_view src, st
static void WriteEncoderEnd(ShaderCode& code)
{
code.Write("}}\n");
IntensityConstantAdded = false;
}

static void WriteI8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{
WriteSwizzler(code, params, EFBCopyFormat::R8, api_type);
code.Write(" float3 texSample;\n");

WriteSampleColor(code, "rgb", "texSample", 0, api_type, params);
WriteColorToIntensity(code, "texSample", "ocol0.b");

WriteSampleColor(code, "rgb", "texSample", 1, api_type, params);
WriteColorToIntensity(code, "texSample", "ocol0.g");

WriteSampleColor(code, "rgb", "texSample", 2, api_type, params);
WriteColorToIntensity(code, "texSample", "ocol0.r");

WriteSampleColor(code, "rgb", "texSample", 3, api_type, params);
WriteColorToIntensity(code, "texSample", "ocol0.a");

// See WriteColorToIntensity
code.Write(" ocol0.rgba += IntensityConst.aaaa;\n");

WriteEncoderEnd(code);
}

static void WriteI4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{
WriteSwizzler(code, params, EFBCopyFormat::R4, api_type);
code.Write(" float3 texSample;\n"
" float4 color0;\n"
" float4 color1;\n");

WriteSampleColor(code, "rgb", "texSample", 0, api_type, params);
WriteColorToIntensity(code, "texSample", "color0.b");

WriteSampleColor(code, "rgb", "texSample", 1, api_type, params);
WriteColorToIntensity(code, "texSample", "color1.b");

WriteSampleColor(code, "rgb", "texSample", 2, api_type, params);
WriteColorToIntensity(code, "texSample", "color0.g");

WriteSampleColor(code, "rgb", "texSample", 3, api_type, params);
WriteColorToIntensity(code, "texSample", "color1.g");

WriteSampleColor(code, "rgb", "texSample", 4, api_type, params);
WriteColorToIntensity(code, "texSample", "color0.r");

WriteSampleColor(code, "rgb", "texSample", 5, api_type, params);
WriteColorToIntensity(code, "texSample", "color1.r");

WriteSampleColor(code, "rgb", "texSample", 6, api_type, params);
WriteColorToIntensity(code, "texSample", "color0.a");

WriteSampleColor(code, "rgb", "texSample", 7, api_type, params);
WriteColorToIntensity(code, "texSample", "color1.a");

code.Write(" color0.rgba += IntensityConst.aaaa;\n"
" color1.rgba += IntensityConst.aaaa;\n");

WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1");

code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code);
}

static void WriteIA8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{
WriteSwizzler(code, params, EFBCopyFormat::RA8, api_type);
code.Write(" float4 texSample;\n");

WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
code.Write(" ocol0.b = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "ocol0.g");

WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.Write(" ocol0.r = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "ocol0.a");

code.Write(" ocol0.ga += IntensityConst.aa;\n");

WriteEncoderEnd(code);
}

static void WriteIA4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{
WriteSwizzler(code, params, EFBCopyFormat::RA4, api_type);
code.Write(" float4 texSample;\n"
" float4 color0;\n"
" float4 color1;\n");

WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
code.Write(" color0.b = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.b");

WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.Write(" color0.g = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.g");

WriteSampleColor(code, "rgba", "texSample", 2, api_type, params);
code.Write(" color0.r = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.r");

WriteSampleColor(code, "rgba", "texSample", 3, api_type, params);
code.Write(" color0.a = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.a");

code.Write(" color1.rgba += IntensityConst.aaaa;\n");

WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1");

code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code);
}

static void WriteRGB565Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
Expand Down Expand Up @@ -763,10 +651,7 @@ static void WriteXFBEncoder(ShaderCode& code, APIType api_type, const EFBCopyPar
WriteSampleColor(code, "rgb", "color1", 1, api_type, params);

// Convert to YUV.
code.Write(" const float3 y_const = float3(0.257, 0.504, 0.098);\n"
" const float3 u_const = float3(-0.148, -0.291, 0.439);\n"
" const float3 v_const = float3(0.439, -0.368, -0.071);\n"
" float3 average = (color0 + color1) * 0.5;\n"
code.Write(" float3 average = (color0 + color1) * 0.5;\n"
" ocol0.b = dot(color0, y_const) + 0.0625;\n"
" ocol0.g = dot(average, u_const) + 0.5;\n"
" ocol0.r = dot(color1, y_const) + 0.0625;\n"
Expand All @@ -779,25 +664,19 @@ std::string GenerateEncodingShader(const EFBCopyParams& params, APIType api_type
{
ShaderCode code;

if (params.yuv || params.copy_format == EFBCopyFormat::XFB)
WriteColorToIntensity(code);

switch (params.copy_format)
{
case EFBCopyFormat::R4:
if (params.yuv)
WriteI4Encoder(code, api_type, params);
else
WriteC4Encoder(code, "r", api_type, params);
WriteC4Encoder(code, "r", api_type, params);
break;
case EFBCopyFormat::RA4:
if (params.yuv)
WriteIA4Encoder(code, api_type, params);
else
WriteCC4Encoder(code, "ar", api_type, params);
WriteCC4Encoder(code, "ar", api_type, params);
break;
case EFBCopyFormat::RA8:
if (params.yuv)
WriteIA8Encoder(code, api_type, params);
else
WriteCC8Encoder(code, "ar", api_type, params);
WriteCC8Encoder(code, "ar", api_type, params);
break;
case EFBCopyFormat::RGB565:
WriteRGB565Encoder(code, api_type, params);
Expand All @@ -816,11 +695,7 @@ std::string GenerateEncodingShader(const EFBCopyParams& params, APIType api_type
break;
case EFBCopyFormat::R8_0x1:
case EFBCopyFormat::R8:
if (params.yuv)
WriteI8Encoder(code, api_type, params);
else
WriteC8Encoder(code, "r", api_type, params);
break;
WriteC8Encoder(code, "r", api_type, params);
case EFBCopyFormat::G8:
if (params.depth)
WriteZ8Encoder(code, "256.0", api_type, params); // Z8M
Expand Down Expand Up @@ -1355,6 +1230,7 @@ static const std::map<TextureFormat, DecodingShaderInfo> s_decoding_shader_info{

// We do the inverse BT.601 conversion for YCbCr to RGB
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
// TODO: Increase precision here
{TextureFormat::XFB,
{TEXEL_BUFFER_FORMAT_RGBA8_UINT, 0, 8, 8, false,
R"(
Expand Down
45 changes: 14 additions & 31 deletions Source/Core/VideoCommon/TextureConverterShaderGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,39 +233,21 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
break;
}
}
else if (uid_data->is_intensity)
else
{
const bool has_four_bits =
(uid_data->dst_format == EFBCopyFormat::R4 || uid_data->dst_format == EFBCopyFormat::RA4);
const bool has_alpha =
(uid_data->dst_format == EFBCopyFormat::RA4 || uid_data->dst_format == EFBCopyFormat::RA8);

if (has_four_bits)
out.Write(" texcol_raw = texcol_raw & 0xF0u;\n");
out.Write(" float4 texcol = float4(texcol_raw) / 240.0;\n");

switch (uid_data->dst_format)
if (uid_data->is_intensity)
{
case EFBCopyFormat::R4: // I4
case EFBCopyFormat::R8_0x1: // I8
case EFBCopyFormat::R8: // I8
case EFBCopyFormat::RA4: // IA4
case EFBCopyFormat::RA8: // IA8
// TODO - verify these coefficients
out.Write(" const float3 coefficients = float3(0.257, 0.504, 0.098);\n"
" float intensity = dot(texcol.rgb, coefficients) + 16.0 / 255.0;\n"
" ocol0 = float4(intensity, intensity, intensity, {});\n",
has_alpha ? "texcol.a" : "intensity");
break;

default:
ERROR_LOG_FMT(VIDEO, "Unknown copy intensity format: {}", uid_data->dst_format);
out.Write(" ocol0 = texcol;\n");
break;
out.Write(
" // Intensity/YUV format conversion\n"
" const float3 y_const = float3(0.2578125, 0.50390625, 0.09765625);\n"
" const float3 u_const = float3(-0.1484375, -0.2890625, 0.4375);\n"
" const float3 v_const = float3(0.4375, -0.3671875, -0.0703125);\n"
" // Add .00001 to ensure that round rounds up\n"
" texcol_raw.rgb = uint3(round(float3(dot(y_const, texcol_raw.rgb) + 16.00001,\n"
" dot(u_const, texcol_raw.rgb) + 128.00001,\n"
" dot(v_const, texcol_raw.rgb) + 128.00001)));\n");
}
}
else
{

switch (uid_data->dst_format)
{
case EFBCopyFormat::R4: // R4
Expand Down Expand Up @@ -330,7 +312,8 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
break;

default:
ERROR_LOG_FMT(VIDEO, "Unknown copy color format: {}", uid_data->dst_format);
ERROR_LOG_FMT(VIDEO, "Unknown copy/intensity color format: {} {}", uid_data->dst_format,
uid_data->is_intensity);
out.Write(" ocol0 = float4(texcol_raw.rgba) / 255.0;\n");
break;
}
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/VideoCommon/TextureDecoder_Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth

// We do the inverse BT.601 conversion for YCbCr to RGB
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
// TODO: Increase precision
u8 R = std::clamp(int(1.164f * Y + 1.596f * V), 0, 255);
u8 G = std::clamp(int(1.164f * Y - 0.392f * U - 0.813f * V), 0, 255);
u8 B = std::clamp(int(1.164f * Y + 2.017f * U), 0, 255);
Expand Down Expand Up @@ -772,6 +773,7 @@ void TexDecoder_DecodeXFB(u8* dst, const u8* src, u32 width, u32 height, u32 str

// We do the inverse BT.601 conversion for YCbCr to RGB
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
// TODO: increase precision
u8 R1 = static_cast<u8>(std::clamp(int(1.164f * Y1 + 1.596f * V), 0, 255));
u8 G1 = static_cast<u8>(std::clamp(int(1.164f * Y1 - 0.392f * U - 0.813f * V), 0, 255));
u8 B1 = static_cast<u8>(std::clamp(int(1.164f * Y1 + 2.017f * U), 0, 255));
Expand Down

0 comments on commit be26203

Please sign in to comment.