[performance] Архитектурный аудит FPS и микрофризов: GPU instancing, объёмные эффекты и разделение simulation/render #126

Open
opened 2026-11-05 21:32:16 +00:00 by chexit · 1 comment
Owner

🤖 Filed by Codex, AI-агент Владимира (chexit).

Проблема и границы доказанного

Владимир сообщает: Ryzen5600X + RTX3060, при налётах FPS иногда ниже40, частые микрофризы; раньше просадки были и на MacM5. Build/разрешение/trace конкретного сеанса не предоставлены. Аудит кода выполнен на AirDef 61aaa0bf359026711ba811a7f7e9877ce253ead2 и Horizon 278f356aa604fa21c2d15d0081823bbe0f891df2 5–6 ноября2026. Ни RTX3060, ни M5 в этой среде не профилировались. Ниже разделены доказанное устройство кода и архитектурные кандидаты, чью долю стоимости нужно измерить.

Цель — системно уменьшить CPU/GPU frame time и spikes, сохранив качество/частоту/правила симуляции и визуальное качество выбранного пресета. Не закрывать задачу выключением записи/дыма, уменьшением числа целей, частоты20Гц, точности коллизии или простым увеличением fuel. DLSS/FG из #124 — отдельная помощь GPU, не лечение CPU-stall. RT #125 — отдельный режим, не оптимизация текущего HD.

Что уже есть — не начинать расследование с нуля

#9/#44 уже дали GPU pass timing и графический полигон. #26/#30 сократили часть поисков и строк; runtime передаёт модели по handles. #116 ограничивает очередь acoustic worker. RAID-STABILITY и #122 воспроизвели конкретный WASM fuel spike: полный обход height-cache на каждом взрыве заменён локальным удалением. В архиве max tick fuel уменьшился с300M trap до135.3M при том же лимите. Это не GPU/FPS и не доказательство причины текущих микрофризов. Actual WASM отчёты исключают host CPU/GPU, а frame hook стенда не является рендером. Статический hd_bench с несколькими позами также не заменяет бой с p99/frame-time trace.

Подтверждённые крупные пути работы в текущем коде

  1. mod_model_geometry каждый отрисованный кадр перебирает poses, интерполирует transform, вызывает block_model_vertices и заново преобразует геометрию моделей. До этого строится special-material таблица из atlas; затем для каждого нового quad линейно ищется material по UV. Есть отсечение по4096м, но не полноценный per-view frustum на этом этапе. gfx.rs затем загружает baked entity vertices. Новые более детальные модели увеличивают эту работу. Это конкретный кандидат на retained GPU mesh + instancing, а не причина требовать упрощённые модели.
  2. HD smoke: CPU на каждом активном кадре запекает возраст сегментов, сортирует и строит brick lists. near.prepare и far.prepare повторяют это для двух128×64×128 lattice; обе сетки заново заполняются в1024² atlas. Подготовка создаёт временные vectors/uploads. Фиксированный объём near/far grid не исчезает при уменьшении render scale; только последующий экранный fog дешевеет. Кандидат: sparse/dirty clipmap и GPU-подготовка, с сохранением плотности и каналов clear tunnel.
  3. Sim::step последовательно выполняет планирование/AI, движение/логистику/РЛС/бой, запись и формирование presentation output. AirDef::tick добавляет smoke/HUD/map/SDK передачу. Game::tick_mods синхронно вызывает WASM и затем обрабатывает модели/вывод. Даже при наличии streaming/server workers это не превращает AirDef в независимую parallel simulation.
  4. В effects.wgsl:172–235 при HD fade становится0 после k>=0.28, но fbm raymarch6–14steps ещё выполняется для подходящих пикселей. После k>=0.55 нулевым становится и glow. Это статически видимая работа с нулевым вкладом; можно выделять фазы эффекта и прекращать такую работу без потери картинки. Это локальная часть большого плана, не замена ему.

Архитектурные направления и порядок проверки

A. Постоянная GPU-геометрия моделей — первый крупный кандидат. Кэш model mesh/material по registry/resource/model generation, один upload на изменение ассета. Per-instance buffers: stableID, current/previous transform, light/material parameters и animation parts; GPU interpolation, batched instanced draws/indirect где поддерживается. Frustum/occlusion per camera, shadow-caster lists отдельно. Не потерять детали вне main view, видимые в shadow/reflection/offscreen views. Сохранить геометрические нормали #123, lighting, поворот шарниров, muzzle, outlines и resource reload. Считать bytes uploaded/frame и CPU bake/model/quad counts до/после.

B. Объёмный дым/эффекты как отдельная сцена. Общие immutable segment records, компактный clock и incremental updates вместо повторной полной упаковки. GPU age evaluation/binning либо sparse активные bricks/clipmaps с dirty/version tracking; сравнить стоимость этих вариантов. Работа только над областями с вкладом; сохранить итоговое sampling/свет/плотность/туннели, включая clear без видимого дыма. Инвалидация по camera snap, segment birth/death, возрастным изменениям, world/seek/cut. Избежать повторного CPU bake для near/far там, где данные общие. Нет права просто снизить grid resolution/MAX_SEGMENTS или убрать дальние следы.

C. Scene snapshots и разделение частот. Боевой state20Гц, render poses/интерполяция на кадровой частоте, неизменяемые scene snapshots и отдельные ordered event streams (#88). Static UI/status/catalog/model data пересчитываются по invalidation, не каждый tick; camera-independent data готовятся один раз, per-view только необходимое. Не рассчитывать live presentation, чтобы сразу выбросить её при replay, если можно безопасно отделить extraction от combat. Подтвердить выигрыш профилем; не объявлять native core SDK-free автоматически выполнением этого разделения.

D. Содержательная многопоточность, а не std::thread внутри гостя. Исследовать owned simulation worker/Wasmtime instance на native стороне: versioned world-input snapshot, input/command sequence, фиксированный tick, immutable output snapshots. Один поток владеет mutable combat state; render/game threads получают сообщения. Host world writes/queries, sound, UI, GPU выполняются владельцами соответствующих подсистем, а не из произвольного worker. Ограниченные очереди, cancellation/generation, отсутствие stale geometry при unload/seek. Визуальные snapshots можно coalesce, combat events/выстрелы/взрывы нельзя терять. Поток не имеет права «догонять» нагрузку пропуском симуляционных тиков; отставание явно диагностируется. Учитывать server-thread и in-frame пути.

E. Параллельные чистые задачи + детерминированный commit. Если профиль покажет CPU core bottleneck: пространственные индексы кандидатов/коллизии, переиспользуемые scratch arenas/ID indexes, версии мира для переиспользования горизонтов, чистые per-unit вычисления на immutable snapshot. Результаты принимать в стабильном порядке; общий RNG и порядок damage/target assignment сохраняются. Не размазывать mutable Sim между потоками mutex-ами и не менять решения из-за очередности завершения jobs. Ускорять поиск точного ответа, не заменять его грубой моделью.

F. Общие бюджеты очередей и native interop. Объединять dirty section/remesh/upload задачи по version, bounded queues для streaming/audio/render preparation и instrumentation backlog/age. Переиспользовать staging/upload rings; timeline/fence ownership без per-frame blocking wait/readback. Pipeline/shader compilation кэшировать/прогревать; startup и resource reload измерять отдельно. Capture readback/offline audio — отдельный режим, который не должен незаметно выполняться в live. Новая worker схема не повторяет снятие busy-guard из #116.

Измерения, которые делают аудит проверяемым

Сначала один воспроизводимый scene/record/seed и реальные terrain replies; фиксировать build+engine+driver/backend, resolution/scale, VSync, preset, monitors/grid, звук, запись и counts реально активных drones/missiles/models/smoke. Состав матрицы: idle → одиночный налёт → два перекрывающихся максимальных → взрывы/изменение чанков → плотный дым → monitor grid → replay/cut/export. Проверять факт принятия обоих приказов и полёт, а не прохождение сценария в ожидании unknown geometry.

Trace должен связывать: CPU frame/tick p50/p95/p99/max, host/guest wall-time и fuel отдельно, simulation phases, allocations/peak RAM, geometry bake/upload bytes, draw/instance counts, per-pass GPU timestamps, VRAM, worker queue length/age и pipeline compile events. Показать frame-time график и spikes, не только средний FPS. Архивные M5 Polygon/GPU и synthetic acoustic цифры не выдавать за новые измерения боевой сцены3060.

Для каждой большой переделки: baseline → один вариант → измерение на той же записи, затем следующий. Целевой frame budget определить для зафиксированного разрешения/пресета по baseline; не обещать «будет60» без замера. Главная цель — устранить длинные stalls и улучшитьp95/p99, а не увеличить счётчик presented FPS.

Обязательные инварианты и приёмка

  • Одинаковые input/seed/world answers дают тот же ordered combat trace: позиции/velocities, tracks, damage, RNG, решения пусков и логистики. Одних совпавших итоговых kills/hits недостаточно. Для разрешённых float-различий задать явный допуск, не скрывать изменение модели.
  • Физика20Гц, непрерывная коллизия, freshness РЛС/AI, боезапас/снабжение, запись/replay и visual detail сохраняются. Не отключать систему ради прохождения perf-теста.
  • Бounded RAM/VRAM/queues после продолжительного боя, unload/world switches, повторных replay/export/cancel. Улучшение среднего кадра не принимается при регрессии p99/latency или росте памяти без границ.
  • Итоговый отчёт различает выполненные изменения, измеренный эффект на5600X/3060 и M5 и оставшиеся гипотезы. При отсутствии устройства это честный pending acceptance.

Связано: #9/#44, #87/#88/#89, #102/#116/#122/#123; #124 (upscaling) и #125 (RT). Масштабная оптимизация может состоять из нескольких реализационных PR/подзадач; это issue фиксирует общий контракт и измеримый план, а не требует один гигантский rewrite-коммит.

🤖 Filed by Codex, AI-агент Владимира (chexit). ### Проблема и границы доказанного Владимир сообщает: Ryzen5600X + RTX3060, при налётах FPS иногда ниже40, частые микрофризы; раньше просадки были и на MacM5. Build/разрешение/trace конкретного сеанса не предоставлены. Аудит кода выполнен на AirDef `61aaa0bf359026711ba811a7f7e9877ce253ead2` и Horizon `278f356aa604fa21c2d15d0081823bbe0f891df2` 5–6 ноября2026. Ни RTX3060, ни M5 в этой среде не профилировались. Ниже разделены **доказанное устройство кода** и архитектурные кандидаты, чью долю стоимости нужно измерить. Цель — системно уменьшить CPU/GPU frame time и spikes, **сохранив качество/частоту/правила симуляции и визуальное качество выбранного пресета**. Не закрывать задачу выключением записи/дыма, уменьшением числа целей, частоты20Гц, точности коллизии или простым увеличением fuel. DLSS/FG из #124 — отдельная помощь GPU, не лечение CPU-stall. RT #125 — отдельный режим, не оптимизация текущего HD. ### Что уже есть — не начинать расследование с нуля #9/#44 уже дали GPU pass timing и графический полигон. #26/#30 сократили часть поисков и строк; runtime передаёт модели по handles. #116 ограничивает очередь acoustic worker. [RAID-STABILITY](https://gitea.maoniao.team/chexit/airdef/src/commit/61aaa0bf359026711ba811a7f7e9877ce253ead2/docs/RAID-STABILITY.md) и #122 воспроизвели конкретный WASM fuel spike: полный обход height-cache на каждом взрыве заменён локальным удалением. В архиве max tick fuel уменьшился с300M trap до135.3M при том же лимите. **Это не GPU/FPS и не доказательство причины текущих микрофризов.** Actual WASM отчёты исключают host CPU/GPU, а frame hook стенда не является рендером. Статический hd_bench с несколькими позами также не заменяет бой с p99/frame-time trace. ### Подтверждённые крупные пути работы в текущем коде 1. [mod_model_geometry](https://gitea.maoniao.team/chexit/horizon-airdef/src/commit/278f356aa604fa21c2d15d0081823bbe0f891df2/crates/horizon-client/src/game/mod_models.rs#L390) **каждый отрисованный кадр** перебирает poses, интерполирует transform, вызывает block_model_vertices и заново преобразует геометрию моделей. До этого строится special-material таблица из atlas; затем для каждого нового quad линейно ищется material по UV. Есть отсечение по4096м, но не полноценный per-view frustum на этом этапе. [gfx.rs](https://gitea.maoniao.team/chexit/horizon-airdef/src/commit/278f356aa604fa21c2d15d0081823bbe0f891df2/crates/horizon-client/src/gfx.rs#L5098) затем загружает baked entity vertices. Новые более детальные модели увеличивают эту работу. Это конкретный кандидат на **retained GPU mesh + instancing**, а не причина требовать упрощённые модели. 2. [HD smoke](https://gitea.maoniao.team/chexit/horizon-airdef/src/commit/278f356aa604fa21c2d15d0081823bbe0f891df2/crates/horizon-client/src/gfx/hd/smoke.rs#L8): CPU на каждом активном кадре запекает возраст сегментов, сортирует и строит brick lists. `near.prepare` и `far.prepare` повторяют это для двух128×64×128 lattice; обе сетки заново заполняются в1024² atlas. Подготовка создаёт временные vectors/uploads. Фиксированный объём near/far grid не исчезает при уменьшении render scale; только последующий экранный fog дешевеет. Кандидат: sparse/dirty clipmap и GPU-подготовка, с сохранением плотности и каналов clear tunnel. 3. [Sim::step](https://gitea.maoniao.team/chexit/airdef/src/commit/61aaa0bf359026711ba811a7f7e9877ce253ead2/airdef-mod/src/sim/mod.rs#L1532) последовательно выполняет планирование/AI, движение/логистику/РЛС/бой, запись и формирование presentation output. [AirDef::tick](https://gitea.maoniao.team/chexit/airdef/src/commit/61aaa0bf359026711ba811a7f7e9877ce253ead2/airdef-mod/src/lib.rs) добавляет smoke/HUD/map/SDK передачу. [Game::tick_mods](https://gitea.maoniao.team/chexit/horizon-airdef/src/commit/278f356aa604fa21c2d15d0081823bbe0f891df2/crates/horizon-client/src/game/mods.rs#L719) синхронно вызывает WASM и затем обрабатывает модели/вывод. Даже при наличии streaming/server workers это не превращает AirDef в независимую parallel simulation. 4. В [effects.wgsl:172–235](https://gitea.maoniao.team/chexit/airdef/src/commit/61aaa0bf359026711ba811a7f7e9877ce253ead2/airdef-mod/shaders/effects.wgsl#L172) при HD `fade` становится0 после k>=0.28, но fbm raymarch6–14steps ещё выполняется для подходящих пикселей. После k>=0.55 нулевым становится и glow. Это статически видимая работа с нулевым вкладом; можно выделять фазы эффекта и прекращать такую работу без потери картинки. Это локальная часть большого плана, не замена ему. ### Архитектурные направления и порядок проверки **A. Постоянная GPU-геометрия моделей — первый крупный кандидат.** Кэш model mesh/material по registry/resource/model generation, один upload на изменение ассета. Per-instance buffers: stableID, current/previous transform, light/material parameters и animation parts; GPU interpolation, batched instanced draws/indirect где поддерживается. Frustum/occlusion per camera, shadow-caster lists отдельно. Не потерять детали вне main view, видимые в shadow/reflection/offscreen views. Сохранить геометрические нормали #123, lighting, поворот шарниров, muzzle, outlines и resource reload. Считать bytes uploaded/frame и CPU bake/model/quad counts до/после. **B. Объёмный дым/эффекты как отдельная сцена.** Общие immutable segment records, компактный clock и incremental updates вместо повторной полной упаковки. GPU age evaluation/binning либо sparse активные bricks/clipmaps с dirty/version tracking; сравнить стоимость этих вариантов. Работа только над областями с вкладом; сохранить итоговое sampling/свет/плотность/туннели, включая clear без видимого дыма. Инвалидация по camera snap, segment birth/death, возрастным изменениям, world/seek/cut. Избежать повторного CPU bake для near/far там, где данные общие. Нет права просто снизить grid resolution/MAX_SEGMENTS или убрать дальние следы. **C. Scene snapshots и разделение частот.** Боевой state20Гц, render poses/интерполяция на кадровой частоте, неизменяемые scene snapshots и отдельные ordered event streams (#88). Static UI/status/catalog/model data пересчитываются по invalidation, не каждый tick; camera-independent data готовятся один раз, per-view только необходимое. Не рассчитывать live presentation, чтобы сразу выбросить её при replay, если можно безопасно отделить extraction от combat. Подтвердить выигрыш профилем; не объявлять native core SDK-free автоматически выполнением этого разделения. **D. Содержательная многопоточность, а не std::thread внутри гостя.** Исследовать owned simulation worker/Wasmtime instance на native стороне: versioned world-input snapshot, input/command sequence, фиксированный tick, immutable output snapshots. Один поток владеет mutable combat state; render/game threads получают сообщения. Host world writes/queries, sound, UI, GPU выполняются владельцами соответствующих подсистем, а не из произвольного worker. Ограниченные очереди, cancellation/generation, отсутствие stale geometry при unload/seek. Визуальные snapshots можно coalesce, **combat events/выстрелы/взрывы нельзя терять**. Поток не имеет права «догонять» нагрузку пропуском симуляционных тиков; отставание явно диагностируется. Учитывать server-thread и in-frame пути. **E. Параллельные чистые задачи + детерминированный commit.** Если профиль покажет CPU core bottleneck: пространственные индексы кандидатов/коллизии, переиспользуемые scratch arenas/ID indexes, версии мира для переиспользования горизонтов, чистые per-unit вычисления на immutable snapshot. Результаты принимать в стабильном порядке; общий RNG и порядок damage/target assignment сохраняются. Не размазывать mutable Sim между потоками mutex-ами и не менять решения из-за очередности завершения jobs. Ускорять поиск точного ответа, не заменять его грубой моделью. **F. Общие бюджеты очередей и native interop.** Объединять dirty section/remesh/upload задачи по version, bounded queues для streaming/audio/render preparation и instrumentation backlog/age. Переиспользовать staging/upload rings; timeline/fence ownership без per-frame blocking wait/readback. Pipeline/shader compilation кэшировать/прогревать; startup и resource reload измерять отдельно. Capture readback/offline audio — отдельный режим, который не должен незаметно выполняться в live. Новая worker схема не повторяет снятие busy-guard из #116. ### Измерения, которые делают аудит проверяемым Сначала один воспроизводимый scene/record/seed и реальные terrain replies; фиксировать build+engine+driver/backend, resolution/scale, VSync, preset, monitors/grid, звук, запись и counts реально активных drones/missiles/models/smoke. Состав матрицы: idle → одиночный налёт → два перекрывающихся максимальных → взрывы/изменение чанков → плотный дым → monitor grid → replay/cut/export. Проверять факт принятия обоих приказов и полёт, а не прохождение сценария в ожидании unknown geometry. Trace должен связывать: CPU frame/tick p50/p95/p99/max, host/guest wall-time и fuel отдельно, simulation phases, allocations/peak RAM, geometry bake/upload bytes, draw/instance counts, per-pass GPU timestamps, VRAM, worker queue length/age и pipeline compile events. Показать frame-time график и spikes, не только средний FPS. Архивные M5 Polygon/GPU и synthetic acoustic цифры не выдавать за новые измерения боевой сцены3060. Для каждой большой переделки: baseline → один вариант → измерение на той же записи, затем следующий. Целевой frame budget определить для зафиксированного разрешения/пресета по baseline; не обещать «будет60» без замера. Главная цель — устранить длинные stalls и улучшитьp95/p99, а не увеличить счётчик presented FPS. ### Обязательные инварианты и приёмка - Одинаковые input/seed/world answers дают тот же ordered combat trace: позиции/velocities, tracks, damage, RNG, решения пусков и логистики. Одних совпавших итоговых kills/hits недостаточно. Для разрешённых float-различий задать явный допуск, не скрывать изменение модели. - Физика20Гц, непрерывная коллизия, freshness РЛС/AI, боезапас/снабжение, запись/replay и visual detail сохраняются. Не отключать систему ради прохождения perf-теста. - Бounded RAM/VRAM/queues после продолжительного боя, unload/world switches, повторных replay/export/cancel. Улучшение среднего кадра не принимается при регрессии p99/latency или росте памяти без границ. - Итоговый отчёт различает выполненные изменения, измеренный эффект на5600X/3060 и M5 и оставшиеся гипотезы. При отсутствии устройства это честный pending acceptance. Связано: #9/#44, #87/#88/#89, #102/#116/#122/#123; #124 (upscaling) и #125 (RT). Масштабная оптимизация может состоять из нескольких реализационных PR/подзадач; это issue фиксирует общий контракт и измеримый план, а не требует один гигантский rewrite-коммит.
Author
Owner

🤖 Agent: Codex — дополнение по upstream Horizon, 6 ноября 2026; полное сравнение и план в #130.

Нашлись два конкретных небольших исправления, которые можно проверить раньше большой архитектурной переработки:

  1. f7b77fbc8844e8dd4d49ea1db29c04cce1c2868c: удержание oversized staging buffers при постепенно растущем entity upload. В нашем gfx/upload.rs старый путь сохранён, а entity batch проходит через него каждый кадр. Upstream округляет staging allocation до степени двойки, сохраняя реальную длину копирования. Патч с заменой только пути client-render→client проходит git apply --check на 278f356aa. Вместе с backport перенести upstream regression/readback tests и измерить удержанную память на серии растущих налётов. Это источник возможного роста памяти, а не уже измеренная причина пользовательских фризов.
  2. da4ded1d7cf9b750160a039244d2e07efd7c9207 / wgpu#10714: Windows Vulkan не должен ждать acquire fence в non-FIFO режиме. Полный патч с notices проходит git apply --check без адаптации. Проверить на 5600X/RTX3060 с VSync off и отдельно FIFO; к Metal/DX12 этот конкретный путь не относится.

Дополнительно исследовать бюджетирование far-terrain результатов cb6a80698ce8d0207e4ffbf4b1a06e88fb22708a и освобождение arena pages вне render thread (#6687 upstream). Их upstream A/B не выдаётся за AirDef FPS.

Правки в рабочие файлы не применялись, объединённый клиент/эти backport не собирались и игровой A/B здесь не запускался. Для полной новой базы возникает отдельный блокер: upstream удалил WASM runtime, SDK и sandbox tests; прежние успешные native/WASM тесты текущего форка не подтверждают совместимость с этим Nightly.

🤖 Agent: Codex — дополнение по upstream Horizon, 6 ноября 2026; полное сравнение и план в #130. Нашлись два конкретных небольших исправления, которые можно проверить раньше большой архитектурной переработки: 1. f7b77fbc8844e8dd4d49ea1db29c04cce1c2868c: удержание oversized staging buffers при постепенно растущем entity upload. В нашем gfx/upload.rs старый путь сохранён, а entity batch проходит через него каждый кадр. Upstream округляет staging allocation до степени двойки, сохраняя реальную длину копирования. Патч с заменой только пути client-render→client проходит git apply --check на 278f356aa. Вместе с backport перенести upstream regression/readback tests и измерить удержанную память на серии растущих налётов. Это источник возможного роста памяти, а не уже измеренная причина пользовательских фризов. 2. da4ded1d7cf9b750160a039244d2e07efd7c9207 / wgpu#10714: Windows Vulkan не должен ждать acquire fence в non-FIFO режиме. Полный патч с notices проходит git apply --check без адаптации. Проверить на 5600X/RTX3060 с VSync off и отдельно FIFO; к Metal/DX12 этот конкретный путь не относится. Дополнительно исследовать бюджетирование far-terrain результатов cb6a80698ce8d0207e4ffbf4b1a06e88fb22708a и освобождение arena pages вне render thread (#6687 upstream). Их upstream A/B не выдаётся за AirDef FPS. Правки в рабочие файлы не применялись, объединённый клиент/эти backport не собирались и игровой A/B здесь не запускался. Для полной новой базы возникает отдельный блокер: upstream удалил WASM runtime, SDK и sandbox tests; прежние успешные native/WASM тесты текущего форка не подтверждают совместимость с этим Nightly.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chexit/airdef#126