@@ -57,23 +57,30 @@ struct TextureAndTLUTFormat
struct EFBCopyParams
{
EFBCopyParams(PixelFormat efb_format_, EFBCopyFormat copy_format_, bool depth_, bool yuv_,
bool copy_filter_)
bool all_copy_filter_coefs_needed_, bool copy_filter_can_overflow_,
bool apply_gamma_)
: efb_format(efb_format_), copy_format(copy_format_), depth(depth_), yuv(yuv_),
copy_filter(copy_filter_)
all_copy_filter_coefs_needed(all_copy_filter_coefs_needed_),
copy_filter_can_overflow(copy_filter_can_overflow_), apply_gamma(apply_gamma_)
{
}

bool operator<(const EFBCopyParams& rhs) const
{
return std::tie(efb_format, copy_format, depth, yuv, copy_filter) <
std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv, rhs.copy_filter);
return std::tie(efb_format, copy_format, depth, yuv, all_copy_filter_coefs_needed,
copy_filter_can_overflow,
apply_gamma) < std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv,
rhs.all_copy_filter_coefs_needed,
rhs.copy_filter_can_overflow, rhs.apply_gamma);
}

PixelFormat efb_format;
EFBCopyFormat copy_format;
bool depth;
bool yuv;
bool copy_filter;
bool all_copy_filter_coefs_needed;
bool copy_filter_can_overflow;
bool apply_gamma;
};

template <>
@@ -89,19 +96,13 @@ struct fmt::formatter<EFBCopyParams>
else
copy_format = fmt::to_string(uid.copy_format);
return fmt::format_to(ctx.out(),
"format: {}, copy format: {}, depth: {}, yuv: {}, copy filter: {}",
uid.efb_format, copy_format, uid.depth, uid.yuv, uid.copy_filter);
"format: {}, copy format: {}, depth: {}, yuv: {}, apply_gamma: {}, "
"all_copy_filter_coefs_needed: {}, copy_filter_can_overflow: {}",
uid.efb_format, copy_format, uid.depth, uid.yuv, uid.apply_gamma,
uid.all_copy_filter_coefs_needed, uid.copy_filter_can_overflow);
}
};

// Reduced version of the full coefficient array, with a single value for each row.
struct EFBCopyFilterCoefficients
{
float upper;
float middle;
float lower;
};

class TextureCacheBase
{
private:
@@ -267,8 +268,8 @@ class TextureCacheBase
// Save States
void DoState(PointerWrap& p);

// Returns false if the top/bottom row coefficients are zero.
static bool NeedsCopyFilterInShader(const EFBCopyFilterCoefficients& coefficients);
static bool AllCopyFilterCoefsNeeded(const std::array<u32, 3>& coefficients);
static bool CopyFilterCanOverflow(const std::array<u32, 3>& coefficients);

protected:
// Decodes the specified data to the GPU texture specified by entry.
@@ -285,12 +286,12 @@ class TextureCacheBase
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const MathUtil::Rectangle<int>& src_rect, bool scale_by_half,
bool linear_filter, float y_scale, float gamma, bool clamp_top,
bool clamp_bottom, const EFBCopyFilterCoefficients& filter_coefficients);
bool clamp_bottom, const std::array<u32, 3>& filter_coefficients);
virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const MathUtil::Rectangle<int>& src_rect, bool scale_by_half,
bool linear_filter, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const EFBCopyFilterCoefficients& filter_coefficients);
const std::array<u32, 3>& filter_coefficients);

alignas(16) u8* temp = nullptr;
size_t temp_size = 0;
@@ -338,9 +339,9 @@ class TextureCacheBase
void UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_row, u32 num_blocks_y);

// Precomputing the coefficients for the previous, current, and next lines for the copy filter.
static EFBCopyFilterCoefficients
static std::array<u32, 3>
GetRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients);
static EFBCopyFilterCoefficients
static std::array<u32, 3>
GetVRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients);

// Flushes a pending EFB copy to RAM from the host to the guest RAM.

Large diffs are not rendered by default.

@@ -6,13 +6,15 @@
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"

namespace TextureConversionShaderGen
{
TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_intensity,
bool scale_by_half, bool copy_filter)
bool scale_by_half, float gamma_rcp,
const std::array<u32, 3>& filter_coefficients)
{
TCShaderUid out;

@@ -22,7 +24,11 @@ TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_i
uid_data->is_depth_copy = is_depth_copy;
uid_data->is_intensity = is_intensity;
uid_data->scale_by_half = scale_by_half;
uid_data->copy_filter = copy_filter;
uid_data->all_copy_filter_coefs_needed =
TextureCacheBase::AllCopyFilterCoefsNeeded(filter_coefficients);
uid_data->copy_filter_can_overflow = TextureCacheBase::CopyFilterCanOverflow(filter_coefficients);
// If the gamma is needed, then include that too.
uid_data->apply_gamma = gamma_rcp != 1.0f;

return out;
}
@@ -31,7 +37,7 @@ static void WriteHeader(APIType api_type, ShaderCode& out)
{
out.Write("UBO_BINDING(std140, 1) uniform PSBlock {{\n"
" float2 src_offset, src_size;\n"
" float3 filter_coefficients;\n"
" uint3 filter_coefficients;\n"
" float gamma_rcp;\n"
" float2 clamp_tb;\n"
" float pixel_height;\n"
@@ -78,11 +84,25 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
WriteHeader(api_type, out);

out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
out.Write("float4 SampleEFB(float3 uv, float y_offset) {{\n"
" return texture(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), "
"clamp_tb.x, clamp_tb.y), {}));\n"
"}}\n",
out.Write("uint4 SampleEFB(float3 uv, float y_offset) {{\n"
" float4 tex_sample = texture(samp0, float3(uv.x, clamp(uv.y + (y_offset * "
"pixel_height), clamp_tb.x, clamp_tb.y), {}));\n",
mono_depth ? "0.0" : "uv.z");
if (uid_data->is_depth_copy)
{
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
out.Write(" tex_sample.x = 1.0 - tex_sample.x;\n");

out.Write(" uint depth = uint(tex_sample.x * 16777216.0);\n"
" return uint4((depth >> 16) & 255u, (depth >> 8) & 255u, depth & 255u, 255u);\n"
"}}\n");
}
else
{
out.Write(" return uint4(tex_sample * 255.0);\n"
"}}\n");
}

if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{
out.Write("VARYING_LOCATION(0) in VertexData {{\n"
@@ -93,201 +113,125 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
{
out.Write("VARYING_LOCATION(0) in vec3 v_tex0;\n");
}

out.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n"
"void main()\n{{\n");

// The copy filter applies to both color and depth copies. This has been verified on hardware.
// The filter is only applied to the RGB channels, the alpha channel is left intact.
if (uid_data->copy_filter)
if (uid_data->all_copy_filter_coefs_needed)
{
out.Write(" float4 prev_row = SampleEFB(v_tex0, -1.0f);\n"
" float4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" float4 next_row = SampleEFB(v_tex0, 1.0f);\n"
" float4 texcol = float4(min(prev_row.rgb * filter_coefficients[0] +\n"
" current_row.rgb * filter_coefficients[1] +\n"
" next_row.rgb * filter_coefficients[2], \n"
" float3(1, 1, 1)), current_row.a);\n");
out.Write(" uint4 prev_row = SampleEFB(v_tex0, -1.0f);\n"
" uint4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" uint4 next_row = SampleEFB(v_tex0, 1.0f);\n"
" uint3 combined_rows = prev_row.rgb * filter_coefficients[0] +\n"
" current_row.rgb * filter_coefficients[1] +\n"
" next_row.rgb * filter_coefficients[2];\n");
}
else
{
out.Write(
" float4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" float4 texcol = float4(min(current_row.rgb * filter_coefficients[1], float3(1, 1, 1)),\n"
" current_row.a);\n");
out.Write(" uint4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" uint3 combined_rows = current_row.rgb * filter_coefficients[1];\n");
}

if (uid_data->is_depth_copy)
out.Write(" // Shift right by 6 to divide by 64, as filter coefficients\n"
" // that sum to 64 result in no change in brightness\n"
" uint4 texcol_raw = uint4(combined_rows.rgb >> 6, {});\n",
uid_data->efb_has_alpha ? "current_row.a" : "255");

if (uid_data->copy_filter_can_overflow)
out.Write(" texcol_raw &= 0x1ffu;\n");
// Note that overflow occurs when the sum of values is >= 128, but this max situation can be hit
// on >= 64, so we always include it.
out.Write(" texcol_raw = min(texcol_raw, uint4(255, 255, 255, 255));\n");

if (uid_data->apply_gamma)
{
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
out.Write("texcol.x = 1.0 - texcol.x;\n");

out.Write(" int depth = int(texcol.x * 16777216.0);\n"

// Convert to Z24 format
" int4 workspace;\n"
" workspace.r = (depth >> 16) & 255;\n"
" workspace.g = (depth >> 8) & 255;\n"
" workspace.b = depth & 255;\n"

// Convert to Z4 format
" workspace.a = (depth >> 16) & 0xF0;\n"

// Normalize components to [0.0..1.0]
" texcol = float4(workspace) / 255.0;\n");
switch (uid_data->dst_format)
{
case EFBCopyFormat::R4: // Z4
out.Write(" ocol0 = texcol.aaaa;\n");
break;

case EFBCopyFormat::R8_0x1: // Z8
case EFBCopyFormat::R8: // Z8H
out.Write(" ocol0 = texcol.rrrr;\n");
break;

case EFBCopyFormat::RA8: // Z16
out.Write(" ocol0 = texcol.gggr;\n");
break;

case EFBCopyFormat::RG8: // Z16 (reverse order)
out.Write(" ocol0 = texcol.rrrg;\n");
break;

case EFBCopyFormat::RGBA8: // Z24X8
out.Write(" ocol0 = float4(texcol.rgb, 1.0);\n");
break;

case EFBCopyFormat::G8: // Z8M
out.Write(" ocol0 = texcol.gggg;\n");
break;

case EFBCopyFormat::B8: // Z8L
out.Write(" ocol0 = texcol.bbbb;\n");
break;

case EFBCopyFormat::GB8: // Z16L - copy lower 16 depth bits
// expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits
// stored as alpha)
// Used e.g. in Zelda: Skyward Sword
out.Write(" ocol0 = texcol.gggb;\n");
break;

default:
ERROR_LOG_FMT(VIDEO, "Unknown copy zbuf format: {:#X}",
static_cast<int>(uid_data->dst_format));
out.Write(" ocol0 = float4(texcol.bgr, 0.0);\n");
break;
}
out.Write(" texcol_raw = uint4(round(pow(abs(float4(texcol_raw) / 255.0),\n"
" float4(gamma_rcp, gamma_rcp, gamma_rcp, 1.0)) * 255.0));\n");
}
else if (uid_data->is_intensity)

if (uid_data->is_intensity)
{
if (!uid_data->efb_has_alpha)
out.Write(" texcol.a = 1.0;\n");

bool has_four_bits =
(uid_data->dst_format == EFBCopyFormat::R4 || uid_data->dst_format == EFBCopyFormat::RA4);
bool has_alpha =
(uid_data->dst_format == EFBCopyFormat::RA4 || uid_data->dst_format == EFBCopyFormat::RA8);

switch (uid_data->dst_format)
{
case EFBCopyFormat::R4: // I4
case EFBCopyFormat::R8_0x1: // I8
case EFBCopyFormat::R8: // I8
case EFBCopyFormat::RA4: // IA4
case EFBCopyFormat::RA8: // IA8
if (has_four_bits)
out.Write(" texcol = float4(int4(texcol * 255.0) & 0xF0) * (1.0 / 240.0);\n");

// 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: {:#X}",
static_cast<int>(uid_data->dst_format));
out.Write(" ocol0 = texcol;\n");
break;
}
out.Write(" // Intensity/YUV format conversion constants determined by hardware testing\n"
" const float4 y_const = float4( 66, 129, 25, 16);\n"
" const float4 u_const = float4(-38, -74, 112, 128);\n"
" const float4 v_const = float4(112, -94, -18, 128);\n"
" // Intensity/YUV format conversion\n"
" texcol_raw.rgb = uint3(dot(y_const, float4(texcol_raw.rgb, 256)),\n"
" dot(u_const, float4(texcol_raw.rgb, 256)),\n"
" dot(v_const, float4(texcol_raw.rgb, 256)));\n"
" // Divide by 256 and round .5 and higher up\n"
" texcol_raw.rgb = (texcol_raw.rgb >> 8) + ((texcol_raw.rgb >> 7) & 1);\n");
}
else

switch (uid_data->dst_format)
{
if (!uid_data->efb_has_alpha)
out.Write(" texcol.a = 1.0;\n");

switch (uid_data->dst_format)
{
case EFBCopyFormat::R4: // R4
out.Write(" float red = float(int(texcol.r * 255.0) & 0xF0) * (1.0 / 240.0);\n"
" ocol0 = float4(red, red, red, red);\n");
break;

case EFBCopyFormat::R8_0x1: // R8
case EFBCopyFormat::R8: // R8
out.Write(" ocol0 = texcol.rrrr;\n");
break;

case EFBCopyFormat::RA4: // RA4
out.Write(" float2 red_alpha = float2(int2(texcol.ra * 255.0) & 0xF0) * (1.0 / 240.0);\n"
" ocol0 = red_alpha.rrrg;\n");
break;

case EFBCopyFormat::RA8: // RA8
out.Write(" ocol0 = texcol.rrra;\n");
break;

case EFBCopyFormat::A8: // A8
out.Write(" ocol0 = texcol.aaaa;\n");
break;

case EFBCopyFormat::G8: // G8
out.Write(" ocol0 = texcol.gggg;\n");
break;

case EFBCopyFormat::B8: // B8
out.Write(" ocol0 = texcol.bbbb;\n");
break;

case EFBCopyFormat::RG8: // RG8
out.Write(" ocol0 = texcol.rrrg;\n");
break;

case EFBCopyFormat::GB8: // GB8
out.Write(" ocol0 = texcol.gggb;\n");
break;

case EFBCopyFormat::RGB565: // RGB565
out.Write(" float2 red_blue = float2(int2(texcol.rb * 255.0) & 0xF8) * (1.0 / 248.0);\n"
" float green = float(int(texcol.g * 255.0) & 0xFC) * (1.0 / 252.0);\n"
" ocol0 = float4(red_blue.r, green, red_blue.g, 1.0);\n");
break;

case EFBCopyFormat::RGB5A3: // RGB5A3
// TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection
// will need to be implemented once we move away from floats.
out.Write(" float3 color = float3(int3(texcol.rgb * 255.0) & 0xF8) * (1.0 / 248.0);\n"
" float alpha = float(int(texcol.a * 255.0) & 0xE0) * (1.0 / 224.0);\n"
" ocol0 = float4(color, alpha);\n");
break;

case EFBCopyFormat::RGBA8: // RGBA8
out.Write(" ocol0 = texcol;\n");
break;

case EFBCopyFormat::XFB:
out.Write(" ocol0 = float4(pow(abs(texcol.rgb), float3(gamma_rcp, gamma_rcp, gamma_rcp)), "
"1.0f);\n");
break;

default:
ERROR_LOG_FMT(VIDEO, "Unknown copy color format: {:#X}",
static_cast<int>(uid_data->dst_format));
out.Write(" ocol0 = texcol;\n");
break;
}
case EFBCopyFormat::R4: // R4
out.Write(" float red = float(texcol_raw.r & 0xF0u) / 240.0;\n"
" ocol0 = float4(red, red, red, red);\n");
break;

case EFBCopyFormat::R8_0x1: // R8
case EFBCopyFormat::R8: // R8
out.Write(" ocol0 = float4(texcol_raw).rrrr / 255.0;\n");
break;

case EFBCopyFormat::RA4: // RA4
out.Write(" float2 red_alpha = float2(texcol_raw.ra & 0xF0u) / 240.0;\n"
" ocol0 = red_alpha.rrrg;\n");
break;

case EFBCopyFormat::RA8: // RA8
out.Write(" ocol0 = float4(texcol_raw).rrra / 255.0;\n");
break;

case EFBCopyFormat::A8: // A8
out.Write(" ocol0 = float4(texcol_raw).aaaa / 255.0;\n");
break;

case EFBCopyFormat::G8: // G8
out.Write(" ocol0 = float4(texcol_raw).gggg / 255.0;\n");
break;

case EFBCopyFormat::B8: // B8
out.Write(" ocol0 = float4(texcol_raw).bbbb / 255.0;\n");
break;

case EFBCopyFormat::RG8: // RG8
out.Write(" ocol0 = float4(texcol_raw).rrrg / 255.0;\n");
break;

case EFBCopyFormat::GB8: // GB8
out.Write(" ocol0 = float4(texcol_raw).gggb / 255.0;\n");
break;

case EFBCopyFormat::RGB565: // RGB565
out.Write(" float2 red_blue = float2(texcol_raw.rb & 0xF8u) / 248.0;\n"
" float green = float(texcol_raw.g & 0xFCu) / 252.0;\n"
" ocol0 = float4(red_blue.r, green, red_blue.g, 1.0);\n");
break;

case EFBCopyFormat::RGB5A3: // RGB5A3
// TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection
// will need to be implemented once we move away from floats.
out.Write(" float3 color = float3(texcol_raw.rgb & 0xF8u) / 248.0;\n"
" float alpha = float(texcol_raw.a & 0xE0u) / 224.0;\n"
" ocol0 = float4(color, alpha);\n");
break;

case EFBCopyFormat::RGBA8: // RGBA8
out.Write(" ocol0 = float4(texcol_raw.rgba) / 255.0;\n");
break;

case EFBCopyFormat::XFB:
out.Write(" ocol0 = float4(float3(texcol_raw.rgb) / 255.0, 1.0);\n");
break;

default:
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;
}

out.Write("}}\n");
@@ -25,7 +25,9 @@ struct UidData
u32 is_depth_copy : 1;
u32 is_intensity : 1;
u32 scale_by_half : 1;
u32 copy_filter : 1;
u32 all_copy_filter_coefs_needed : 1;
u32 copy_filter_can_overflow : 1;
u32 apply_gamma : 1;
};
#pragma pack()

@@ -35,7 +37,8 @@ ShaderCode GenerateVertexShader(APIType api_type);
ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data);

TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_intensity,
bool scale_by_half, bool copy_filter);
bool scale_by_half, float gamma_rcp,
const std::array<u32, 3>& filter_coefficients);

} // namespace TextureConversionShaderGen

@@ -53,8 +56,10 @@ struct fmt::formatter<TextureConversionShaderGen::UidData>
dst_format = fmt::to_string(uid.dst_format);
return fmt::format_to(ctx.out(),
"dst_format: {}, efb_has_alpha: {}, is_depth_copy: {}, is_intensity: {}, "
"scale_by_half: {}, copy_filter: {}",
"scale_by_half: {}, all_copy_filter_coefs_needed: {}, "
"copy_filter_can_overflow: {}, apply_gamma: {}",
dst_format, uid.efb_has_alpha, uid.is_depth_copy, uid.is_intensity,
uid.scale_by_half, uid.copy_filter);
uid.scale_by_half, uid.all_copy_filter_coefs_needed,
uid.copy_filter_can_overflow, uid.apply_gamma);
}
};
@@ -50,8 +50,7 @@ int TexDecoder_GetTexelSizeInNibbles(TextureFormat format)
case TextureFormat::XFB:
return 4;
default:
PanicAlertFmt("Invalid Texture Format ({:#X})! (GetTexelSizeInNibbles)",
static_cast<int>(format));
PanicAlertFmt("Invalid Texture Format {}! (GetTexelSizeInNibbles)", format);
return 1;
}
}
@@ -90,8 +89,7 @@ int TexDecoder_GetBlockWidthInTexels(TextureFormat format)
case TextureFormat::XFB:
return 16;
default:
PanicAlertFmt("Invalid Texture Format ({:#X})! (GetBlockWidthInTexels)",
static_cast<int>(format));
PanicAlertFmt("Invalid Texture Format {}! (GetBlockWidthInTexels)", format);
return 8;
}
}
@@ -125,8 +123,7 @@ int TexDecoder_GetBlockHeightInTexels(TextureFormat format)
case TextureFormat::XFB:
return 1;
default:
PanicAlertFmt("Invalid Texture Format ({:#X})! (GetBlockHeightInTexels)",
static_cast<int>(format));
PanicAlertFmt("Invalid Texture Format {}! (GetBlockHeightInTexels)", format);
return 4;
}
}
@@ -160,8 +157,7 @@ int TexDecoder_GetEFBCopyBlockWidthInTexels(EFBCopyFormat format)
case EFBCopyFormat::XFB:
return 16;
default:
PanicAlertFmt("Invalid EFB Copy Format ({:#X})! (GetEFBCopyBlockWidthInTexels)",
static_cast<int>(format));
PanicAlertFmt("Invalid EFB Copy Format {}! (GetEFBCopyBlockWidthInTexels)", format);
return 8;
}
}
@@ -195,8 +191,7 @@ int TexDecoder_GetEFBCopyBlockHeightInTexels(EFBCopyFormat format)
case EFBCopyFormat::XFB:
return 1;
default:
PanicAlertFmt("Invalid EFB Copy Format ({:#X})! (GetEFBCopyBlockHeightInTexels)",
static_cast<int>(format));
PanicAlertFmt("Invalid EFB Copy Format {}! (GetEFBCopyBlockHeightInTexels)", format);
return 4;
}
}
@@ -247,8 +242,7 @@ TextureFormat TexDecoder_GetEFBCopyBaseFormat(EFBCopyFormat format)
case EFBCopyFormat::XFB:
return TextureFormat::XFB;
default:
PanicAlertFmt("Invalid EFB Copy Format ({:#X})! (GetEFBCopyBaseFormat)",
static_cast<int>(format));
PanicAlertFmt("Invalid EFB Copy Format {}! (GetEFBCopyBaseFormat)", format);
return static_cast<TextureFormat>(format);
}
}
@@ -259,77 +253,6 @@ void TexDecoder_SetTexFmtOverlayOptions(bool enable, bool center)
TexFmt_Overlay_Center = center;
}

static const char* texfmt[] = {
// pixel
"I4",
"I8",
"IA4",
"IA8",
"RGB565",
"RGB5A3",
"RGBA8",
"0x07",
"C4",
"C8",
"C14X2",
"0x0B",
"0x0C",
"0x0D",
"CMPR",
"0x0F",
// Z-buffer
"0x10",
"Z8",
"0x12",
"Z16",
"0x14",
"0x15",
"Z24X8",
"0x17",
"0x18",
"0x19",
"0x1A",
"0x1B",
"0x1C",
"0x1D",
"0x1E",
"0x1F",
// pixel + copy
"CR4",
"0x21",
"CRA4",
"CRA8",
"0x24",
"0x25",
"CYUVA8",
"CA8",
"CR8",
"CG8",
"CB8",
"CRG8",
"CGB8",
"0x2D",
"0x2E",
"XFB",
// Z + copy
"CZ4",
"0x31",
"0x32",
"0x33",
"0x34",
"0x35",
"0x36",
"0x37",
"0x38",
"CZ8M",
"CZ8L",
"0x3B",
"CZ16L",
"0x3D",
"0x3E",
"0x3F",
};

static void TexDecoder_DrawOverlay(u8* dst, int width, int height, TextureFormat texformat)
{
int w = std::min(width, 40);
@@ -344,11 +267,11 @@ static void TexDecoder_DrawOverlay(u8* dst, int width, int height, TextureFormat
yoff = 0;
}

const char* fmt = texfmt[static_cast<int>(texformat) & 15];
while (*fmt)
const auto fmt_str = fmt::to_string(texformat);
for (char ch : fmt_str)
{
int xcnt = 0;
int nchar = sfont_map[(int)*fmt];
int nchar = sfont_map[ch];

const unsigned char* ptr = sfont_raw[nchar]; // each char is up to 9x10

@@ -369,7 +292,6 @@ static void TexDecoder_DrawOverlay(u8* dst, int width, int height, TextureFormat
ptr += 9;
}
xoff += xcnt;
fmt++;
}
}

@@ -707,6 +629,8 @@ 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: Use more precise numbers for this conversion (although on real hardware, the XFB isn't
// in a real texture format, so does this conversion actually ever happen?)
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);
@@ -772,6 +696,8 @@ 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: Use more precise numbers for this conversion (although on real hardware, the XFB
// isn't in a real texture format, so does this conversion actually ever happen?)
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));
@@ -1495,8 +1495,7 @@ void _TexDecoder_DecodeImpl(u32* dst, const u8* src, int width, int height, Text
break;

default:
PanicAlertFmt("Invalid Texture Format ({:#X})! (_TexDecoder_DecodeImpl)",
static_cast<int>(texformat));
PanicAlertFmt("Invalid Texture Format {}! (_TexDecoder_DecodeImpl)", texformat);
break;
}
}
@@ -4,7 +4,7 @@
static const unsigned char sfont_map[] = {
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,63,64,10,10,10,10,10,10,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,10,10,10,10,
10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,
26,27,28,29,30,31,32,33,34,35,36,10,10,10,10,10,
@@ -713,5 +713,27 @@ static const unsigned char sfont_raw[][9*10] = {
0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78,
},{
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
},{
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0x00, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0x00, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
0xff, 0xff, 0xff, 0xff, 0x78, 0x78, 0x78, 0x78, 0x78,
},
};