# SimVulcan на Rust Порт редактора `SimVulcan` с C++20 на Rust. Делает то же самое: орбитальная камера над тремя опорными плоскостями сетки, цветные оси XYZ, загрузка `.obj` и три режима отображения модели. Тот же Vulkan 1.3 с динамическим рендерингом и synchronization2, те же шейдеры. Существует ради сравнения: обе версии лежат в репозитории рядом и собираются независимо. Разбор различий с цифрами — в [`../docs/rust_vs_cpp.md`](../docs/rust_vs_cpp.md). ## Требования * Rust 1.82+ (проверялось на 1.97.1) * Vulkan SDK 1.3.290+ — нужен только `glslangValidator` для сборки шейдеров (ищется в `%VULKAN_SDK%\Bin`, затем в `PATH`) * Драйвер Vulkan 1.3 с `fillModeNonSolid` ## Сборка и запуск ```sh cd rust cargo build --release ./target/release/SimVulcan ``` Запускать можно из любого каталога. Шейдеры вшиты в исполняемый файл на этапе сборки, а `assets/meshes` ищется подъёмом по предкам от самого бинаря — в отличие от C++-версии, которой нужен запуск из её собственного каталога. Рядом с исполняемым файлом создаётся `pipeline_cache.bin` — сериализованный `VkPipelineCache`, он перечитывается при следующем запуске. Файл одноразовый: если конвейеры повели себя странно, его можно удалить. Слой валидации Khronos запрашивается во всех конфигурациях, включая `--release`, — как и в C++-версии. Если слоя нет, приложение об этом предупредит и пойдёт дальше. **Для работы над кодом собирайте `cargo build` без `--release`.** В профиле выпуска стоит `lto = "thin"`, и правка одной строки заставляет заново оптимизировать и слинковать весь бинарь: 52 секунды против 5 в отладочной сборке. Замеры — в [`../docs/rust_vs_cpp.md`](../docs/rust_vs_cpp.md). ## Тесты ```sh cargo test ``` Чистая математика без видеокарты: габариты меша, сварка вершин, чтение `Cube.obj`, пространство отсечения камеры. Тесты живут прямо в модулях (`#[cfg(test)] mod tests`), отдельной цели под них нет. ## Раскладка Пять крейтов повторяют карту целей CMake из корня репозитория: ``` crates/simv-core/ журнал с категориями, окно → log, chrono, winit crates/simv-mesh/ чтение .obj, сварка вершин, габариты → glam, tobj crates/simv-vk/ весь Vulkan + build.rs (GLSL → SPIR-V) → ash, gpu-allocator, egui-ash-renderer crates/simv-editor/ камера, ввод, панели → egui crates/simv-app/ App и точка входа → всё вышеперечисленное ``` Главный инвариант проекта — «весь 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&`), и держится это лишь на том, что `simv_vk` не линкует `simv_core`, а видит его заголовки. Cargo цикл между крейтами отвергает. * **Состояние сцены возвращается из замыкания интерфейса**, а не ставится сеттерами. Замыкание вызывается из метода рендерера, поэтому трогать рендерер оттуда нельзя. * **Загруженный меш выгружается на видеокарту после кадра**, а не из середины записи команд, как в C++, где колбэк панели дёргает `vkDeviceWaitIdle` уже после захвата образа цепочки показа. * **`Buffer` и `Image` задействованы.** В C++ обе обёртки собираются в цель `simv_vk`, но ими не пользуется никто. * **Нет pImpl.** Приватные поля модуля дают ту же изоляцию, ради которой в C++ заведён `struct Renderer::Impl`. * **Цикл событий принадлежит winit.** `ShouldClose`/`PollEvents` заменены методами `ApplicationHandler`.