diff options
| -rw-r--r-- | docs/reference/render-frame.md | 329 | ||||
| -rw-r--r-- | docs/tomes/05-render.md | 9 |
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 байт: |
