diff --git a/Cargo.lock b/Cargo.lock index 6d3b85f8e0..7a15df740c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4606,6 +4606,7 @@ version = "0.6.0" dependencies = [ "lazy_static", "libloading 0.6.2", + "log", "notify", "vek", "veloren-common", diff --git a/voxygen/src/anim/Cargo.toml b/voxygen/src/anim/Cargo.toml index b515860a7c..c2b2b72633 100644 --- a/voxygen/src/anim/Cargo.toml +++ b/voxygen/src/anim/Cargo.toml @@ -9,7 +9,7 @@ name = "voxygen_anim" crate-type = ["lib", "cdylib"] [features] -use-dyn-lib = ["libloading", "notify", "lazy_static"] +use-dyn-lib = ["libloading", "notify", "lazy_static", "log"] be-dyn-lib = [] default = ["be-dyn-lib"] @@ -20,4 +20,4 @@ common = { package = "veloren-common", path = "../../../common" } libloading = { version = "0.6.2", optional = true } notify = { version = "5.0.0-pre.2", optional = true } lazy_static = { version = "1.4.0", optional = true } - +log = { version = "0.4.8", optional = true } diff --git a/voxygen/src/anim/src/dyn_lib.rs b/voxygen/src/anim/src/dyn_lib.rs index 790899273d..6474819212 100644 --- a/voxygen/src/anim/src/dyn_lib.rs +++ b/voxygen/src/anim/src/dyn_lib.rs @@ -3,7 +3,9 @@ use libloading::Library; use notify::{immediate_watcher, EventKind, RecursiveMode, Watcher}; use std::{ process::{Command, Stdio}, - sync::Mutex, + sync::{mpsc, Mutex}, + thread, + time::Duration, }; lazy_static! { @@ -17,21 +19,17 @@ pub struct LoadedLib { impl LoadedLib { fn compile_load() -> Self { // Compile - let _output = Command::new("cargo") - .stderr(Stdio::inherit()) - .stdout(Stdio::inherit()) - .arg("build") - .arg("--package") - .arg("veloren-voxygen-anim") - .output() - .unwrap(); + compile(); + + #[cfg(target_os = "windows")] + copy(); Self::load() } fn load() -> Self { #[cfg(target_os = "windows")] - let lib = Library::new("../target/debug/voxygen_anim.dll").unwrap(); + let lib = Library::new("../target/debug/voxygen_anim_active.dll").unwrap(); #[cfg(not(target_os = "windows"))] let lib = Library::new("../target/debug/libvoxygen_anim.so").unwrap(); @@ -41,8 +39,11 @@ impl LoadedLib { // Starts up watcher pub fn init() { + // Make sure first compile is done + drop(LIB.lock()); + // TODO: use crossbeam - let (reload_send, reload_recv) = std::sync::mpsc::channel(); + let (reload_send, reload_recv) = mpsc::channel(); // Start watcher let mut watcher = immediate_watcher(move |res| event_fn(res, &reload_send)).unwrap(); @@ -51,10 +52,24 @@ pub fn init() { // Start reloader that watcher signals // "Debounces" events since I can't find the option to do this in the latest // `notify` - std::thread::spawn(move || { - while let Ok(()) = reload_recv.recv() { - // Wait for another modify event before reloading - while let Ok(()) = reload_recv.recv_timeout(std::time::Duration::from_millis(300)) {} + thread::spawn(move || { + let mut modified_paths = std::collections::HashSet::new(); + + while let Ok(path) = reload_recv.recv() { + modified_paths.insert(path); + // Wait for to see if there are more modify events before reloading + while let Ok(path) = reload_recv.recv_timeout(Duration::from_millis(300)) { + modified_paths.insert(path); + } + + let mut info = "Hot reloading animations because these files were modified:".to_owned(); + for path in std::mem::take(&mut modified_paths) { + info.push('\n'); + info.push('\"'); + info.push_str(&path); + info.push('\"'); + } + log::warn!("{}", info); // Reload reload(); @@ -67,32 +82,48 @@ pub fn init() { // Recompiles and hotreloads the lib if the source is changed // Note: designed with voxygen dir as working dir, could be made more flexible -fn event_fn(res: notify::Result, sender: &std::sync::mpsc::Sender<()>) { +fn event_fn(res: notify::Result, sender: &mpsc::Sender) { match res { Ok(event) => match event.kind { EventKind::Modify(_) => { - if event + event .paths .iter() - .any(|p| p.extension().map(|e| e == "rs").unwrap_or(false)) - { - println!( - "Hot reloading animations because these files were modified:\n{:?}", - event.paths - ); - + .filter(|p| p.extension().map(|e| e == "rs").unwrap_or(false)) + .map(|p| p.to_string_lossy().into_owned()) // Signal reloader - let _ = sender.send(()); - } + .for_each(|p| { let _ = sender.send(p); }); }, _ => {}, }, - Err(e) => println!("watch error: {:?}", e), + Err(e) => log::error!("Animation hotreload watch error: {:?}", e), } } fn reload() { - // Compile + // Stop if recompile failed + if !compile() { + return; + } + + let mut lock = LIB.lock().unwrap(); + + // Close lib + lock.take().unwrap().lib.close().unwrap(); + + // Rename lib file on windows + // Called after closing lib so file will be unlocked + #[cfg(target_os = "windows")] + copy(); + + // Open new lib + *lock = Some(LoadedLib::load()); + + log::warn!("Updated animations"); +} + +// Returns false if compile failed +fn compile() -> bool { let output = Command::new("cargo") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) @@ -102,21 +133,23 @@ fn reload() { .output() .unwrap(); - // Stop if recompile failed + // If compile failed if !output.status.success() { - println!("Failed to compile anim crate"); - return; + log::error!("Failed to compile anim crate"); + false + } else { + log::warn!("Animation recompile success!!"); + true } - - println!("Compile Success!!"); - - let mut lock = LIB.lock().unwrap(); - - // Close lib - lock.take().unwrap().lib.close().unwrap(); - - // Open new lib - *lock = Some(LoadedLib::load()); - - println!("Updated"); +} + +// Copy lib file if on windows since loading the lib locks the file blocking +// future compilation +#[cfg(target_os = "windows")] +fn copy() { + std::fs::copy( + "../target/debug/voxygen_anim.dll", + "../target/debug/voxygen_anim_active.dll", + ) + .expect("Failed to rename animations dll"); } diff --git a/voxygen/src/anim/src/lib.rs b/voxygen/src/anim/src/lib.rs index 17bec61d54..9b8a833ac2 100644 --- a/voxygen/src/anim/src/lib.rs +++ b/voxygen/src/anim/src/lib.rs @@ -20,6 +20,8 @@ pub mod quadruped_small; #[cfg(feature = "use-dyn-lib")] pub use dyn_lib::init; +#[cfg(feature = "use-dyn-lib")] +use std::ffi::CStr; use vek::*; // TODO: replace with inner type everywhere @@ -83,7 +85,16 @@ pub trait Skeleton: Send + Sync + 'static { let lib = &lock.as_ref().unwrap().lib; let compute_fn: libloading::Symbol ([FigureBoneData; 16], Vec3)> = - unsafe { lib.get(Self::COMPUTE_FN).unwrap() }; + unsafe { lib.get(Self::COMPUTE_FN) }.unwrap_or_else(|err| { + panic!( + "Trying to use: {} but had error: {:?}", + CStr::from_bytes_with_nul(Self::COMPUTE_FN) + .map(CStr::to_str) + .unwrap() + .unwrap(), + err + ) + }); compute_fn(self) } @@ -135,10 +146,20 @@ pub trait Animation { > = unsafe { //let start = std::time::Instant::now(); // Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue) - let f = lib.get(Self::UPDATE_FN).unwrap(); + let f = lib.get(Self::UPDATE_FN); //println!("{}", start.elapsed().as_nanos()); f - }; + } + .unwrap_or_else(|err| { + panic!( + "Trying to use: {} but had error: {:?}", + CStr::from_bytes_with_nul(Self::UPDATE_FN) + .map(CStr::to_str) + .unwrap() + .unwrap(), + err + ) + }); update_fn(skeleton, dependency, anim_time, rate, skeleton_attr) } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 25fbeea417..0af88d92d5 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -4,7 +4,7 @@ use crate::{ render::{FigurePipeline, Mesh, Model, Renderer}, scene::camera::CameraMode, }; -use anim::{Skeleton}; +use anim::Skeleton; use common::{ assets::watch::ReloadIndicator, comp::{ diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 50c1f1f630..0408cbc275 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -2103,21 +2103,21 @@ impl FigureState { fn figure_bone_data_from_anim(mats: [anim::FigureBoneData; 16]) -> [FigureBoneData; 16] { [ - FigureBoneData::new(mats[0].0), - FigureBoneData::new(mats[1].0), - FigureBoneData::new(mats[2].0), - FigureBoneData::new(mats[3].0), - FigureBoneData::new(mats[4].0), - FigureBoneData::new(mats[5].0), - FigureBoneData::new(mats[6].0), - FigureBoneData::new(mats[7].0), - FigureBoneData::new(mats[8].0), - FigureBoneData::new(mats[9].0), - FigureBoneData::new(mats[10].0), - FigureBoneData::new(mats[11].0), - FigureBoneData::new(mats[12].0), - FigureBoneData::new(mats[13].0), - FigureBoneData::new(mats[14].0), - FigureBoneData::new(mats[15].0), + FigureBoneData::new(mats[0].0), + FigureBoneData::new(mats[1].0), + FigureBoneData::new(mats[2].0), + FigureBoneData::new(mats[3].0), + FigureBoneData::new(mats[4].0), + FigureBoneData::new(mats[5].0), + FigureBoneData::new(mats[6].0), + FigureBoneData::new(mats[7].0), + FigureBoneData::new(mats[8].0), + FigureBoneData::new(mats[9].0), + FigureBoneData::new(mats[10].0), + FigureBoneData::new(mats[11].0), + FigureBoneData::new(mats[12].0), + FigureBoneData::new(mats[13].0), + FigureBoneData::new(mats[14].0), + FigureBoneData::new(mats[15].0), ] }