Files
goldeneye-pc-port/port/fast3d/gfx_pc.h
T
James Dansereau f800275247 fast3d/D217: key CI texture cache on palette content, not just TLUT address (#49)
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
2026-09-11 18:17:29 -04:00

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