Files
CFDManager/rust
NotBigGhostandClaude Opus 5 d7881496b0 Сверка документации с кодом: исправлены расхождения
Сплошная проверка документов против дерева. Код не менялся.

docs/rust_vs_cpp.md — числа, снятые с чужого каталога сборки:
- «775 МБ исходников зависимостей» и «10 пакетов в графе C++» получены
  на build/, сконфигурированном из другого дерева (его _deps содержит
  262 МБ nlohmann_json, который проект не объявляет). На деле девять
  объявленных репозиториев дают ~400 МБ исходников;
- пакетов в графе Rust 82, а не 76 (cargo tree по текущему Cargo.lock);
- итог по строкам 2976/494, а не 2962/492: строка «меш» устарела на 17
  строк игнорируемого диагностического теста, тесты в модулях — 176;
- «перевес почти весь в Vulkan-слое» — на деле 346 из 542 (64 %), из них
  305 на Context и Swapchain; остальное приходится на меш, редактор и
  приложение;
- «ни один замер не даёт разницы в порядок величины» неверно:
  инкрементальная release — 52.3 против 4.4 с, это 11.9x;
- граней с пятью и более вершинами в plane.obj 119, а не 121;
- из Catch2 перенесены все три случая, и добавлено ещё два, а не
  «три перенесены дословно»;
- деструктор Renderer::Impl — 22 строки, а не тридцать;
- путевых зависимостей у порта две: assets/meshes с откатом на текущий
  каталог и pipeline_cache.bin рядом с бинарём;
- граф целей CMake ацикличен, цикл существует на уровне исходников —
  именно поэтому CMake и молчит.

rust/README.md:
- объявленный rust-version = "1.82" недостижим: залоченные egui,
  egui-winit и epaint 0.36.1 требуют 1.95;
- перечни зависимостей крейтов были неполны, приведены целиком;
- build.rs читает ../../../shaders, а не ../../shaders;
- тесты не «чистая математика»: тесты загрузчика читают файлы из
  assets/meshes и требуют клон репозитория;
- assets/ порту никто не копирует, он находит корневую копию сам;
- те же поправки про цикл в CMake и про момент ожидания простоя.

README.md:
- панель называется Mesh, а не «Mesh load»;
- пресета clang-cl не существует, компилятор пресеты не фиксируют;
- версии Vulkan SDK и компиляторов сборкой не проверяются;
- Buffer и Image перечислены среди рабочих модулей vk/, хотя ими не
  пользуется никто.

docs/theory/solver_2x_sdf/README.md:
- предлагался несуществующий переключатель cfg.collision="bgk" и реестр
  операторов get; тот же файл двумя разделами ниже говорит, что оператор
  зафиксирован.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 14:11:06 +03:00
..

SimVulcan на Rust

Порт редактора SimVulcan с C++20 на Rust. Делает то же самое: орбитальная камера над тремя опорными плоскостями сетки, цветные оси XYZ, загрузка .obj и три режима отображения модели. Тот же Vulkan 1.3 с динамическим рендерингом и synchronization2, те же шейдеры.

Существует ради сравнения: обе версии лежат в репозитории рядом и собираются независимо. Разбор различий с цифрами — в ../docs/rust_vs_cpp.md.

Требования

  • Rust 1.95+ (проверялось на 1.97.1). В Cargo.toml объявлено rust-version = "1.82", но это значение недостижимо: залоченные egui, egui-winit и epaint 0.36.1 сами требуют 1.95, и на 1.82 Cargo обрывается на разрешении зависимостей
  • Vulkan SDK 1.3.290+ — нужен только glslangValidator для сборки шейдеров (ищется в %VULKAN_SDK%\Bin, затем в PATH)
  • Драйвер Vulkan 1.3 с fillModeNonSolid

Сборка и запуск

cd rust
cargo build --release
./target/release/SimVulcan

Запускать можно из любого каталога. Шейдеры вшиты в исполняемый файл на этапе сборки, а assets/meshes ищется подъёмом по предкам от самого бинаря и только затем — от текущего каталога; в отличие от C++-версии, которой нужен запуск из её собственного каталога. В отличие от неё же, assets/ никуда не копируется: порт находит копию в корне репозитория, поднимаясь от target/<профиль>/.

Рядом с исполняемым файлом создаётся pipeline_cache.bin — сериализованный VkPipelineCache, он перечитывается при следующем запуске. Файл одноразовый: если конвейеры повели себя странно, его можно удалить.

Слой валидации Khronos запрашивается во всех конфигурациях, включая --release, — как и в C++-версии. Если слоя нет, приложение об этом предупредит и пойдёт дальше.

Для работы над кодом собирайте cargo build без --release. В профиле выпуска стоит lto = "thin", и правка одной строки заставляет заново оптимизировать и слинковать весь бинарь: 52 секунды против 5 в отладочной сборке. Замеры — в ../docs/rust_vs_cpp.md.

Тесты

cargo test

Видеокарта не нужна: габариты меша, сварка вершин (три случая), чтение Cube.obj и его путь ошибки, пространство отсечения камеры и ограничители наклона и приближения, разбор категорий журнала — тринадцать тестов плюс один помеченный #[ignore] диагностический, тот самый, которым получена таблица расхождения загрузчиков. Чистой математикой это, впрочем, не является: тесты загрузчика читают настоящие файлы из assets/meshes через $CARGO_MANIFEST_DIR, поэтому им нужен рабочий клон репозитория. Тесты живут прямо в модулях (#[cfg(test)] mod tests), отдельной цели под них нет.

Раскладка

Пять крейтов повторяют карту целей CMake из корня репозитория:

Ниже перечислены сторонние зависимости; вдобавок каждый крейт зависит от тех крейтов рабочего пространства, что указаны в скобках.

crates/simv-core/     журнал с категориями, окно      → log, chrono, winit
crates/simv-mesh/     чтение .obj, сварка, габариты   → glam, tobj, log, thiserror
                                                        (+ simv-core)
crates/simv-vk/       весь Vulkan + build.rs          → ash, ash-window, raw-window-handle,
                      (GLSL → SPIR-V)                   gpu-allocator, egui, egui-winit,
                                                        egui-ash-renderer, winit, glam,
                                                        bytemuck, log, thiserror
                                                        (+ simv-core, simv-mesh)
crates/simv-editor/   камера, ввод, панели            → egui, glam, log
                                                        (+ simv-core, simv-mesh)
crates/simv-app/      App и точка входа               → четыре крейта выше
                                                        + egui, winit, glam, log, anyhow

Главный инвариант проекта — «весь Vulkan живёт в одном месте» — здесь проверяет компилятор: у simv-mesh и simv-editor в зависимостях нет ни ash, ни simv-vk, так что нарушить границу нельзя даже по невнимательности.

Шейдеры общие с C++-версией: build.rs крейта simv-vk компилирует ../../../shaders/editor/*.{vert,frag} тем же glslangValidator -V --target-env vulkan1.3 и кладёт SPIR-V в OUT_DIR, откуда он попадает в бинарь через include_bytes!.

Чем отличается от C++-версии

Снаружи — почти ничем, кроме внешнего вида панелей: вместо Dear ImGui взят egui. Причина: крейт imgui — это биндинги, и сборка всё равно тянула бы за собой исходники Dear ImGui на C++, что обесценило бы сравнение.

Внутри отличия сведены в отдельный документ, здесь только самые заметные:

  • App живёт в исполняемом крейте, а не в simv-core. В C++ цикличны исходники (core::App владеет vk::Renderer, vk::Renderer принимает core::Window&), тогда как граф целей CMake ацикличен — потому CMake и молчит: simv_vk не линкует simv_core, а лишь видит его заголовки, и символы Window находятся при сборке исполняемого файла. Cargo такую схему отвергает.
  • Состояние сцены возвращается из замыкания интерфейса, а не ставится сеттерами. Замыкание вызывается из метода рендерера, поэтому трогать рендерер оттуда нельзя.
  • Загруженный меш выгружается на видеокарту после кадра, а не изнутри него, как в C++, где колбэк панели дёргает vkDeviceWaitIdle уже после захвата образа цепочки показа — хотя запись команд там ещё не началась, vkBeginCommandBuffer идёт позже.
  • Buffer и Image задействованы. В C++ обе обёртки собираются в цель simv_vk, но ими не пользуется никто.
  • Нет pImpl. Приватные поля модуля дают ту же изоляцию, ради которой в C++ заведён struct Renderer::Impl.
  • Цикл событий принадлежит winit. ShouldClose/PollEvents заменены методами ApplicationHandler.