⏱️ Temporal Hysteresis & Zero-Allocation Math (Minecraft 26.2)
High-speed occlusion culling can introduce two common performance flaws:
- Camera-Grazing Flicker (Z-Fighting / Boundary Pop-in): Small camera rotations crossing block edges can cause entities to rapidly flicker between rendered and unrendered states on alternating frames.
- Garbage Collection Stutter Spikes: Continuous allocation of
new Vec3()objects during raycasting triggers JVM Young-Gen garbage collection pauses.
Camera Culling solves both issues with an Adaptive Distance-Scaled Grace Buffer and a Zero-Allocation Hot-Path Engine.
📋 Hysteresis & Allocation Infobox
| Property | Value |
|---|---|
| Near Distance Grace Buffer | $d \le 32\text{m} \implies 4\text{ consecutive occluded frames}$ |
| Medium Distance Grace Buffer | $32\text{m} < d \le 64\text{m} \implies 8\text{ consecutive occluded frames}$ |
| Far Distance Grace Buffer | $d > 64\text{m} \implies 12\text{ consecutive occluded frames}$ |
| Visibility Transition | Instantaneous ($0\text{ frame delay}$) upon establishing sightline |
| Tracking Structure | it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap (Zero boxing overhead) |
| Per-Frame Allocations | $0\text{ bytes}$ (Primitive double coordinates passed directly) |
📐 Distance-Scaled Grace Buffer Formula
$$\text{RequiredStreak}(d) = \begin{cases} 12\text{ frames} & \text{if } d^2 > 64.0^2 \ (4096.0\text{m}^2) \ 8\text{ frames} & \text{if } d^2 > 32.0^2 \ (1024.0\text{m}^2) \ 4\text{ frames} & \text{if } d^2 \le 32.0^2 \end{cases}$$
text
[ENTITY SIGHTLINE LOST]
│
├── Frame 1 occluded ──► RENDER (Grace Decay)
├── Frame 2 occluded ──► RENDER (Grace Decay)
├── Frame 3 occluded ──► RENDER (Grace Decay)
└── Frame 4 occluded ──► CULL (Streak Met)- Instant Unculling: As soon as a single raycast sample establishes a clear line of sight, the occlusion streak is removed (
OCCLUDED_STREAK.remove(id)), making the entity visible immediately. - Asymmetric Decay: Becoming occluded requires multiple consecutive frames; becoming visible takes 1 frame. This eliminates camera-turning flicker.
⚡ Zero-Allocation Primitive Architecture
In CullingRaycastHelper.java, intermediate vector heap allocations are completely eliminated:
java
public static boolean hasLineOfSight(Level level, Vec3 from, double toX, double toY, double toZ) {
Vec3 to = new Vec3(toX, toY, toZ);
ClipContext ctx = new ClipContext(from, to, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, CollisionContext.empty());
BlockHitResult hit = level.clip(ctx);
if (hit.getType() == HitResult.Type.MISS) {
return true;
}
...
}