raindrop/crates/renderer/build.rs
2025-04-01 21:41:24 -04:00

155 lines
5.7 KiB
Rust

use anyhow::{Context, Result};
use shaderc::{CompileOptions, Compiler, ShaderKind};
use std::{
env,
fs::{self, File},
io::Write,
path::PathBuf,
};
use walkdir::WalkDir;
// Configuration
const SHADER_SOURCE_DIR: &str = "../../shaders"; // Directory containing GLSL shaders
// Output directory will be determined by Cargo (OUT_DIR)
fn main() -> Result<()> {
let out_dir = PathBuf::from(env::var("OUT_DIR")?).join("shaders"); // Put shaders in a subdirectory for clarity
fs::create_dir_all(&out_dir).context("Failed to create shader output directory")?;
println!("cargo:rerun-if-changed=build.rs");
let compiler = Compiler::new().context("Failed to create shader compiler")?;
let mut options = CompileOptions::new().context("Failed to create compile options")?;
// --- Optional: Add compile options ---
// Example: Optimize for performance in release builds
if env::var("PROFILE")? == "release" {
options.set_optimization_level(shaderc::OptimizationLevel::Performance);
eprintln!("Build.rs: Compiling shaders with Performance optimization.");
} else {
options.set_optimization_level(shaderc::OptimizationLevel::Zero); // Faster compile for debug
options.set_generate_debug_info(); // Include debug info for debug builds
eprintln!("Build.rs: Compiling shaders with Zero optimization and Debug info.");
}
// Add other options like defines if needed:
// options.add_macro_definition("MY_DEFINE", Some("1"));
options.set_target_env(
shaderc::TargetEnv::Vulkan,
shaderc::EnvVersion::Vulkan1_3 as u32,
); // Specify Vulkan version if needed
eprintln!(
"Build.rs: Compiling shaders from '{}' to '{}'",
SHADER_SOURCE_DIR,
out_dir.display()
);
// --- Find and Compile Shaders ---
for entry in WalkDir::new(SHADER_SOURCE_DIR)
.into_iter()
.filter_map(|e| e.ok()) // Ignore directory reading errors
.filter(|e| e.file_type().is_file())
// Only process files
{
println!("cargo:rerun-if-changed={:?}", entry.path());
let in_path = entry.path();
// Determine shader kind from extension
let extension = match in_path.extension().and_then(|s| s.to_str()) {
Some(ext) => ext,
None => {
eprintln!(
"cargo:warning=Skipping file with no extension: {}",
in_path.display()
);
continue; // Skip files without extensions
}
};
let shader_kind = match extension {
"vert" => ShaderKind::Vertex,
"frag" => ShaderKind::Fragment,
"comp" => ShaderKind::Compute,
"geom" => ShaderKind::Geometry,
"tesc" => ShaderKind::TessControl,
"tese" => ShaderKind::TessEvaluation,
// Add other shader kinds if needed (ray tracing, mesh, etc.)
_ => {
eprintln!(
"cargo:warning=Skipping file with unknown shader extension ({}): {}",
extension,
in_path.display()
);
continue; // Skip unknown shader types
}
};
let source_text = fs::read_to_string(in_path)
.with_context(|| format!("Failed to read shader source: {}", in_path.display()))?;
let input_file_name = in_path.to_string_lossy(); // For error messages
// Compile the shader
let compiled_spirv = compiler
.compile_into_spirv(
&source_text,
shader_kind,
&input_file_name, // Source file name for errors
"main", // Entry point function name
Some(&options), // Pass compile options
)
.with_context(|| format!("Failed to compile shader: {}", input_file_name))?;
let spirv_bytes = compiled_spirv.as_binary_u8();
let byte_count = spirv_bytes.len();
eprintln!(
"Build.rs: SPIR-V for {} has {} bytes.",
input_file_name, byte_count
);
// Check if it's a multiple of 4 right here
if byte_count % 4 != 0 {
eprintln!(
"cargo:warning=Byte count for {} ({}) is NOT a multiple of 4!",
input_file_name, byte_count
);
// Optionally bail out here:
// bail!("Generated SPIR-V for {} has invalid byte count {}", input_file_name, byte_count);
}
// Check for warnings
if compiled_spirv.get_num_warnings() > 0 {
eprintln!(
"cargo:warning=Shader compilation warnings for {}:\n{}",
input_file_name,
compiled_spirv.get_warning_messages()
);
}
// Determine output path
let out_filename = format!(
"{}.spv",
in_path
.file_stem() // Get filename without extension
.unwrap_or_default() // Handle potential weird filenames
.to_string_lossy()
);
let out_path = out_dir.join(out_filename);
// Determine output path...
// ...
// Write the compiled SPIR-V binary
let mut outfile = File::create(&out_path)
.with_context(|| format!("Failed to create output file: {}", out_path.display()))?;
outfile
.write_all(spirv_bytes) // Use the stored bytes
.with_context(|| format!("Failed to write SPIR-V to: {}", out_path.display()))?;
eprintln!(
"Build.rs: Compiled {} -> {}",
in_path.display(),
out_path.display()
);
}
eprintln!("Build.rs: Shader compilation finished.");
Ok(())
}