rend: yet another hash fix of vq textures

The calculated size for vq textures was half the correct value, which
affects the texture hash. Calculate the old hash and use it as fallback
for backward-compatibility.
Issue #1291
Make the texture data start address really point to max mipmap data, no
more codebook skipping.
This commit is contained in:
Flyinghead 2023-11-13 14:37:38 +01:00
parent 46eab502e4
commit 4cd90d8ebb
3 changed files with 49 additions and 48 deletions

View File

@ -338,17 +338,17 @@ static const PvrTexInfo *pvrTexInfo = opengl::pvrTexInfo;
extern const u32 VQMipPoint[11] = extern const u32 VQMipPoint[11] =
{ {
0x00000,//1 VQ_CODEBOOK_SIZE + 0x00000, // 1
0x00001,//2 VQ_CODEBOOK_SIZE + 0x00001, // 2
0x00002,//4 VQ_CODEBOOK_SIZE + 0x00002, // 4
0x00006,//8 VQ_CODEBOOK_SIZE + 0x00006, // 8
0x00016,//16 VQ_CODEBOOK_SIZE + 0x00016, // 16
0x00056,//32 VQ_CODEBOOK_SIZE + 0x00056, // 32
0x00156,//64 VQ_CODEBOOK_SIZE + 0x00156, // 64
0x00556,//128 VQ_CODEBOOK_SIZE + 0x00556, // 128
0x01556,//256 VQ_CODEBOOK_SIZE + 0x01556, // 256
0x05556,//512 VQ_CODEBOOK_SIZE + 0x05556, // 512
0x15556//1024 VQ_CODEBOOK_SIZE + 0x15556 // 1024
}; };
extern const u32 OtherMipPoint[11] = extern const u32 OtherMipPoint[11] =
{ {
@ -410,22 +410,22 @@ bool BaseTextureCacheData::NeedsUpdate() {
void BaseTextureCacheData::protectVRam() void BaseTextureCacheData::protectVRam()
{ {
u32 end = sa + size - 1; u32 end = mmStartAddress + size - 1;
if (end >= VRAM_SIZE) if (end >= VRAM_SIZE)
{ {
WARN_LOG(PVR, "protectVRam: end >= VRAM_SIZE. Tried to lock area out of vram"); WARN_LOG(PVR, "protectVRam: end >= VRAM_SIZE. Tried to lock area out of vram");
end = VRAM_SIZE - 1; end = VRAM_SIZE - 1;
} }
if (sa_tex > end) if (startAddress > end)
{ {
WARN_LOG(PVR, "vramlock_Lock: sa_tex > end. Tried to lock negative block"); WARN_LOG(PVR, "vramlock_Lock: startAddress > end. Tried to lock negative block");
return; return;
} }
vram_block *block = new vram_block(); vram_block *block = new vram_block();
block->end = end; block->end = end;
block->start = sa_tex; block->start = startAddress;
block->texture = this; block->texture = this;
{ {
@ -482,8 +482,8 @@ BaseTextureCacheData::BaseTextureCacheData(TSP tsp, TCW tcw)
//decode info from tsp/tcw into the texture struct //decode info from tsp/tcw into the texture struct
tex = &pvrTexInfo[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry tex = &pvrTexInfo[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry
sa_tex = (tcw.TexAddr << 3) & VRAM_MASK; //texture start address startAddress = (tcw.TexAddr << 3) & VRAM_MASK; // texture start address
sa = sa_tex; //data texture start address (modified for MIPs, as needed) mmStartAddress = startAddress; // data texture start address (modified for MIPs, as needed)
width = 8 << tsp.TexU; //tex width width = 8 << tsp.TexU; //tex width
height = 8 << tsp.TexV; //tex height height = 8 << tsp.TexV; //tex height
@ -534,16 +534,18 @@ BaseTextureCacheData::BaseTextureCacheData(TSP tsp, TCW tcw)
{ {
verify(tex->VQ != NULL || tex->VQ32 != NULL); verify(tex->VQ != NULL || tex->VQ32 != NULL);
if (tcw.MipMapped) if (tcw.MipMapped)
sa += VQMipPoint[tsp.TexU + 3]; mmStartAddress += VQMipPoint[tsp.TexU + 3];
else
mmStartAddress += VQ_CODEBOOK_SIZE;
texconv = tex->VQ; texconv = tex->VQ;
texconv32 = tex->VQ32; texconv32 = tex->VQ32;
size = width * height / 8 + 256 * 8; size = width * height / 4;
} }
else else
{ {
verify(tex->TW != NULL || tex->TW32 != NULL); verify(tex->TW != NULL || tex->TW32 != NULL);
if (tcw.MipMapped) if (tcw.MipMapped)
sa += OtherMipPoint[tsp.TexU + 3] * tex->bpp / 8; mmStartAddress += OtherMipPoint[tsp.TexU + 3] * tex->bpp / 8;
texconv = tex->TW; texconv = tex->TW;
texconv32 = tex->TW32; texconv32 = tex->TW32;
size = width * height * tex->bpp / 8; size = width * height * tex->bpp / 8;
@ -556,14 +558,13 @@ void BaseTextureCacheData::ComputeHash()
{ {
// Include everything but texaddr, reserved and stride. Palette textures don't have ScanOrder // Include everything but texaddr, reserved and stride. Palette textures don't have ScanOrder
const u32 tcwMask = IsPaletted() ? 0xF8000000 : 0xFC000000; const u32 tcwMask = IsPaletted() ? 0xF8000000 : 0xFC000000;
u32 hashSize = size;
if (tcw.VQ_Comp) if (tcw.VQ_Comp)
{ {
// The size for VQ textures wasn't correctly calculated. // The size for VQ textures wasn't correctly calculated.
// We use the old size to compute the hash for backward-compatibility // We use the old size to compute the hash for backward-compatibility
// with existing custom texture packs. // with existing custom texture packs.
hashSize = size - 256 * 8; int oldSize = width * height / 8;
old_vqtexture_hash = XXH32(&vram[sa], hashSize, 7); old_vqtexture_hash = XXH32(&vram[mmStartAddress - VQ_CODEBOOK_SIZE], oldSize, 7);
if (IsPaletted()) if (IsPaletted())
old_vqtexture_hash ^= palette_hash; old_vqtexture_hash ^= palette_hash;
old_texture_hash = old_vqtexture_hash; old_texture_hash = old_vqtexture_hash;
@ -572,9 +573,9 @@ void BaseTextureCacheData::ComputeHash()
XXH32_state_t *state = XXH32_createState(); XXH32_state_t *state = XXH32_createState();
XXH32_reset(state, 7); XXH32_reset(state, 7);
// hash vq codebook // hash vq codebook
XXH32_update(state, &vram[sa_tex], 256 * 8); XXH32_update(state, &vram[startAddress], VQ_CODEBOOK_SIZE);
// hash texture // hash texture
XXH32_update(state, &vram[sa + 256 * 8], hashSize); XXH32_update(state, &vram[mmStartAddress], size);
texture_hash = XXH32_digest(state); texture_hash = XXH32_digest(state);
XXH32_freeState(state); XXH32_freeState(state);
if (IsPaletted()) if (IsPaletted())
@ -584,7 +585,7 @@ void BaseTextureCacheData::ComputeHash()
else else
{ {
old_vqtexture_hash = 0; old_vqtexture_hash = 0;
texture_hash = XXH32(&vram[sa], hashSize, 7); texture_hash = XXH32(&vram[mmStartAddress], size, 7);
if (IsPaletted()) if (IsPaletted())
texture_hash ^= palette_hash; texture_hash ^= palette_hash;
old_texture_hash = texture_hash; old_texture_hash = texture_hash;
@ -630,7 +631,7 @@ bool BaseTextureCacheData::Update()
} }
if (tcw.VQ_Comp) if (tcw.VQ_Comp)
::vq_codebook = &vram[sa_tex]; // might be used if VQ tex ::vq_codebook = &vram[startAddress];
//texture conversion work //texture conversion work
u32 stride = width; u32 stride = width;
@ -643,19 +644,19 @@ bool BaseTextureCacheData::Update()
} }
const u32 original_h = height; const u32 original_h = height;
if (sa_tex > VRAM_SIZE || sa + size > VRAM_SIZE) if (startAddress > VRAM_SIZE || mmStartAddress + size > VRAM_SIZE)
{ {
height = 0; height = 0;
if (sa < VRAM_SIZE && sa + size > VRAM_SIZE && tcw.ScanOrder) if (mmStartAddress < VRAM_SIZE && mmStartAddress + size > VRAM_SIZE && tcw.ScanOrder)
{ {
// Shenmue Space Harrier mini-arcade loads a texture that goes beyond the end of VRAM // Shenmue Space Harrier mini-arcade loads a texture that goes beyond the end of VRAM
// but only uses the top portion of it // but only uses the top portion of it
height = (VRAM_SIZE - sa) * 8 / stride / tex->bpp; height = (VRAM_SIZE - mmStartAddress) * 8 / stride / tex->bpp;
size = stride * height * tex->bpp/8; size = stride * height * tex->bpp/8;
} }
if (height == 0) if (height == 0)
{ {
WARN_LOG(RENDERER, "Warning: invalid texture. Address %08X %08X size %d", sa_tex, sa, size); WARN_LOG(RENDERER, "Warning: invalid texture. Address %08X %08X size %d", startAddress, mmStartAddress, size);
dirty = 1; dirty = 1;
height = original_h; height = original_h;
unprotectVRam(); unprotectVRam();
@ -706,7 +707,7 @@ bool BaseTextureCacheData::Update()
u32 vram_addr; u32 vram_addr;
if (tcw.VQ_Comp) if (tcw.VQ_Comp)
{ {
vram_addr = sa_tex + VQMipPoint[i]; vram_addr = startAddress + VQMipPoint[i];
if (i == 0) if (i == 0)
{ {
PixelBuffer<u32> pb0; PixelBuffer<u32> pb0;
@ -717,7 +718,7 @@ bool BaseTextureCacheData::Update()
} }
} }
else else
vram_addr = sa_tex + OtherMipPoint[i] * tex->bpp / 8; vram_addr = startAddress + OtherMipPoint[i] * tex->bpp / 8;
if (tcw.PixelFmt == PixelYUV && i == 0) if (tcw.PixelFmt == PixelYUV && i == 0)
// Special case for YUV at 1x1 LoD // Special case for YUV at 1x1 LoD
pvrTexInfo[Pixel565].TW32(&pb32, &vram[vram_addr], 1, 1); pvrTexInfo[Pixel565].TW32(&pb32, &vram[vram_addr], 1, 1);
@ -729,7 +730,7 @@ bool BaseTextureCacheData::Update()
else else
{ {
pb32.init(width, height); pb32.init(width, height);
texconv32(&pb32, (u8*)&vram[sa], stride, height); texconv32(&pb32, (u8*)&vram[mmStartAddress], stride, height);
// xBRZ scaling // xBRZ scaling
if (textureUpscaling) if (textureUpscaling)
@ -757,7 +758,7 @@ bool BaseTextureCacheData::Update()
for (u32 i = 0; i <= tsp.TexU + 3u; i++) for (u32 i = 0; i <= tsp.TexU + 3u; i++)
{ {
pb8.set_mipmap(i); pb8.set_mipmap(i);
u32 vram_addr = sa_tex + OtherMipPoint[i] * tex->bpp / 8; u32 vram_addr = startAddress + OtherMipPoint[i] * tex->bpp / 8;
texconv8(&pb8, &vram[vram_addr], 1 << i, 1 << i); texconv8(&pb8, &vram[vram_addr], 1 << i, 1 << i);
} }
pb8.set_mipmap(0); pb8.set_mipmap(0);
@ -765,7 +766,7 @@ bool BaseTextureCacheData::Update()
else else
{ {
pb8.init(width, height); pb8.init(width, height);
texconv8(&pb8, &vram[sa], stride, height); texconv8(&pb8, &vram[mmStartAddress], stride, height);
} }
temp_tex_buffer = pb8.data(); temp_tex_buffer = pb8.data();
} }
@ -780,7 +781,7 @@ bool BaseTextureCacheData::Update()
u32 vram_addr; u32 vram_addr;
if (tcw.VQ_Comp) if (tcw.VQ_Comp)
{ {
vram_addr = sa_tex + VQMipPoint[i]; vram_addr = startAddress + VQMipPoint[i];
if (i == 0) if (i == 0)
{ {
PixelBuffer<u16> pb0; PixelBuffer<u16> pb0;
@ -791,7 +792,7 @@ bool BaseTextureCacheData::Update()
} }
} }
else else
vram_addr = sa_tex + OtherMipPoint[i] * tex->bpp / 8; vram_addr = startAddress + OtherMipPoint[i] * tex->bpp / 8;
texconv(&pb16, (u8*)&vram[vram_addr], 1 << i, 1 << i); texconv(&pb16, (u8*)&vram[vram_addr], 1 << i, 1 << i);
} }
pb16.set_mipmap(0); pb16.set_mipmap(0);
@ -799,7 +800,7 @@ bool BaseTextureCacheData::Update()
else else
{ {
pb16.init(width, height); pb16.init(width, height);
texconv(&pb16,(u8*)&vram[sa],stride,height); texconv(&pb16,(u8*)&vram[mmStartAddress],stride,height);
} }
temp_tex_buffer = pb16.data(); temp_tex_buffer = pb16.data();
} }

View File

@ -12,6 +12,7 @@
#include <utility> #include <utility>
extern const u8 *vq_codebook; extern const u8 *vq_codebook;
constexpr int VQ_CODEBOOK_SIZE = 256 * 8;
extern u32 palette_index; extern u32 palette_index;
extern u32 palette16_ram[1024]; extern u32 palette16_ram[1024];
extern u32 palette32_ram[1024]; extern u32 palette32_ram[1024];
@ -446,7 +447,6 @@ void texture_TW(PixelBuffer<typename PixelConvertor::unpacked_type>* pb, const u
template<class PixelConvertor> template<class PixelConvertor>
void texture_VQ(PixelBuffer<typename PixelConvertor::unpacked_type>* pb, const u8* p_in, u32 Width, u32 Height) void texture_VQ(PixelBuffer<typename PixelConvertor::unpacked_type>* pb, const u8* p_in, u32 Width, u32 Height)
{ {
p_in += 256 * 4 * 2; // Skip VQ codebook
pb->amove(0, 0); pb->amove(0, 0);
const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp;
@ -578,10 +578,10 @@ public:
tsp = other.tsp; tsp = other.tsp;
tcw = other.tcw; tcw = other.tcw;
tex_type = other.tex_type; tex_type = other.tex_type;
sa_tex = other.sa_tex; startAddress = other.startAddress;
dirty = other.dirty; dirty = other.dirty;
std::swap(lock_block, other.lock_block); std::swap(lock_block, other.lock_block);
sa = other.sa; mmStartAddress = other.mmStartAddress;
width = other.width; width = other.width;
height = other.height; height = other.height;
size = other.size; size = other.size;
@ -605,12 +605,12 @@ public:
// Decoded/filtered texture format // Decoded/filtered texture format
TextureType tex_type; TextureType tex_type;
u32 sa_tex; // texture data start address in vram u32 startAddress; // texture data start address in vram
u32 dirty; // frame number at which texture was overwritten u32 dirty; // frame number at which texture was overwritten
vram_block* lock_block; vram_block* lock_block;
u32 sa; // pixel data start address of max level mipmap u32 mmStartAddress; // pixel data start address of max level mipmap
u16 width, height; // width & height of the texture u16 width, height; // width & height of the texture
u32 size; // size in bytes of max level mipmap in vram u32 size; // size in bytes of max level mipmap in vram

View File

@ -141,12 +141,12 @@ static bool pvrParse(const u8 *data, u32 len, u32& width, u32& height, std::vect
p += (OtherMipPoint[texU] - 2) * 2; p += (OtherMipPoint[texU] - 2) * 2;
else if (imgType == PvrVQMipmaps) else if (imgType == PvrVQMipmaps)
p += VQMipPoint[texU]; p += VQMipPoint[texU];
else if (imgType == PvrVQ)
p += VQ_CODEBOOK_SIZE;
u32 expectedSize = width * height; u32 expectedSize = width * height;
if (imgType == PvrVQ || imgType == PvrVQMipmaps) { if (imgType == PvrVQ || imgType == PvrVQMipmaps)
expectedSize /= 4; // 4 pixels per byte expectedSize /= 4; // 4 pixels per byte
expectedSize += 256 * 4 * 2; // VQ codebook size
}
else else
expectedSize *= 2; // 2 bytes per pixel expectedSize *= 2; // 2 bytes per pixel
if (end - p < expectedSize) if (end - p < expectedSize)