use std::env;
use std::path::{Path, PathBuf};
fn main() {
println!("cargo:rustc-check-cfg=cfg(rav2d_neon_mc)");
println!("cargo:rustc-check-cfg=cfg(rav2d_neon_ipred)");
if env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("aarch64") {
build_neon_mc();
build_neon_ipred();
}
}
fn dav2d_root() -> PathBuf {
let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let submodule = manifest.join("../../dav2d");
if submodule.join("src/arm/64/mc.S").exists() && submodule.join("build/config.h").exists() {
return submodule.canonicalize().unwrap_or(submodule);
}
let vendored = manifest.join("vendor/dav2d-asm");
vendored.canonicalize().unwrap_or(vendored)
}
fn build_neon_mc() {
let dav2d = dav2d_root();
let src = dav2d.join("src");
let include = dav2d.join("include");
let build = dav2d.join("build");
let build_src = build.join("src");
let mc_s = src.join("arm/64/mc.S");
let mc_dotprod_s = src.join("arm/64/mc_dotprod.S");
let config_h = build.join("config.h");
if !mc_s.exists() || !config_h.exists() {
println!(
"cargo:warning=dav2d asm sources not found ({}); rav2d MC will use scalar Rust",
mc_s.display()
);
return;
}
println!("cargo:rerun-if-changed={}", mc_s.display());
println!("cargo:rerun-if-changed={}", mc_dotprod_s.display());
println!("cargo:rerun-if-changed={}", src.join("arm/asm.S").display());
println!(
"cargo:rerun-if-changed={}",
src.join("arm/64/util.S").display()
);
println!("cargo:rerun-if-changed={}", config_h.display());
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let tables_c = out_dir.join("mc_tables.c");
generate_mc_tables(&src.join("tables.c"), &tables_c);
println!("cargo:rerun-if-changed={}", src.join("tables.c").display());
let includes: [&Path; 6] = [
&src.join("arm/64"), &src, &dav2d, &include, &build, &build_src, ];
let mut cc = cc::Build::new();
cc.file(&mc_s).file(&mc_dotprod_s).file(&tables_c);
for inc in includes {
cc.include(inc);
}
cc.define("NDEBUG", None);
cc.flag_if_supported("-std=c99");
cc.warnings(false);
cc.compile("rav2d_mc_neon");
println!("cargo:rustc-cfg=rav2d_neon_mc");
}
fn dav2d_includes(
src: &Path,
dav2d: &Path,
include: &Path,
build: &Path,
build_src: &Path,
) -> Vec<PathBuf> {
vec![
src.join("arm/64"), src.to_path_buf(), dav2d.to_path_buf(),
include.to_path_buf(),
build.to_path_buf(),
build_src.to_path_buf(),
]
}
fn build_neon_ipred() {
let dav2d = dav2d_root();
let src = dav2d.join("src");
let include = dav2d.join("include");
let build = dav2d.join("build");
let build_src = build.join("src");
let ipred_s = src.join("arm/64/ipred.S");
let config_h = build.join("config.h");
if !ipred_s.exists() || !config_h.exists() {
println!(
"cargo:warning=dav2d ipred asm not found ({}); rav2d ipred will use scalar Rust",
ipred_s.display()
);
return;
}
println!("cargo:rerun-if-changed={}", ipred_s.display());
println!("cargo:rerun-if-changed={}", src.join("tables.c").display());
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let tables_c = out_dir.join("ipred_tables.c");
generate_ipred_tables(&src.join("tables.c"), &tables_c);
let mut cc = cc::Build::new();
cc.file(&ipred_s).file(&tables_c);
for inc in dav2d_includes(&src, &dav2d, &include, &build, &build_src) {
cc.include(inc);
}
cc.define("NDEBUG", None);
cc.flag_if_supported("-std=c99");
cc.warnings(false);
cc.compile("rav2d_ipred_neon");
println!("cargo:rustc-cfg=rav2d_neon_ipred");
}
fn generate_ipred_tables(tables_c: &Path, out: &Path) {
let src = std::fs::read_to_string(tables_c).expect("read dav2d tables.c");
let lines: Vec<&str> = src.lines().collect();
let grab = |marker: &str| -> String {
let start = lines
.iter()
.position(|l| l.contains(marker))
.unwrap_or_else(|| panic!("table {marker} not found in tables.c"));
let mut out = Vec::new();
for l in &lines[start..] {
out.push(*l);
if l.trim() == "};" {
break;
}
}
out.join("\n")
};
let sm_weights = grab("dav2d_sm_weights[3 /* scale */][64]");
let filter_taps = grab("#if ARCH_X86");
let content = format!(
"/* Generated by rav2d build.rs from dav2d/src/tables.c (verbatim). Provides\n\
* the read-only data tables dav2d's NEON ipred kernels reference. Do not edit. */\n\
#include \"config.h\"\n\
#include <stdint.h>\n\
#include \"common/attributes.h\"\n\
#include \"dav2d/headers.h\"\n\n\
{sm_weights}\n\n\
{filter_taps}\n"
);
std::fs::write(out, content).expect("write generated ipred_tables.c");
}
fn generate_mc_tables(tables_c: &Path, out: &Path) {
let src = std::fs::read_to_string(tables_c).expect("read dav2d tables.c");
let lines: Vec<&str> = src.lines().collect();
let grab = |marker: &str| -> String {
let start = lines
.iter()
.position(|l| l.contains(marker))
.unwrap_or_else(|| panic!("table {marker} not found in tables.c"));
let mut out = Vec::new();
for l in &lines[start..] {
out.push(*l);
if l.trim() == "};" {
break;
}
}
out.join("\n")
};
let subpel = grab("dav2d_mc_subpel_filters[6][15][8]");
let warp = grab("dav2d_mc_warp_filter[7*64+1][8]");
let content = format!(
"/* Generated by rav2d build.rs from dav2d/src/tables.c (verbatim). Provides\n\
* the read-only data tables dav2d's NEON MC kernels reference. Do not edit. */\n\
#include \"config.h\"\n\
#include <stdint.h>\n\
#include \"common/attributes.h\"\n\
#include \"dav2d/headers.h\"\n\n\
{subpel}\n\n\
{warp}\n"
);
std::fs::write(out, content).expect("write generated mc_tables.c");
}