aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-17 23:44:12 +0300
committerValentin Popov <valentin@popov.link>2026-10-17 23:44:12 +0300
commit4f8f3f54e037259870599051c59810d166b57aea (patch)
tree21f89e6a41076d1aaf194fcb9af0bb3b9b6c6fa8
parent9037fbc810384eb24359584ef71e8d6a5b57b099 (diff)
downloadfparkan-4f8f3f54e037259870599051c59810d166b57aea.tar.xz
fparkan-4f8f3f54e037259870599051c59810d166b57aea.zip
docs: translate render frame referencedevel
-rw-r--r--docs/reference/render-frame.md329
-rw-r--r--docs/tomes/05-render.md9
2 files changed, 167 insertions, 171 deletions
diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md
index 015f9f3..f8aee38 100644
--- a/docs/reference/render-frame.md
+++ b/docs/reference/render-frame.md
@@ -1,174 +1,169 @@
-# Render frame
+# Кадр отрисовки
-Кадр является последней стадией цикла, а не самостоятельной функцией renderer-а.
-До draw calls уже накоплен input, рассчитан tick, применены отложенные операции,
-выбрана камера и обновлён 3D sound listener.
+Кадр завершает цикл и не является отдельной операцией рендерера. До вызовов
+отрисовки собирается ввод, вычисляется такт, применяются отложенные операции,
+выбирается камера и обновляется слушатель 3D-звука.
-## Frame skeleton
+## Порядок кадра
```text
-system messages and input
- -> simulation calculation
- -> deferred object operations
- -> animation and transforms
- -> camera and sound listener
- -> visibility and render queues
- -> materials and draw passes
- -> renderer completion
- -> end-of-render callbacks and UI
+системные сообщения и ввод
+ -> расчёт симуляции
+ -> отложенные операции над объектами
+ -> анимация и преобразования
+ -> камера и звуковой слушатель
+ -> видимость и очереди отрисовки
+ -> материалы и проходы отрисовки
+ -> завершение рендерера
+ -> обратные вызовы после отрисовки и UI
```
-В `World3D::stdRenderGame` доказан крупный порядок: camera передаётся Terrain,
-настраиваются viewport/matrices, вызываются renderer boundary slots,
-устанавливается `in_render`, выполняется traversal мира, закрывается world/shade
-pass, вызывается renderer completion, снимается `in_render`, рассылается
-end-of-render.
-
-## Draw item
-
-Подготовленный draw item содержит:
-
-- node world matrix;
-- batch flags and index range;
-- WEAR material handle;
-- MAT0 active phase and coefficients;
-- texture handle;
-- optional lightmap handle;
-- render phase and sorting key;
-- legacy pipeline state.
-
-Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase
-или texture cache посреди прохода не должно менять уже собранную очередь.
-
-## MAT0 state and transparent backgrounds
-
-For mission world ranges, the selected MAT0 material supplies its native
-pipeline category. The category sets blend and alpha-test state while preserving
-the range's depth and cull state; an animated material selector updates the
-category with the selector. Queue membership is independent: sampled opacity
-below one or native batch flags `0x108` mark a world range transparent, and a
-queued range with depth enabled tests depth without writing it. Fixed
-environment ranges keep their explicit states.
-
-The fixed-camera preview explicitly marks nebula/stars, dome and cloud ranges as
-transparent backgrounds. Opaque world ranges stay in their world slots; the
-transparent world queue is sorted back-to-front and inserted immediately after
-the last marked background. Other fixed ranges retain their relative order.
-Thus cloud layers can be drawn before transparent foliage, while later sun,
-weather and other foreground ranges keep their existing relative order. This
-is a narrow queue rule, not a reordering of every sky or effects pass.
-
-## Regression evidence
-
-`cargo test -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order --offline`
-checks that category 1 remains source-alpha with alpha testing, retains depth
-and cull settings in either update order, and rejects unsupported categories.
-`cargo test -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects --offline`
-checks the explicit queue order: screen gradient, opaque terrain/building,
-shadow, nebula/dome/clouds, far-to-near transparent canopy, then sun/weather.
-
-Viewer-only paired readbacks used `--frames 3 --validation
---legacy-camera-capture` with the pre-fix and patched builds. On AutoDemo TMA
-object 4 (`wor_l_n1.dat`, `fr_l_plant`), the 1280x720 camera at eye
-`[464.319, 904.719, 38.0]`, aimed at `[374.319, 814.719, 12.0]` with FOV
-`0.62`, changed the two black facade panels to tinted, textured portal
-surfaces. On Mission.01 object 12 (`s_tree_04`), the 1280x1024 camera at eye
-`[492.63855, 312.90244, 46.5]` aimed at
-`[492.63855, 442.90244, 55.0]` crosses the canopy against open sky; paired
-three-frame captures at the same synthetic atmosphere phase (600 seconds) show
-the patched build restoring the missing upper/right fronds along that sky
-boundary. Both runs reported clean Vulkan validation. These captures verify our
-preview behavior and do not prove the native renderer's pass order, portal
-transmittance or exact color.
-
-## GOG camera dispatch evidence
-
-For the GOG `World3D.dll` baseline with SHA-256
+По анализу вызовов установлен общий порядок в `World3D::stdRenderGame`: камера
+передаётся в Terrain, задаются `viewport` и матрицы, вызываются граничные слоты
+рендерера, выставляется `in_render`, обходится мир, завершаются проходы мира и
+затенения, вызывается завершение рендерера, снимается `in_render`, затем
+рассылается `end-of-render`.
+
+## Элемент отрисовки
+
+Подготовленный элемент отрисовки содержит:
+
+- мировую матрицу узла;
+- флаги пакета `batch` и диапазон индексов;
+- дескриптор материала WEAR;
+- активную фазу и коэффициенты MAT0;
+- дескриптор текстуры;
+- необязательный дескриптор карты освещения;
+- фазу отрисовки и ключ сортировки;
+- состояние конвейера устаревшей отрисовки.
+
+Подготовленный элемент должен ссылаться на неизменяемые данные кадра. Изменение
+фазы или кэша текстур посреди прохода не должно менять уже собранную очередь.
+
+## Состояние MAT0 и прозрачные фоны
+
+Для диапазонов мира миссии выбранный материал MAT0 задаёт нативную категорию
+конвейера. Категория определяет смешивание и альфа-тест, сохраняя настройки
+проверки глубины и отсечения граней. Если селектор материала анимируется,
+вместе с ним обновляется и категория.
+Принадлежность к очереди определяется отдельно: прозрачным считается диапазон
+мира с выбранной прозрачностью меньше единицы или с нативными флагами пакета
+`batch` `0x108`. Такой диапазон при включённой проверке глубины не записывает
+глубину. За фиксированными диапазонами окружения сохраняются их явные состояния.
+
+В превью с фиксированной камерой диапазоны туманности и звёзд, купола и облаков
+явно помечаются как прозрачный фон. Непрозрачные диапазоны мира остаются на
+своих местах; прозрачная очередь мира сортируется от дальних элементов к
+ближним и вставляется сразу после последнего помеченного фона. Порядок остальных
+фиксированных диапазонов сохраняется. Поэтому облака могут отрисовываться перед
+прозрачной листвой, а расположенные позднее солнце, погода и другие передние
+эффекты сохраняют свой относительный порядок. Это локальное правило очереди, а
+не перестановка всех проходов неба или эффектов.
+
+## Проверки
+
+`cargo test --locked --offline -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order`
+Проверяет, что категория 1 сохраняет смешивание `source-alpha` и альфа-тест,
+сохраняет настройки проверки глубины и отсечения граней при любом порядке
+обновления и отклоняет неподдерживаемые категории.
+`cargo test --locked --offline -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects`
+Проверяет заданный порядок очереди: градиент экрана, непрозрачная местность и
+здание, тень, туманность, купол и облака, прозрачная крона от дальних элементов
+к ближним, затем солнце и погода.
+
+## Свидетельства диспетчеризации камеры GOG
+
+Для версии GOG `World3D.dll` с SHA-256
`17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e`,
-the exported `stdRenderGame` is RVA `0x13BD0`. It first calls
-`Terrain::stdSetCurrentCamera2(camera)`, stores the camera pointer only for
-the frame, and clears it before return. `sendEndOfRender` is a separate export
-at RVA `0x13D90`. This is frame-order evidence only: the camera ABI, matrices
-and viewport values are not recovered by this call-order analysis. The
-implemented preview path below accepts selector-0 input produced through the
-[native frame capture workflow](../../tools/native-frame-capture/README.md).
-The capture workflow also records an observed mission path and, when
-its thread/generation and clock checks succeed, an `atmosphere_seconds` sample.
-For a fixed-camera preview, the renderer uses that optional absolute schedule
-phase when no `--atmosphere-seconds` override is supplied and keeps schedule
-sampling at that exact phase across readback frames. An explicit CLI value takes
-precedence. The phase sample is not full simulation time: weather, RNG, and
-other mission state remain unknown, so this evidence does not establish
-full-frame parity.
-
-Receiver-side GOG Terrain evidence refines this contract. `stdSetCurrentCamera2`
-(RVA `0x4FD40` in Terrain SHA-256
-`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) queries
-selector `18` on the supplied object and invokes slot `+12` on that result. It
-does not store the supplied pointer in `stdGetCurrentCamera2`; that getter
-returns a Terrain global populated by selector `8` during Terrain initialization.
-
-`Terrain.dll!LoadCamera` adds a separate factory-side contract. Its GOG entry
-at RVA `0x4EBE0` is `__stdcall` with four machine-word arguments, allocates
-`0x1A4` bytes, calls the constructor at RVA `0x4EC60`, and returns the interface
-at object offset `+0x134`. The constructor passes arguments 3 and 4 into base
-initialization at `this + 4`, writes argument 3 to `this + 0x138`, and calls a
-helper at RVA `0x4CEF0` with arguments 1, 2 and 4 plus `this`. Argument 1 is a
-mode string: only `REFLECTION` and `REFLECTION_SHIFTED` produce a non-zero mode
-value at `this + 0x1A0`. This is factory ABI evidence, not a recovered view or
-projection matrix contract.
-
-## Legacy camera preview path
-
-`fparkan-game --legacy-camera-capture <json>` consumes the selector-0 transform,
-the native D3D7 viewport RECT `[left, top, right, bottom]`, near/far planes and
-field of view. It derives the D3D7 view and clip transform from those inputs,
-carries the resulting `clip_from_world` matrix into the preview camera, and
-keeps the view-matrix camera axes directly, so the captured basis, including
-roll, is not reduced to yaw/pitch. The RECT becomes a physical drawable extent
-of `right - left` by `bottom - top`. The preview window is fixed to that extent;
-startup and non-zero resize events reject a physical-size mismatch instead of
-stretching the captured view. Compare the result with the corresponding
-`[left, top, right, bottom]` crop of the original PNG. `FreeFlightCamera` is not
-created in this mode, but redraws still update environment and projected
-shadows from the captured camera. Before environment updates can emit sound
-events, the audio listener receives the captured position, forward and up axes;
-initial world draw sorting uses the same captured position. A phase capture may
-include an observed mission path and a verified `atmosphere_seconds` value; the
-latter is used only when no explicit CLI override is supplied. This value pins
-the atmosphere schedule sample during a fixed-camera comparison while FX still
-receives real frame time. `simulation_time_seconds`, weather, RNG and complete
-mission state remain unknown, and a camera/phase match alone does not establish
-visual parity with a native frame.
-
-The preview's sky heading is `atan2(forward.x, forward.y)`, matching the second
-output calculated at `Terrain.dll` RVA `0x4F9C0`; `CSky` adds π at RVA `0x481C5`
-when selecting screen-gradient sectors. This mapping is applied only at the sky
-boundary and does not change `FreeFlightCamera` yaw or movement controls.
-Matched same-camera, same-phase three-frame captures at phases 56.227 and
-998.893 showed zero changed pixels before and after this correction; the
-remaining sky and color mismatch is unresolved, so this source-level match does
-not establish visual parity.
-
-## Parity risks
-
-`VulkanStaticCamera::from_legacy_d3d7` keeps the D3D7 view and projection
-reconstruction in `fparkan-render`, then negates the vertical projection term
-once at the Vulkan adapter boundary. D3D7's top-down viewport places positive
-NDC Y at the top; the Vulkan swapchain uses a positive viewport height, so the
-adapter flips NDC Y while the shader preserves `gl_Position`. The generic
-`from_row_major_view_projection` path remains unchanged; its callers supply
-Vulkan-ready matrices, as the free-flight camera already does.
-
-- x87 precision and rounding;
-- scalar/SIMD `g_FastProc` differences;
-- object, batch and transparent primitive order;
-- depth, cull, alpha test and blend transitions;
-- mip-skip, palette and Page coordinates;
-- material fallback and phase selection;
-- RNG sequence for FX and atmosphere;
-- device capability fallback;
-- simulation time quantization.
-
-Для отладки нужен deterministic frame capture: camera state, visible object IDs,
-draw-item list, pipeline keys, matrices и hashes промежуточных buffers.
+экспорт `stdRenderGame` находится по RVA `0x13BD0`. Вначале он вызывает
+`Terrain::stdSetCurrentCamera2(camera)`, сохраняет указатель камеры на время
+кадра и очищает его перед возвратом. `sendEndOfRender` — отдельный экспорт по
+RVA `0x13D90`. Эти данные подтверждают только порядок вызовов в кадре: анализ
+не восстанавливает ABI камеры, матрицы или значения `viewport`. Реализованный
+путь превью принимает входные данные с матрицей `selector0_words`, полученные через
+[процедуру захвата нативного кадра](../../tools/native-frame-capture/README.md).
+
+Процедура захвата записывает наблюдавшийся путь миссии и значение
+`atmosphere_seconds`, если проверка потока, поколения и часов прошла успешно.
+В превью с фиксированной камерой эта абсолютная фаза расписания используется,
+если не задан параметр `--atmosphere-seconds`; на всех кадрах чтения сохраняется
+та же фаза. Явный параметр командной строки имеет приоритет. Эта фаза не равна
+времени полной симуляции: погода, RNG и другие состояния миссии остаются
+неизвестны, поэтому она не подтверждает полного совпадения кадра.
+
+Анализ принимающей стороны уточняет этот контракт. `stdSetCurrentCamera2`
+(RVA `0x4FD40` в Terrain с SHA-256
+`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`)
+запрашивает селектор `18` у переданного объекта и вызывает слот `+12` полученного
+интерфейса. Указатель не сохраняется в `stdGetCurrentCamera2`: этот геттер
+возвращает глобальное значение Terrain, установленное селектором `8` при
+инициализации Terrain.
+
+`Terrain.dll!LoadCamera` задаёт отдельный контракт фабрики. Точка входа GOG по
+RVA `0x4EBE0` использует соглашение о вызовах `__stdcall` и принимает четыре
+машинных слова, выделяет `0x1A4` байта, вызывает конструктор по RVA `0x4EC60` и
+возвращает интерфейс по смещению объекта `+0x134`. Конструктор передаёт
+аргументы 3 и 4 в инициализацию базового объекта по `this + 4`, записывает
+аргумент 3 в `this + 0x138` и вызывает вспомогательную функцию по RVA `0x4CEF0`,
+передавая ей аргументы 1, 2 и 4 вместе с `this`. Аргумент 1 — строка режима:
+ненулевое значение по `this + 0x1A0` дают только `REFLECTION` и
+`REFLECTION_SHIFTED`. Это свидетельство ABI фабрики, а не восстановленный
+контракт матрицы вида или проекции.
+
+## Предпросмотр по захвату камеры D3D7
+
+`fparkan-game --legacy-camera-capture <json>` принимает матрицу камеры
+`selector0_words`, прямоугольник области отображения `viewport` D3D7
+`[left, top, right, bottom]`, ближнюю и дальнюю плоскости и угол поля зрения.
+По этим данным превью строит матрицы вида и проекции D3D7, передаёт полученную
+матрицу `clip_from_world` камере превью и напрямую использует оси камеры из
+матрицы вида. Поэтому базис захвата, включая крен, не сводится к рысканию и
+тангажу. Прямоугольник задаёт физический размер области отрисовки:
+`right - left` на `bottom - top`. Размер окна превью закреплён: если при
+запуске или после ненулевого изменения окна физический размер не совпадает с
+заданным, приложение завершает работу, а не растягивает изображение.
+Сравнивайте результат с фрагментом исходного PNG по прямоугольнику
+`[left, top, right, bottom]`. В этом режиме `FreeFlightCamera` не создаётся,
+но при повторной отрисовке обновляются окружение и проецируемые тени для
+захваченной камеры. До обновления окружения, которое может создавать звуковые
+события, слушателю задаются захваченные позиция, направление вперёд и верхняя
+ось; начальная сортировка диапазонов мира использует ту же позицию.
+
+Захват может содержать наблюдавшийся путь миссии и проверенное значение
+`atmosphere_seconds`; последнее используется только при отсутствии явного
+параметра командной строки. Это значение фиксирует фазу расписания атмосферы
+при сравнении с неподвижной камерой, тогда как FX продолжает получать реальное
+время кадра. `simulation_time_seconds`, погода, RNG и полное состояние миссии
+остаются неизвестными, поэтому совпадение камеры и фазы не подтверждает
+визуального совпадения с нативным кадром.
+
+Для неба превью вычисляет направление как `atan2(forward.x, forward.y)`. Оно
+совпадает со вторым выходным значением, вычисляемым в `Terrain.dll` по RVA
+`0x4F9C0`; при выборе сектора экранного градиента `CSky` добавляет π по RVA
+`0x481C5`. Это преобразование применяется только на границе неба и не меняет
+рыскание или управление движением `FreeFlightCamera`.
+
+## Ограничения точного совпадения
+
+`VulkanStaticCamera::from_legacy_d3d7` выполняет восстановление вида и проекции
+D3D7 в `fparkan-render`, а затем один раз меняет знак вертикального члена
+проекции на границе адаптера Vulkan. В области отображения D3D7 положительная
+координата NDC Y направлена вверх экрана; Vulkan использует положительную высоту
+области отображения, поэтому адаптер меняет знак NDC Y, а шейдер сохраняет
+`gl_Position`. Общий путь
+`from_row_major_view_projection` не меняется: вызывающие его функции передают
+матрицы, уже подготовленные для Vulkan, как и камера свободного полёта.
+
+- точность и округление x87;
+- различия scalar/SIMD в `g_FastProc`;
+- порядок объектов, пакетов и прозрачных примитивов;
+- переходы состояний проверки глубины, отсечения граней, альфа-теста и смешивания;
+- пропуск mip-уровней, палитра и координаты Page;
+- запасной материал и выбор фазы;
+- последовательность RNG для FX и атмосферы;
+- переход на запасной набор возможностей устройства;
+- квантование времени симуляции.
+
+Для отладки требуется детерминированный захват кадра: состояние камеры,
+идентификаторы видимых объектов, список элементов отрисовки, ключи конвейера,
+матрицы и хэши промежуточных буферов.
diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md
index e717736..dce7724 100644
--- a/docs/tomes/05-render.md
+++ b/docs/tomes/05-render.md
@@ -175,10 +175,11 @@ struct Node38 {
};
```
-`slot_index` образует матрицу `3 LOD x 5 groups`. Выбор выполняется как
-`slot_index[lod * 5 + group]`; `0xFFFF` означает отсутствие geometry для этой
-комбинации. Поле `parent_or_link` участвует в иерархии или связи узлов, но
-название остаётся описательным.
+`slot_index` хранит 3 состояния по 5 LOD: `slot_index[state * 5 + lod]`;
+`0xFFFF` означает отсутствие геометрии для комбинации. Публичные Rust-обёртки
+сохраняют исторические имена `Lod` и `Group`: аргумент `Lod` задаёт нативное
+состояние (0..2), а `Group` — нативный уровень LOD (0..4). Поле `parent_or_link`
+участвует в иерархии или связи узлов, но название остаётся описательным.
Type 2 начинается с header `0x8C`, затем содержит slots по 68 байт: