Commit Graph

282 Commits

Author SHA1 Message Date
MarcelineVQ 0a37b1096e luaalloc: segment-table lookup, VirtualAlloc pages, 1560ms (was 2000ms)
Replace 4-byte per-allocation header with 64KB segment table for O(1)
class lookup. VirtualAlloc guarantees 64KB-aligned pages, so ptr>>16
maps directly to the class index. Zero per-allocation overhead.

- VirtualAlloc for slab pages (guaranteed 64KB alignment)
- VirtualAlloc for large allocs (>4096) with size+magic header
- pool_ctx==0 passthrough for non-Lua callers
2026-04-04 18:09:36 -07:00
MarcelineVQ 1cae8c9781 luaalloc: pure Zig slab allocator replacing WoW's 6-class pool
WoW's memory_pool_allocate (0x6FAE90) does O(classes * pages) linear
scan on every free/realloc to find which pool owns a pointer. Our slab
stores slot size in a 4-byte header for O(1) lookup.

Baseline 2000ms -> testing with allocator enabled next.

- 15 size classes (16-4096) vs WoW's 6 (16-256)
- @memcpy for cross-class realloc (WoW uses manual dword loop)
- Large allocs (>4092 usable) fall through to game heap
- No C dependencies (replaces mimalloc which couldn't cross-compile)
2026-04-04 17:31:08 -07:00
MarcelineVQ b58234f176 clickthrough: filter non-interactable NPCs, skip cascade in GM mode
NPC priority pass now whitelists standard vanilla interaction flags
(0x7FFF: gossip through repair) instead of treating any non-zero
npc_flags as interactable. Fixes void zones and visual-effect NPCs
(e.g. C'Thun Portal, npc_flags=0x2000000) blocking player selection.

Cascade raycast disabled entirely in GM mode (PLAYER_FLAGS_GM) so
GMs get unfiltered targeting.
2026-04-04 10:56:32 -07:00
MarcelineVQ 7a32d81fbb fix: allow multiple variant DLLs to coexist (fixes #7, fixes #4)
Remove the core mutex that prevented secondary DLLs from installing
shared infrastructure hooks (Lua registration, file serving, engine
init/shutdown). zhook already has explicit E9-chain detection -- each
DLL's trampoline chains to the previous DLL's detour, and per-module
mutexes still prevent duplicate module hooks. DLL_PROCESS_DETACH fires
in reverse load order so detach unwinds correctly.
2026-04-01 13:58:38 -07:00
MarcelineVQ 5e0b8c768c fix: remove filecache hook on shutdown to prevent SGroupPtr crash
weirdperformance's filecache hook (File_FindInArchive) stayed active
during game teardown, returning stale archive/block pointers after
Storm freed its MPQ archives. This corrupted heap metadata, causing
ERROR #124 (SGroupPtr invalid block) on game close.

Set remove_on_shutdown=true so the hook is detached during
logoutDetour before Storm archive teardown begins.

Also wires up superweirdo module and clickthrough lateInit.
2026-03-31 21:25:14 -07:00
MarcelineVQ 737c686f8d build: restrict base target to SSE2, isolate FMA/AVX to SSE objects
The base DLL target was compiled with SSE4.1+FMA+AVX, which meant LLVM
could emit FMA instructions (VFMADD etc.) anywhere in the main module.
This crashes on pre-Haswell CPUs that lack FMA support (e.g. i7-3930K).

Split into two targets: base target (SSE2 only) for all main code, and
sse_target (SSE4.1+FMA+AVX) only for the separately-compiled SSE object
files that replace hot game functions. This ensures FMA/AVX instructions
are confined to the performance modules.

Fixes #6.
2026-03-31 21:22:19 -07:00
MarcelineVQ 96687bfaaa clickthrough: block unusable player-summoned portals and rituals
Filter ritual (type 18) and mage portal (type 22) GOs whose creator
is a player not in the local player's party or raid. Completely
unclickable, not just deprioritized.

Also:
- Add wow.isInGroup() shared group membership check (party + raid)
- Add party/raid addresses to shared offsets.zig
- Fix dpslog party member GUID address (was 0xBC7600, correct: 0xBC6F48)
  Verified from client's is_player_in_allowed_list @ 0x4e7f70
2026-03-28 11:42:38 -07:00
MarcelineVQ 5b85390206 build: all-variants builds everything, -D flags control inclusion
- all-variants now builds combined DLL + noperf + standalone variants
- -D flags exclude modules from all outputs (combined, noperf, variants)
- Simplify RELEASING.md to single build command
- Remove local README.md (lives in remote/WeirdUtils/ only)
2026-03-28 06:27:22 -07:00
MarcelineVQ db675c7a7e fix: MSVC ABI, heap allocator, deferred timer - vanillafixes compat
- Restore MSVC ABI (was accidentally GNU since v0.6.0, broke .CRT section)
- Replace game allocator with Windows process heap for filecache and
  libdeflate malloc/free - game allocator not initialized during DllMain
  when injected via CreateRemoteThread
- Defer timer calibration (Sleep 500ms) to lateInit - blocks under
  loader lock during DllMain
- Remove exported malloc/free symbols from DLL
- Eliminate addObject compilation units for SSE files - direct @import
  with AVX target instead
- Heap-allocate filecache (was 9.3MB static BSS)
- Strip transform44 of performance/ externs, pure profiling only
- Rename performance/ to weirdperformance/ to match module convention
- Skip default-off modules in all-variants build step
- Remove dead debug vars and stride logging from particle_sse
2026-03-28 05:31:31 -07:00
MarcelineVQ dd745e9728 fix: guard getObjectByGUID against NULL object manager
Crash during world teardown when minimap hooks call into WoW's
FindObjectByGUID while the object manager at 0xB41414 is already NULL.
2026-03-26 23:10:35 -07:00
MarcelineVQ 14ba112b0d perf: fix deferred divide precision, threadlocal TLS, MSVC target
Removed deferred divide from both ray-tri functions -- the det-scaled
epsilon comparisons lose precision for near-parallel rays, potentially
accepting triangles the original rejects. Downstream SetupBoxFrustum
then writes to wrong globals near 0xCE6738 (SGroupPtr), causing
ERROR #124 on exit.

inflate_hook: replaced manual FS:0x24 thread ID pool with Zig native
threadlocal. Fixes potential crash on Wine where FS segment layout
may differ.

Build: switched from GNU to MSVC ABI (4-5K smaller per DLL, no
.eh_frame unwind tables). Added setjmp.h stub for libdeflate MSVC
build. Added noperf build variant (zig build noperf).
2026-03-26 11:38:33 -07:00
MarcelineVQ bf5a7aa624 perf: GUID cache with destruction hook, remove glyph cache
GUID lookup cache: 4096-entry direct-mapped with proper invalidation.
MoveObjectToDeletedList (0x464920) hook evicts entries AFTER the
original runs (prevents re-caching from internal FindObjectByGUID call).
DestroyObjectManager (0x467700) hook flushes on zone change/logout.
93% hit rate, 1.15x speedup on FindObjectByGUID (10K+ calls/frame).

Glyph shadow cache removed: game has internal glyph cache at
GetOrCreateCharacterGlyph (0x5CA2D0). Our hook only saw cache misses
(~30/frame), providing no benefit. The 3.65% perf profile was the
game's own hash table work, not redundant computation.

Standalone guidcache module for isolated testing.
2026-03-26 04:14:34 -07:00
MarcelineVQ 1f318a8565 perf: GUID lookup cache (97% hit, 2.6x)
FindObjectByGUID (0x464890): 4096-entry direct-mapped cache with
validate-on-hit. 97% hit rate at 10K+ calls/frame, reducing from
0.8% to 0.3% frame time. Validates cached pointers by checking
GUID at obj+0x30/+0x34 on every hit.

AddToSpatialGrid (0x6816F0): SSE rewrite attempted, no measurable
gain (memory-bound linked list ops dominate). Not shipped.
2026-03-25 14:46:56 -07:00
MarcelineVQ ad33eb9b90 perf: SSE ray_tri_indexed_int (2.2x), new profiling hooks
rayTriIntersectIndexedInt (0x7C2C40): SSE Moller-Trumbore with deferred
divide, matching original's epsilon thresholds. Parity-tested against
original for edge hits, backfaces, parallel rays, and per-triangle t/uv.
JMP-patched in weirdperformance.

Added transform44 profiling hooks for ray_tri_indexed_int (0x7C2C40)
and ProcessStaticObjectsCulling (0x683BF0).

Bench: added rayTriIndexedInt bench with exhaustive parity tests.
2026-03-25 13:36:33 -07:00
MarcelineVQ 41ca30cfd5 perf: SSE spatial culling, inlined ray-tri, clickthrough CC fix
PerformSpatialCulling (0x6B8C60): Zig rewrite with SSE outcode
computation. 1.4x speedup, JMP-patched in weirdperformance.

performCollisionDetection (0x6B88E0): fully inlined SSE Moller-Trumbore
ray-triangle intersection, eliminating 4 SetVector3 calls and the
ray_tri function pointer call per triangle. ~22 cyc/tri in bench.

Both graduated from transform44 A/B testing to production JMP patches.

entity_sse.zig: reimplementations of UpdateEntityAndChunksPositions and
updateEntitiesInBounds (A/B tested, 1.2x bench, not shipped - memory
bound with negligible real-world gain).

clickthrough: fixed CheckObjectTypePermissions hook from fastcall to
thiscall (ECX preservation), fixed ClntObjMgrObjectPtr from fastcall(4)
to fastcall(5) with correct arg count.

bench: added performCollisionDetection bench with synthetic mesh data
and patched FindOrCreateHashEntry stub.
2026-03-25 12:34:57 -07:00
MarcelineVQ 4274729a77 docs: update clickthrough description for cascade raycast approach 2026-03-25 01:47:44 -07:00
MarcelineVQ 28514c029c clickthrough: cascade raycast with custom CanTargetEntity filter bits
Replace the re-raycast approach with a priority cascade: loot > GO > NPC
> normal. Each pass uses custom flag bits (0x01/02/04 in upper byte) that
our CanTargetEntity hook reads to exclude non-matching objects at the
raycast level. Terrain/WMO occlusion applies per pass.

Hook CanTargetEntity (0x480610) for per-object filtering: strips custom
bits before calling original, then checks lootable/interactable/NPC
based on which pass is active. Objects that don't match the current pass
return NULL (invisible to raycast, ray continues through them).

Fix isLootable: UNIT_DYNAMIC_FLAGS was at wrong descriptor offset
(0x96*4=0x258, should be 0x8F*4=0x23C per server UpdateFields index 143).
Also add UNIT_DYNFLAG_TAPPED and UNIT_DYNFLAG_TAPPED_BY_PLAYER offsets.
Fix isLootable base pointer: use getDescriptor (obj+0x08) not
getUnitDescriptor (obj+0x110), consistent with getNpcFlags and all
other descriptor reads in the project.
2026-03-25 01:30:16 -07:00
MarcelineVQ e78ebfd74d particle: fix tail texcoords and transformVec4 input size
Tail vertices V0/V1 read texture U/V from wrong addresses (no offset
from base instead of +8/+16 stride). Assembly reads 0x87D734/738 for
V0 and 0x87D73C/740 for V1, not the base 0x87D72C/730. This made both
base vertices share UVs, collapsing the texture and causing light beams
to taper to a point instead of maintaining width.

Also fix transformVec4 input: must be [4]f32 with w=0.0, not [3]f32.
The function reads all 4 components including [EDX+0xC]. Reading past
a 3-element array produced garbage w values that corrupted the velocity
transform, causing tail particles to extend wildly.

Fixed in both SSE and reference implementations.
2026-03-24 22:01:32 -07:00
MarcelineVQ 9a3e06a7cc core: disableAll only removes module hooks, core stays until DLL unload
Core hooks (file serving, Lua registration, engine init/shutdown) are
shared infrastructure that other loaded DLLs depend on. Only release
them in uninstall (DLL_PROCESS_DETACH), never via the runtime API.
2026-03-24 18:52:51 -07:00
MarcelineVQ 170f14a91a build: link module object deps conditionally from single definition
ModuleObjects.linkFor dispatches object/library linkage per module name.
Called from both the main weirdutils build and the variant loop, so adding
a new SSE object only requires updating one place. Fixes variant builds
for weirdperformance, transform44, silicon, and ssemaths.
2026-03-24 18:37:54 -07:00
MarcelineVQ d748978d1c core: add mutex to prevent double-hooking shared infrastructure
When both weirdutils.dll and a standalone module DLL are loaded,
the core mutex (Local\WeirdUtils_weirdutils_<PID>) ensures only one
installs the shared hooks (file serving, Lua registration, engine
init/shutdown). Module hooks are unaffected (own mutexes). Addon
registration runs for every DLL since LoadAddonTOC is idempotent.
2026-03-24 18:03:42 -07:00
MarcelineVQ 4629ea71d6 rename performance to weirdperformance, move file cache hook into module
Move fileFindDetour from main.zig into filecache.zig with its own
install/remove. Remove file_cache import and all references from
main.zig. Rename module to weirdperformance throughout (build flag,
module_name, source file, main.zig variable).
2026-03-24 17:48:22 -07:00
MarcelineVQ 979e5729bc perf: consolidate filecache, timer calibration into performance module
Move filecache.zig and timer_fix.zig from standalone modules into
src/performance/. Filecache no longer has its own hooks/mutex — stats
are dumped by performance's worldupdate hook. Timer calibration runs
during performance install instead of transform44. Remove filecache
from build module list (enabled automatically with performance).
Merge DLL_README sections into single Performance entry.
2026-03-24 17:28:19 -07:00
MarcelineVQ 3cab0db8c8 addons: prefix Outline and Log Sessions TOC titles with [WeirdUtils] 2026-03-24 17:09:08 -07:00
MarcelineVQ c7a35c3c2f worldmarkers: use shared wow.zig utilities, remove dead code
Replace custom getNameFromGUID with wow.getNameByGUID, use wow.readGUID
for GUID comparisons, remove getPlayerGUID wrapper. Drop unused
PARTY_MEMBER_GUIDS, FN_NAME_CACHE_LOOKUP, NAME_CACHE_OBJ from offsets.
2026-03-24 17:07:59 -07:00
MarcelineVQ a7fe79c668 minimapicons: replace quest available icon with pfQuest-derived grayscale version 2026-03-24 17:01:40 -07:00
MarcelineVQ 7b1f13b138 docs: update clickthrough and minimapicons descriptions, add Find Fish tracking, remove /tracking slash command 2026-03-24 15:22:18 -07:00
MarcelineVQ 80a7cdea3f inflate: thread-local libdeflate with gnu ABI — 2.2x speedup, 100% coverage
Root cause of crashes was thread safety: libdeflate's decompressor struct
has mutable decode tables rebuilt per-block, so sharing between main thread
and FMOD audio thread caused wild writes. Fixed with a thread-local pool
keyed by Windows thread ID (FS:[0x24]).

Switched build ABI from msvc to gnu — eliminates stub headers, libdeflate
uses Zig's bundled MinGW libc. Relaxed zlib header filter from == 0x78 to
(CMF & 0x0F) == 0x08 to catch both 32K and 4K window sizes.

Benchmarked: stock=2681ms, per-call-alloc=1304ms (2.05x), tls=1194ms (2.2x)
on 84k calls. Zero errors, zero fallbacks in production.
2026-03-24 14:57:28 -07:00
MarcelineVQ b612e6f953 inflate: fair timing comparison — 2.4x speedup confirmed
Fixed apples-to-apples comparison: track orig time only for calls where
libdeflate also ran. Previous numbers were misleading (all calls vs subset).

Results (same calls compared):
  Loading: 3352 matched calls, orig=160ms fast=66ms (2.4x)
  Gameplay: 150 matched calls, orig=5ms fast=2ms (2.5x)
  Projected total saving: ~1.76s off loading, ~100ms/period gameplay

Also fixed: thread safety (main thread only), zlib header validation,
input buffer save (256KB static), separate output buffer, FreeMemory
4-param calling convention.

Remaining: 8% failures (277/3352), not yet running on all threads.
2026-03-24 04:41:09 -07:00
MarcelineVQ 21ac8df11e inflate: research doc, saved-input fix for buffer modification bug
DecompressData_WithOptions modifies the input buffer during overlap
handling. Our hook must save the input before calling the original,
then pass the saved copy to libdeflate. Also documents all compression
types, function signatures, and bugs found during integration.

libdeflate sanity test passes (hello → hello, cpu_features=0x8000001F).
2026-03-24 04:20:11 -07:00
MarcelineVQ 2b11ae5cf1 inflate: logging reveals 100% zlib (type 0x02), standard 78 9C header
All 93K decompression calls during loading are pure zlib (type 0x02).
Header bytes 78 9C confirm standard zlib-wrapped deflate. No PKWare,
bzip2, or ADPCM observed.

Volume: 170MB compressed → 380MB decompressed in 2.9s during loading,
~5K calls/period during gameplay.

Format: [0x02] [zlib_stream...] — straightforward for libdeflate.
Previous crashes were from reading out_size AFTER original modified it
and from writing to out_buf unsafely.
2026-03-24 03:49:44 -07:00
MarcelineVQ 59ef452f22 perf: libdeflate inflate timing hook — runs both, compares results
Hooks DecompressData_WithOptions (0x661A80) to run both the original
zlib inflate and libdeflate on the same data. Times both, logs the
comparison every ~450 frames, counts mismatches.

malloc/free provided via game's Storm memory manager (SMemAlloc/SMemFree)
since we link without libc on the msvc target.

libdeflate compiled with -g0 to avoid .debug_frame COFF section name
warning. AVX-512 disabled (32-bit), SSE2/AVX2 paths active.
2026-03-24 03:01:15 -07:00
MarcelineVQ fb27ff54a0 perf: vendor libdeflate for fast inflate replacement
Vendored libdeflate decompress-only C sources into src/performance/.
Compiled as static lib (x86-windows-gnu for libc headers), linked into
the DLL. AVX-512 disabled (not available on 32-bit x86), SSE2/AVX2
paths active.

Target: replace WoW's embedded zlib inflate (~3% CPU) with libdeflate's
~2.3x faster implementation. Hook integration next.
2026-03-24 02:27:51 -07:00
MarcelineVQ 62bcf11d58 perf: performance module on by default, includes silicon patches
- performance module default=true, transform44 and silicon default=false
- Silicon JMP patches (22 functions) moved into performance.zig directly
- Single flag: 'zig build' gives all optimizations, zero profiling
- Dev mode: 'zig build -Dtransform44=true' for A/B testing
2026-03-24 01:24:42 -07:00
MarcelineVQ 3b48b500ec perf: new 'performance' module — zero-overhead production SSE hooks
Clean module at src/performance/ with all verified permanent
optimizations and zero profiling overhead. No rdtsc, no A/B testing,
no probe counters, no dumpStats.

Hooks: transformMatrix4x4 bone SSE, RenderParticleSprites SSE,
GetOrCreateCharacterGlyph cache, OnWorldUpdate cache reset,
teardown guard.

SSE compilation units (bone_sse, particle_sse, clip_sse, silicon_sse)
moved to src/performance/ as canonical location.

Build: zig build -Dperformance=true -Dsilicon=true
(transform44 module remains available for profiling/development)
2026-03-24 01:21:10 -07:00
MarcelineVQ 2f0e8a8bc0 fix: particle color byte masking, add reference version
Color channel packing missing & 0xFF after >> 14 extraction — upper
bits bled into adjacent channels causing broken particle fading.
Same class of bug as the alpha output fix earlier.

Added particle_sse_reference.zig (faithful recreation from commit
574f96f) as a separate compilation unit for correctness comparison.
2026-03-24 01:08:01 -07:00
MarcelineVQ 112246e687 perf: make all verified optimizations permanent, remove A/B toggles
- Glyph cache: unconditional (was A/B toggled)
- RenderParticleSprites SSE: unconditional (was A/B toggled)
- transform44 bone SSE: already permanent (teardown guard only)
- processLinkedListCollision: already permanent (JMP patch)
- frustumCullBoundingBox: already permanent (JMP patch)
- silicon functions (ftol, normalize, matmul, etc.): already permanent

Also adds decompilation of UpdateEntityAndChunksPositions — analyzed
but not optimizable (game function calls dominate, math is ~50 cycles
of the ~375 cycle total).
2026-03-24 00:44:18 -07:00
MarcelineVQ 3d872fdb1e spritequad: recreation + analysis (disabled, no improvement)
Faithful recreation of RenderSpriteQuads (0x5A0F50) with hoisted
invariant division and inlined DisplayMode_CalculateOffset. No
measurable improvement — cost is dominated by getAdapterInfo (7
sub-calls to D3D device per invocation × 3203 calls/frame) and
DrawPrimitive/DrawIndexedPrimitive virtual dispatch.

Added decompilation and assembly dumps for future reference.
2026-03-24 00:36:37 -07:00
MarcelineVQ 1046c0a4e3 particle: WIP setupParticleRendering recreation (disabled)
Faithful recreation attempt of SetupParticleRendering (0x7B3D20).
All game function CCs verified from assembly. Vertex buffer setup
works (8 verts produced), but D3D draw submission doesn't produce
visible output. Disabled pending investigation of GfxDeviceMethod
param struct layout. The function remains as timing-only pass-through.

Fixes found during work:
- max_particle_sprites global: 0xCF58F4 → 0xCF5B60
- billboard_matrix global: 0xCF5898 → 0xCF5888
- index_buffer_6/12: 0xCF58D0/D4 → 0xCF5BAC/0xCF5AF4
- BuildIndexBuffer takes renders_count not field_28
- Identity matrices must be mutable (game writes to them)
2026-03-24 00:25:12 -07:00
MarcelineVQ a73b5273fd research: Ghidra decompilation of SetupParticleRendering (326 lines)
10% of frame time, ~275 calls/frame, ~5800 cyc/call. Builds identity
matrices (32 stores of dead work), does 1-5 matmul calls, copies to
g_worldMatrix global. Translation matrix is identity+offset — matmul
chain can be simplified to direct computation.
2026-03-23 22:50:49 -07:00
MarcelineVQ 5373686bd4 particle: V4 vertex store, inline for unroll — ~42% total reduction
- V4 store (vmovups) writes xyz+color as one 16-byte op instead of 4
  scalar stores. Unaligned but still 1 μop on modern CPUs.
- inline for unrolls 4-vertex loops, letting LLVM schedule stores
  across vertices and fill pipeline bubbles.
- Hoisted world_pos to locals to prevent array re-reads.
- A/B: BASELINE ~433ms → CUSTOM ~299ms (~31% per-period reduction).
  Total from original: 520ms → 299ms = 42% reduction.
2026-03-23 22:47:50 -07:00
MarcelineVQ 379fcb62eb particle: contiguous vertex writes, skip normals, stride logging
- Detected interleaved 24-byte vertex layout: xyz(12)+color(4)+uv(8).
  Fast path writes 6 sequential u32s instead of scattered stores.
- Normal stride=0 (shared global) — write once in writeback, not 4x.
- Added stride_info export + logging for vertex layout analysis.
- A/B: ~30% peak reduction, baseline also improved due to less overhead.
2026-03-23 22:41:16 -07:00
MarcelineVQ 0d994db4b3 particle: VBState all paths, cached setupRender, @mulAdd — ~25% speedup
- VBState caching on all 5 vertex paths (was only 2D and sin/cos).
  Eliminates pointer re-reads: load once, emit 4 vertices, writeback.
- Cache setupRender() result per-frame via static + resetParticleCache()
  called from worldUpdateDetour. Saves ~7K function calls/frame.
- @mulAdd throughout for FMA codegen on vertex position and texcoord.
- A/B verified: BASELINE ~521ms → CUSTOM ~387ms (~25% reduction).
2026-03-23 22:29:12 -07:00
MarcelineVQ 1dc1350645 particle: inline calcColor + mat*vec3, VBState caching — ~20% speedup
- Inline calcColor: eliminates function call, allows OoO overlap of
  cache misses on colorCtx with vertex math. Pow path falls back to
  game function.
- Inline mat*vec3 transform: V4 FMA chain replaces call to 0x7BCA80.
- VBState: cache VB pointers/strides in locals, write back once after
  4 vertices. Eliminates ~80 pointer re-reads per particle.
- @mulAdd throughout vertex loops for FMA codegen.
- A/B verified: BASELINE ~470ms → CUSTOM ~382ms (~20% reduction).
2026-03-23 22:22:31 -07:00
MarcelineVQ 574f96f661 particle: faithful RenderParticleSprites recreation, A/B verified
Full recreation of RenderParticleSprites (0x7B2A50, 2688 bytes) in
particle_sse.zig. All 5 code paths: 2D billboard, 3D billboard,
2D+rotation (sin/cos), 3D+rotation (axis-angle matrix), tail particles.

Key fixes during verification:
- colorCtx address: removed Ghidra's spurious -0x12 offset
- calcColor arg2: pass raw u32 from emitter+0x1A8, not truncated
- Texture coord lookups: +8 offset to match assembly's eax increment
  between position and texcoord reads in the vertex loop

Verified in-game: particles render identically in CUSTOM vs BASELINE.
Next: optimize with SSE (inline calcColor, V4 vertex math).
2026-03-23 22:12:17 -07:00
MarcelineVQ d205693dbb particle: Ghidra decompilations, particle_sse.zig scaffold, bench
- Ghidra C decompilation of RenderParticleSprites (422 lines) and 5
  helper functions (calculateColorValues, matVec3Transform, etc.)
- particle_sse.zig with calcColorValues_SSE (10.7x bench but cache-miss
  bound in-game — needs inlining into full function replacement)
- Bench harness for calcColorValues with correctness check
- build.zig: particle_sse as separate ReleaseFast compilation unit
- colorDetour reverted to pass-through (SSE has no in-game effect due
  to L1 cache misses on scattered ColorCtx structs)
2026-03-23 21:56:57 -07:00
MarcelineVQ fec9ac9952 research: particle system SSE optimization analysis
Assembly dumps and research doc for RenderParticleSprites (1.73% CPU),
calculateColorValues (0.63%), SetupParticleRendering, and
ProcessActiveParticles. Identifies SSE opportunities: V4 color interp,
billboard vertex math, rotation block. Plan for particle_sse.zig.
2026-03-23 21:23:19 -07:00
MarcelineVQ 4b5fcd6fa6 perf: glyph cache A/B testing, fast hash, frustumCull rcpss+cvtss2si
- glyph cache: add A/B toggle so BASELINE/CUSTOM periods alternate
  between original function and shadow cache. Swap Murmur2 hash for
  fast golden-ratio integer mix (3 insns vs multi-step).
- frustumCullBBox: fastRecip (vrcpss+NR) and cvtss2si replace vdivss
  and @round bloat. 2.0x speedup (was 1.6x).
- Fix all Zig operator precedence bugs in silicon_sse: & and | bind
  looser than == in Zig, so (flags & 0x8 == 0) was always false.
  Affected frustumCullBBox and processLinkedListCollision.
2026-03-23 21:15:15 -07:00
MarcelineVQ 0576a06ef0 fix: Zig operator precedence bugs in silicon SSE functions
All bitwise & and | comparisons were missing parentheses — Zig's ==
and != bind tighter than & and |, so `flags & 0x8 == 0` parsed as
`flags & (0x8 == 0)` = `flags & 0` = always 0.

Affected: si_frustumCullBBox (behind-camera check never ran, occlusion
flag check always passed), si_processLinkedListCollision (early-out
never triggered, node skip broken, AABB hit test only checked X-axis).

Also adds fastRecip (vrcpss+NR) and cvtss2si helpers for frustumCull
perspective divide optimization (2.0x speedup, was 1.6x).
2026-03-23 20:57:19 -07:00
MarcelineVQ 035355b757 perf: FrustumCullBoundingBox SSE replacement, 1.6x speedup
- silicon_sse: si_frustumCullBBox (0x686000) — inline V4 mat*vec3
  transforms, SSE perspective divide, 4-wide horizon buffer scan.
  Benched 1.6x (117→72 cyc/call). Installed via JMP patch (544 bytes,
  won't fit in 380-byte original).
- bench: add frustumCullBBox benchmark with mapped globals, identity
  matrices, and horizon buffer test fixture.
- Remove patch table entry for frustumCullBBox, install via detour hook
  instead (allows future A/B testing if needed).
2026-03-23 20:46:28 -07:00