diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 48e1a375fa..36bd191a97 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -88,7 +88,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat) { texturePixels = 512; if (shift) { - WRITE(p, " index = ((index >> %i) & 0x%02x)", shift, mask); + WRITE(p, " index = (int(uint(index) >> %i) & 0x%02x)", shift, mask); } else { WRITE(p, " index = (index & 0x%02x)", mask); } @@ -213,6 +213,7 @@ void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLa } // Offset by half a texel (plus clutBase) to turn NEAREST filtering into FLOOR. + // Technically, the clutBase should be |'d, not added, but that's hard with floats. float texel_offset = ((float)clutBase + 0.5f) / texturePixels; sprintf(offset, " + %f", texel_offset); diff --git a/GPU/Directx9/DepalettizeShaderDX9.cpp b/GPU/Directx9/DepalettizeShaderDX9.cpp index decf55a60b..8cfc8cb7de 100644 --- a/GPU/Directx9/DepalettizeShaderDX9.cpp +++ b/GPU/Directx9/DepalettizeShaderDX9.cpp @@ -109,7 +109,7 @@ LPDIRECT3DTEXTURE9 DepalShaderCacheDX9::GetClutTexture(const u32 clutID, u32 *ra memcpy(rect.pBits, rawClut, 1024); tex->texture->UnlockRect(0); - pD3Ddevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + pD3Ddevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); pD3Ddevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); pD3Ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index ab26b71b3f..5b14e75aab 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -70,14 +70,14 @@ TextureCacheDX9::TextureCacheDX9() : cacheSizeEstimate_(0), secondCacheSizeEstim tmpTexBuf16.resize(1024 * 512); // 1MB tmpTexBufRearrange.resize(1024 * 512); // 2MB - // Aren't these way too big? - clutBufConverted_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB - clutBufRaw_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB + // TODO: Clamp down to 256/1KB? Need to check mipmapShareClut and clamp loadclut. + clutBufConverted_ = (u32 *)AllocateAlignedMemory(1024 * sizeof(u32), 16); // 4KB + clutBufRaw_ = (u32 *)AllocateAlignedMemory(1024 * sizeof(u32), 16); // 4KB // Zap these so that reads from uninitialized parts of the CLUT look the same in // release and debug - memset(clutBufConverted_, 0, 4096 * sizeof(u32)); - memset(clutBufRaw_, 0, 4096 * sizeof(u32)); + memset(clutBufConverted_, 0, 1024 * sizeof(u32)); + memset(clutBufRaw_, 0, 1024 * sizeof(u32)); D3DCAPS9 pCaps; ZeroMemory(&pCaps, sizeof(pCaps)); @@ -865,7 +865,12 @@ void TextureCacheDX9::UpdateCurrentClut() { const u32 clutBaseBytes = clutBase * (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16)); // Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier. // If not, we're going to hash random data, which hopefully doesn't cause a performance issue. - const u32 clutExtendedBytes = clutTotalBytes_ + clutBaseBytes; + // + // TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data. + // clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash + // unrelated old entries for small palettes. + // Adding clutBaseBytes may just be mitigating this for some usage patterns. + const u32 clutExtendedBytes = std::min(clutTotalBytes_ + clutBaseBytes, clutMaxBytes_); clutHash_ = DoReliableHash32((const char *)clutBufRaw_, clutExtendedBytes, 0xC0108888); clutBuf_ = clutBufRaw_; @@ -973,11 +978,10 @@ void TextureCacheDX9::SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebu dxstate.viewport.restore(); const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); - const u32 clutBase = gstate.getClutIndexStartPos(); const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); - const u32 clutExtendedColors = (clutTotalBytes_ / bytesPerColor) + clutBase; + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; - TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(gstate.getClutPaletteFormat()), clutExtendedColors, clutExtendedColors, 1); + TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(gstate.getClutPaletteFormat()), clutTotalColors, clutTotalColors, 1); gstate_c.textureFullAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL; gstate_c.textureSimpleAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE; } else { diff --git a/GPU/GLES/DepalettizeShader.cpp b/GPU/GLES/DepalettizeShader.cpp index 6bb010860a..111a7a06bf 100644 --- a/GPU/GLES/DepalettizeShader.cpp +++ b/GPU/GLES/DepalettizeShader.cpp @@ -152,7 +152,7 @@ GLuint DepalShaderCache::GetClutTexture(const u32 clutID, u32 *rawClut) { glTexImage2D(GL_TEXTURE_2D, 0, components, texturePixels, 1, 0, components2, dstFmt, (void *)rawClut); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 0939ca8f47..77ef5cf4e4 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -75,14 +75,14 @@ TextureCache::TextureCache() : cacheSizeEstimate_(0), secondCacheSizeEstimate_(0 tmpTexBuf16.resize(1024 * 512); // 1MB tmpTexBufRearrange.resize(1024 * 512); // 2MB - // Aren't these way too big? - clutBufConverted_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB - clutBufRaw_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB + // TODO: Clamp down to 256/1KB? Need to check mipmapShareClut and clamp loadclut. + clutBufConverted_ = (u32 *)AllocateAlignedMemory(1024 * sizeof(u32), 16); // 4KB + clutBufRaw_ = (u32 *)AllocateAlignedMemory(1024 * sizeof(u32), 16); // 4KB // Zap these so that reads from uninitialized parts of the CLUT look the same in // release and debug - memset(clutBufConverted_, 0, 4096 * sizeof(u32)); - memset(clutBufRaw_, 0, 4096 * sizeof(u32)); + memset(clutBufConverted_, 0, 1024 * sizeof(u32)); + memset(clutBufRaw_, 0, 1024 * sizeof(u32)); glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel); SetupTextureDecoder(); @@ -992,13 +992,18 @@ void TextureCache::UpdateCurrentClut() { const u32 clutBaseBytes = clutBase * (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16)); // Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier. // If not, we're going to hash random data, which hopefully doesn't cause a performance issue. - const u32 clutExtendedBytes = clutTotalBytes_ + clutBaseBytes; + // + // TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data. + // clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash + // unrelated old entries for small palettes. + // Adding clutBaseBytes may just be mitigating this for some usage patterns. + const u32 clutExtendedBytes = std::min(clutTotalBytes_ + clutBaseBytes, clutMaxBytes_); clutHash_ = DoReliableHash32((const char *)clutBufRaw_, clutExtendedBytes, 0xC0108888); // Avoid a copy when we don't need to convert colors. if (UseBGRA8888() || clutFormat != GE_CMODE_32BIT_ABGR8888) { - const int numColors = (clutMaxBytes_ + clutBaseBytes) / (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16)); + const int numColors = clutMaxBytes_ / (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16)); ConvertColors(clutBufConverted_, clutBufRaw_, getClutDestFormat(clutFormat), numColors); clutBuf_ = clutBufConverted_; } else { @@ -1147,11 +1152,10 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffe framebufferManager_->RebindFramebuffer(); const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); - const u32 clutBase = gstate.getClutIndexStartPos(); const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); - const u32 clutExtendedColors = (clutTotalBytes_ / bytesPerColor) + clutBase; + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; - TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(gstate.getClutPaletteFormat()), clutExtendedColors, clutExtendedColors, 1); + TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(gstate.getClutPaletteFormat()), clutTotalColors, clutTotalColors, 1); gstate_c.textureFullAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL; gstate_c.textureSimpleAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE; } else { diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 36f71cc6db..81c367c370 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -309,7 +309,11 @@ struct GPUgstate int getClutIndexShift() const { return (clutformat >> 2) & 0x1F; } int getClutIndexMask() const { return (clutformat >> 8) & 0xFF; } int getClutIndexStartPos() const { return ((clutformat >> 16) & 0x1F) << 4; } - int transformClutIndex(int index) const { return ((index >> getClutIndexShift()) & getClutIndexMask()) | getClutIndexStartPos(); } + u32 transformClutIndex(u32 index) const { + // We need to wrap any entries beyond the first 1024 bytes. + u32 mask = getClutPaletteFormat() == GE_CMODE_32BIT_ABGR8888 ? 0xFF : 0x1FF; + return ((index >> getClutIndexShift()) & getClutIndexMask()) | (getClutIndexStartPos() & mask); + } bool isClutIndexSimple() const { return (clutformat & ~3) == 0xC500FF00; } // Meaning, no special mask, shift, or start pos. bool isTextureSwizzled() const { return texmode & 1; } bool isClutSharedForMipmaps() const { return (texmode & 0x100) == 0; }