From a35c07e098ed7b131c3e8c4f9d3c9dee1cde5ed0 Mon Sep 17 00:00:00 2001 From: tittu Date: Tue, 16 Jun 2026 20:35:22 +0530 Subject: [PATCH 1/2] minor refactor --- src/vulkan.rs | 160 +++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/src/vulkan.rs b/src/vulkan.rs index 944d219..b12dfef 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -239,46 +239,46 @@ pub fn vulkan_surfchain( } -// --------------------------------------------------------------------- / record commands - -impl VulkanCore { - pub(crate) fn record_commands( - &self, - f: impl FnOnce(vk::CommandBuffer), - ) -> Result<(), Box> { - unsafe { - self.device - .reset_command_pool(self.command_pool, vk::CommandPoolResetFlags::empty())?; - } - - let begin_info = vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - unsafe { - self.device - .begin_command_buffer(self.command_buffer, &begin_info)?; - } - - f(self.command_buffer); - unsafe { - self.device.end_command_buffer(self.command_buffer)?; - } +// --------------------------------------------------------------------- / set mode - let submit_info = vk::SubmitInfo::default() - .command_buffers(std::slice::from_ref(&self.command_buffer)); - let fence = unsafe { - self.device - .create_fence(&vk::FenceCreateInfo::default(), None)? - }; - unsafe { - self.device - .queue_submit(self.graphics_queue, &[submit_info], fence)?; - self.device - .wait_for_fences(&[fence], true, u64::MAX)?; - self.device.destroy_fence(fence, None); +fn mode_set( + img_w: u32, + img_h: u32, + scr_w: u32, + scr_h: u32, + anchor_x: f32, + anchor_y: f32, + mode: &str, +) -> (u32, u32, u32, u32, i32, i32, u32, u32, bool) { + if mode == "fit" { + let scale = (scr_w as f64 / img_w as f64).min(scr_h as f64 / img_h as f64); + let sw = (img_w as f64 * scale) as u32; + let sh = (img_h as f64 * scale) as u32; + let dx = ((scr_w - sw) as f32 * anchor_x) as i32; + let dy = ((scr_h - sh) as f32 * anchor_y) as i32; + return (0, 0, img_w, img_h, dx, dy, sw, sh, true); + } + if mode == "original" { + if img_w <= scr_w && img_h <= scr_h { + let dx = ((scr_w - img_w) as f32 * anchor_x) as i32; + let dy = ((scr_h - img_h) as f32 * anchor_y) as i32; + return (0, 0, img_w, img_h, dx, dy, img_w, img_h, true); } - - Ok(()) } + let src_aspect = img_w as f64 / img_h as f64; + let dst_aspect = scr_w as f64 / scr_h as f64; + let (sx, sy, sw, sh) = if src_aspect > dst_aspect { + let new_w = (img_h as f64 * dst_aspect) as u32; + let max_x = (img_w - new_w) as f32; + let x = (max_x * anchor_x) as u32; + (x, 0, new_w, img_h) + } else { + let new_h = (img_w as f64 / dst_aspect) as u32; + let max_y = (img_h - new_h) as f32; + let y = (max_y * anchor_y) as u32; + (0, y, img_w, new_h) + }; + (sx, sy, sw, sh, 0, 0, scr_w, scr_h, false) } @@ -346,6 +346,49 @@ impl VulkanCore { } +// --------------------------------------------------------------------- / record commands + +impl VulkanCore { + pub(crate) fn record_commands( + &self, + f: impl FnOnce(vk::CommandBuffer), + ) -> Result<(), Box> { + unsafe { + self.device + .reset_command_pool(self.command_pool, vk::CommandPoolResetFlags::empty())?; + } + + let begin_info = vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + unsafe { + self.device + .begin_command_buffer(self.command_buffer, &begin_info)?; + } + + f(self.command_buffer); + unsafe { + self.device.end_command_buffer(self.command_buffer)?; + } + + let submit_info = vk::SubmitInfo::default() + .command_buffers(std::slice::from_ref(&self.command_buffer)); + let fence = unsafe { + self.device + .create_fence(&vk::FenceCreateInfo::default(), None)? + }; + unsafe { + self.device + .queue_submit(self.graphics_queue, &[submit_info], fence)?; + self.device + .wait_for_fences(&[fence], true, u64::MAX)?; + self.device.destroy_fence(fence, None); + } + + Ok(()) + } +} + + // --------------------------------------------------------------------- / fill background impl VulkanCore { @@ -759,49 +802,6 @@ impl VulkanCore { } -// --------------------------------------------------------------------- / set mode - -fn mode_set( - img_w: u32, - img_h: u32, - scr_w: u32, - scr_h: u32, - anchor_x: f32, - anchor_y: f32, - mode: &str, -) -> (u32, u32, u32, u32, i32, i32, u32, u32, bool) { - if mode == "fit" { - let scale = (scr_w as f64 / img_w as f64).min(scr_h as f64 / img_h as f64); - let sw = (img_w as f64 * scale) as u32; - let sh = (img_h as f64 * scale) as u32; - let dx = ((scr_w - sw) as f32 * anchor_x) as i32; - let dy = ((scr_h - sh) as f32 * anchor_y) as i32; - return (0, 0, img_w, img_h, dx, dy, sw, sh, true); - } - if mode == "original" { - if img_w <= scr_w && img_h <= scr_h { - let dx = ((scr_w - img_w) as f32 * anchor_x) as i32; - let dy = ((scr_h - img_h) as f32 * anchor_y) as i32; - return (0, 0, img_w, img_h, dx, dy, img_w, img_h, true); - } - } - let src_aspect = img_w as f64 / img_h as f64; - let dst_aspect = scr_w as f64 / scr_h as f64; - let (sx, sy, sw, sh) = if src_aspect > dst_aspect { - let new_w = (img_h as f64 * dst_aspect) as u32; - let max_x = (img_w - new_w) as f32; - let x = (max_x * anchor_x) as u32; - (x, 0, new_w, img_h) - } else { - let new_h = (img_w as f64 / dst_aspect) as u32; - let max_y = (img_h - new_h) as f32; - let y = (max_y * anchor_y) as u32; - (0, y, img_w, new_h) - }; - (sx, sy, sw, sh, 0, 0, scr_w, scr_h, false) -} - - // --------------------------------------------------------------------- / destroy core pub fn destroy_wallbash( From 4e8cdf2358ffbb55a97b5e671d78f3ce2f89b68c Mon Sep 17 00:00:00 2001 From: tittu Date: Wed, 17 Jun 2026 21:31:57 +0530 Subject: [PATCH 2/2] minor refactor --- build.rs | 30 ++++++++--- src/main.rs | 24 +++++---- src/vulkan.rs | 44 +++++++++++----- src/wallbash.rs | 132 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 161 insertions(+), 69 deletions(-) diff --git a/build.rs b/build.rs index d7e3f23..1a185fc 100644 --- a/build.rs +++ b/build.rs @@ -4,15 +4,33 @@ use std::path::Path; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); - let path = Path::new("shaders").join("blur.comp"); - let source = fs::read_to_string(&path).expect("Failed to read blur.comp"); let compiler = shaderc::Compiler::new().unwrap(); let mut options = shaderc::CompileOptions::new().unwrap(); options.set_optimization_level(shaderc::OptimizationLevel::Performance); + + let shader_dir = Path::new("shaders"); + for entry in fs::read_dir(shader_dir).expect("Failed to read shaders directory") { + let entry = entry.unwrap(); + let path = entry.path(); + if path.extension().and_then(|s| s.to_str()) == Some("comp") { + let name = path.file_name().unwrap().to_str().unwrap(); + compile_shader(&compiler, &options, &out_dir, name); + } + } +} + +fn compile_shader( + compiler: &shaderc::Compiler, + options: &shaderc::CompileOptions, + out_dir: &str, + name: &str, +) { + let path = Path::new("shaders").join(name); + let source = fs::read_to_string(&path).expect("Failed to read shader"); let result = compiler - .compile_into_spirv(&source, shaderc::ShaderKind::Compute, "blur.comp", "main", Some(&options)) - .unwrap(); - let out_name = Path::new(&out_dir).join("blur.comp.spv"); - fs::write(out_name, result.as_binary_u8()).unwrap(); + .compile_into_spirv(&source, shaderc::ShaderKind::Compute, name, "main", Some(options)) + .expect("Failed to compile shader"); + let out_name = Path::new(out_dir).join(format!("{}.spv", name)); + fs::write(out_name, result.as_binary_u8()).expect("Failed to write SPIR-V"); } diff --git a/src/main.rs b/src/main.rs index 5bf67ab..0802e20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,17 +24,19 @@ const LOG_FILE: &str = "/tmp/wallbash.log"; // --------------------------------------------------------------------- / funtions fn print_usage() { - eprintln!(r"::Usage - wallbash start | Start the wallpaper daemon - wallbash set /path/to/file.img | Set wallpaper (auto start daemon) - wallbash stop | Stop the daemon - wallbash status | Show daemon status - -::Options - wallbash set [option] - -m, --mode | Scaling mode (cover, fit, original) - -a, --anchor <1-9> | Anchor point (1=top-left ... 9=bottom-right) - -w, --wall | Wallpaper file /path/to/file.img"); + eprintln!(r" + ::Usage + wallbash start | Start the wallpaper daemon + wallbash set /path/to/file.img | Set wallpaper (auto start daemon) + wallbash stop | Stop the daemon + wallbash status | Show daemon status + + ::Options + wallbash set [option] + -m, --mode | Scaling mode (cover, fit, original) + -a, --anchor <1-9> | Anchor point (1=top-left ... 9=bottom-right) + -w, --wall | Wallpaper file /path/to/file.img +" ); } fn send_command(cmd: &str) -> Result<(), Box> { diff --git a/src/vulkan.rs b/src/vulkan.rs index b12dfef..3345379 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -46,6 +46,14 @@ pub struct VulkanTexture { pub height: u32, } +pub struct VulkanCleanup { + pub surfchain: Option, + pub filter_module: Option, + pub filter_pipeline: Option, + pub filter_desc_layout: Option, + pub wallpaper_texture: Option, +} + // --------------------------------------------------------------------- / init vulkan @@ -806,15 +814,16 @@ impl VulkanCore { pub fn destroy_wallbash( vk_core: &VulkanCore, - surfchain: Option<&mut VulkanSurfchain>, - filter_module: Option, - filter_pipeline: Option, - filter_desc_layout: Option, + config: VulkanCleanup, level: u32, ) { - // destroy filter resources if provided and level ≥ 0 - if let (Some(module), Some(pipeline), Some(desc)) = (filter_module, filter_pipeline, filter_desc_layout) { + // destroy filter resources if provided + if let (Some(module), Some(pipeline), Some(desc)) = ( + config.filter_module, + config.filter_pipeline, + config.filter_desc_layout, + ) { unsafe { vk_core.device.destroy_pipeline(pipeline, None); vk_core.device.destroy_descriptor_set_layout(desc, None); @@ -824,21 +833,32 @@ pub fn destroy_wallbash( // destroy swapchain and surface (level ≥ 1) if level >= 1 { - if let Some(sc) = surfchain { + if let Some(sc) = config.surfchain { unsafe { - let swapchain_loader = ash::khr::swapchain::Device::new(&vk_core.instance, &vk_core.device); + let swapchain_loader = + ash::khr::swapchain::Device::new(&vk_core.instance, &vk_core.device); swapchain_loader.destroy_swapchain(sc.swapchain, None); - let surface_loader = ash::khr::surface::Instance::new(&vk_core.entry, &vk_core.instance); + let surface_loader = + ash::khr::surface::Instance::new(&vk_core.entry, &vk_core.instance); surface_loader.destroy_surface(sc.surface, None); } } } - // destroy core Vulkan objects (level ≥ 2) + // destroy wallpaper texture and core (level ≥ 2) if level >= 2 { + if let Some(tex) = config.wallpaper_texture { + unsafe { + vk_core.device.destroy_image(tex.image, None); + vk_core.device.free_memory(tex._memory, None); + } + } unsafe { - vk_core.device.device_wait_idle().expect("device wait failed"); - vk_core.device.destroy_command_pool(vk_core.command_pool, None); + vk_core.device + .device_wait_idle() + .expect("device wait failed"); + vk_core.device + .destroy_command_pool(vk_core.command_pool, None); vk_core.device.destroy_device(None); vk_core.instance.destroy_instance(None); } diff --git a/src/wallbash.rs b/src/wallbash.rs index 3b59be9..d792870 100644 --- a/src/wallbash.rs +++ b/src/wallbash.rs @@ -10,7 +10,7 @@ use crate::{vulkan, wayland, filters}; use std::{ os::unix::net::{UnixListener, UnixStream}, io::{BufRead, BufReader}, - sync::mpsc, + sync::mpsc,time::Instant, }; @@ -53,6 +53,19 @@ fn start_ipc(socket_path: &str) -> Result, Box(label: &str, f: F) -> R +where + F: FnOnce() -> R, +{ + let start = Instant::now(); + let result = f(); + println!("[perf] {}: {:.2?}", label, start.elapsed()); + result +} + + // --------------------------------------------------------------------- / wallpaper fn set_wallpaper( @@ -69,12 +82,17 @@ fn set_wallpaper( ) -> Result<(), Box> { // load the wallpaper - let img = image::open(path)?; - let pixel_format = img.to_rgba8(); - let pixel_bytes = pixel_format.into_raw(); + let (img, pixel_bytes) = timer("load+decode", || { + let img = image::open(path).expect("failed to open image"); + let rgba = img.to_rgba8(); + let bytes = rgba.into_raw(); + (img, bytes) + }); // call the vulkan pipeline - let texture = vk_core.upload_texture(&pixel_bytes, img.width(), img.height(),)?; + let texture = timer("upload", || { + vk_core.upload_texture(&pixel_bytes, img.width(), img.height()) + })?; // drop the old texture resources (if any) if let Some(old_tex) = wallpaper.take() { @@ -86,20 +104,23 @@ fn set_wallpaper( *wallpaper = Some(texture); // create a blurred version for fit/original modes - let background_texture = effect(wallpaper.as_ref().unwrap()); - let background = background_texture.as_ref().map(|b| (b.image, b.width, b.height)); - - // draw the wallpaper - vk_core.draw_wallpaper( - vk_surfchain, - wallpaper.as_ref().unwrap(), - layer_width, - layer_height, - anchor_x, - anchor_y, - background, - mode, - )?; + let background_texture = timer("effect+draw", || { + let bg = effect(wallpaper.as_ref().unwrap()); + let bg_params = bg.as_ref().map(|b| (b.image, b.width, b.height)); + + vk_core.draw_wallpaper( + vk_surfchain, + wallpaper.as_ref().unwrap(), + layer_width, + layer_height, + anchor_x, + anchor_y, + bg_params, + mode, + )?; + + Ok::<_, Box>(bg) + })?; // destroy the temporary blurred texture (if any) if let Some(bg) = background_texture { @@ -118,12 +139,22 @@ fn set_wallpaper( fn set_surfchain( vk_core: &vulkan::VulkanCore, wl_core: &wayland::WaylandCore, - old: Option<&mut vulkan::VulkanSurfchain>, + old: Option, ) -> Result> { // destroy only the swapchain (level 1) – no filter resources if let Some(old_sc) = old { - vulkan::destroy_wallbash(vk_core, Some(old_sc), None, None, None, 1); + vulkan::destroy_wallbash( + vk_core, + vulkan::VulkanCleanup { + surfchain: Some(old_sc), + filter_module: None, + filter_pipeline: None, + filter_desc_layout: None, + wallpaper_texture: None, + }, + 1, + ); } vulkan::vulkan_surfchain( @@ -157,7 +188,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { // init vulkan let vk_core = vulkan::vulkan_core()?; let (blur_module, blur_pipeline, blur_desc_layout) = filters::filter_pipeline(&vk_core.device, "blur")?; - let mut vk_surfchain = set_surfchain(&vk_core, &wl_core, None)?; + let mut vk_surfchain = Some(set_surfchain(&vk_core, &wl_core, None)?); // blank surface until a command arrives let mut wallpaper: Option = None; @@ -171,7 +202,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { // check for commands from the IPC if let Ok(raw) = rx.try_recv() { let raw = raw.trim().to_string(); - println!("[wallbash] received {}", raw); + println!("[wallbash] received '{:?}'", raw); if raw == "stop" { println!("[wallbash] stopping daemon."); @@ -207,7 +238,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { match set_wallpaper( &resolved, &vk_core, - &vk_surfchain, + vk_surfchain.as_ref().unwrap(), wl_core.state.layer_width, wl_core.state.layer_height, &mut wallpaper, @@ -221,19 +252,44 @@ pub fn run(socket_path: &str) -> Result<(), Box> { println!("[wallbash] swapchain out of date, recreating..."); // destroy old swapchain and surface - vk_surfchain = match set_surfchain(&vk_core, &wl_core, Some(&mut vk_surfchain)) { - Ok(sc) => sc, + match vulkan::vulkan_surfchain( + &vk_core.entry, + &vk_core.instance, + vk_core.physical_device, + vk_core.graphics_family_index, + &vk_core.device, + &wl_core.display, + &wl_core.surface, + wl_core.state.layer_width, + wl_core.state.layer_height, + ) { + Ok(new_sc) => { + // Destroy the old swapchain (now that we have a working new one) + let old = vk_surfchain.take().unwrap(); + vulkan::destroy_wallbash( + &vk_core, + vulkan::VulkanCleanup { + surfchain: Some(old), + filter_module: None, + filter_pipeline: None, + filter_desc_layout: None, + wallpaper_texture: None, + }, + 1, + ); + vk_surfchain = Some(new_sc); + } Err(e2) => { eprintln!("[wallbash] failed to recreate swapchain {}", e2); continue; } - }; + } // retry setting the wallpaper once if let Err(e3) = set_wallpaper( &resolved, &vk_core, - &vk_surfchain, + vk_surfchain.as_ref().unwrap(), wl_core.state.layer_width, wl_core.state.layer_height, &mut wallpaper, @@ -251,23 +307,19 @@ pub fn run(socket_path: &str) -> Result<(), Box> { eprintln!("[wallbash] unknown {}", raw); } } - std::thread::sleep(std::time::Duration::from_millis(25)); + std::thread::sleep(std::time::Duration::from_millis(16)); } // clean shutdowon - unsafe { vk_core.device.device_wait_idle()?; } - if let Some(tex) = wallpaper.take() { - unsafe { - vk_core.device.destroy_image(tex.image, None); - vk_core.device.free_memory(tex._memory, None); - } - } vulkan::destroy_wallbash( &vk_core, - Some(&mut vk_surfchain), - Some(blur_module), - Some(blur_pipeline), - Some(blur_desc_layout), + vulkan::VulkanCleanup { + surfchain: vk_surfchain.take(), + filter_module: Some(blur_module), + filter_pipeline: Some(blur_pipeline), + filter_desc_layout: Some(blur_desc_layout), + wallpaper_texture: wallpaper.take(), + }, 2, );