phase(N.4): WbDrawDispatcher perf pass — sort, cull, hash memoization
Four small wins on top of the grouped-instanced refactor. 1. Drop unused animState lookup. Was a side-effect-free _entitySpawnAdapter.GetState call per per-instance entity, made redundant by the Issue #47 fix that trusts MeshRefs. 2. Front-to-back sort opaque groups. Squared distance from camera to each group's first-instance translation; ascending sort. Lets the GPU's depth test reject fragments behind closer geometry — real win on dense scenes (Holtburg courtyard, Foundry interior). 3. Per-entity AABB frustum cull. 5m-radius AABB check per entity before walking parts. Skips work for distant entities even when their landblock is partially visible. Animated entities (other characters, NPCs, monsters) bypass — they always need per-frame work for animation regardless. Conservative radius covers typical entity bounds; large outliers stay landblock-culled. 4. Memoize palette hash per entity. TextureCache.HashPaletteOverride is now internal; new GetOrUploadWithPaletteOverride overload takes a precomputed hash. The dispatcher computes it ONCE per entity and reuses across every (part, batch) lookup, avoiding the per-batch FNV-1a fold over SubPalettes. Trees / scenery without palette overrides skip entirely (palHash stays 0). Visual output unchanged; FPS up further, especially in dense scenes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7b41efc281
commit
573526dae5
2 changed files with 89 additions and 25 deletions
|
|
@ -123,10 +123,23 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab
|
|||
uint surfaceId,
|
||||
uint? overrideOrigTextureId,
|
||||
PaletteOverride paletteOverride)
|
||||
=> GetOrUploadWithPaletteOverride(surfaceId, overrideOrigTextureId, paletteOverride,
|
||||
HashPaletteOverride(paletteOverride));
|
||||
|
||||
/// <summary>
|
||||
/// Overload that accepts a precomputed palette hash. Lets callers (e.g.
|
||||
/// the WB draw dispatcher) compute the hash ONCE per entity and reuse
|
||||
/// it across every (part, batch) lookup, avoiding the per-batch
|
||||
/// FNV-1a fold over <see cref="PaletteOverride.SubPalettes"/>.
|
||||
/// </summary>
|
||||
public uint GetOrUploadWithPaletteOverride(
|
||||
uint surfaceId,
|
||||
uint? overrideOrigTextureId,
|
||||
PaletteOverride paletteOverride,
|
||||
ulong precomputedPaletteHash)
|
||||
{
|
||||
ulong hash = HashPaletteOverride(paletteOverride);
|
||||
uint origTexKey = overrideOrigTextureId ?? 0;
|
||||
var key = (surfaceId, origTexKey, hash);
|
||||
var key = (surfaceId, origTexKey, precomputedPaletteHash);
|
||||
if (_handlesByPalette.TryGetValue(key, out var h))
|
||||
return h;
|
||||
|
||||
|
|
@ -138,9 +151,10 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab
|
|||
|
||||
/// <summary>
|
||||
/// Cheap 64-bit hash over a palette override's identity so two
|
||||
/// entities with the same palette setup share a decode.
|
||||
/// entities with the same palette setup share a decode. Internal so
|
||||
/// the WB dispatcher can compute it once per entity.
|
||||
/// </summary>
|
||||
private static ulong HashPaletteOverride(PaletteOverride p)
|
||||
internal static ulong HashPaletteOverride(PaletteOverride p)
|
||||
{
|
||||
// Not cryptographic — just needs to distinguish override setups
|
||||
// for caching. Start with base palette id, fold in each entry.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue