arkavo-org / arkavo-org/VRMMetalKit
Per-frame render-item string classification and cache inefficiency
- Dominant language
- Swift
- Stars
- 6
- Forks
- 2
- Avg merge
- 18h 51m
- Merged PRs (30d)
- 26
Description
## Summary
The render loop performs heavy string classification (`lowercased()`, `contains(...)`) on a per-primitive basis every frame. Additionally, transparent items rebuild a Z-sort dictionary every frame, and `EncoderStateCache` is underutilized because render items are sorted by `renderOrder` (face categories) rather than by pipeline state.
## Impact
- ~10–15 `contains(...)` checks per primitive → ~1,500 string searches/frame for a 100-primitive model.
- `viewZByIndex` dictionary cleared and repopulated every frame.
- Frequent pipeline state switches because skinned/non-skinned and opaque/blend items are interleaved.
## Location
- String classification: `VRMRenderer.swift:1585–1799`, `:2533–2537`, `:3044`
- Z-sort rebuild: `VRMRenderer.swift:1844–1849`
- State cache bypass: `VRMRenderer.swift:3669` (outline pass)
- Sort order: `VRMRenderItemBuilder.swift:267–280`
## Evidence
```swift
// Rebuild every frame
viewZByIndex.removeAll(keepingCapacity: true)
viewZByIndex.reserveCapacity(blendCount)
for item in allItems where item.materialRenderQueue >= 2500 {
viewZByIndex[item.primitiveIndex] = (viewMatrix * worldPos).z
}
```
## Suggested Fixes
1. **Pre-compute category bitflags at load time** (`isFace`, `isBody`, `isEye`, etc.) and store them on `RenderItem`. Eliminates all runtime string matching.
2. **Reuse Z-sort array** instead of a dictionary; use a stable sort or radix sort.
3. **Sort by pipeline state within each render-order bucket** (opaque skinned → opaque static → blend skinned → blend static) to maximize `EncoderStateCache` hit rate.
4. **Route outline pass through `EncoderStateCache`** instead of direct `setRenderPipelineState`.
Contributor guide
Research direction
Start in VRMRenderer.swift at lines 1585–1799, 1844–1849, 2533–2537, 3044, and 3669, then inspect VRMRenderItemBuilder.swift:267–280 and the RenderItem definition. Trace how categories, transparent-item depth data, sorting, and EncoderStateCache are used across the render loop. Done means the selected optimizations are implemented consistently and per-frame string searches, dictionary rebuilding, and avoidable pipeline-state cache bypasses are removed without changing render ordering or output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- computer-graphics, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100