mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
GE assembles weapon and character model display lists in scratch arena RAM
and reissues gDPLoadTLUT from a repeated source address with different
palette *content* between materials and frames. The CI TextureCacheKey
keyed only on {texel addr, palette_addrs[0/1], palette_index, size_bytes}
with no content check, so gfx_texture_cache_lookup() (a plain map.find)
could return a stale HIT whose GL texture was decoded against a different
palette. Proven with an env-gated probe on -level_34: the same
{addr, paladdr} pair re-imported many times per frame with materially
different rdp.palette content, all taking HITs.
Fix: FNV-1a hash of the 512-byte rdp.palette, recomputed only in
gfx_dp_load_tlut() (rare vs draws -> no per-texel or per-draw cost), added
as a trailing field of the CI cache key. Non-CI keys carry 0. Cannot
produce a worse decode than the stale HIT it replaces; worst case is a few
extra texture uploads per frame (~2 measured on -level_34).
Partial for D217: this removes a real stale-palette-HIT class (the D217
finding's own "test a palette-content hash" next-step) but does NOT resolve
the visible green PP7 grip. That has an upstream cause - the grip's count=1
model TLUT is fed different source bytes per level (Bunker 0x0001 -> black,
Facility 0x1A00 -> green) for the same tile; fast3d faithfully stores what
it is given. See scratchpad D217-report.md. Full 21-level world-CI
regression sweep still owed before merge.
Claude-Session: https://claude.ai/code/session_016TaKy6AAsVUTfjkhrX6CjG
74 lines
2.1 KiB
C++
74 lines
2.1 KiB
C++
#ifndef GFX_PC_H
|
|
#define GFX_PC_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <unordered_map>
|
|
#include <list>
|
|
#include <cstddef>
|
|
|
|
#include <PR/gbi.h>
|
|
|
|
#include "system.h"
|
|
|
|
#define SCREEN_WIDTH ((int32_t)gfx_current_native_viewport.width)
|
|
#define SCREEN_HEIGHT ((int32_t)gfx_current_native_viewport.height)
|
|
|
|
extern uintptr_t gfxFramebuffer;
|
|
|
|
struct GfxRenderingAPI;
|
|
struct GfxWindowManagerAPI;
|
|
|
|
struct TextureCacheKey {
|
|
const uint8_t* texture_addr;
|
|
const uint8_t* palette_addrs[2];
|
|
uint8_t fmt, siz;
|
|
uint8_t palette_index;
|
|
// D74: the same source address can be loaded with different sizes
|
|
// (gDPLoadBlock mip chains vs partial loads); include the size so a
|
|
// truncated first import cannot poison later full imports.
|
|
uint32_t size_bytes;
|
|
// D217: for CI (palettized) textures, a hash of the decoded rdp.palette
|
|
// content at import time. GE assembles weapon / character model DLs in
|
|
// scratch arena RAM and reissues gDPLoadTLUT with different palette
|
|
// *content* from a repeated source address, so
|
|
// {texture_addr, palette_addrs, palette_index, size_bytes} alone collides
|
|
// across materials/frames -> a CI tile takes a stale cache HIT decoded
|
|
// against a different palette. Recomputed only in gfx_dp_load_tlut(), so
|
|
// there is no per-texel or per-draw cost. Zero for non-CI textures.
|
|
uint32_t palette_hash;
|
|
|
|
bool operator==(const TextureCacheKey&) const noexcept = default;
|
|
|
|
struct Hasher {
|
|
size_t operator()(const TextureCacheKey& key) const noexcept {
|
|
uintptr_t addr = (uintptr_t)key.texture_addr;
|
|
return (size_t)(addr ^ (addr >> 5));
|
|
}
|
|
};
|
|
};
|
|
|
|
typedef std::unordered_map<TextureCacheKey, struct TextureCacheValue, TextureCacheKey::Hasher> TextureCacheMap;
|
|
typedef std::pair<const TextureCacheKey, struct TextureCacheValue> TextureCacheNode;
|
|
|
|
struct TextureCacheValue {
|
|
uint32_t texture_id;
|
|
uint8_t cms, cmt;
|
|
bool linear_filter;
|
|
|
|
std::list<struct TextureCacheMapIter>::iterator lru_location;
|
|
};
|
|
|
|
struct TextureCacheMapIter {
|
|
TextureCacheMap::iterator it;
|
|
};
|
|
|
|
extern "C" {
|
|
|
|
#include "gfx_api.h"
|
|
|
|
}
|
|
|
|
#endif
|