Какие знаешь ограничение Dynamic Batching?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Ограничения Dynamic Batching
Отличный вопрос, который показывает знание low-level оптимизации. Dynamic Batching — это одна из ключевых техник оптимизации в Unity, но у неё есть множество ограничений, которые разработчики часто упускают.
Что такое Dynamic Batching
Dynamic Batching объединяет draw calls для объектов, которые используют один и тот же материал, в один call. Вместо:
DrawCall 1: Mesh (Box)
DrawCall 2: Mesh (Box)
DrawCall 3: Mesh (Box)
Получаем:
DrawCall 1: Meshes (Box, Box, Box) — объединены
Это уменьшает CPU overhead, но не бесплатно — есть CPU cost на объединение.
Основные ограничения
1. Размер вертексов
Ограничение: Максимум 900 вертексов на batched объект (для трёхмерных моделей)
// ❌ Не батчится — модель имеет 2000 вертексов
public class ComplexEnemy : MonoBehaviour {
// Model с 2000 вертексов
// Будет отдельный draw call, несмотря на попытку батчинга
}
// ✅ Батчится — модель оптимизирована
public class SimpleEnemy : MonoBehaviour {
// Model с 800 вертексов
// Может быть объединена с другими объектами
}
Для UI вроде Image/Text это 300 вертексов (limit меньше).
2. Материалы и свойства
Ограничение: Объекты должны использовать ОДИН и ТОТ ЖЕ материал (включая текстуры)
// ❌ Не батчится — разные материалы
var enemy1 = new GameObject("Enemy");
var renderer1 = enemy1.AddComponent<MeshRenderer>();
renderer1.material = new Material(redMaterial); // Red material
var enemy2 = new GameObject("Enemy");
var renderer2 = enemy2.AddComponent<MeshRenderer>();
renderer2.material = new Material(blueMaterial); // Blue material
// ✅ Батчится — одинаковые материалы
renderer1.material = sharedMaterial;
renderer2.material = sharedMaterial; // Один shared материал
Важно: Даже если использовать один материал, но менять его properties (color, texture):
// ❌ Нарушает батчинг — изменение material properties
renderer1.material.color = new Color(1, 0, 0); // Red
renderer2.material.color = new Color(0, 1, 0); // Green
// Это создаёт две разные material instances
// ✅ Правильно — использовать MaterialPropertyBlock
var mpb = new MaterialPropertyBlock();
mpb.SetColor("_Color", new Color(1, 0, 0));
renderer1.SetPropertyBlock(mpb);
var mpb2 = new MaterialPropertyBlock();
mpb2.SetColor("_Color", new Color(0, 1, 0));
renderer2.SetPropertyBlock(mpb2);
// Материал остаётся shared, меняется только блок свойств
3. Transform связность
Ограничение: Объекты в одной batch должны находиться рядом в иерархии Unity (для vertex-bound батчинга) или иметь определённое расположение в памяти
// ❌ Плохо для батчинга — объекты в разных местах иерархии
var parent1 = new GameObject("Parent1");
var child1 = new GameObject("Enemy");
child1.transform.parent = parent1.transform;
var parent2 = new GameObject("Parent2");
var child2 = new GameObject("Enemy");
child2.transform.parent = parent2.transform;
// ✅ Лучше — общий parent
var parent = new GameObject("Enemies");
child1.transform.parent = parent.transform;
child2.transform.parent = parent.transform;
4. Рендеринг очерёдность
Ограничение: Батчинг работает только для объектов, которые рендерятся последовательно
Если в очереди рендеринга:
1. Enemy (DrawCall 1)
2. Particle Effect (DrawCall 2)
3. Enemy (DrawCall 3) ← Не батчится с #1, потому что между ними другой DrawCall
5. Сортировка по глубине (Depth sorting)
Ограничение: Если камера требует сортировки по глубине (orthographic UI), динамический батчинг может не работать или работать хуже
6. Lighting и Shadows
Ограничение: Объекты с разными light probes или shadow settings не батчатся вместе
// ❌ Разные light probes
renderer1.probeAnchor = probe1;
renderer2.probeAnchor = probe2;
// Не батчатся
// ✅ Один light probe
renderer1.probeAnchor = probe1;
renderer2.probeAnchor = probe1;
// Могут батчиться
7. Scale и Rotation
Ограничение: Для некоторых шейдеров, особенно с vertex animation, негативный scale может предотвратить батчинг
// ❌ Может сломать батчинг
transform.scale = new Vector3(-1, 1, 1); // Negated scale
// ✅ Лучше решение
transform.scale = Vector3.one;
// Флип сделать в шейдере или отдельной текстуре
Практические советы
Профилирование
// Используй Frame Debugger в Unity Editor
// Window > Frame Debugger
// Там видишь exactно какие DrawCalls есть и почему они не батчатся
// Или код:
var materials = GetComponent<MeshRenderer>().sharedMaterials;
Debug.Log($"Material count: {materials.Length}");
Оптимизация для батчинга
- Используй SharedMaterial:
// ❌ Плохо
renderer.material.color = color; // Создаёт новый Material instance
// ✅ Хорошо
renderer.sharedMaterial = enemyMaterial;
var mpb = new MaterialPropertyBlock();
mpb.SetColor("_Color", color);
renderer.SetPropertyBlock(mpb);
- Комбинируй меши:
// Если много мелких объектов с одним материалом,
// рассмотри их комбинирование в один меш
public void CombineMeshes(GameObject[] objects) {
var combine = new CombineInstance[objects.Length];
for (int i = 0; i < objects.Length; i++) {
combine[i].mesh = objects[i].GetComponent<MeshFilter>().mesh;
combine[i].transform = objects[i].transform.localToWorldMatrix;
}
var combinedMesh = new Mesh();
combinedMesh.CombineMeshes(combine);
GetComponent<MeshFilter>().mesh = combinedMesh;
}
- Используй Static Batching для неподвижных объектов:
// Static batching не имеет ограничений по вертексам,
// но объект не может двигаться
GameObject.staticBatchingFlags = StaticBatchingFlags.Objects;
Когда батчинг НЕ важен
- GPU-bound сценарии (проблема в графике, не в CPU)
- Когда draw calls уже <1000 в frame
- На высокопроизводительных машинах (PC, консоли)
- Когда один big batch хуже многих маленьких (GPU memory pressure)
Итог
Dynamic Batching — это мощный инструмент оптимизации CPU, но требует понимания его ограничений. Не всегда это лучший выбор. Иногда лучше:
- Static Batching для неподвижного content
- GPU Instancing для повторяющихся объектов
- Просто хороший код без микро-оптимизаций
Профилируй, чтобы знать, где реальная проблема!