⏱️ Временной гистерезис и нулевое выделение памяти (Minecraft 26.1.2)
Высокоскоростное окклюзионное отсечение может порождать две распространенные проблемы производительности:
- Мерцание на границах обзора (Z-Fighting / Boundary Pop-in): Небольшие повороты камеры, пересекающие ребро блока, могут вызывать быстрое мерцание сущностей между видимым и скрытым состоянием на чередующихся кадрах.
- Задержки из-за сборщика мусора: Непрерывное выделение объектов
new Vec3()во время трассировки лучей вызывает паузы сборщика мусора JVM Young-Gen.
Camera Culling решает обе проблемы с помощью адаптивного буфера задержки в зависимости от расстояния и движка критического пути с нулевым выделением памяти.
📋 Инфобокс движка гистерезиса
| Параметр | Значение |
|---|---|
| Ближний буфер задержки | $d \le 32\text{m} \implies 4\text{ кадра}$ стабильности перекрытия |
| Средний буфер задержки | $32\text{m} < d \le 64\text{m} \implies 8\text{ кадров}$ стабильности перекрытия |
| Дальний буфер задержки | $d > 64\text{m} \implies 12\text{ кадров}$ стабильности перекрытия |
| Задержка отображения (Unculling) | Мгновенно ($0\text{ кадров}$ задержки при первом же луче) |
| Выделение памяти в куче | $0\text{ байт/кадр}$ (примитивные координаты double) |
📐 Формула буфера задержки в зависимости от расстояния
Требуемая серия кадров перекрытия (occlusion streak) перед переводом сущности в состояние [CULLED] рассчитывается следующим образом:
$$\text{RequiredStreak}(d) = \begin{cases} 12\text{ кадров} & \text{если } d^2 > 64.0^2 \ (4096.0\text{m}^2) \ 8\text{ кадров} & \text{если } d^2 > 32.0^2 \ (1024.0\text{m}^2) \ 4\text{ кадра} & \text{иначе} \end{cases}$$
text
Entity Sightline Lost
├── Frame 1 occluded ──► Grace State (Still Rendered)
├── Frame 2 occluded ──► Grace State (Still Rendered)
├── Frame 3 occluded ──► Grace State (Still Rendered)
└── Frame 4 occluded ──► CULL (Streak Met)- Мгновенная отмена отсечения: Как только хотя бы одна точка выборки трассировки подтверждает прямую видимость, счетчик перекрытия сбрасывается (
OCCLUDED_STREAK.remove(id)), делая сущность видимой немедленно. - Асимметричное затухание: Для отсечения требуется несколько последовательных кадров скрытия, а для отображения достаточно 1 кадра. Это полностью устраняет мерцание при поворотах камеры.
⚡ Движок критического пути с нулевым выделением памяти
В CullingRaycastHelper.java промежуточные выделения векторов в куче полностью устранены:
java
// Нулевое выделение в куче: координаты передаются в виде примитивных double
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;
}
// Оценка попадания в пол и допусков без конструирования объектов
...
}