# UNDF: UNDF-2026-000000989 --- a/model_shared.c +++ b/model_shared.c @@ -4473,12 +4473,36 @@ void Mod_Mesh_Restart(model_t *mod) mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it } +// Hash table for Mod_Mesh_GetTexture to avoid O(T) linear scan per draw call. +// Key: texture name + drawflag + texflags + materialflags, Value: texture index. +#define MOD_MESH_TEX_HASHSIZE 256 +static unsigned Mod_Mesh_TexHashKey(const char *name, int drawflag, int texflags, int materialflags) +{ + unsigned hash = 0; + const unsigned char *p; + for (p = (const unsigned char *)name; *p; p++) + hash = hash * 31 + *p; + hash ^= (unsigned)drawflag * 2654435761u; + hash ^= (unsigned)texflags * 2246822519u; + hash ^= (unsigned)materialflags * 3266489917u; + return hash % MOD_MESH_TEX_HASHSIZE; +} + texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags) { - int i; - texture_t *t; + int i, hashkey; + texture_t *t, *oldtextures; int drawflag = defaultdrawflags & DRAWFLAG_MASK; - for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++) + + // Use hash table for O(1) average lookup instead of O(T) linear scan. + // This function is called per draw call (every quad, character, image), + // so O(T) was a per-frame bottleneck scaling with texture count. + hashkey = Mod_Mesh_TexHashKey(name, drawflag, defaulttexflags, defaultmaterialflags); + for (i = mod->mesh_texture_hashtable[hashkey]; i >= 0; i = mod->mesh_texture_hashnext[i]) + { + t = &mod->data_textures[i]; + if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags) + return t; + } + + // Fallback: linear scan for textures not yet in hash table (should not happen) + for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++) if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags) return t; if (mod->max_textures <= mod->num_textures) --- a/model_shared.h +++ b/model_shared.h @@ -428,6 +428,10 @@ typedef struct model_s int max_textures; // preallocated for expansion (Mod_Mesh_GetTexture) int num_textures; texture_t *data_textures; + // Hash table for Mod_Mesh_GetTexture O(1) lookup. + // mesh_texture_hashtable[hashkey] = first texture index, -1 if empty. + int mesh_texture_hashtable[256]; + int *mesh_texture_hashnext; // per-texture chain, -1 = end int num_surfaces; int max_surfaces; // preallocated for expansion (Mod_Mesh_AddSurface) msurface_t *data_surfaces;