Arena Performance Optimizations (February 2026)
Commit: c20d0fc09ff44219a306d869b9f71bef6f39a25bPR: #938
Author: Ting Chien Meng (@tcm390)
Summary
Massive rendering performance improvement for the duel arena system by converting ~846 individual meshes to InstancedMesh (97% draw call reduction) and replacing 28 dynamic PointLights with GPU-driven TSL emissive materials.Performance Impact
Before
- Draw Calls: ~846 individual meshes
- Lights: 28 dynamic PointLights (expensive per-pixel shading)
- FPS: Significant drops in arena areas
After
- Draw Calls: ~20 InstancedMesh batches (97% reduction)
- Lights: 0 dynamic lights (replaced with TSL emissive materials)
- FPS: Smooth performance in all arena areas
Changes
1. InstancedMesh Conversion
Converted repeated geometry to instanced draws: Fence Components (4 draw calls):- Posts: 288 instances → 1 draw call
- Caps: 288 instances → 1 draw call
- X-Rails: 36 instances → 1 draw call
- Z-Rails: 36 instances → 1 draw call
- Bases: 32 instances → 1 draw call
- Shafts: 32 instances → 1 draw call
- Capitals: 32 instances → 1 draw call
- Brazier bowls: 24 instances → 1 draw call
- Border strips: 24 instances → 2 draw calls (N/S + E/W)
- Banner poles: 12 instances → 1 draw call
- Arena floors: 6 meshes (need unique
arenaIduserData) - Forfeit pillars: 12 meshes (need unique
entityIdfor interaction) - Banner cloths: 12 meshes (3 shared materials)
2. Dynamic Light Elimination
Removed: 28 PointLights (24 arena corner torches + 4 lobby braziers) Replaced With: TSL emissive material on brazier bowls GPU-Driven Flicker:- Zero CPU cost per frame (all calculations on GPU)
- No per-light state updates
- Consistent flicker across all braziers
- Eliminates expensive per-pixel lighting calculations
3. Fire Particle Shader Enhancement
Removed: “torch” particle preset (redundant) Enhanced: “fire” preset with improved fragment shader New Features:- Smooth Value Noise: Bilinear interpolated hash lattice for organic flame shapes
- Soft Radial Falloff: Designed for additive blending - overlapping particles merge into cohesive flame
- Turbulent Vertex Motion: Per-particle jitter for natural flickering
- Height-Based Color Gradient: White-yellow core → orange-red tips
- Scrolling Noise: Upward motion feel with organic edges
- Fire: 18 → 28 particles (tighter spawn spread compensates)
- Torch: Removed (unified on fire preset)
4. Dead Code Removal
Removed unused functions:createArenaMarker()- Number markers (1-6 dots) were never usedcreateAmbientDust()- Dust particles were never registeredcreateLobbyBenches()- Benches were never added to scene
Implementation Details
Shared Materials
All instanced meshes share materials (compiled once during init):Instance Matrix Updates
All instance matrices are set once during initialization:TSL Time Uniform
Single time uniform drives all brazier glow animations:Migration Guide
For Developers
No migration needed - changes are fully backward compatible. If you’re adding new arena geometry:-
Use InstancedMesh for repeated geometry:
-
Use TSL emissive materials instead of PointLights:
-
Keep individual meshes only when needed for raycasting/interaction:
- Floors (need layer 0+2 for click-to-move)
- Interactive objects (need unique
entityIduserData)
For Asset Creators
Fire Particles: Use the unified “fire” preset:Performance Metrics
Draw Call Reduction
| Component | Before | After | Reduction |
|---|---|---|---|
| Fence Posts | 288 | 1 | 99.7% |
| Fence Caps | 288 | 1 | 99.7% |
| Fence Rails | 72 | 2 | 97.2% |
| Pillars | 96 | 3 | 96.9% |
| Braziers | 24 | 1 | 95.8% |
| Borders | 24 | 2 | 91.7% |
| Banner Poles | 12 | 1 | 91.7% |
| Total | ~846 | ~20 | 97.6% |
Lighting Performance
| Metric | Before | After | Improvement |
|---|---|---|---|
| Dynamic Lights | 28 | 0 | 100% |
| Per-Pixel Shading | Yes | No | Eliminated |
| Light Updates/Frame | 28 | 0 | 100% |
| GPU Shader Complexity | High | Low | Significant |
Memory Usage
| Resource | Before | After | Change |
|---|---|---|---|
| Mesh Objects | ~846 | ~50 | -94% |
| Material Instances | ~846 | ~15 | -98% |
| Light Objects | 28 | 0 | -100% |
| Geometry Buffers | ~846 | ~20 | -98% |
Visual Quality
Maintained
- ✅ Stone texture detail (TSL procedural materials)
- ✅ Fire particle appearance (enhanced shader)
- ✅ Brazier glow (TSL emissive matches old PointLight flicker)
- ✅ Overall arena atmosphere
Improved
- ✅ Fire particles: More organic flame shapes with noise
- ✅ Brazier glow: Consistent flicker across all instances
- ✅ Performance: Smooth 60 FPS in arena areas
Removed
- ❌ Arena number markers (1-6 dot patterns) - never used
- ❌ Lobby benches - never added to scene
- ❌ Ambient dust particles - never registered
Related Files
Modified
packages/shared/src/systems/client/DuelArenaVisualsSystem.ts- Main arena rendering systempackages/shared/src/entities/managers/particleManager/GlowParticleManager.ts- Fire particle shader
Workflow
.github/workflows/deploy-vast.yml- Deployment with maintenance mode
Future Improvements
Potential Optimizations
- Texture Atlasing: Combine stone textures into single atlas
- LOD System: Reduce geometry detail at distance
- Frustum Culling: Skip rendering off-screen arenas
- Occlusion Culling: Skip rendering occluded geometry
Monitoring
- Track FPS in arena areas
- Monitor draw call count via DevTools
- Profile GPU usage with Chrome DevTools Performance