docs: translate render frame reference
Docs Deploy / Build and Deploy MkDocs (push) Successful in 34s

This commit is contained in:
Valentin Popov committed 2026-10-18 00:44:12 +04:00
1 parent 9037fbc810
commit 4f8f3f54e0
2 files changed
+147 -151

No files matched your search

+142 -147
View File
@@ -1,174 +1,169 @@
# Render frame # Кадр отрисовки
Кадр является последней стадией цикла, а не самостоятельной функцией renderer-а. Кадр завершает цикл и не является отдельной операцией рендерера. До вызовов
До draw calls уже накоплен input, рассчитан tick, применены отложенные операции, отрисовки собирается ввод, вычисляется такт, применяются отложенные операции,
выбрана камера и обновлён 3D sound listener. выбирается камера и обновляется слушатель 3D-звука.
## Frame skeleton ## Порядок кадра
```text ```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, По анализу вызовов установлен общий порядок в `World3D::stdRenderGame`: камера
настраиваются viewport/matrices, вызываются renderer boundary slots, передаётся в Terrain, задаются `viewport` и матрицы, вызываются граничные слоты
устанавливается `in_render`, выполняется traversal мира, закрывается world/shade рендерера, выставляется `in_render`, обходится мир, завершаются проходы мира и
pass, вызывается renderer completion, снимается `in_render`, рассылается затенения, вызывается завершение рендерера, снимается `in_render`, затем
end-of-render. рассылается `end-of-render`.
## Draw item ## Элемент отрисовки
Подготовленный draw item содержит: Подготовленный элемент отрисовки содержит:
- node world matrix; - мировую матрицу узла;
- batch flags and index range; - флаги пакета `batch` и диапазон индексов;
- WEAR material handle; - дескриптор материала WEAR;
- MAT0 active phase and coefficients; - активную фазу и коэффициенты MAT0;
- texture handle; - дескриптор текстуры;
- optional lightmap handle; - необязательный дескриптор карты освещения;
- render phase and sorting key; - фазу отрисовки и ключ сортировки;
- legacy pipeline state. - состояние конвейера устаревшей отрисовки.
Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase Подготовленный элемент должен ссылаться на неизменяемые данные кадра. Изменение
или texture cache посреди прохода не должно менять уже собранную очередь. фазы или кэша текстур посреди прохода не должно менять уже собранную очередь.
## MAT0 state and transparent backgrounds ## Состояние MAT0 и прозрачные фоны
For mission world ranges, the selected MAT0 material supplies its native Для диапазонов мира миссии выбранный материал MAT0 задаёт нативную категорию
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. `batch` `0x108`. Такой диапазон при включённой проверке глубины не записывает
глубину. За фиксированными диапазонами окружения сохраняются их явные состояния.
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` `cargo test --locked --offline -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order`
checks that category 1 remains source-alpha with alpha testing, retains depth Проверяет, что категория 1 сохраняет смешивание `source-alpha` и альфа-тест,
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, `cargo test --locked --offline -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects`
shadow, nebula/dome/clouds, far-to-near transparent canopy, then sun/weather. Проверяет заданный порядок очереди: градиент экрана, непрозрачная местность и
здание, тень, туманность, купол и облака, прозрачная крона от дальних элементов
к ближним, затем солнце и погода.
Viewer-only paired readbacks used `--frames 3 --validation ## Свидетельства диспетчеризации камеры GOG
--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 Для версии GOG `World3D.dll` с SHA-256
For the GOG `World3D.dll` baseline with SHA-256
`17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e`, `17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e`,
the exported `stdRenderGame` is RVA `0x13BD0`. It first calls экспорт `stdRenderGame` находится по RVA `0x13BD0`. Вначале он вызывает
`Terrain::stdSetCurrentCamera2(camera)`, stores the camera pointer only for `Terrain::stdSetCurrentCamera2(camera)`, сохраняет указатель камеры на время
the frame, and clears it before return. `sendEndOfRender` is a separate export кадра и очищает его перед возвратом. `sendEndOfRender` — отдельный экспорт по
at RVA `0x13D90`. This is frame-order evidence only: the camera ABI, matrices RVA `0x13D90`. Эти данные подтверждают только порядок вызовов в кадре: анализ
and viewport values are not recovered by this call-order analysis. The не восстанавливает ABI камеры, матрицы или значения `viewport`. Реализованный
implemented preview path below accepts selector-0 input produced through the путь превью принимает входные данные с матрицей `selector0_words`, полученные через
[native frame capture workflow](../../tools/native-frame-capture/README.md). [процедуру захвата нативного кадра](../../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 `atmosphere_seconds`, если проверка потока, поколения и часов прошла успешно.
`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) queries В превью с фиксированной камерой эта абсолютная фаза расписания используется,
selector `18` on the supplied object and invokes slot `+12` on that result. It если не задан параметр `--atmosphere-seconds`; на всех кадрах чтения сохраняется
does not store the supplied pointer in `stdGetCurrentCamera2`; that getter та же фаза. Явный параметр командной строки имеет приоритет. Эта фаза не равна
returns a Terrain global populated by selector `8` during Terrain initialization. времени полной симуляции: погода, RNG и другие состояния миссии остаются
неизвестны, поэтому она не подтверждает полного совпадения кадра.
`Terrain.dll!LoadCamera` adds a separate factory-side contract. Its GOG entry Анализ принимающей стороны уточняет этот контракт. `stdSetCurrentCamera2`
at RVA `0x4EBE0` is `__stdcall` with four machine-word arguments, allocates (RVA `0x4FD40` в Terrain с SHA-256
`0x1A4` bytes, calls the constructor at RVA `0x4EC60`, and returns the interface `af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`)
at object offset `+0x134`. The constructor passes arguments 3 and 4 into base запрашивает селектор `18` у переданного объекта и вызывает слот `+12` полученного
initialization at `this + 4`, writes argument 3 to `this + 0x138`, and calls a интерфейса. Указатель не сохраняется в `stdGetCurrentCamera2`: этот геттер
helper at RVA `0x4CEF0` with arguments 1, 2 and 4 plus `this`. Argument 1 is a возвращает глобальное значение Terrain, установленное селектором `8` при
mode string: only `REFLECTION` and `REFLECTION_SHIFTED` produce a non-zero mode инициализации Terrain.
value at `this + 0x1A0`. This is factory ABI evidence, not a recovered view or
projection matrix contract.
## Legacy camera preview path `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 фабрики, а не восстановленный
контракт матрицы вида или проекции.
`fparkan-game --legacy-camera-capture <json>` consumes the selector-0 transform, ## Предпросмотр по захвату камеры D3D7
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 `fparkan-game --legacy-camera-capture <json>` принимает матрицу камеры
output calculated at `Terrain.dll` RVA `0x4F9C0`; `CSky` adds π at RVA `0x481C5` `selector0_words`, прямоугольник области отображения `viewport` D3D7
when selecting screen-gradient sectors. This mapping is applied only at the sky `[left, top, right, bottom]`, ближнюю и дальнюю плоскости и угол поля зрения.
boundary and does not change `FreeFlightCamera` yaw or movement controls. По этим данным превью строит матрицы вида и проекции D3D7, передаёт полученную
Matched same-camera, same-phase three-frame captures at phases 56.227 and матрицу `clip_from_world` камере превью и напрямую использует оси камеры из
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. `right - left` на `bottom - top`. Размер окна превью закреплён: если при
запуске или после ненулевого изменения окна физический размер не совпадает с
заданным, приложение завершает работу, а не растягивает изображение.
Сравнивайте результат с фрагментом исходного PNG по прямоугольнику
`[left, top, right, bottom]`. В этом режиме `FreeFlightCamera` не создаётся,
но при повторной отрисовке обновляются окружение и проецируемые тени для
захваченной камеры. До обновления окружения, которое может создавать звуковые
события, слушателю задаются захваченные позиция, направление вперёд и верхняя
ось; начальная сортировка диапазонов мира использует ту же позицию.
## Parity risks Захват может содержать наблюдавшийся путь миссии и проверенное значение
`atmosphere_seconds`; последнее используется только при отсутствии явного
параметра командной строки. Это значение фиксирует фазу расписания атмосферы
при сравнении с неподвижной камерой, тогда как FX продолжает получать реальное
время кадра. `simulation_time_seconds`, погода, RNG и полное состояние миссии
остаются неизвестными, поэтому совпадение камеры и фазы не подтверждает
визуального совпадения с нативным кадром.
`VulkanStaticCamera::from_legacy_d3d7` keeps the D3D7 view and projection Для неба превью вычисляет направление как `atan2(forward.x, forward.y)`. Оно
reconstruction in `fparkan-render`, then negates the vertical projection term совпадает со вторым выходным значением, вычисляемым в `Terrain.dll` по RVA
once at the Vulkan adapter boundary. D3D7's top-down viewport places positive `0x4F9C0`; при выборе сектора экранного градиента `CSky` добавляет π по RVA
NDC Y at the top; the Vulkan swapchain uses a positive viewport height, so the `0x481C5`. Это преобразование применяется только на границе неба и не меняет
adapter flips NDC Y while the shader preserves `gl_Position`. The generic рыскание или управление движением `FreeFlightCamera`.
`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, `VulkanStaticCamera::from_legacy_d3d7` выполняет восстановление вида и проекции
draw-item list, pipeline keys, matrices и hashes промежуточных buffers. 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 и атмосферы;
- переход на запасной набор возможностей устройства;
- квантование времени симуляции.
Для отладки требуется детерминированный захват кадра: состояние камеры,
идентификаторы видимых объектов, список элементов отрисовки, ключи конвейера,
матрицы и хэши промежуточных буферов.
+5 -4
View File
@@ -175,10 +175,11 @@ struct Node38 {
}; };
``` ```
`slot_index` образует матрицу `3 LOD x 5 groups`. Выбор выполняется как `slot_index` хранит 3 состояния по 5 LOD: `slot_index[state * 5 + lod]`;
`slot_index[lod * 5 + group]`; `0xFFFF` означает отсутствие geometry для этой `0xFFFF` означает отсутствие геометрии для комбинации. Публичные Rust-обёртки
комбинации. Поле `parent_or_link` участвует в иерархии или связи узлов, но сохраняют исторические имена `Lod` и `Group`: аргумент `Lod` задаёт нативное
название остаётся описательным. состояние (0..2), а `Group` — нативный уровень LOD (0..4). Поле `parent_or_link`
участвует в иерархии или связи узлов, но название остаётся описательным.
Type 2 начинается с header `0x8C`, затем содержит slots по 68 байт: Type 2 начинается с header `0x8C`, затем содержит slots по 68 байт: