mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-25 06:24:56 +00:00
#2874 on steroids Before you mark this PR as ready for review, make sure that you have: - [ ] Added a solid test coverage and/or screenshots from doing manual testing - [ ] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A --------- Co-authored-by: Eric Holk <eric@zed.dev>
242 lines
7.5 KiB
Rust
242 lines
7.5 KiB
Rust
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
|
|
|
|
fn main() {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
// Compile HLSL shaders
|
|
#[cfg(not(debug_assertions))]
|
|
compile_shaders();
|
|
}
|
|
}
|
|
|
|
#[cfg(all(target_os = "windows", not(debug_assertions)))]
|
|
mod shader_compilation {
|
|
use std::{
|
|
fs,
|
|
io::Write,
|
|
path::{Path, PathBuf},
|
|
process::{self, Command},
|
|
};
|
|
|
|
pub fn compile_shaders() {
|
|
let shader_path =
|
|
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/shaders.hlsl");
|
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
|
|
|
println!("cargo:rerun-if-changed={}", shader_path.display());
|
|
|
|
// Check if fxc.exe is available
|
|
let fxc_path = find_fxc_compiler();
|
|
|
|
// Define all modules
|
|
let modules = [
|
|
"quad",
|
|
"shadow",
|
|
"path_rasterization",
|
|
"path_sprite",
|
|
"underline",
|
|
"monochrome_sprite",
|
|
"subpixel_sprite",
|
|
"polychrome_sprite",
|
|
];
|
|
|
|
let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir);
|
|
if Path::new(&rust_binding_path).exists() {
|
|
fs::remove_file(&rust_binding_path)
|
|
.expect("Failed to remove existing Rust binding file");
|
|
}
|
|
for module in modules {
|
|
compile_shader_for_module(
|
|
module,
|
|
&out_dir,
|
|
&fxc_path,
|
|
shader_path.to_str().unwrap(),
|
|
&rust_binding_path,
|
|
);
|
|
}
|
|
|
|
{
|
|
let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
|
.join("src/color_text_raster.hlsl");
|
|
compile_shader_for_module(
|
|
"emoji_rasterization",
|
|
&out_dir,
|
|
&fxc_path,
|
|
shader_path.to_str().unwrap(),
|
|
&rust_binding_path,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Locate `binary` in the newest installed Windows SDK.
|
|
pub fn find_latest_windows_sdk_binary(
|
|
binary: &str,
|
|
) -> Result<Option<PathBuf>, Box<dyn std::error::Error>> {
|
|
let key = windows_registry::LOCAL_MACHINE
|
|
.open("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0")?;
|
|
|
|
let install_folder: String = key.get_string("InstallationFolder")?; // "C:\Program Files (x86)\Windows Kits\10\"
|
|
let install_folder_bin = Path::new(&install_folder).join("bin");
|
|
|
|
let mut versions: Vec<_> = std::fs::read_dir(&install_folder_bin)?
|
|
.flatten()
|
|
.filter(|entry| entry.path().is_dir())
|
|
.filter_map(|entry| entry.file_name().into_string().ok())
|
|
.collect();
|
|
|
|
versions.sort_by_key(|s| {
|
|
s.split('.')
|
|
.filter_map(|p| p.parse().ok())
|
|
.collect::<Vec<u32>>()
|
|
});
|
|
|
|
let arch = match std::env::consts::ARCH {
|
|
"x86_64" => "x64",
|
|
"aarch64" => "arm64",
|
|
_ => Err(format!(
|
|
"Unsupported architecture: {}",
|
|
std::env::consts::ARCH
|
|
))?,
|
|
};
|
|
|
|
if let Some(highest_version) = versions.last() {
|
|
return Ok(Some(
|
|
install_folder_bin
|
|
.join(highest_version)
|
|
.join(arch)
|
|
.join(binary),
|
|
));
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
/// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
|
|
fn find_fxc_compiler() -> String {
|
|
// Check environment variable
|
|
if let Ok(path) = std::env::var("GPUI_FXC_PATH")
|
|
&& Path::new(&path).exists()
|
|
{
|
|
return path;
|
|
}
|
|
|
|
// Try to find in PATH
|
|
// NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe`
|
|
if let Ok(output) = std::process::Command::new("where.exe")
|
|
.arg("fxc.exe")
|
|
.output()
|
|
&& output.status.success()
|
|
{
|
|
let path = String::from_utf8_lossy(&output.stdout);
|
|
return path.trim().to_string();
|
|
}
|
|
|
|
if let Ok(Some(path)) = find_latest_windows_sdk_binary("fxc.exe") {
|
|
return path.to_string_lossy().into_owned();
|
|
}
|
|
|
|
panic!("Failed to find fxc.exe");
|
|
}
|
|
|
|
fn compile_shader_for_module(
|
|
module: &str,
|
|
out_dir: &str,
|
|
fxc_path: &str,
|
|
shader_path: &str,
|
|
rust_binding_path: &str,
|
|
) {
|
|
// Compile vertex shader
|
|
let output_file = format!("{}/{}_vs.h", out_dir, module);
|
|
let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase());
|
|
compile_shader_impl(
|
|
fxc_path,
|
|
&format!("{module}_vertex"),
|
|
&output_file,
|
|
&const_name,
|
|
shader_path,
|
|
"vs_4_1",
|
|
);
|
|
generate_rust_binding(&const_name, &output_file, rust_binding_path);
|
|
|
|
// Compile fragment shader
|
|
let output_file = format!("{}/{}_ps.h", out_dir, module);
|
|
let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase());
|
|
compile_shader_impl(
|
|
fxc_path,
|
|
&format!("{module}_fragment"),
|
|
&output_file,
|
|
&const_name,
|
|
shader_path,
|
|
"ps_4_1",
|
|
);
|
|
generate_rust_binding(&const_name, &output_file, rust_binding_path);
|
|
}
|
|
|
|
fn compile_shader_impl(
|
|
fxc_path: &str,
|
|
entry_point: &str,
|
|
output_path: &str,
|
|
var_name: &str,
|
|
shader_path: &str,
|
|
target: &str,
|
|
) {
|
|
let output = Command::new(fxc_path)
|
|
.args([
|
|
"/T",
|
|
target,
|
|
"/E",
|
|
entry_point,
|
|
"/Fh",
|
|
output_path,
|
|
"/Vn",
|
|
var_name,
|
|
"/O3",
|
|
shader_path,
|
|
])
|
|
.output();
|
|
|
|
match output {
|
|
Ok(result) => {
|
|
if result.status.success() {
|
|
return;
|
|
}
|
|
println!(
|
|
"cargo::error=Shader compilation failed for {}:\n{}",
|
|
entry_point,
|
|
String::from_utf8_lossy(&result.stderr)
|
|
);
|
|
process::exit(1);
|
|
}
|
|
Err(e) => {
|
|
println!("cargo::error=Failed to run fxc for {}: {}", entry_point, e);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) {
|
|
let header_content = fs::read_to_string(head_file).expect("Failed to read header file");
|
|
let const_definition = {
|
|
let global_var_start = header_content.find("const BYTE").unwrap();
|
|
let global_var = &header_content[global_var_start..];
|
|
let equal = global_var.find('=').unwrap();
|
|
global_var[equal + 1..].trim()
|
|
};
|
|
let rust_binding = format!(
|
|
"const {}: &[u8] = &{}\n",
|
|
const_name,
|
|
const_definition.replace('{', "[").replace('}', "]")
|
|
);
|
|
let mut options = fs::OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open(output_path)
|
|
.expect("Failed to open Rust binding file");
|
|
options
|
|
.write_all(rust_binding.as_bytes())
|
|
.expect("Failed to write Rust binding file");
|
|
}
|
|
}
|
|
|
|
#[cfg(all(target_os = "windows", not(debug_assertions)))]
|
|
use shader_compilation::compile_shaders;
|