xonotic-0001: Mod_Mesh_GetTexture O(T) linear scan per draw call, 43x model_shared.c:4481 scans all textures for every quad/char/image drawn. Fix: hash table keyed on (name, drawflag, texflags, matflags). xonotic-0002: SV_ModelIndex O(M) linear scan, 81x sv_main.c:1421 scans model_precache[] (up to 8192) on every model ref. Fix: hash table on model filename. xonotic-0003: SV_SoundIndex O(S) linear scan, 66x sv_main.c:1484 scans sound_precache[] (up to 4096) on every sound ref. Fix: hash table on sound filename. xonotic-0004: S_FindName O(N) linked list scan, 72x snd_main.c:913 traverses linked list (has "TODO: hash table search?"). Fix: hash chain on sfx name. MOAD-0002 (Intertangle): CLEAN, god objects are idiomatic Quake engine. MOAD-0003 (Leaked Context): CLEAN, no thread_local usage. MOAD-0004 (Logged Secret): CLEAN, rcon_password uses CF_PRIVATE flag. MOAD-0005 (Thundering Herd): CLEAN, single-threaded cache access.
59 lines
2.6 KiB
Diff
59 lines
2.6 KiB
Diff
--- 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;
|