minemod/build.rs
2025-01-20 06:22:36 -04:00

139 lines
5.1 KiB
Rust

// Requires crates: shaderc, num_cpus, jobserver
// This file is licensed to anyone who obtains a copy by lever1209 under the CC BY 4.0 license available here: https://creativecommons.org/licenses/by/4.0/
const SHADER_SRC_ROOT:&'static str = "shaders/";
const SUPPORTED_SHADER_FORMAT_EXTENSIONS_GLSL:[&'static str; 1] = ["glsl"];
const SUPPORTED_SHADER_FORMAT_EXTENSIONS_HLSL:[&'static str; 2] = ["cg", "hlsl"];
const COMPILED_SHADER_EXTENSION:&'static str = ".spv";
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs");
compile_shaders()?;
Ok(())
}
fn compile_shaders() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed={SHADER_SRC_ROOT}");
let client = unsafe { jobserver::Client::from_env() }.unwrap_or({
println!("cargo:warning=Could not connect to the gnu jobserver. Assuming max supported threads.");
jobserver::Client::new(num_cpus::get())?
});
let out_dir = std::env::var("OUT_DIR").map_err(|err| format!("Could not read environment variable OUT_DIR: `{err}`"))?;
let shader_out_dir = std::path::Path::new(&out_dir).join(SHADER_SRC_ROOT);
std::fs::create_dir_all(&shader_out_dir)?;
let mut running_tasks = vec![];
let mut shader_source_files = vec![];
if let Err(err) = walk_dir(SHADER_SRC_ROOT.into(), &mut shader_source_files) {
println!("cargo:warning=There was an error walking the shader source directory: `{err}`");
};
for input_path in shader_source_files {
let aquired = client
.acquire()
.map_err(|err| format!("There was an error while aquiring a jobserver token: `{err}`"))?;
let shader_out_dir = shader_out_dir.clone();
running_tasks.push((input_path.clone(), std::thread::spawn(move || -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
let input_file_stem = match input_path.file_stem().and_then(|f| <&str>::try_from(f).ok()) {
Some(stem) => stem.to_owned(),
None => return Err(format!("Could not read file stem of file: {input_path:?}").into()),
};
let shader_kind = match &input_file_stem[0..=1] {
"f_" => shaderc::ShaderKind::Fragment,
"v_" => shaderc::ShaderKind::Vertex,
_ => return Err(format!("Shader folder contains a file that is neither a vertex or fragment shader: {input_path:?}").into()),
};
let new_file_name = input_file_stem + COMPILED_SHADER_EXTENSION;
let output_path = shader_out_dir.join(&new_file_name);
if let Err(error) = compile_shader(&input_path, &output_path, shader_kind) {
println!("cargo:warning=Compilation process for input file: {input_path:?} was not successful: `{error}`");
println!("cargo:warning=I cant be assed to fix this myself but cargo cuts off newlines in warning messages, so if the above error gets cut off youre going to need to find the text file or re run cargo with the -vv flag");
}
drop(aquired);
Ok(())
})));
}
for (input_file, task) in running_tasks {
match task.join() {
Ok(Ok(())) => (),
Ok(Err(err)) => {
println!("cargo:warning=There was an error running a task: `{err}`");
},
Err(_) => {
println!("cargo:warning=A thread panicked while processing this file!: {input_file:?}",);
},
}
}
Ok(())
}
fn walk_dir(path:std::path::PathBuf, file_paths_buf:&mut Vec<std::path::PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
if std::path::PathBuf::from(&path).is_file() {
file_paths_buf.push(path);
return Ok(());
} else {
let dir = std::fs::read_dir(path)?;
for f in dir {
walk_dir(f?.path(), file_paths_buf)?;
}
return Ok(());
}
}
fn compile_shader(input_path:&std::path::PathBuf, output_path:&std::path::PathBuf, shader_kind:shaderc::ShaderKind) -> Result<(), Box<dyn std::error::Error>> {
let compiler = match shaderc::Compiler::new() {
Some(compiler) => compiler,
None => return Err(format!("Could not initialize shaderc compiler.").into()),
};
let target = if let Some(extension) = input_path.extension().and_then(|s| <&str>::try_from(s).ok()) {
if SUPPORTED_SHADER_FORMAT_EXTENSIONS_GLSL.contains(&extension) {
shaderc::SourceLanguage::GLSL
} else if SUPPORTED_SHADER_FORMAT_EXTENSIONS_HLSL.contains(&extension) {
shaderc::SourceLanguage::HLSL
} else {
return Err(format!("Could not determine compile target! This file has an invalid extension.").into());
}
} else {
return Err(format!("Could not determine compile target! This file is missing an extension.").into());
};
let options = match shaderc::CompileOptions::new() {
Some(mut options) => {
options.set_optimization_level(shaderc::OptimizationLevel::Performance);
options.set_source_language(target);
options
},
None => return Err(format!("Could not initialize shaderc compiler options.").into()),
};
let source = std::fs::read_to_string(input_path)?;
let file_name = match input_path.file_name() {
Some(file_name) => file_name,
None => return Err("This is a directory!".into()),
};
let file_name = match file_name.to_str() {
Some(file_name) => file_name,
None => return Err("Could not load this file name.".into()),
};
let binary_result = compiler.compile_into_spirv(&source, shader_kind, file_name, "main", Some(&options))?;
std::fs::write(output_path, binary_result.as_binary_u8())?;
Ok(())
}