我一直在使用Vulkano来进行一些简单的3D图形处理。通常,我喜欢用文本编写GLSL着色器,然后重新启动程序,甚至在程序运行时更改着色器。在Vulkano中给出的示例似乎使用宏将GLSL转换为某种形式的基于SPIR-V的附加了锈功能的着色器,但GLSL实际上已编译为二进制文件(即使使用文件路径)。>
我已经成功地获得了板条箱shaderc来快速构建SPIR-V:
let mut f = File::open("src/grafx/vert.glsl")
.expect("Can't find file src/bin/runtime-shader/vert.glsl
This example needs to be run from the root of the example crate.");
let mut source = String::new();
f.read_to_string(&mut source);
//let source = "#version 310 es\n void EP() {}";
let mut compiler = shaderc::Compiler::new().unwrap();
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some("main"));
let binary_result = compiler.compile_into_spirv(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert_eq!(Some(&0x07230203), binary_result.as_binary().first());
let text_result = compiler.compile_into_spirv_assembly(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert!(text_result.as_text().starts_with("; SPIR-V\n"));
//println!("Compiled Vertex Shader: {}", text_result.as_text());
let vert_spirv = {
unsafe { ShaderModule::new(device.clone(), binary_result.as_binary_u8()) }.unwrap()
};
vert_spirv
到目前为止,很好,我们有一个 ShaderModule ,这似乎是第一步。但是,我们实际上需要的是 GraphicsEntryPoint ,然后可以将其放入 GraphicsPipeline 中。显然, GraphicsPipeline 是我们将着色器,三角形和深度图以及所有这些可爱的东西串在一起的地方。
麻烦的是,我不知道执行此壮举的代码是怎么回事:
pub fn shade_vertex <'a, S> (vert_spirv: &'a Arc<ShaderModule>) ->
GraphicsEntryPoint<'a, S, VertInput, VertOutput, VertLayout> {
let tn = unsafe {
vert_spirv.graphics_entry_point(
CStr::from_bytes_with_nul_unchecked(b"main\0"),
VertInput,
VertOutput,
VertLayout(ShaderStages { vertex: true, ..ShaderStages::none() }),
GraphicsShaderType::Vertex
)
};
tn
}
具体来说,什么是 VertInput 和 VertOutput ?我从这里的示例中复制了它们:https://github.com/vulkano-rs/vulkano/blob/master/examples/src/bin/runtime-shader/main.rs
这是我能找到的最接近的示例,该示例涉及动态加载着色器。看起来像输入和输出的 正在寻找SPIR-V的入口点,但我不知道该怎么做。我希望现有宏中的某个功能可以为我解决这个问题。我已经走了这么远,但是我似乎有点卡住了。
还有其他人尝试在运行时加载着色器吗?
答案 0 :(得分:0)
我正在使用wgpu,已将我的设备,render_pipeline设为多线程:
let rx = Arc::new(Mutex::new(rx));
let window = Arc::new(Mutex::new(window));
let fs = Arc::new(Mutex::new(fs));
let fs_module = Arc::new(Mutex::new(fs_module));
let render_pipeline = Arc::new(Mutex::new(render_pipeline));
let device = Arc::new(Mutex::new(device));
使用notify
来监听变更事件:
notify = "4.0.15"
use notify::{RecommendedWatcher, Watcher, RecursiveMode};
//mainxx
let (tx, rx) = mpsc::channel();
let mut watcher: RecommendedWatcher =
Watcher::new(tx, Duration::from_millis(500)).unwrap();
log::info!("Starting watcher on {:?}", *FRAG_SHADER_PATH);
watcher.watch((*FRAG_SHADER_PATH).clone(), RecursiveMode::NonRecursive).unwrap();
然后产生一个侦听更改的线程:
thread::spawn(move || {
log::info!("Shader watcher thread spawned");
loop {
if let Ok(notify::DebouncedEvent::Write(..)) = rx.lock().unwrap().recv() {
log::info!("Write event in fragment shader");
window.lock().unwrap().set_title("Loading shader.frag...");
*fs.lock().unwrap() = load_fs().unwrap();
*fs_module.lock().unwrap() = load_fs_module(Arc::clone(&device), &Arc::clone(&fs).lock().unwrap());
*render_pipeline.lock().unwrap() = create_render_pipeline_multithreaded(Arc::clone(&device), Arc::clone(&fs_module));
render.lock().unwrap().deref_mut()();
window.lock().unwrap().set_title(TITLE);
};
}
});
其中load_fs
是使用glsl_to_spirv
的闭包:
let load_fs = move || -> Result<Vec<u32>, std::io::Error> {
log::info!("Loading fragment shader");
let mut buffer = String::new();
let mut f = File::open(&*FRAG_SHADER_PATH)?;
f.read_to_string(&mut buffer)?;
// Load fragment shader
wgpu::read_spirv(
glsl_to_spirv::compile(
&buffer,
glsl_to_spirv::ShaderType::Fragment
).expect("Compilation failed")
)
};