nvidia-streamline 0.1.0

Vulkan bindings for NVIDIA DLSS Ray Reconstruction through Streamline
Documentation
use std::{
    env,
    error::Error,
    fs::{self, File},
    io::{self, Read as _},
    path::{Path, PathBuf},
};

const HEADERS: [&str; 12] = [
    "sl.h",
    "sl_appidentity.h",
    "sl_consts.h",
    "sl_core_api.h",
    "sl_core_types.h",
    "sl_device_wrappers.h",
    "sl_dlss.h",
    "sl_dlss_d.h",
    "sl_result.h",
    "sl_security.h",
    "sl_struct.h",
    "sl_version.h",
];
const RUNTIME_FILES: [&str; 6] = [
    "sl.interposer.dll",
    "sl.common.dll",
    "sl.pcl.dll",
    "sl.dlss_d.dll",
    "NvLowLatencyVk.dll",
    "nvngx_dlssd.dll",
];
const TARGET: &str = "x86_64-pc-windows-msvc";

fn main() -> Result<(), Box<dyn Error>> {
    println!("cargo:rustc-check-cfg=cfg(nvidia_streamline_native)");
    println!("cargo:rerun-if-env-changed=DOCS_RS");

    if env::var_os("DOCS_RS").is_some() {
        return Ok(());
    }

    println!("cargo:rerun-if-env-changed=STREAMLINE_SDK");
    println!("cargo:rerun-if-changed=native/streamline.cpp");

    if !is_native_target() {
        return Ok(());
    }

    build_native()
}

fn is_native_target() -> bool {
    env::var("TARGET").as_deref() == Ok(TARGET)
        && env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows")
        && env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("x86_64")
        && env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc")
}

fn build_native() -> Result<(), Box<dyn Error>> {
    for variable in [
        "NVIDIA_SDK_ROOT",
        "NVIDIA_SDK_CACHE",
        "NVIDIA_SDK_OFFLINE",
        "CARGO_NET_OFFLINE",
        "XDG_CACHE_HOME",
        "HOME",
        "LOCALAPPDATA",
    ] {
        println!("cargo:rerun-if-env-changed={variable}");
    }

    let manifest_dir = PathBuf::from(
        env::var_os("CARGO_MANIFEST_DIR")
            .ok_or_else(|| io::Error::other("cargo did not provide CARGO_MANIFEST_DIR"))?,
    );
    let sdk_dir = sdk_dir()?;
    let include_dir = sdk_dir.join("include");
    let bin_dir = sdk_dir.join("bin/x64");

    for header in HEADERS {
        let path = include_dir.join(header);
        require_complete_file(&path, 160, "streamline 2.9.0 header")?;

        rerun_if_changed(&path);
    }

    validate_version(&include_dir.join("sl_version.h"))?;

    let runtime_sources = RUNTIME_FILES.map(|name| bin_dir.join(name));

    for path in &runtime_sources {
        require_complete_file(path, 4096, "streamline 2.9.0 runtime")?;

        rerun_if_changed(path);
    }

    let notice_sources = [
        (sdk_dir.join("license.txt"), "streamline-license.txt"),
        (
            sdk_dir.join("3rd-party-licenses.md"),
            "streamline-3rd-party-licenses.md",
        ),
        (
            bin_dir.join("nvngx_dlss.license.txt"),
            "nvngx_dlss.license.txt",
        ),
        (bin_dir.join("reflex.license.txt"), "reflex.license.txt"),
        (
            manifest_dir.join("DLSS-THIRD-PARTY-NOTICES.txt"),
            "dlss-third-party-notices.txt",
        ),
    ];

    for (path, _) in &notice_sources {
        require_complete_file(path, 256, "streamline runtime notice")?;

        rerun_if_changed(path);
    }

    cc::Build::new()
        .cpp(true)
        .file(manifest_dir.join("native/streamline.cpp"))
        .include(&include_dir)
        .flag_if_supported("/std:c++17")
        .flag_if_supported("/EHsc")
        .compile("nvidia_streamline_native");

    let runtime_dir = PathBuf::from(
        env::var_os("OUT_DIR").ok_or_else(|| io::Error::other("cargo did not provide OUT_DIR"))?,
    )
    .join("streamline");
    prepare_directory(&runtime_dir)?;

    for (source, name) in runtime_sources.iter().zip(RUNTIME_FILES) {
        copy(source, &runtime_dir.join(name))?;
    }

    for (source, name) in notice_sources {
        copy(&source, &runtime_dir.join(name))?;
    }

    let runtime_dir = runtime_dir.to_str().ok_or_else(|| {
        io::Error::other(format!(
            "streamline runtime path is not valid unicode: {}",
            runtime_dir.display()
        ))
    })?;

    println!("cargo:rustc-cfg=nvidia_streamline_native");
    println!("cargo:rustc-env=NVIDIA_STREAMLINE_RUNTIME_DIR={runtime_dir}");

    Ok(())
}

fn sdk_dir() -> Result<PathBuf, Box<dyn Error>> {
    let sdk = nvidia_sdk::Sdk::Streamline.resolve(&nvidia_sdk::ResolveOptions::for_cargo()?)?;
    Ok(dunce::canonicalize(&sdk.path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "canonicalizing streamline 2.9.0 sdk directory {}: {error}",
                sdk.path.display()
            ),
        )
    })?)
}

fn require_complete_file(path: &Path, minimum_size: u64, description: &str) -> io::Result<()> {
    let metadata = fs::metadata(path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "reading metadata for {description} {}: {error}",
                path.display()
            ),
        )
    })?;

    if !metadata.is_file() {
        return Err(io::Error::other(format!(
            "{description} is not a file: {}",
            path.display()
        )));
    }

    let mut file = File::open(path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!("opening {description} {}: {error}", path.display()),
        )
    })?;
    let mut prefix = [0_u8; 128];
    let prefix_len = file.read(&mut prefix).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!("reading {description} {}: {error}", path.display()),
        )
    })?;

    if prefix[..prefix_len].starts_with(b"version https://git-lfs.github.com/spec/v1") {
        return Err(io::Error::other(format!(
            "{description} is a git lfs pointer; fetch the sdk file: {}",
            path.display()
        )));
    }

    if metadata.len() < minimum_size {
        return Err(io::Error::other(format!(
            "{description} is incomplete ({} bytes, expected at least {minimum_size}): {}",
            metadata.len(),
            path.display()
        )));
    }

    Ok(())
}

fn validate_version(path: &Path) -> io::Result<()> {
    let contents = fs::read_to_string(path).map_err(|error| {
        io::Error::new(error.kind(), format!("reading {}: {error}", path.display()))
    })?;
    let version = (
        cpp_define(&contents, "SL_VERSION_MAJOR")?,
        cpp_define(&contents, "SL_VERSION_MINOR")?,
        cpp_define(&contents, "SL_VERSION_PATCH")?,
    );

    if version != (2, 9, 0) {
        return Err(io::Error::other(format!(
            "STREAMLINE_SDK must point to streamline 2.9.0, found {}.{}.{}",
            version.0, version.1, version.2
        )));
    }

    Ok(())
}

fn cpp_define(contents: &str, name: &str) -> io::Result<u32> {
    contents
        .lines()
        .find_map(|line| {
            let mut fields = line.split_whitespace();
            (fields.next() == Some("#define") && fields.next() == Some(name))
                .then(|| fields.next())
                .flatten()
        })
        .ok_or_else(|| io::Error::other(format!("missing {name} in sl_version.h")))?
        .parse()
        .map_err(|error| io::Error::other(format!("parsing {name} in sl_version.h: {error}")))
}

fn prepare_directory(path: &Path) -> io::Result<()> {
    match fs::symlink_metadata(path) {
        Ok(metadata) if metadata.file_type().is_symlink() => {
            return Err(io::Error::other(format!(
                "refusing to replace streamline runtime directory symlink {}",
                path.display()
            )));
        }
        Ok(metadata) if metadata.is_dir() => fs::remove_dir_all(path).map_err(|error| {
            io::Error::new(
                error.kind(),
                format!("removing runtime directory {}: {error}", path.display()),
            )
        })?,
        Ok(_) => fs::remove_file(path).map_err(|error| {
            io::Error::new(
                error.kind(),
                format!("removing runtime file {}: {error}", path.display()),
            )
        })?,
        Err(error) if error.kind() == io::ErrorKind::NotFound => {}
        Err(error) => {
            return Err(io::Error::new(
                error.kind(),
                format!(
                    "reading runtime directory metadata {}: {error}",
                    path.display()
                ),
            ));
        }
    }

    fs::create_dir_all(path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!("creating runtime directory {}: {error}", path.display()),
        )
    })
}

fn copy(source: &Path, destination: &Path) -> io::Result<()> {
    fs::copy(source, destination).map(|_| ()).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "copying {} to {}: {error}",
                source.display(),
                destination.display()
            ),
        )
    })
}

fn rerun_if_changed(path: &Path) {
    println!("cargo:rerun-if-changed={}", path.display());
}