Порт редактора 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>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
//! Шейдерные модули — перенос `simv::vk::Shader`.
|
||||
//!
|
||||
//! Главное отличие от C++-версии: SPIR-V не ищется на диске, а зашит в исполняемый
|
||||
//! файл на этапе сборки (см. `build.rs`). Вместе с `FindSpvPath` исчезли и
|
||||
//! POST_BUILD-копирование каталога `spirv/`, и требование запускать редактор из
|
||||
//! каталога бинаря, и целый класс ошибок «шейдер не найден» во время выполнения:
|
||||
//! отсутствующий шейдер теперь ломает сборку, а не запуск.
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use ash::vk;
|
||||
|
||||
use crate::error::{Ctx, Result};
|
||||
|
||||
/// Скомпилированный SPIR-V, вшитый в бинарь. Пути соответствуют раскладке
|
||||
/// `shaders/editor/` в корне репозитория.
|
||||
pub mod spirv {
|
||||
pub const MESH_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/mesh.vert.spv"));
|
||||
pub const MESH_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/mesh.frag.spv"));
|
||||
pub const GRID_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/grid.vert.spv"));
|
||||
pub const GRID_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/grid.frag.spv"));
|
||||
}
|
||||
|
||||
/// Владеющая обёртка над `VkShaderModule`: уничтожается на выходе из области
|
||||
/// видимости, в том числе если сборка конвейера завершилась ошибкой.
|
||||
pub struct ShaderModule {
|
||||
device: ash::Device,
|
||||
handle: vk::ShaderModule,
|
||||
}
|
||||
|
||||
impl ShaderModule {
|
||||
pub fn new(device: &ash::Device, spirv_bytes: &[u8]) -> Result<Self> {
|
||||
// `read_spv` заодно снимает вопрос выравнивания: `include_bytes!` даёт срез
|
||||
// байтов, а Vulkan хочет 32-битные слова.
|
||||
let words = ash::util::read_spv(&mut Cursor::new(spirv_bytes))
|
||||
.map_err(|e| crate::error::VkError::Other(format!("битый SPIR-V: {e}")))?;
|
||||
let ci = vk::ShaderModuleCreateInfo::default().code(&words);
|
||||
let handle =
|
||||
unsafe { device.create_shader_module(&ci, None) }.ctx("vkCreateShaderModule")?;
|
||||
Ok(Self {
|
||||
device: device.clone(),
|
||||
handle,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> vk::ShaderModule {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ShaderModule {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.device.destroy_shader_module(self.handle, None) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user