LIVE SHADER RELOADING!!

This commit is contained in:
2025-12-01 21:17:51 +00:00
parent 3d994908ad
commit 978e296a69
7 changed files with 348 additions and 78 deletions
+12 -2
View File
@@ -36,6 +36,8 @@ pub(crate) struct GState {
pub(crate) lights: Vec<Light>,
pub(crate) csg: Vec<Arc<RwLock<CSG>>>,
pub(crate) new_csg_needed: bool,
pub(crate) fps: [f64; 128],
pub(crate) debug: PreviousDebug,
@@ -47,6 +49,8 @@ pub(crate) struct GState {
pub(crate) sheen_tint: f32,
pub(crate) clear_coat_gloss: f32,
pub(crate) clear_coat: f32,
pub(crate) recreate_pipelines: bool,
}
impl Default for GState {
@@ -59,6 +63,8 @@ impl Default for GState {
lights: vec![],
csg: vec![],
new_csg_needed: false,
fps: [0.0; 128],
debug: Default::default(),
@@ -70,11 +76,13 @@ impl Default for GState {
clear_coat_gloss: 0.63,
subsurface: 0.,
clear_coat: 0.85,
recreate_pipelines: false,
}
}
}
pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState, new_csg_needed: &mut bool) {
pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState) {
gui.immediate_ui(|gui| {
let ctx = gui.context();
egui::SidePanel::left(Id::new("main_left"))
@@ -220,7 +228,7 @@ pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState, new_csg_needed: &mut boo
state.csg.remove(i);
}
if state.csg.len() < 32 && ui.small_button("add csg").clicked() {
*new_csg_needed = true;
state.new_csg_needed = true;
}
ui.heading(format!("Lights ({})", state.lights.len()));
let mut lightdel = vec![];
@@ -315,6 +323,8 @@ pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState, new_csg_needed: &mut boo
&mut state.debug.use_linear_ao,
"Use linear falloff for occlusion calculation",
);
state.recreate_pipelines = ui.button("Force recompile pipelines").clicked();
});
});
});
+222 -64
View File
@@ -18,7 +18,7 @@ use std::{
fs::{File, remove_file, rename},
io::{Cursor, Read, Write},
path::{Path, PathBuf},
sync::{Arc, Mutex, mpmc, mpsc},
sync::{Arc, Mutex, atomic::AtomicBool, mpmc, mpsc},
thread::{self, JoinHandle},
time::Instant,
};
@@ -28,6 +28,7 @@ use egui_winit_vulkano::{Gui, GuiConfig};
use foldhash::{HashMap, HashMapExt, HashSet};
use glam::{self, EulerRot, Mat3, Mat4, Vec3, vec3};
use log::{error, info, trace};
use notify::{RecommendedWatcher, Watcher};
use rspirv::dr::Module;
use simplelog::{CombinedLogger, Config, TermLogger, WriteLogger};
use vulkano::{
@@ -72,7 +73,7 @@ use vulkano::{
layout::PipelineDescriptorSetLayoutCreateInfo,
},
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::{ShaderModule, SpecializationConstant},
shader::{ShaderModule, ShaderModuleCreateInfo, SpecializationConstant},
swapchain::{
PresentMode, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
acquire_next_image,
@@ -219,6 +220,8 @@ struct App {
thread_work_creation: mpmc::Sender<WorkItem>,
thread_work_completion: mpsc::Receiver<WorkComplete>,
rcx: Arc<Mutex<Option<RenderContext>>>,
watcher: RecommendedWatcher,
glsl_changed: Arc<AtomicBool>,
}
#[derive(Debug, Clone)]
@@ -489,7 +492,7 @@ impl App {
.wait(None)
.unwrap();
let trace_spv_code = include_bytes!("trace.recompiled.spv");
let trace_spv_code = include_bytes!("../shaders_out/trace.recompiled.spv");
let trace_spv_code_u32 = trace_spv_code
.as_chunks::<4>()
.0
@@ -500,7 +503,7 @@ impl App {
rspirv::binary::parse_words(trace_spv_code_u32, &mut loader).unwrap();
let trace_module = loader.module();
let normals_spv_code = include_bytes!("normals.recompiled.spv");
let normals_spv_code = include_bytes!("../shaders_out/normals.recompiled.spv");
let normals_spv_code_u32 = normals_spv_code
.as_chunks::<4>()
.0
@@ -524,6 +527,45 @@ impl App {
})
.collect::<Vec<_>>();
let glsl_changed = Arc::new(AtomicBool::new(false));
let watcher_glsl_changed = glsl_changed.clone();
let mut watcher = notify::recommended_watcher(
move |res: Result<notify::Event, notify::Error>| match res {
Ok(event) => {
//info!("event: {:?}", event);
#[cfg(target_os = "linux")]
match event.kind {
notify::EventKind::Modify(notify::event::ModifyKind::Data(_)) => {
info!("event: {:?}", event);
watcher_glsl_changed.store(true, std::sync::atomic::Ordering::SeqCst);
},
_ => {},
}
},
Err(e) => error!("watch error: {:?}", e),
},
)
.unwrap();
#[cfg(target_os = "linux")]
{
let paths = std::fs::read_dir("src").unwrap();
for path in paths {
if let Ok(path) = path {
if path
.path()
.extension()
.map(|ext| ext == "glsl")
.unwrap_or(false)
{
watcher
.watch(&path.path(), notify::RecursiveMode::NonRecursive)
.unwrap();
}
}
}
}
App {
instance,
device,
@@ -550,14 +592,15 @@ impl App {
thread_work_creation: thread_work_creation_sender,
thread_work_completion: thread_work_completion_receiver,
rcx: Arc::new(Mutex::new(None)),
watcher,
glsl_changed,
}
}
}
mod trace_vs {
vulkano_shaders::shader! {
ty: "vertex",
path: "src/trace.vert.glsl",
bytes: "shaders_out/trace.vert.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
@@ -566,8 +609,7 @@ mod trace_vs {
mod fullscreen_vs {
vulkano_shaders::shader! {
ty: "vertex",
path: "src/fullscreen.vert.glsl",
bytes: "shaders_out/fullscreen.vert.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
@@ -576,8 +618,7 @@ mod fullscreen_vs {
mod lighting_fs {
vulkano_shaders::shader! {
ty: "fragment",
path: "src/lighting.frag.glsl",
bytes: "shaders_out/lighting.frag.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
@@ -1367,6 +1408,123 @@ impl App {
}
}
fn load_all_spv_from_file(&mut self, rcx: &mut RenderContext) {
let trace_frag = load_spv_from_file("trace.recompiled.spv");
if trace_frag.is_err() {
error!("Could not read trace frag file");
return;
}
let trace_frag = trace_frag.unwrap();
let trace_vert = load_spv_from_file("trace.vert.spv");
if trace_vert.is_err() {
error!("Could not read trace vert file");
return;
}
let trace_vert = trace_vert.unwrap();
let normals_frag = load_spv_from_file("normals.recompiled.spv");
if normals_frag.is_err() {
error!("Could not read normals frag file");
return;
}
let normals_frag = normals_frag.unwrap();
let fullscreen_vert = load_spv_from_file("fullscreen.vert.spv");
if fullscreen_vert.is_err() {
error!("Could not read fullscreen vert file");
return;
}
let fullscreen_vert = fullscreen_vert.unwrap();
let lighting_frag = load_spv_from_file("lighting.frag.spv");
if lighting_frag.is_err() {
error!("Could not read lighting frag file");
return;
}
let lighting_frag = lighting_frag.unwrap();
let mut trace_loader = rspirv::dr::Loader::new();
rspirv::binary::parse_words(trace_frag, &mut trace_loader).unwrap();
let mut normals_loader = rspirv::dr::Loader::new();
rspirv::binary::parse_words(normals_frag, &mut normals_loader).unwrap();
let trace_vert = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&trace_vert),
)
.unwrap()
};
let fullscreen_vert = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&fullscreen_vert),
)
.unwrap()
};
let lighting_frag = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&lighting_frag),
)
.unwrap()
};
rcx.shader_modules = ShaderModules {
trace_vs: trace_vert,
fullscreen_vs: fullscreen_vert,
lighting_fs: lighting_frag,
};
self.trace_module = trace_loader.module();
self.normals_module = normals_loader.module();
}
fn recreate_pipelines(
&mut self,
rcx: &mut RenderContext,
work_for_later: &mut Vec<WorkComplete>,
) {
self.load_all_spv_from_file(rcx);
let mut csg_left = self.gstate.csg.len();
for (i, csg) in self.gstate.csg.iter().enumerate() {
self.thread_work_creation
.send(WorkItem::RecompilePipelines(
csg.clone(),
self.device.clone(),
self.trace_module.clone(),
self.normals_module.clone(),
rcx.render_pass.clone(),
self.pipeline_cache.clone(),
rcx.shader_modules.clone(),
self.previous_debug.clone(),
i,
))
.unwrap();
}
rcx.lighting_pipeline = pipeline_recompile(
&rcx.render_pass,
&self.pipeline_cache,
&rcx.shader_modules,
&self.gstate.debug,
);
while csg_left > 0 {
for work in self.thread_work_completion.try_iter() {
match work {
WorkComplete::RecompilePipelines(_index) => {
csg_left -= 1;
},
other => work_for_later.push(other),
}
}
}
}
fn redraw(&mut self, rcx: &mut RenderContext) {
let window_size = rcx.window.inner_size();
if window_size.width == 0 || window_size.height == 0 {
@@ -1389,33 +1547,50 @@ impl App {
rcx.render_start = Instant::now();
#[cfg(target_os = "linux")]
if self.glsl_changed.load(std::sync::atomic::Ordering::SeqCst) {
self.glsl_changed
.store(false, std::sync::atomic::Ordering::SeqCst);
if std::process::Command::new("bash")
.arg("assemble_trace.sh")
.status()
.is_err()
{
error!("Could not compile shaders");
}
rcx.recreate_pipelines = true;
}
let mut work_for_later = vec![];
if self.draw_gui {
let mut csg_needed = false;
gui_up(&mut rcx.gui, &mut self.gstate, &mut csg_needed);
if csg_needed {
self.thread_work_creation
.send(WorkItem::CreateCSG(
self.device.clone(),
self.trace_module.clone(),
self.normals_module.clone(),
rcx.render_pass.clone(),
self.pipeline_cache.clone(),
rcx.shader_modules.clone(),
self.previous_debug.clone(),
self.block_enable_allocator.clone(),
self.host_visible_allocator.clone(),
self.descriptor_set_allocator.clone(),
rcx.swapchain.image_count(),
DEFAULT_SUBDIVISION,
rand::random(),
-1,
))
.unwrap();
}
gui_up(&mut rcx.gui, &mut self.gstate);
}
if self.gstate.new_csg_needed {
self.thread_work_creation
.send(WorkItem::CreateCSG(
self.device.clone(),
self.trace_module.clone(),
self.normals_module.clone(),
rcx.render_pass.clone(),
self.pipeline_cache.clone(),
rcx.shader_modules.clone(),
self.previous_debug.clone(),
self.block_enable_allocator.clone(),
self.host_visible_allocator.clone(),
self.descriptor_set_allocator.clone(),
rcx.swapchain.image_count(),
DEFAULT_SUBDIVISION,
rand::random(),
-1,
))
.unwrap();
self.gstate.new_csg_needed = false;
}
rcx.recreate_pipelines |= self.gstate.recreate_pipelines;
let mut push_constants = vec![
PushConstantData {
world: Default::default(),
@@ -1465,38 +1640,7 @@ impl App {
}
if rcx.recreate_pipelines {
let mut csg_left = self.gstate.csg.len();
for (i, csg) in self.gstate.csg.iter().enumerate() {
self.thread_work_creation
.send(WorkItem::RecompilePipelines(
csg.clone(),
self.device.clone(),
self.trace_module.clone(),
self.normals_module.clone(),
rcx.render_pass.clone(),
self.pipeline_cache.clone(),
rcx.shader_modules.clone(),
self.previous_debug.clone(),
i,
))
.unwrap();
}
rcx.lighting_pipeline = pipeline_recompile(
&rcx.render_pass,
&self.pipeline_cache,
&rcx.shader_modules,
&self.gstate.debug,
);
while csg_left > 0 {
for work in self.thread_work_completion.try_iter() {
match work {
WorkComplete::RecompilePipelines(_index) => {
csg_left -= 1;
},
other => work_for_later.push(other),
}
}
}
self.recreate_pipelines(rcx, &mut work_for_later);
rcx.recreate_pipelines = false;
}
@@ -1927,6 +2071,7 @@ fn get_spec_constants(debug: &PreviousDebug) -> HashMap<u32, SpecializationConst
spec_constants
}
fn pipeline_recompile(
render_pass: &Arc<RenderPass>,
cache: &Arc<PipelineCache>,
@@ -2130,3 +2275,16 @@ fn get_pipeline_cache(device: Arc<Device>) -> Arc<PipelineCache> {
fn relpath(path: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join(path)
}
fn load_spv_from_file(name: &str) -> std::io::Result<Vec<u32>> {
let mut spv_file = std::fs::File::open(format!("shaders_out/{name}"))?;
let mut buf = vec![];
spv_file.read_to_end(&mut buf)?;
let spv_code_u32 = buf
.as_chunks::<4>()
.0
.into_iter()
.map(|u| u32::from_le_bytes(*u))
.collect::<Vec<_>>();
Ok(spv_code_u32)
}
+1
View File
@@ -1,4 +1,5 @@
#version 460
#extension GL_GOOGLE_include_directive:require
#include "include.glsl"
#include "implicit_include.glsl"