GS/HW: Fix alpha min/max crashing on BC1 textures

This commit is contained in:
Stenzek 2023-07-22 16:49:06 +10:00 committed by Connor McLaughlin
parent 1e32fcaebe
commit 40f7ec3ca1
1 changed files with 65 additions and 44 deletions

View File

@ -117,6 +117,8 @@ namespace GSTextureReplacements
static std::optional<TextureName> ParseReplacementName(const std::string& filename);
static std::string GetGameTextureDirectory();
static std::string GetDumpFilename(const TextureName& name, u32 level);
template <GSTexture::Format format>
std::pair<u8, u8> GetBCAlphaMinMax(ReplacementTexture& rtex);
static void SetReplacementTextureAlphaMinMax(ReplacementTexture& rtex);
static std::optional<ReplacementTexture> LoadReplacementTexture(const TextureName& name, const std::string& filename, bool only_base_image);
static void QueueAsyncReplacementTextureLoad(const TextureName& name, const std::string& filename, bool mipmap, bool cache_only);
@ -485,61 +487,80 @@ GSTexture* GSTextureReplacements::LookupReplacementTexture(const GSTextureCache:
}
}
void GSTextureReplacements::SetReplacementTextureAlphaMinMax(ReplacementTexture& rtex)
template <GSTexture::Format format>
std::pair<u8, u8> GSTextureReplacements::GetBCAlphaMinMax(ReplacementTexture& rtex)
{
if (rtex.format >= GSTexture::Format::BC1 && rtex.format <= GSTexture::Format::BC7)
constexpr u32 BC_BLOCK_SIZE = 4;
constexpr u32 BC_BLOCK_BYTES = (format == GSTexture::Format::BC1) ? 8 : 16;
const u32 blocks_wide = (rtex.width + (BC_BLOCK_SIZE - 1)) / BC_BLOCK_SIZE;
const u32 blocks_high = (rtex.height + (BC_BLOCK_SIZE - 1)) / BC_BLOCK_SIZE;
GSVector4i minc = GSVector4i::xffffffff();
GSVector4i maxc = GSVector4i::zero();
for (u32 y = 0; y < blocks_high; y++)
{
constexpr u32 BC_BLOCK_SIZE = 4;
constexpr u32 BC_BLOCK_BYTES = 16;
const u8* block_in = rtex.data.data() + y * rtex.pitch;
alignas(16) u8 block_pixels_out[BC_BLOCK_SIZE * BC_BLOCK_SIZE * sizeof(u32)];
const u32 blocks_wide = (rtex.width + (BC_BLOCK_SIZE - 1)) / BC_BLOCK_SIZE;
const u32 blocks_high = (rtex.height + (BC_BLOCK_SIZE - 1)) / BC_BLOCK_SIZE;
GSVector4i minc = GSVector4i::xffffffff();
GSVector4i maxc = GSVector4i::zero();
for (u32 y = 0; y < blocks_high; y++)
for (u32 x = 0; x < blocks_wide; x++, block_in += BC_BLOCK_BYTES)
{
const u8* block_in = rtex.data.data() + y * rtex.pitch;
alignas(16) u8 block_pixels_out[BC_BLOCK_SIZE * BC_BLOCK_SIZE * sizeof(u32)];
for (u32 x = 0; x < blocks_wide; x++, block_in += BC_BLOCK_BYTES)
switch (format)
{
switch (rtex.format)
{
case GSTexture::Format::BC1:
DecompressBlockBC1(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC2:
DecompressBlockBC2(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC3:
DecompressBlockBC3(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC1:
DecompressBlockBC1(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC2:
DecompressBlockBC2(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC3:
DecompressBlockBC3(0, 0, sizeof(u32) * BC_BLOCK_SIZE, block_in, block_pixels_out);
break;
case GSTexture::Format::BC7:
bc7decomp::unpack_bc7(block_in, reinterpret_cast<bc7decomp::color_rgba*>(block_pixels_out));
break;
}
case GSTexture::Format::BC7:
bc7decomp::unpack_bc7(block_in, reinterpret_cast<bc7decomp::color_rgba*>(block_pixels_out));
break;
}
const u8* out_ptr = block_pixels_out;
for (u32 i = 0; i < ((BC_BLOCK_SIZE * BC_BLOCK_SIZE * sizeof(u32)) / sizeof(GSVector4i)); i++)
{
const GSVector4i v = GSVector4i::load<true>(out_ptr);
out_ptr += sizeof(GSVector4i);
minc = minc.min_u32(v);
maxc = maxc.max_u32(v);
}
const u8* out_ptr = block_pixels_out;
for (u32 i = 0; i < ((BC_BLOCK_SIZE * BC_BLOCK_SIZE * sizeof(u32)) / sizeof(GSVector4i)); i++)
{
const GSVector4i v = GSVector4i::load<true>(out_ptr);
out_ptr += sizeof(GSVector4i);
minc = minc.min_u32(v);
maxc = maxc.max_u32(v);
}
}
rtex.alpha_minmax =
std::make_pair<u8, u8>(static_cast<u8>(minc.minv_u32() >> 24), static_cast<u8>(maxc.maxv_u32() >> 24));
}
else
return std::make_pair<u8, u8>(static_cast<u8>(minc.minv_u32() >> 24), static_cast<u8>(maxc.maxv_u32() >> 24));
}
void GSTextureReplacements::SetReplacementTextureAlphaMinMax(ReplacementTexture& rtex)
{
switch (rtex.format)
{
pxAssert(rtex.format == GSTexture::Format::Color);
rtex.alpha_minmax = GSGetRGBA8AlphaMinMax(rtex.data.data(), rtex.width, rtex.height, rtex.pitch);
case GSTexture::Format::BC1:
rtex.alpha_minmax = GetBCAlphaMinMax<GSTexture::Format::BC1>(rtex);
break;
case GSTexture::Format::BC2:
rtex.alpha_minmax = GetBCAlphaMinMax<GSTexture::Format::BC2>(rtex);
break;
case GSTexture::Format::BC3:
rtex.alpha_minmax = GetBCAlphaMinMax<GSTexture::Format::BC3>(rtex);
break;
case GSTexture::Format::BC7:
rtex.alpha_minmax = GetBCAlphaMinMax<GSTexture::Format::BC7>(rtex);
break;
default:
pxAssert(rtex.format == GSTexture::Format::Color);
rtex.alpha_minmax = GSGetRGBA8AlphaMinMax(rtex.data.data(), rtex.width, rtex.height, rtex.pitch);
break;
}
}