From 9217f9a5ccb6a9976d835ad137ecf217db111ed1 Mon Sep 17 00:00:00 2001 From: chexit Date: Wed, 4 Nov 2026 20:38:56 +0300 Subject: [PATCH] Report camera readiness in /airdef capabilities Pin the engine at 6a53a626c (API 30) and show render.camera-status: the calling run's set-views cameras, whether the last noted frame was a world frame, the request's world generation, each camera's reason and the run's own main-view selection. As the other lines, this is a host milestone: availability, request, preparation and why, not a GPU frame or a visible picture. A unit test pins the line: readiness stays per camera and apart from the run's main-view selection. docs/ENGINE.md, the 4 November continuation and the status table now say what prepared means (a picture encoded into the camera's target, or a world frame drawn from it as this run's screen view, only for the run whose selection the screen used), the per-run/per-world resets, and what remains: GPU acceptance. Verified: python3 tools/airdef.py check (gitlink, packer, mod needs 30, fork 30, game 30); test-wasm 27 steps; airdef-mod 65 unit, 1 boundary and 5 scenarios; python3 -m unittest discover -s tools 28 tests. Co-Authored-By: Kimi Code --- airdef-mod/src/capabilities.rs | 75 ++++++++++++++++++++++++++++++++- docs/CONTINUATION-2026-11-04.md | 10 ++++- docs/ENGINE.md | 23 +++++++--- docs/IMPLEMENTATION-STATUS.md | 9 ++-- horizon | 2 +- 5 files changed, 106 insertions(+), 13 deletions(-) diff --git a/airdef-mod/src/capabilities.rs b/airdef-mod/src/capabilities.rs index 7dd5293..949292d 100644 --- a/airdef-mod/src/capabilities.rs +++ b/airdef-mod/src/capabilities.rs @@ -59,12 +59,42 @@ fn line( format!("{name}: API={available}, включено={enabled}, запрос={accepted}, подготовлено={prepared}, поколение={generation}, {why}") } +fn camera_line(s: &render::CameraState) -> String { + let cameras: Vec = s + .cameras + .iter() + .map(|c| { + let why = match c.reason { + render::CameraReason::AwaitingFrame => "awaiting-frame", + render::CameraReason::Ready => "ready", + }; + format!("{}:{}", c.id, why) + }) + .collect(); + let main = match s.main_view { + Some(id) => id.to_string(), + None => "нет".into(), + }; + format!( + "Камеры: API={}, включено={}, запрос={}, подготовлено={}, поколение={}, {}, main={}, [{}]", + s.available, + s.enabled, + s.request_accepted, + s.prepared, + s.generation, + reason(s.reason), + main, + cameras.join("; ") + ) +} + pub fn diagnostics() -> String { let smoke = render::smoke_status(); let acoustic = acoustics(); let capture = render::capture_owner_status(); let lights = render::point_lights_status(); let sources = render::sound_sources_status(); + let cameras = render::camera_status(); let smoke_reason = match smoke.reason { render::SmokeReason::AwaitingFrame => "awaiting-frame", render::SmokeReason::Disabled => "disabled", @@ -72,7 +102,7 @@ pub fn diagnostics() -> String { render::SmokeReason::Ready => "ready", }; [ - "Возможности движка (API 29; подготовка не означает готовый GPU-кадр, слышимый звук или файл):".into(), + "Возможности движка (API 30; подготовка не означает готовый GPU-кадр, слышимый звук или файл):".into(), line( "Дым", smoke.generation, @@ -109,6 +139,7 @@ pub fn diagnostics() -> String { sources.prepared, reason(sources.reason), ), + camera_line(&cameras), line( "Захват этого мода", capture.generation, @@ -164,4 +195,46 @@ mod tests { s.reason = CapabilityReason::Failed; assert!(capture_problem(&s).is_some()); } + fn camera(id: u32, prepared: bool) -> render::CameraReadiness { + render::CameraReadiness { + id, + request_accepted: true, + prepared, + reason: if prepared { + render::CameraReason::Ready + } else { + render::CameraReason::AwaitingFrame + }, + } + } + #[test] + fn the_camera_line_separates_readiness_from_the_screens_selection() { + let line = camera_line(&render::CameraState { + generation: 5, + available: true, + enabled: true, + request_accepted: true, + prepared: true, + reason: CapabilityReason::Ready, + cameras: vec![camera(7, true), camera(8, false)], + main_view: Some(7), + }); + assert_eq!( + line, + "Камеры: API=true, включено=true, запрос=true, подготовлено=true, поколение=5, \ + ready, main=7, [7:ready; 8:awaiting-frame]" + ); + // No accepted submission: no cameras, no main view, and the reason says so. + let line = camera_line(&render::CameraState { + generation: 5, + available: true, + enabled: false, + request_accepted: false, + prepared: false, + reason: CapabilityReason::NotRequested, + cameras: Vec::new(), + main_view: None, + }); + assert!(line.ends_with("not-requested, main=нет, []"), "{line}"); + } } diff --git a/docs/CONTINUATION-2026-11-04.md b/docs/CONTINUATION-2026-11-04.md index 098b720..8ab0881 100644 --- a/docs/CONTINUATION-2026-11-04.md +++ b/docs/CONTINUATION-2026-11-04.md @@ -32,7 +32,13 @@ `/airdef capabilities` показывает оба состояния (`c1a595b`). `57f8b2853` также устраняет коллизию камер: texture targets, scheduler, HUD/canvas/corner и main-view адресуются `(run_serial, local_id)`. Проверены два настоящих гостя с одинаковым ID, - поиск target и scheduler, компиляция клиента. Camera-ready API и GPU-проверка ещё не добавлены. + поиск target и scheduler, компиляция клиента. + `efe017ffb`/`278f356aa` добавляют API30 `camera-status`: `prepared` — закодированный в target + кадр камеры или кадр мира с её main-view, причём milestone получает только тот запуск, чей выбор + экран реально использовал (чужой тот же local ID и чужая картинка его не готовят). `enabled` — + последний отмеченный кадр был кадром мира, не заголовком. Пустая `set-views` и смена мира + очищают заявку/выбор/готовность, reload начинает заново; главный вид теряет picture target до + нового кадра. Проверено настоящими Wasmtime-гостями; GPU-приёмка не выполнялась. - **#96:** engine `e44ad82a9`: opt-in `audio::tests::export_moving_mass_raid` проводит 100 движущихся двигателей и 16 импульсных источников через настоящие `Audio::tick_acoustics` и `film_mix`: эхо/реверб, камера, инвалидация и смена epoch, новый capture без старого хвоста. @@ -82,7 +88,7 @@ GUI/MP4 и варианты комплексов (разные ракеты/пр command max 1013387/1000000000, ошибок 0, build identity совпал. Не завершены: субъективное прослушивание, настоящий MP4 с акустикой, полный 10-минутный -replay/export, расширенная модельная/ночная/FPS приёмка, camera-ready milestone и полные +replay/export, расширенная модельная/ночная/FPS приёмка, GPU-приёмка camera-ready картинки и полные query/voice/peak-memory бюджеты. Старый допуск broadband RT60 35% не ужесточался до 15%. Попытка живого MP4 в этом проходе не дала проверенного файла и за приёмку не считается; изолированные процессы закрыты. Продолжать с текущего pin, не с временных пакетов агентов. diff --git a/docs/ENGINE.md b/docs/ENGINE.md index babbff8..a53d8c4 100644 --- a/docs/ENGINE.md +++ b/docs/ENGINE.md @@ -63,8 +63,8 @@ runtime закреплённого движка. Используется вре ## Акустика и владение захватом (API 27) -Текущий pin: `73b9d307048fc44d7f21fa3c829fc5a855d144a6` (API 29; поверх `57f8b2853` — очистка -неудачного захвата). Акустические +Текущий pin: `278f356aa604fa21c2d15d0081823bbe0f891df2` (API 30; поверх API29 — нормали моделей +и очистка неудачного захвата). Акустические `render.acoustics-status` и `render.capture-owner-status` возвращают поколение, `available`, `enabled`, `request-accepted`, `prepared` и причину. В игре всё это показывает `/airdef capabilities`. Отсутствие запроса, выключение, ожидание backend и ошибка различаются. @@ -152,10 +152,23 @@ API28/29 добавляют отдельные `point-lights-status` и `sound-s после cache/main-view HD обработки, звук — после source-manager apply с live/offline output. Заявки привязаны к запуску мода и поколению мира; звук также к source epoch. Clear/reload/выход снимают подтверждение. Это не GPU fence, загрузка сэмпла, наличие голоса или слышимость. -Камеры и их targets теперь изолированы по `(run_serial, local_id)` вместо глобального ID. -Camera-ready API пока отсутствует; проверки хоста не заменяют GPU-приёмку. +Камеры и их targets изолированы по `(run_serial, local_id)` вместо глобального ID. -Остаток #102: статус камер, полные query/voice/peak budgets, +API30 добавляет `camera-status` для камер `set-views` вызывающего запуска: доступность API, +включение пути картинок в последнем отмеченном кадре, принятие заявки поколения и поштучная +готовность каждой камеры. `prepared` означает, что рендерер закодировал кадр камеры в её +texture target, либо кадр мира отрендерен с неё как main-view **этого** запуска: если одинаковый +local ID выбрали два мода, экран использует выбор первого, и milestone получает только он; чужая +картинка с тем же ID чужую камеру не готовит. `enabled` — последний отмеченный кадр был кадром +мира (не заголовок/загрузка). Это не регистрация target и не GPU fence: видимый пиксель, +представление кадра и обход ротации per-frame бюджета не гарантируются. Пустая `set-views` +снимает заявку и готовность; смена мира очищает заявку, выбор main-view и готовность (камеры +другого мира не рисуются), перезагрузка мода начинает заново, а повторная отправка и камера, +отданная в main-view, ждут нового кадра (у такой камеры нет своего picture target). +Проверено двумя настоящими Wasmtime-гостями с одинаковым local ID, пустой заявкой, сменой мира +и reload. `/airdef capabilities` показывает строку камер. Проверки хоста не заменяют GPU-приёмку. + +Остаток #102: полные query/voice/peak budgets, GPU/готовый MP4 и платформенная приёмка. Эти результаты текущими тестами не объявляются выполненными. ## Измеренный fuel и массовые взрывы diff --git a/docs/IMPLEMENTATION-STATUS.md b/docs/IMPLEMENTATION-STATUS.md index 9458dcd..e5a7772 100644 --- a/docs/IMPLEMENTATION-STATUS.md +++ b/docs/IMPLEMENTATION-STATUS.md @@ -5,9 +5,10 @@ Новый проверенный объём: [продолжение пяти задач 4 ноября](CONTINUATION-2026-11-04.md). #87: typed battery/world/damage diagnostics и адаптеры текста; #91: общий content/sound catalog -для Rust и Python; #102: измеренный fuel, API29 light/source milestones и изоляция камер; -#96: массовый deterministic offline capture; #115: authored Lancet/Х-101 и кадры инстансов. -Текущий engine pin — API29; старые номера/числа ниже описывают предыдущие этапы. +для Rust и Python; #102: измеренный fuel, API29 light/source milestones, API30 camera-status +и изоляция камер; #96: массовый deterministic offline capture; #115: authored Lancet/Х-101 +и кадры инстансов. +Текущий engine pin — API30; старые номера/числа ниже описывают предыдущие этапы. Проблема нормалей модели #123 исправлена в engine `45b43f00d`; парные HD-кадры и ночная проверка Lancet записаны в галерее. | Issue | Реализовано | Что остаётся для полной приёмки | @@ -19,7 +20,7 @@ | #91 | Общий каталог доступности и потребители меню/инвентаря; явный RM48U-профиль; корректные подписи очередей; `catalog/specs.json` — единственный источник числовых строк, из него генерируются Rust-таблицы для мода и `airdef-core`, CI проверяет свежесть | Варианты комплексов (разные ракеты/профили по батареям) и игровая приёмка; каталог остаётся статичными данными, не динамическим ECS | | #92 | Authored overrides, проверка ссылок/PNG/границ, manifest, безопасная транзакционная генерация; registry/texture/icon modules | Дальнейшее ручное улучшение контента относится к #115 | | #96 | Акустические пути; измеренные синтетические occlusion/echo/reverb fixtures; лицензии и исходные notices в архивах; настоящий MP4 с акустикой: пары on/off, статусы ready, окклюзия/поглощение и диффузный wet-остаток в замкнутой геометрии | Прослушивание, игровой массовый звук/CPU, чистый хвост/RT60 в MP4; точность RT60 пока по допуску 35%, не 15% | -| #102 | Pinned engine/API29, packer/build identity; smoke/acoustics/capture-owner/light/source milestones; fallback/владение; WASM27 и flat stress271; измеренный callback fuel; JSON provenance/failure tests; Metal/no-audio smoke; URL/Git-регрессия; owner-scoped camera targets | Старое69-шаговое утверждение о перекрытии отозвано; статус камер, полные query/voice/peak budgets, GPU/готовый MP4 и Windows-приёмка остаются | +| #102 | Pinned engine/API30, packer/build identity; smoke/acoustics/capture-owner/light/source/owner-scoped camera milestones; fallback/владение; WASM27 и flat stress271; измеренный callback fuel; JSON provenance/failure tests; Metal/no-audio smoke; URL/Git-регрессия; camera-ready по закодированному кадру с per-run/per-world сбросом | Старое69-шаговое утверждение о перекрытии отозвано; GPU-приёмка картинки камер, полные query/voice/peak budgets, GPU/готовый MP4 и Windows-приёмка остаются | | #104 | Движок вынесен в `chexit/horizon-airdef`, ветка `airdef`; AirDef использует точный gitlink | Отдельная Windows-проверка миграции старого standalone checkout | | #107 | ПУ оценивается по наблюдениям: обратный курс не выдаёт точную ПУ | Повторная игровая проверка сценариев с маневрирующими целями | | #110 | Изменения чанков инвалидируют акустические снимки/кэш; включено в pin и проверено engine-тестами | Внешняя игровая приёмка на целевых машинах | diff --git a/horizon b/horizon index 73b9d30..278f356 160000 --- a/horizon +++ b/horizon @@ -1 +1 @@ -Subproject commit 73b9d307048fc44d7f21fa3c829fc5a855d144a6 +Subproject commit 278f356aa604fa21c2d15d0081823bbe0f891df2