Files
CFDManager/rust/crates/simv-vk/build.rs
T
NotBigGhostandClaude Opus 5 f5c9e61589 Порт редактора SimVulcan на Rust
Та же программа на ash + winit + egui: орбитальная камера, три плоскости сетки,
загрузка .obj, три режима отображения, единый проход динамического рендеринга с
глубинным вложением и submit через synchronization2. Шейдеры общие с C++-версией —
build.rs компилирует их из shaders/ и вшивает SPIR-V в бинарь.

Пять крейтов повторяют карту целей CMake, но инвариант «весь Vulkan в одном месте»
теперь проверяет компилятор: у simv-mesh и simv-editor нет ash в зависимостях.

Расхождения, вынужденные языком, а не вкусом:
 * App переехал в исполняемый крейт — в C++ граф целей цикличен (core владеет vk,
   vk принимает core::Window&), и держится это лишь на том, что simv_vk видит
   заголовки core, не линкуя цель; Cargo такой цикл отвергает;
 * состояние сцены возвращается из замыкания интерфейса, а не ставится сеттерами:
   замыкание вызывается из метода рендерера, трогать рендерер оттуда нельзя;
 * выгрузка меша на видеокарту уехала из середины записи кадра, где C++-версия
   зовёт vkDeviceWaitIdle уже после захвата образа цепочки показа.

Vulkan-контекст и цепочка показа написаны руками: замены vk-bootstrap в экосистеме
Rust нет. Обёртки Buffer и Image, лежащие в C++-версии без применения, здесь
задействованы всеми потребителями.

13 тестов, из них три перенесены из Catch2 дословно. cargo clippy -D warnings чист,
слой валидации молчит.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 20:58:19 +03:00

96 lines
3.9 KiB
Rust

//! Компиляция GLSL в SPIR-V на этапе сборки — аналог `cmake/ShaderCompile.cmake`.
//!
//! Шейдеры берутся из общего с C++-версией каталога `shaders/` в корне репозитория:
//! обе сборки компилируют одни и те же файлы теми же ключами, поэтому расхождение в
//! картинке нельзя списать на шейдер.
//!
//! В отличие от CMake результат не копируется рядом с исполняемым файлом, а зашивается
//! в бинарь через `include_bytes!`. Отсюда исчезают и `FindSpvPath`, и POST_BUILD-копия,
//! и требование «запускать из каталога исполняемого файла».
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
// crates/simv-vk → rust → корень репозитория.
let shader_root = manifest.join("../../../shaders");
let shader_root = shader_root.canonicalize().unwrap_or(shader_root);
let editor_dir = shader_root.join("editor");
println!("cargo:rerun-if-env-changed=VULKAN_SDK");
println!("cargo:rerun-if-changed={}", editor_dir.display());
let compiler = find_glslang();
let mut sources: Vec<PathBuf> = std::fs::read_dir(&editor_dir)
.unwrap_or_else(|e| panic!("не читается каталог шейдеров {}: {e}", editor_dir.display()))
.filter_map(|entry| entry.ok().map(|e| e.path()))
.filter(|p| {
matches!(
p.extension().and_then(|e| e.to_str()),
Some("vert") | Some("frag")
)
})
.collect();
// Порядок обхода каталога зависит от файловой системы — сортируем, чтобы сборка
// была воспроизводимой.
sources.sort();
assert!(
!sources.is_empty(),
"в {} нет ни одного .vert/.frag",
editor_dir.display()
);
let out_editor = out_dir.join("editor");
std::fs::create_dir_all(&out_editor).unwrap();
for src in &sources {
println!("cargo:rerun-if-changed={}", src.display());
let name = src.file_name().unwrap().to_string_lossy().to_string();
let dst = out_editor.join(format!("{name}.spv"));
let output = Command::new(&compiler)
.arg("-V")
.args(["--target-env", "vulkan1.3"])
// glslangValidator требует -I<путь> без пробела.
.arg(format!("-I{}", shader_root.display()))
.arg("-o")
.arg(&dst)
.arg(src)
.output()
.unwrap_or_else(|e| panic!("не запускается {}: {e}", compiler.display()));
if !output.status.success() {
panic!(
"GLSL → SPIR-V не удалось для {}:\n{}{}",
src.display(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
}
}
/// Ищем компилятор там же, где его ищет CMake: сначала в Vulkan SDK, потом в PATH.
fn find_glslang() -> PathBuf {
let exe = if cfg!(windows) {
"glslangValidator.exe"
} else {
"glslangValidator"
};
if let Ok(sdk) = std::env::var("VULKAN_SDK") {
let candidate = Path::new(&sdk).join("Bin").join(exe);
if candidate.exists() {
return candidate;
}
let candidate = Path::new(&sdk).join("bin").join(exe);
if candidate.exists() {
return candidate;
}
}
PathBuf::from(exe)
}