Files
open-wc/RENDER.md
T

547 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Renderer Notes
Цель renderer-работы в этом проекте: добиться ощущения производительности оригинального клиента WoW 3.3.5a в Godot, без видимых фризов при переходе ADT -> ADT и без постоянного отката видимых участков к низкому качеству.
Этот документ фиксирует текущее состояние рендера, сделанные оптимизации и практические правила дальнейшей работы.
## Главный принцип
Оригинальный клиент WoW выживал на слабых CPU за счет агрессивного сокращения CPU work:
- мало draw calls;
- крупные батчи terrain;
- instancing/батчинг повторяющихся props;
- ленивое и заранее подготовленное подключение деталей;
- отдельные LOD/visibility решения для terrain, M2 и WMO;
- заранее подготовленные mipmaps и компактные texture formats.
В Godot мы идем тем же путем: runtime не должен строить тяжелую геометрию, декодировать много текстур или инстанцировать большие сцены в момент, когда игрок пересекает границу ADT.
## Основные файлы
- `src/scenes/streaming/streaming_world_loader.gd` - главный runtime streamer мира.
- `src/scenes/streaming/eastern_kingdoms_streaming.tscn` - текущая сцена для проверки Azeroth/Eastern Kingdoms.
- `addons/mpq_extractor/loaders/adt_builder.gd` - сборка ADT terrain, control splat материалов и MH2O liquids.
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
- `addons/mpq_extractor/loaders/wmo_builder.gd` - сборка WMO group meshes, doodads и MLIQ liquids.
- `addons/mpq_extractor/loaders/wow_liquid_material.gd` - общий shader material для ADT/WMO жидкостей.
- `src/scenes/sky/wow_sky_controller.gd` - DBC-based outdoor lighting/sky controller.
- `src/scenes/player/third_person_wow_controller.gd` - тестовый third-person controller.
- `src/native/src/adt_loader.cpp` - ADT/MH2O parsing.
- `src/native/src/blp_loader.cpp` - BLP decoding.
- `src/native/src/m2_loader.cpp` / `.h` - M2/SKIN parsing.
- `src/native/src/wmo_loader.cpp` - WMO/MLIQ parsing.
## Runtime Streaming
`StreamingWorldLoader` строит мир вокруг focus/camera/player. Карта берется из `WDT`, поэтому для Azeroth загружается реальный список ADT tiles, например:
```text
StreamingWorld: loaded 687 ADT tiles from WDT for Azeroth
```
Важные runtime очереди:
- `tile` - загрузка/парсинг ADT tile.
- `terrainup` - upgrade terrain до полного качества.
- `terrainctl` - подключение control splat terrain cache.
- `terrainsplat` - legacy/high quality splat path, сейчас не основной путь.
- `detail` - запуск деталей tile.
- `m2task` - background grouping M2 placements.
- `m2mesh` - загрузка/финализация M2 mesh resource.
- `m2build` - создание MultiMesh групп.
- `wmobuild` - подключение WMO instances.
- `wmogroups` - подключение render-cache групп WMO.
- `lod+` / `lod-` - создание/удаление tile LOD.
- `chunk+` / `chunk-` - legacy chunk mesh ops.
Фриз-профили пишутся строками вида:
```text
HITCH 22.75ms refresh=false queues[...] terrainup=21.99 terrainq=0.28 m2build=0.08
```
Главный смысл этих логов: если hitch больше порога, смотреть самый большой named section. Мы уже находили так bottlenecks:
- `details=4000-5800ms` - тяжелая синхронная сборка деталей.
- `m2build=20-75ms` - слишком крупные MultiMesh записи за один frame.
- `wmobuild=300-1100ms` - тяжелое инстанцирование WMO `.tscn`.
- `terrainsplat=150-220ms` - runtime high-quality splat generation.
- `terrainup=20-45ms` - слишком дорогой upgrade terrain за frame.
## Quality Presets And Budgets
Главные настройки находятся в `streaming_world_loader.gd`:
- `quality_preset`: `Performance`, `Balanced`, `High`, `Custom`.
- `tiles_per_tick`
- `max_concurrent_tile_tasks`
- `tile_finalize_ops_per_tick`
- `terrain_upgrade_finalize_ops_per_tick`
- `terrain_control_splat_cache_finalize_ops_per_tick`
- `m2_build_groups_per_tick`
- `m2_multimesh_batch_size`
- `m2_mesh_finalize_ops_per_tick`
- `wmo_build_instances_per_tick`
- `wmo_render_group_ops_per_tick`
Практическое правило: если новая оптимизация возвращает hitch, сначала уменьшать per-frame budgets, а не отключать качество целиком. Видимое качество должно приходить заранее, вне поля зрения игрока.
## Terrain Rendering
### Старый подход
Изначально terrain мог быть слишком мелко разбитым:
- много chunk/node операций;
- отдельные материалы/меши;
- слабый контроль над моментом upgrade;
- низкое качество или мыло при возврате в уже посещенный участок.
Это приводило к CPU overhead и видимому popping.
### Текущий подход
Основной путь качества сейчас - `ControlSplatADTTile`:
- geometry хранится как готовый tile mesh;
- texture set собирается в `Texture2DArray`;
- alpha maps собираются в один atlas;
- layer ids хранятся в layer index map;
- material подключается через `ADTBuilder.apply_control_splat_material`;
- runtime должен только загрузить cache resource и применить material, а не пересобирать splat texture на лету.
Кеш:
```text
res://data/cache/terrain_control_splat_v3/<Map>/<Map>_<x>_<y>.res
```
Формат:
```text
ControlSplatADTTile.FORMAT_VERSION = 3
StreamingWorldLoader.REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION = 3
```
Команда bake:
```powershell
$exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
& $exe --headless --path . --script res://src/tools/build_adt_control_splat_cache.gd -- --map Azeroth --jobs auto --texture-size 256 --lod 0 --force
```
Важно: `--force` пересобирает cache. Не запускать его случайно, если задача не про bake.
### Texture Quality
Что было сделано:
- перешли от baked albedo как основного visible quality к control splat;
- оставили baked/streaming terrain как fallback и warm path;
- добавили/используем full quality terrain радиус;
- добавили retention/cache для full-quality meshes, чтобы возврат в tile не сбрасывал видимый участок обратно в низкое качество;
- добавили логи `TERRAIN_QUALITY`, чтобы понимать, когда видимый набор tile действительно готов.
Ключевые настройки:
- `terrain_full_quality_enabled`
- `terrain_full_quality_radius_tiles`
- `terrain_control_splat_quality_enabled`
- `terrain_control_splat_primary`
- `terrain_control_splat_radius_tiles`
- `max_active_terrain_control_splat_tiles`
- `terrain_quality_mesh_cache_limit`
- `prewarm_tile_margin`
- `retain_tile_margin`
- `full_lod_prewarm_tiles`
- `boundary_prefetch_threshold`
Правило для High: игрок не должен видеть low-res terrain под ногами или прямо перед собой. Low quality допустим только далеко или вне видимости.
### Texture Seams
Была проблема видимых стыков и неправильного повторения texture pattern между чанками. Исправление в shader control splat:
- terrain texture sample теперь использует continuous `chunk_uv`, а не `local_uv`;
- alpha atlas sample clamp'ится внутри 64x64 chunk alpha cell;
- это уменьшает видимые seams на границах MCNK.
Файл: `addons/mpq_extractor/loaders/adt_builder.gd`, `_get_control_splat_shader()`.
## ADT Baking
Были ускорены bake scripts. Главный рабочий bake для качества terrain:
```text
src/tools/build_adt_control_splat_cache.gd
```
Что важно:
- параллельные workers;
- cache resource на ADT tile;
- не пересобирать в runtime;
- incomplete tile считается ошибкой worker и выводится warning;
- cache лежит в `data/cache` и не должен попадать в git.
Пример успешного результата:
```text
Starting parallel ADT control splat cache: jobs=15
Parallel control splat cache finished. jobs=15 failed_workers=0 elapsed=...
```
Если один worker падает:
```text
WARNING: Control splat produced incomplete tile: ...
failed_workers=1
```
Такой tile надо перезапечь отдельно или проверить исходный ADT.
## M2 Rendering
Цель: тысячи props не должны быть тысячами независимых Node3D/MeshInstance3D.
Текущий подход:
- ADT `MDDF` placements группируются по M2 path;
- для каждого M2 path создается один или несколько `MultiMeshInstance3D`;
- transforms пишутся батчами через `m2_multimesh_batch_size`;
- mesh/resource finalization ограничен `m2_mesh_finalize_ops_per_tick`;
- build групп ограничен `m2_build_groups_per_tick`.
Это убрало большие `m2build` фризы, где раньше один frame мог тратить 20-75ms только на M2.
Кеш:
```text
res://data/cache/m2_glb
```
Скрипты:
```text
src/tools/bake_m2_cache.gd
src/tools/collect_world_m2_list.gd
src/tools/m2_to_gltf.py
```
Что улучшали:
- M2 parser читает дополнительные material/texture/batch данные;
- BLP alpha fixes улучшили прозрачность листвы/декора;
- `M2Builder` улучшен для texture/material setup;
- runtime M2 строится через MultiMesh, а не как дерево отдельных props.
Ограничения:
- M2 animation/skinning пока не полноценные;
- particles/ribbons не реализованы;
- WoW material flags не полностью совпадают;
- billboards/leaf bones пока не как в клиенте.
## WMO Rendering
Цель: WMO не должны фризить сцену при догрузке, особенно большие здания/города.
Что сделано:
- `WMOLoader` читает root/group WMO данные и MLIQ;
- `WMOBuilder` строит group meshes;
- runtime registry дедуплицирует WMO по `unique_id`;
- WMO на границе ADT не дублируются;
- большие WMO не должны инстанцироваться как тяжелые `.tscn` во время движения;
- добавлен lightweight render-cache path для WMO groups;
- `wmo_render_group_ops_per_tick` ограничивает подключение WMO groups по кадрам;
- `wmo_max_runtime_scene_mb` защищает от тяжелого runtime `.tscn` instantiate.
Кеши:
```text
res://data/cache/wmo_tscn
res://data/cache/wmo_render_v1
```
Скрипты:
```text
src/tools/bake_wmo_cache.gd
src/tools/build_wmo_streaming_cache.gd
```
Что улучшали по качеству:
- включен texture repeat для WMO materials;
- улучшены BLP alpha/texture decode проблемы;
- WMO liquid mesh получает нормали/UV;
- WMO liquids используют общий `WowLiquidMaterial`.
Ограничения:
- portal/room based visibility WMO пока не повторяет клиент;
- occluders не являются полноценной заменой WoW portal culling;
- часть WMO material flags еще требует сверки с клиентом.
## Liquids
Open-realm не дал готовую реализацию water shader: в его docs liquid rendering отмечен как future pass. Поэтому добавлен собственный легкий Godot shader.
Файл:
```text
addons/mpq_extractor/loaders/wow_liquid_material.gd
```
Подключение:
- ADT `MH2O` через `ADTBuilder.build_tile_water_scene`;
- WMO `MLIQ` через `WMOBuilder._build_group_liquid`.
Что делает shader:
- один общий `ShaderMaterial` cache для ADT/WMO;
- world-space animated ripples;
- fresnel highlight;
- отдельные цвета/параметры для water, magma, slime;
- без screen texture/refraction, чтобы не раздувать GPU cost;
- shadows у liquid mesh выключены.
Почему так:
- дешево;
- не грузит дополнительные textures;
- не создает D3D12 descriptor pressure;
- дает рабочую визуальную воду уже сейчас.
Ограничения:
- нет настоящего depth fade;
- нет shoreline foam/blending;
- LiquidType.dbc texture selection пока не используется;
- waterfalls/flowing water требуют отдельного M2/material path.
## Sky And Lighting
Добавлен `WowSkyController`:
```text
src/scenes/sky/wow_sky_controller.gd
```
Что он делает:
- читает DBC данные для light profiles;
- определяет текущую area/zone по позиции;
- выбирает lighting/fog profile;
- логирует `SKY_LIGHT`;
- поддерживает LightSkybox model loading;
- двигает skybox model за камерой/player.
Пример лога:
```text
SKY_LIGHT time=13.00 map=0 area=279:... zone=36:... profiles=[...] skybox=0 fog=614..31556 density=0.500
SKYBOX_MODEL id=... path=...
```
Ограничения:
- зона/skybox transitions еще требуют ручной сверки с клиентом;
- не все outdoor skybox models используются на всех локациях;
- lighting пока приближение, а не 1:1 с fixed-function клиентом.
## Shadows
Цель по производительности сейчас важнее полного совпадения dynamic shadows.
Текущее правило:
- terrain shadows выключены;
- M2 shadows выключены;
- WMO shadows выключены;
- liquid shadows выключены.
Настройки:
```text
terrain_cast_shadows
m2_cast_shadows
wmo_cast_shadows
```
Почему:
- old WoW 3.3.5a использовал очень дешевую модель теней;
- полноценные Godot realtime shadows на большом мире быстро создают лишний GPU/CPU cost;
- сначала нужна стабильная streaming performance, затем можно добавлять selective blob/projected shadows.
Для будущего: если брать идеи Cataclysm, делать не full dynamic shadows everywhere, а ограниченные character/near-object shadows с жестким distance budget.
## Third Person Controller
Добавлен тестовый контроллер для оценки рендера "с земли":
```text
src/scenes/player/third_person_wow_controller.gd
```
Зачем:
- свободная камера плохо показывает реальные проблемы игрока;
- нужен WoW-like темп перемещения;
- sprint нужен для стресс-теста ADT boundary streaming.
Модель сейчас не важна, используется простая capsule.
## Texture/BLP Work
Что сделано:
- BLP loader улучшен для alpha handling;
- texture mipmaps генерируются для runtime ImageTexture там, где texture создается из decoded BLP;
- для WMO/M2 материалов включается anisotropic/mipmap filtering там, где texture подключается;
- включен repeat для WMO материалов.
Важная проблема Godot/D3D12:
```text
Cannot create uniform set because there's not enough room in the RESOURCES descriptor heap.
Please increase rendering/rendering_device/d3d12/max_resource_descriptors.
```
Это возникает, когда renderer создает слишком много уникальных texture/uniform resources. Наши меры:
- кешировать materials/textures;
- не создавать уникальный heavy shader/material на каждый chunk;
- использовать `Texture2DArray` для terrain control splat;
- ограничивать активные high-quality terrain tiles;
- избегать screen/refraction textures для water.
Если ошибка возвращается, увеличивать project setting `rendering/rendering_device/d3d12/max_resource_descriptors` и одновременно искать, где плодятся уникальные материалы.
## Caches
Активные cache directories:
```text
res://data/cache/baked_terrain_v2
res://data/cache/baked_terrain_stream_v1
res://data/cache/terrain_control_splat_v3
res://data/cache/terrain_splat_v1
res://data/cache/m2_glb
res://data/cache/wmo_tscn
res://data/cache/wmo_render_v1
```
Правила:
- cache не коммитить;
- при изменении resource format bump'ать `FORMAT_VERSION`;
- при изменении shader/material без изменения cached resource иногда достаточно restart scene;
- при изменении baked geometry/resource payload нужен rebake;
- WMO render cache нужен для больших WMO, иначе будут `wmobuild` фризы.
Версии:
```text
Baked terrain required: 4
Splat terrain required: 1
Control splat required: 3
WMO streaming resource: 1
```
## Debug Logs
Важные логи:
```text
HITCH ...
PERF ...
TERRAIN_QUALITY ...
SKY_LIGHT ...
SKYBOX_MODEL ...
```
Как читать `HITCH`:
- если большой `terrainup` - full-quality terrain подключается слишком тяжело;
- если большой `terrainctl` - control splat cache финализация слишком дорогая;
- если большой `terrainsplat` - runtime splat path не должен быть включен для обычной игры;
- если большой `m2mesh` - mesh resource finalization надо сильнее дробить;
- если большой `m2build` - уменьшить `m2_build_groups_per_tick` или `m2_multimesh_batch_size`;
- если большой `wmobuild` - использовать render cache, уменьшить `wmo_build_instances_per_tick`;
- если большой `wmogroups` - уменьшить `wmo_render_group_ops_per_tick`.
## Reference Findings
По open-realm:
- полезен как документация форматов/пайплайна;
- terrain/ADT/WMO/M2 идеи можно сверять;
- полноценный liquid rendering там не реализован;
- skybox/liquid не стоит напрямую переносить как готовый код.
По WoW 3.3.5a:
- старый клиент не рендерил все как modern physically based renderer;
- он агрессивно экономил CPU;
- основная цель для нас - frame pacing и отсутствие hitch, а не дорогие эффекты.
## Commands
Headless smoke:
```powershell
$exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
& $exe --headless --path . --quit
```
Run scene headless:
```powershell
$exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
& $exe --headless --path . src/scenes/streaming/eastern_kingdoms_streaming.tscn --quit
```
Build native GDExtension:
```powershell
cd src\native
.\build.bat Release
```
Build ADT control splat cache:
```powershell
$exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
& $exe --headless --path . --script res://src/tools/build_adt_control_splat_cache.gd -- --map Azeroth --jobs auto --texture-size 256 --lod 0 --force
```
Build WMO render cache:
```powershell
$exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
& $exe --headless --path . --script res://src/tools/build_wmo_streaming_cache.gd -- --map Azeroth
```
## Current Known Gaps
- M2 animation/skinning.
- M2 particles/ribbons.
- Full WoW material flag parity.
- True WMO portal/room culling.
- Selective cheap dynamic shadows.
- Liquid depth fade/shore blending.
- LiquidType.dbc based water textures.
- Better skybox transition validation per zone.
- Automated renderer regression scenes/screenshots.
## Practical Rule For Future Work
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
High quality must appear before the player arrives, not after crossing the ADT boundary.