diff --git a/alvr/xtask/src/build.rs b/alvr/xtask/src/build.rs index 8b2ef9e147..1438a503fe 100644 --- a/alvr/xtask/src/build.rs +++ b/alvr/xtask/src/build.rs @@ -1,4 +1,4 @@ -use crate::command; +use crate::{CommonBuildFlags, command}; use alvr_filesystem::{self as afs, Layout}; use std::{ env, @@ -27,7 +27,11 @@ impl Display for Profile { } } -pub fn build_server_lib(profile: Profile, root: Option, reproducible: bool) { +pub fn build_server_lib( + profile: Profile, + root: Option, + common_build_flags: CommonBuildFlags, +) { let sh = Shell::new().unwrap(); let mut flags = vec![]; @@ -39,9 +43,12 @@ pub fn build_server_lib(profile: Profile, root: Option, reproducible: bo Profile::Release => flags.push("--release"), Profile::Debug => (), } - if reproducible { + if common_build_flags.locked { flags.push("--locked"); } + if common_build_flags.offline { + flags.push("--offline"); + } let flags_ref = &flags; let artifacts_dir = afs::target_dir().join(profile.to_string()); @@ -75,8 +82,7 @@ pub fn build_streamer( profile: Profile, gpl: bool, root: Option, - reproducible: bool, - profiling: bool, + common_build_flags: CommonBuildFlags, keep_config: bool, ) { let sh = Shell::new().unwrap(); @@ -92,9 +98,15 @@ pub fn build_streamer( Profile::Release => common_flags.push("--release"), Profile::Debug => (), } - if reproducible { + if common_build_flags.locked { common_flags.push("--locked"); } + if common_build_flags.frozen { + common_flags.push("--frozen"); + } + if common_build_flags.offline { + common_flags.push("--offline"); + } let artifacts_dir = if cfg!(all(windows, target_arch = "aarch64")) { // Fix for cross compilation @@ -136,7 +148,7 @@ pub fn build_streamer( vec![] }; - let profiling_flag = if profiling { + let profiling_flag = if common_build_flags.profiling { vec!["--features", "alvr_server_core/trace-performance"] } else { vec![] @@ -273,7 +285,7 @@ pub fn build_streamer( } } -pub fn build_launcher(profile: Profile, reproducible: bool) { +pub fn build_launcher(profile: Profile, common_build_flags: CommonBuildFlags) { let sh = Shell::new().unwrap(); let mut common_flags = vec![]; @@ -285,9 +297,12 @@ pub fn build_launcher(profile: Profile, reproducible: bool) { Profile::Release => common_flags.push("--release"), Profile::Debug => (), } - if reproducible { + if common_build_flags.locked { common_flags.push("--locked"); } + if common_build_flags.offline { + common_flags.push("--offline"); + } let common_flags_ref = &common_flags; sh.create_dir(afs::launcher_build_dir()).unwrap(); diff --git a/alvr/xtask/src/dependencies.rs b/alvr/xtask/src/dependencies.rs deleted file mode 100644 index 3ac3d94c87..0000000000 --- a/alvr/xtask/src/dependencies.rs +++ /dev/null @@ -1,400 +0,0 @@ -use crate::{BuildPlatform, command}; -use alvr_filesystem as afs; -use std::{fs, path::Path}; -use xshell::{Shell, cmd}; - -pub enum OpenXRLoadersSelection { - OnlyGeneric, - OnlyPico, - All, -} - -pub fn choco_install(sh: &Shell, packages: &[&str]) -> Result<(), xshell::Error> { - cmd!( - sh, - "powershell Start-Process choco -ArgumentList \"install {packages...} -y\" -Verb runAs -Wait" - ) - .run() -} - -pub fn prepare_x264_windows(deps_path: &Path) { - let sh = Shell::new().unwrap(); - - const VERSION: &str = "0.164"; - const REVISION: usize = 3086; - - let destination = deps_path.join("x264"); - - command::download_and_extract_zip( - &format!( - "{}/{VERSION}.r{REVISION}/libx264_{VERSION}.r{REVISION}_msvc16.zip", - "https://github.com/ShiftMediaProject/x264/releases/download", - ), - &destination, - ) - .unwrap(); - - fs::write( - afs::deps_dir().join("x264.pc"), - format!( - r#" -prefix={} -exec_prefix=${{prefix}}/bin/x64 -libdir=${{prefix}}/lib/x64 -includedir=${{prefix}}/include - -Name: x264 -Description: x264 library -Version: {VERSION} -Libs: -L${{libdir}} -lx264 -Cflags: -I${{includedir}} -"#, - destination.to_string_lossy().replace('\\', "/") - ), - ) - .unwrap(); - - cmd!(sh, "setx PKG_CONFIG_PATH {deps_path}").run().unwrap(); -} - -pub fn prepare_ffmpeg_windows(deps_path: &Path) { - command::download_and_extract_zip( - &format!( - "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/{}", - "ffmpeg-n7.1-latest-win64-gpl-shared-7.1.zip" - ), - deps_path, - ) - .unwrap(); - - fs::rename( - deps_path.join("ffmpeg-n7.1-latest-win64-gpl-shared-7.1"), - deps_path.join("ffmpeg"), - ) - .unwrap(); -} - -pub fn prepare_windows_deps(skip_admin_priv: bool) { - let sh = Shell::new().unwrap(); - - let deps_path = afs::deps_dir().join("windows"); - sh.remove_path(&deps_path).ok(); - sh.create_dir(&deps_path).unwrap(); - - if !skip_admin_priv { - choco_install( - &sh, - &["zip", "unzip", "llvm", "vulkan-sdk", "pkgconfiglite"], - ) - .unwrap(); - } - - prepare_x264_windows(&deps_path); - prepare_ffmpeg_windows(&deps_path); -} - -pub fn prepare_linux_deps(enable_nvenc: bool) { - let sh = Shell::new().unwrap(); - - let deps_path = afs::deps_dir().join("linux"); - sh.remove_path(&deps_path).ok(); - sh.create_dir(&deps_path).unwrap(); - - build_x264_linux(&deps_path); - build_ffmpeg_linux(enable_nvenc, &deps_path); -} - -pub fn build_x264_linux(deps_path: &Path) { - let sh = Shell::new().unwrap(); - - // x264 0.164 - command::download_and_extract_tar( - "https://code.videolan.org/videolan/x264/-/archive/c196240409e4d7c01b47448d93b1f9683aaa7cf7/x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7.tar.bz2", - deps_path, - ) - .unwrap(); - - let final_path = deps_path.join("x264"); - - fs::rename( - deps_path.join("x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7"), - &final_path, - ) - .unwrap(); - - let flags = ["--enable-static", "--disable-cli", "--enable-pic"]; - - let install_prefix = format!("--prefix={}", final_path.join("alvr_build").display()); - - let _push_guard = sh.push_dir(final_path); - - cmd!(sh, "./configure {install_prefix} {flags...}") - .run() - .unwrap(); - - let nproc = cmd!(sh, "nproc").read().unwrap(); - cmd!(sh, "make -j{nproc}").run().unwrap(); - cmd!(sh, "make install").run().unwrap(); -} - -pub fn build_ffmpeg_linux(enable_nvenc: bool, deps_path: &Path) { - let sh = Shell::new().unwrap(); - - command::download_and_extract_zip( - "https://codeload.github.com/FFmpeg/FFmpeg/zip/n6.0", - deps_path, - ) - .unwrap(); - - let final_path = deps_path.join("ffmpeg"); - - fs::rename(deps_path.join("FFmpeg-n6.0"), &final_path).unwrap(); - - let flags = [ - "--enable-gpl", - "--enable-version3", - "--enable-static", - "--disable-programs", - "--disable-doc", - "--disable-avdevice", - "--disable-avformat", - "--disable-swresample", - "--disable-swscale", - "--disable-postproc", - "--disable-network", - "--disable-everything", - "--enable-encoder=h264_vaapi", - "--enable-encoder=hevc_vaapi", - "--enable-encoder=av1_vaapi", - "--enable-hwaccel=h264_vaapi", - "--enable-hwaccel=hevc_vaapi", - "--enable-hwaccel=av1_vaapi", - "--enable-filter=scale_vaapi", - "--enable-vulkan", - "--enable-libdrm", - "--enable-pic", - "--enable-rpath", - "--fatal-warnings", - ]; - let install_prefix = format!("--prefix={}", final_path.join("alvr_build").display()); - // The reason for 4x$ in LDSOFLAGS var refer to https://stackoverflow.com/a/71429999 - // all varients of --extra-ldsoflags='-Wl,-rpath,$ORIGIN' do not work! don't waste your time trying! - // - let config_vars = r#"-Wl,-rpath,'$$$$ORIGIN'"#; - - let _push_guard = sh.push_dir(final_path); - let _env_vars = sh.push_env("LDSOFLAGS", config_vars); - - // Patches ffmpeg for workarounds and patches that have yet to be unstreamed - let ffmpeg_command = "for p in ../../../alvr/xtask/patches/*; do patch -p1 < $p; done"; - cmd!(sh, "bash -c {ffmpeg_command}").run().unwrap(); - - if enable_nvenc { - /* - Describing Nvidia specific options --nvccflags: - nvcc from CUDA toolkit version 11.0 or higher does not support compiling for 'compute_30' (default in ffmpeg) - 52 is the minimum required for the current CUDA 11 version (Quadro M6000 , GeForce 900, GTX-970, GTX-980, GTX Titan X) - https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ - Anyway below 50 arch card don't support nvenc encoding hevc https://developer.nvidia.com/nvidia-video-codec-sdk (Supported devices) - Nvidia docs: - https://docs.nvidia.com/video-technologies/video-codec-sdk/ffmpeg-with-nvidia-gpu/#commonly-faced-issues-and-tips-to-resolve-them - */ - #[cfg(target_os = "linux")] - { - let codec_header_version = "12.1.14.0"; - let temp_download_dir = deps_path.join("dl_temp"); - command::download_and_extract_zip( - &format!("https://github.com/FFmpeg/nv-codec-headers/archive/refs/tags/n{codec_header_version}.zip"), - &temp_download_dir - ) - .unwrap(); - - let header_dir = deps_path.join("nv-codec-headers"); - let header_build_dir = header_dir.join("build"); - fs::rename( - temp_download_dir.join(format!("nv-codec-headers-n{codec_header_version}")), - &header_dir, - ) - .unwrap(); - fs::remove_dir_all(temp_download_dir).unwrap(); - { - let make_header_cmd = - format!("make install PREFIX='{}'", header_build_dir.display()); - let _header_push_guard = sh.push_dir(&header_dir); - cmd!(sh, "bash -c {make_header_cmd}").run().unwrap(); - } - - let cuda = pkg_config::Config::new().probe("cuda").unwrap(); - let include_flags = cuda - .include_paths - .iter() - .map(|path| format!("-I{}", path.to_string_lossy())) - .reduce(|a, b| format!("{a} {b}")) - .expect("pkg-config cuda entry to have include-paths"); - let link_flags = cuda - .link_paths - .iter() - .map(|path| format!("-L{}", path.to_string_lossy())) - .reduce(|a, b| format!("{a} {b}")) - .expect("pkg-config cuda entry to have link-paths"); - - let nvenc_flags = &[ - "--enable-encoder=h264_nvenc", - "--enable-encoder=hevc_nvenc", - "--enable-encoder=av1_nvenc", - "--enable-nonfree", - "--enable-cuda-nvcc", - "--enable-libnpp", - "--nvccflags=\"-gencode arch=compute_52,code=sm_52 -O2\"", - &format!("--extra-cflags=\"{include_flags}\""), - &format!("--extra-ldflags=\"{link_flags}\""), - ]; - - let env_vars = format!( - "PKG_CONFIG_PATH='{}'", - header_build_dir.join("lib/pkgconfig").display() - ); - let flags_combined = flags.join(" "); - let nvenc_flags_combined = nvenc_flags.join(" "); - - let command = format!( - "{env_vars} ./configure {install_prefix} {flags_combined} {nvenc_flags_combined}" - ); - - cmd!(sh, "bash -c {command}").run().unwrap(); - } - } else { - cmd!(sh, "./configure {install_prefix} {flags...}") - .run() - .unwrap(); - } - - let nproc = cmd!(sh, "nproc").read().unwrap(); - cmd!(sh, "make -j{nproc}").run().unwrap(); - cmd!(sh, "make install").run().unwrap(); -} - -pub fn prepare_macos_deps() {} - -pub fn prepare_server_deps( - platform: Option, - skip_admin_priv: bool, - enable_nvenc: bool, -) { - match platform { - Some(BuildPlatform::Windows) => prepare_windows_deps(skip_admin_priv), - Some(BuildPlatform::Linux) => prepare_linux_deps(enable_nvenc), - Some(BuildPlatform::Macos) => prepare_macos_deps(), - Some(BuildPlatform::Android) => panic!("Android is not supported"), - None => { - if cfg!(windows) { - prepare_windows_deps(skip_admin_priv); - } else if cfg!(target_os = "linux") { - prepare_linux_deps(enable_nvenc); - } else if cfg!(target_os = "macos") { - prepare_macos_deps(); - } else { - panic!("Unsupported platform"); - } - } - } -} - -fn get_android_openxr_loaders(selection: OpenXRLoadersSelection) { - fn get_openxr_loader(name: &str, url: &str, source_dir: &str) { - let sh = Shell::new().unwrap(); - let temp_dir = afs::build_dir().join("temp_download"); - sh.remove_path(&temp_dir).ok(); - sh.create_dir(&temp_dir).unwrap(); - let destination_dir = afs::deps_dir().join("android_openxr/arm64-v8a"); - fs::create_dir_all(&destination_dir).unwrap(); - - command::download_and_extract_zip(url, &temp_dir).unwrap(); - fs::copy( - temp_dir.join(source_dir).join("libopenxr_loader.so"), - destination_dir.join(format!("libopenxr_loader{name}.so")), - ) - .unwrap(); - fs::remove_dir_all(&temp_dir).ok(); - } - - get_openxr_loader( - "", - &format!( - "https://github.com/KhronosGroup/OpenXR-SDK-Source/releases/download/{}", - "release-1.0.34/openxr_loader_for_android-1.0.34.aar", - ), - "prefab/modules/openxr_loader/libs/android.arm64-v8a", - ); - - if matches!(selection, OpenXRLoadersSelection::OnlyGeneric) { - return; - } - - get_openxr_loader( - "_pico_old", - "https://sdk.picovr.com/developer-platform/sdk/PICO_OpenXR_SDK_220.zip", - "libs/android.arm64-v8a", - ); - - if matches!(selection, OpenXRLoadersSelection::OnlyPico) { - return; - } - - get_openxr_loader( - "_quest1", - "https://securecdn.oculus.com/binaries/download/?id=7577210995650755", // Version 64 - "OpenXR/Libs/Android/arm64-v8a/Release", - ); - - get_openxr_loader( - "_yvr", - "https://developer.yvrdream.com/yvrdoc/sdk/openxr/yvr_openxr_mobile_sdk_2.0.0.zip", - "yvr_openxr_mobile_sdk_2.0.0/OpenXR/Libs/Android/arm64-v8a", - ); - - get_openxr_loader( - "_lynx", - "https://portal.lynx-r.com/downloads/download/16", // version 1.0.0 - "jni/arm64-v8a", - ); -} - -pub fn build_android_deps( - skip_admin_priv: bool, - all_targets: bool, - openxr_loaders_selection: OpenXRLoadersSelection, -) { - let sh = Shell::new().unwrap(); - - if cfg!(windows) && !skip_admin_priv { - choco_install(&sh, &["unzip", "llvm"]).unwrap(); - } - - cmd!(sh, "rustup target add aarch64-linux-android") - .run() - .unwrap(); - if all_targets { - cmd!(sh, "rustup target add armv7-linux-androideabi") - .run() - .unwrap(); - cmd!(sh, "rustup target add x86_64-linux-android") - .run() - .unwrap(); - cmd!(sh, "rustup target add i686-linux-android") - .run() - .unwrap(); - } - cmd!(sh, "cargo install cbindgen").run().unwrap(); - cmd!(sh, "cargo install cargo-ndk --version 3.5.4") - .run() - .unwrap(); - cmd!( - sh, - "cargo install --git https://github.com/zarik5/cargo-apk cargo-apk" - ) - .run() - .unwrap(); - - get_android_openxr_loaders(openxr_loaders_selection); -} diff --git a/alvr/xtask/src/dependencies/android.rs b/alvr/xtask/src/dependencies/android.rs new file mode 100644 index 0000000000..b90a209728 --- /dev/null +++ b/alvr/xtask/src/dependencies/android.rs @@ -0,0 +1,106 @@ +use crate::{ + command, + dependencies::{OpenXRLoadersSelection, windows::choco_install}, +}; +use alvr_filesystem as afs; +use std::fs; +use xshell::{Shell, cmd}; + +pub fn build_deps( + skip_admin_priv: bool, + all_targets: bool, + openxr_loaders_selection: OpenXRLoadersSelection, +) { + let sh = Shell::new().unwrap(); + + if cfg!(windows) && !skip_admin_priv { + choco_install(&sh, &["unzip", "llvm"]).unwrap(); + } + + cmd!(sh, "rustup target add aarch64-linux-android") + .run() + .unwrap(); + if all_targets { + cmd!(sh, "rustup target add armv7-linux-androideabi") + .run() + .unwrap(); + cmd!(sh, "rustup target add x86_64-linux-android") + .run() + .unwrap(); + cmd!(sh, "rustup target add i686-linux-android") + .run() + .unwrap(); + } + cmd!(sh, "cargo install cbindgen").run().unwrap(); + cmd!(sh, "cargo install cargo-ndk --version 3.5.4") + .run() + .unwrap(); + cmd!( + sh, + "cargo install --git https://github.com/zarik5/cargo-apk cargo-apk" + ) + .run() + .unwrap(); + + get_android_openxr_loaders(openxr_loaders_selection); +} + +fn get_android_openxr_loaders(selection: OpenXRLoadersSelection) { + fn get_openxr_loader(name: &str, url: &str, source_dir: &str) { + let sh = Shell::new().unwrap(); + let temp_dir = afs::build_dir().join("temp_download"); + sh.remove_path(&temp_dir).ok(); + sh.create_dir(&temp_dir).unwrap(); + let destination_dir = afs::deps_dir().join("android_openxr/arm64-v8a"); + fs::create_dir_all(&destination_dir).unwrap(); + + command::download_and_extract_zip(url, &temp_dir).unwrap(); + fs::copy( + temp_dir.join(source_dir).join("libopenxr_loader.so"), + destination_dir.join(format!("libopenxr_loader{name}.so")), + ) + .unwrap(); + fs::remove_dir_all(&temp_dir).ok(); + } + + get_openxr_loader( + "", + &format!( + "https://github.com/KhronosGroup/OpenXR-SDK-Source/releases/download/{}", + "release-1.0.34/openxr_loader_for_android-1.0.34.aar", + ), + "prefab/modules/openxr_loader/libs/android.arm64-v8a", + ); + + if matches!(selection, OpenXRLoadersSelection::OnlyGeneric) { + return; + } + + get_openxr_loader( + "_pico_old", + "https://sdk.picovr.com/developer-platform/sdk/PICO_OpenXR_SDK_220.zip", + "libs/android.arm64-v8a", + ); + + if matches!(selection, OpenXRLoadersSelection::OnlyPico) { + return; + } + + get_openxr_loader( + "_quest1", + "https://securecdn.oculus.com/binaries/download/?id=7577210995650755", // Version 64 + "OpenXR/Libs/Android/arm64-v8a/Release", + ); + + get_openxr_loader( + "_yvr", + "https://developer.yvrdream.com/yvrdoc/sdk/openxr/yvr_openxr_mobile_sdk_2.0.0.zip", + "yvr_openxr_mobile_sdk_2.0.0/OpenXR/Libs/Android/arm64-v8a", + ); + + get_openxr_loader( + "_lynx", + "https://portal.lynx-r.com/downloads/download/16", // version 1.0.0 + "jni/arm64-v8a", + ); +} diff --git a/alvr/xtask/src/dependencies/linux.rs b/alvr/xtask/src/dependencies/linux.rs new file mode 100644 index 0000000000..98e904bfb8 --- /dev/null +++ b/alvr/xtask/src/dependencies/linux.rs @@ -0,0 +1,249 @@ +use crate::command; +use alvr_filesystem as afs; +use std::fs; +use xshell::{Shell, cmd}; + +pub fn prepare_deps(enable_nvenc: bool) { + let sh = Shell::new().unwrap(); + + let deps_path = deps_path(); + sh.remove_path(&deps_path).ok(); + sh.create_dir(&deps_path).unwrap(); + + download_x264_src(); + download_ffmpeg_src(); + if enable_nvenc { + download_nvidia_ffmpeg_deps(); + } + + build_x264(); + build_ffmpeg(enable_nvenc); +} + +pub fn download_deps(enable_nvenc: bool) { + let sh = Shell::new().unwrap(); + + let deps_path = deps_path(); + sh.remove_path(&deps_path).ok(); + sh.create_dir(&deps_path).unwrap(); + + download_x264_src(); + download_ffmpeg_src(); + if enable_nvenc { + download_nvidia_ffmpeg_deps(); + } +} + +pub fn build_deps(enable_nvenc: bool) { + let deps_path = deps_path(); + assert!(deps_path.exists(), "Please download dependencies first."); + + build_x264(); + build_ffmpeg(enable_nvenc); +} + +fn deps_path() -> std::path::PathBuf { + afs::deps_dir().join("linux") +} + +fn x264_path() -> std::path::PathBuf { + deps_path().join("x264") +} + +fn ffmpeg_path() -> std::path::PathBuf { + deps_path().join("ffmpeg") +} + +fn nvenc_headers_path() -> std::path::PathBuf { + deps_path().join("nv-codec-headers") +} + +fn download_x264_src() { + let deps_path = deps_path(); + // x264 0.164 + command::download_and_extract_tar( + "https://code.videolan.org/videolan/x264/-/archive/c196240409e4d7c01b47448d93b1f9683aaa7cf7/x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7.tar.bz2", + &deps_path, + ) + .unwrap(); + + fs::rename( + deps_path.join("x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7"), + x264_path(), + ) + .unwrap(); +} + +fn download_ffmpeg_src() { + let deps_path = deps_path(); + + command::download_and_extract_zip( + "https://codeload.github.com/FFmpeg/FFmpeg/zip/n6.0", + &deps_path, + ) + .unwrap(); + + fs::rename(deps_path.join("FFmpeg-n6.0"), ffmpeg_path()).unwrap(); +} + +fn download_nvidia_ffmpeg_deps() { + let deps_path = deps_path(); + + let codec_header_version = "12.1.14.0"; + let temp_download_dir = deps_path.join("dl_temp"); + command::download_and_extract_zip( + &format!("https://github.com/FFmpeg/nv-codec-headers/archive/refs/tags/n{codec_header_version}.zip"), + &temp_download_dir + ) + .unwrap(); + + fs::rename( + temp_download_dir.join(format!("nv-codec-headers-n{codec_header_version}")), + nvenc_headers_path(), + ) + .unwrap(); + fs::remove_dir_all(temp_download_dir).unwrap(); +} + +fn build_x264() { + let sh = Shell::new().unwrap(); + + let x264_src_path = x264_path(); + + let flags = ["--enable-static", "--disable-cli", "--enable-pic"]; + + let build_path = x264_src_path.join("alvr_build"); + sh.remove_path(&build_path).ok(); + + let install_prefix = format!("--prefix={}", build_path.display()); + + let _push_guard = sh.push_dir(x264_src_path); + + cmd!(sh, "./configure {install_prefix} {flags...}") + .run() + .unwrap(); + + let nproc = cmd!(sh, "nproc").read().unwrap(); + cmd!(sh, "make -j{nproc}").run().unwrap(); + cmd!(sh, "make install").run().unwrap(); +} + +fn build_ffmpeg(enable_nvenc: bool) { + let ffmpeg_src_path = ffmpeg_path(); + + let sh = Shell::new().unwrap(); + + let flags = [ + "--enable-gpl", + "--enable-version3", + "--enable-static", + "--disable-programs", + "--disable-doc", + "--disable-avdevice", + "--disable-avformat", + "--disable-swresample", + "--disable-swscale", + "--disable-postproc", + "--disable-network", + "--disable-everything", + "--enable-encoder=h264_vaapi", + "--enable-encoder=hevc_vaapi", + "--enable-encoder=av1_vaapi", + "--enable-hwaccel=h264_vaapi", + "--enable-hwaccel=hevc_vaapi", + "--enable-hwaccel=av1_vaapi", + "--enable-filter=scale_vaapi", + "--enable-vulkan", + "--enable-libdrm", + "--enable-pic", + "--enable-rpath", + "--fatal-warnings", + ]; + + let build_path = ffmpeg_src_path.join("alvr_build"); + sh.remove_path(&build_path).ok(); + + let install_prefix = format!("--prefix={}", build_path.display()); + // The reason for 4x$ in LDSOFLAGS var refer to https://stackoverflow.com/a/71429999 + // all varients of --extra-ldsoflags='-Wl,-rpath,$ORIGIN' do not work! don't waste your time trying! + // + let config_vars = r"-Wl,-rpath,'$$$$ORIGIN'"; + + let _push_guard = sh.push_dir(ffmpeg_src_path); + let _env_vars = sh.push_env("LDSOFLAGS", config_vars); + + // Patches ffmpeg for workarounds and patches that have yet to be unstreamed + let ffmpeg_command = "for p in ../../../alvr/xtask/patches/*; do patch -p1 < $p; done"; + cmd!(sh, "bash -c {ffmpeg_command}").run().unwrap(); + + if enable_nvenc { + /* + Describing Nvidia specific options --nvccflags: + nvcc from CUDA toolkit version 11.0 or higher does not support compiling for 'compute_30' (default in ffmpeg) + 52 is the minimum required for the current CUDA 11 version (Quadro M6000 , GeForce 900, GTX-970, GTX-980, GTX Titan X) + https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ + Anyway below 50 arch card don't support nvenc encoding hevc https://developer.nvidia.com/nvidia-video-codec-sdk (Supported devices) + Nvidia docs: + https://docs.nvidia.com/video-technologies/video-codec-sdk/ffmpeg-with-nvidia-gpu/#commonly-faced-issues-and-tips-to-resolve-them + */ + #[cfg(target_os = "linux")] + { + let nvenc_headers_path = nvenc_headers_path(); + let header_build_dir = nvenc_headers_path.join("build"); + sh.remove_path(&header_build_dir).ok(); + { + let make_header_cmd = + format!("make install PREFIX='{}'", header_build_dir.display()); + let _header_push_guard = sh.push_dir(nvenc_headers_path); + cmd!(sh, "bash -c {make_header_cmd}").run().unwrap(); + } + + let cuda = pkg_config::Config::new().probe("cuda").unwrap(); + let include_flags = cuda + .include_paths + .iter() + .map(|path| format!("-I{}", path.to_string_lossy())) + .reduce(|a, b| format!("{a} {b}")) + .expect("pkg-config cuda entry to have include-paths"); + let link_flags = cuda + .link_paths + .iter() + .map(|path| format!("-L{}", path.to_string_lossy())) + .reduce(|a, b| format!("{a} {b}")) + .expect("pkg-config cuda entry to have link-paths"); + + let nvenc_flags = &[ + "--enable-encoder=h264_nvenc", + "--enable-encoder=hevc_nvenc", + "--enable-encoder=av1_nvenc", + "--enable-nonfree", + "--enable-cuda-nvcc", + "--enable-libnpp", + "--nvccflags=\"-gencode arch=compute_52,code=sm_52 -O2\"", + &format!("--extra-cflags=\"{include_flags}\""), + &format!("--extra-ldflags=\"{link_flags}\""), + ]; + + let env_vars = format!( + "PKG_CONFIG_PATH='{}'", + header_build_dir.join("lib/pkgconfig").display() + ); + let flags_combined = flags.join(" "); + let nvenc_flags_combined = nvenc_flags.join(" "); + + let command = format!( + "{env_vars} ./configure {install_prefix} {flags_combined} {nvenc_flags_combined}" + ); + + cmd!(sh, "bash -c {command}").run().unwrap(); + } + } else { + cmd!(sh, "./configure {install_prefix} {flags...}") + .run() + .unwrap(); + } + + let nproc = cmd!(sh, "nproc").read().unwrap(); + cmd!(sh, "make -j{nproc}").run().unwrap(); + cmd!(sh, "make install").run().unwrap(); +} diff --git a/alvr/xtask/src/dependencies/mod.rs b/alvr/xtask/src/dependencies/mod.rs new file mode 100644 index 0000000000..b2137eecd1 --- /dev/null +++ b/alvr/xtask/src/dependencies/mod.rs @@ -0,0 +1,81 @@ +use crate::BuildPlatform; + +pub mod android; +pub mod linux; +pub mod windows; + +pub enum OpenXRLoadersSelection { + OnlyGeneric, + OnlyPico, + All, +} + +pub fn prepare_server_deps( + platform: Option, + skip_admin_priv: bool, + enable_nvenc: bool, +) { + match platform { + Some(BuildPlatform::Windows) => windows::prepare_deps(skip_admin_priv), + Some(BuildPlatform::Linux) => linux::prepare_deps(enable_nvenc), + Some(BuildPlatform::Macos) => prepare_macos_deps(), + Some(BuildPlatform::Android) => panic!("Android is not supported"), + None => { + if cfg!(windows) { + windows::prepare_deps(skip_admin_priv); + } else if cfg!(target_os = "linux") { + linux::prepare_deps(enable_nvenc); + } else if cfg!(target_os = "macos") { + prepare_macos_deps(); + } else { + panic!("Unsupported platform"); + } + } + } +} + +pub fn download_server_deps( + platform: Option, + skip_admin_priv: bool, + enable_nvenc: bool, +) { + match platform { + Some(BuildPlatform::Windows) => windows::prepare_deps(skip_admin_priv), + Some(BuildPlatform::Linux) => linux::download_deps(enable_nvenc), + Some(BuildPlatform::Macos) => prepare_macos_deps(), + Some(BuildPlatform::Android) => panic!("Android is not supported"), + None => { + if cfg!(windows) { + windows::prepare_deps(skip_admin_priv); + } else if cfg!(target_os = "linux") { + linux::download_deps(enable_nvenc); + } else if cfg!(target_os = "macos") { + prepare_macos_deps(); + } else { + panic!("Unsupported platform"); + } + } + } +} + +pub fn build_server_deps(platform: Option, enable_nvenc: bool) { + match platform { + Some(BuildPlatform::Windows) => panic!("Building windows dependencies is unsupported"), + Some(BuildPlatform::Linux) => linux::build_deps(enable_nvenc), + Some(BuildPlatform::Macos) => prepare_macos_deps(), + Some(BuildPlatform::Android) => panic!("Android is not supported"), + None => { + if cfg!(windows) { + panic!("Building windows dependencies is unsupported"); + } else if cfg!(target_os = "linux") { + linux::build_deps(enable_nvenc); + } else if cfg!(target_os = "macos") { + prepare_macos_deps(); + } else { + panic!("Unsupported platform"); + } + } + } +} + +pub fn prepare_macos_deps() {} diff --git a/alvr/xtask/src/dependencies/windows.rs b/alvr/xtask/src/dependencies/windows.rs new file mode 100644 index 0000000000..e16b14ad5d --- /dev/null +++ b/alvr/xtask/src/dependencies/windows.rs @@ -0,0 +1,105 @@ +use crate::command; +use alvr_filesystem as afs; +use std::fs; +use xshell::{Shell, cmd}; + +pub fn choco_install(sh: &Shell, packages: &[&str]) -> Result<(), xshell::Error> { + cmd!( + sh, + "powershell Start-Process choco -ArgumentList \"install {packages...} -y\" -Verb runAs -Wait" + ) + .run() +} + +pub fn prepare_deps(skip_admin_priv: bool) { + let sh = Shell::new().unwrap(); + + let deps_path = deps_path(); + sh.remove_path(&deps_path).ok(); + sh.create_dir(&deps_path).unwrap(); + + if !skip_admin_priv { + choco_install( + &sh, + &["zip", "unzip", "llvm", "vulkan-sdk", "pkgconfiglite"], + ) + .unwrap(); + } + + prepare_prebuilt_x264_windows(); + prepare_prebuilt_ffmpeg_windows(); +} + +fn deps_path() -> std::path::PathBuf { + afs::deps_dir().join("windows") +} + +fn x264_path() -> std::path::PathBuf { + deps_path().join("x264") +} + +fn ffmpeg_path() -> std::path::PathBuf { + deps_path().join("ffmpeg") +} + +fn prepare_prebuilt_x264_windows() { + const VERSION: &str = "0.164"; + const REVISION: usize = 3086; + + let sh = Shell::new().unwrap(); + + let x264_src_path = x264_path(); + + command::download_and_extract_zip( + &format!( + "{}/{VERSION}.r{REVISION}/libx264_{VERSION}.r{REVISION}_msvc16.zip", + "https://github.com/ShiftMediaProject/x264/releases/download", + ), + &x264_src_path, + ) + .unwrap(); + + fs::write( + afs::deps_dir().join("x264.pc"), + format!( + r" +prefix={} +exec_prefix=${{prefix}}/bin/x64 +libdir=${{prefix}}/lib/x64 +includedir=${{prefix}}/include + +Name: x264 +Description: x264 library +Version: {VERSION} +Libs: -L${{libdir}} -lx264 +Cflags: -I${{includedir}} +", + x264_src_path.to_string_lossy().replace('\\', "/") + ), + ) + .unwrap(); + + cmd!(sh, "setx PKG_CONFIG_PATH {x264_src_path}") + .run() + .unwrap(); +} + +fn prepare_prebuilt_ffmpeg_windows() { + let deps_path = deps_path(); + let ffmpeg_path = ffmpeg_path(); + + command::download_and_extract_zip( + &format!( + "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/{}", + "ffmpeg-n7.1-latest-win64-gpl-shared-7.1.zip" + ), + &deps_path, + ) + .unwrap(); + + fs::rename( + deps_path.join("ffmpeg-n7.1-latest-win64-gpl-shared-7.1"), + ffmpeg_path, + ) + .unwrap(); +} diff --git a/alvr/xtask/src/main.rs b/alvr/xtask/src/main.rs index ccd0c6fc5a..a7a1e80322 100644 --- a/alvr/xtask/src/main.rs +++ b/alvr/xtask/src/main.rs @@ -15,7 +15,7 @@ use pico_args::Arguments; use std::{fs, process, time::Instant}; use xshell::{Shell, cmd}; -const HELP_STR: &str = r#" +const HELP_STR: &str = r" cargo xtask Developement actions for ALVR. @@ -23,47 +23,54 @@ USAGE: cargo xtask [FLAG] [ARGS] SUBCOMMANDS: - prepare-deps Download and compile streamer and client external dependencies - build-streamer Build streamer, then copy binaries to build folder - build-launcher Build launcher, then copy binaries to build folder - build-server-lib Build a C-ABI ALVR server library and header - build-client Build client, then copy binaries to build folder - build-client-lib Build a C-ABI ALVR client library and header - build-client-xr-lib Build a C-ABI ALVR OpenXR entry point client library and header - run-streamer Build streamer and then open the dashboard - run-launcher Build launcher and then open it - format Autoformat all code - check-format Check if code is correctly formatted - package-streamer Build streamer with distribution profile, make archive - package-launcher Build launcher with distribution profile, make archive - package-client Build client with distribution profile - package-client-lib Build client library then zip it - clean Removes all build artifacts and dependencies - bump Bump streamer and client package versions - clippy Show warnings for selected clippy lints - kill-oculus Kill all Oculus processes + prepare-deps Download and compile streamer and client external dependencies + download-server-deps Download streamer external dependencies + build-server-deps Compile streamer external dependencies + build-streamer Build streamer, then copy binaries to build folder + build-launcher Build launcher, then copy binaries to build folder + build-server-lib Build a C-ABI ALVR server library and header + build-client Build client, then copy binaries to build folder + build-client-lib Build a C-ABI ALVR client library and header + build-client-xr-lib Build a C-ABI ALVR OpenXR entry point client library and header + run-streamer Build streamer and then open the dashboard + run-launcher Build launcher and then open it + format Autoformat all code + check-format Check if code is correctly formatted + package-streamer Build streamer with distribution profile, make archive + package-launcher Build launcher with distribution profile, make archive + package-client Build client with distribution profile + package-client-lib Build client library then zip it + clean Removes all build artifacts and dependencies + bump Bump streamer and client package versions + clippy Show warnings for selected clippy lints + kill-oculus Kill all Oculus processes FLAGS: - --help Print this text - --keep-config Preserve the configuration file between rebuilds (session.json) - --no-nvidia Disables nVidia support on Linux. For prepare-deps subcommand - --release Optimized build with less debug checks. For build subcommands - --profiling Enable Profiling - --gpl Bundle GPL libraries (FFmpeg). Only for Windows - --nightly Append nightly tag to versions. For bump subcommand - --no-rebuild Do not rebuild the streamer with run-streamer - --ci Do some CI related tweaks. Depends on the other flags and subcommand - --no-stdcpp Disable linking to libc++_shared with build-client-lib - --all-targets For prepare-deps and build-client-lib subcommand, will build for all android supported ABI targets - --meta-store For package-client subcommand, build for Meta Store - --pico-store For package-client subcommand, build for Pico Store + --help Print this text + --keep-config Preserve the configuration file between rebuilds (session.json) + --no-nvidia Disables nVidia support on Linux. For prepare-deps subcommand + --release Optimized build with less debug checks. For build subcommands + --profiling Enable Profiling + --gpl Bundle GPL libraries (FFmpeg, x264) + --nightly Append nightly tag to versions. For bump subcommand + --no-rebuild Do not rebuild the streamer with run-streamer + --ci Do some CI related tweaks. Depends on the other flags and subcommand + --no-stdcpp Disable linking to libc++_shared with build-client-lib + --all-targets For prepare-deps and build-client-lib subcommand, will build for all android supported ABI targets + --meta-store For package-client subcommand, build for Meta Store + --pico-store For package-client subcommand, build for Pico Store + --locked Forces build subcommands to use only dependencies specified from Cargo.lock + --frozen Forces build subcommands to use locally cached dependencies specified in Cargo.lock + and fail if internet access was required during build + --offline Forces build subcommands to fail if they try to use internet. + Note that 'xtask' and 'cargo about' dependencies are downloaded and built during build of alvr ARGS: - --platform Can be one of: windows, linux, macos, android. Can be omitted - --version Specify version to set with the bump-versions subcommand - --root Installation root. By default no root is set and paths are calculated using - relative paths, which requires conforming to FHS on Linux -"#; + --platform Can be one of: windows, linux, macos, android. Can be omitted + --version Specify version to set with the bump-versions subcommand + --root Installation root. By default no root is set and paths are calculated using + relative paths, which requires conforming to FHS on Linux +"; enum BuildPlatform { Windows, @@ -72,6 +79,14 @@ enum BuildPlatform { Android, } +#[derive(Default)] +pub struct CommonBuildFlags { + locked: bool, + frozen: bool, + offline: bool, + profiling: bool, +} + pub fn print_help_and_exit(message: &str) -> ! { eprintln!("\n{message}"); eprintln!("{HELP_STR}"); @@ -176,7 +191,6 @@ fn main() { } else { Profile::Debug }; - let profiling = args.contains("--profiling"); let gpl = args.contains("--gpl"); let is_nightly = args.contains("--nightly"); let no_rebuild = args.contains("--no-rebuild"); @@ -184,6 +198,12 @@ fn main() { let keep_config = args.contains("--keep-config"); let link_stdcpp = !args.contains("--no-stdcpp"); let all_targets = args.contains("--all-targets"); + let common_build_flags = CommonBuildFlags { + locked: args.contains("--locked"), + frozen: args.contains("--frozen"), + offline: args.contains("--offline"), + profiling: args.contains("--profiling"), + }; let platform: Option = args.opt_value_from_str("--platform").unwrap(); let platform = platform.as_deref().map(|platform| match platform { @@ -210,7 +230,7 @@ fn main() { "prepare-deps" => { if let Some(platform) = platform { if matches!(platform, BuildPlatform::Android) { - dependencies::build_android_deps( + dependencies::android::build_deps( for_ci, all_targets, OpenXRLoadersSelection::All, @@ -221,18 +241,22 @@ fn main() { } else { dependencies::prepare_server_deps(platform, for_ci, !no_nvidia); - dependencies::build_android_deps( + dependencies::android::build_deps( for_ci, all_targets, OpenXRLoadersSelection::All, ); } } + "download-server-deps" => { + dependencies::download_server_deps(platform, for_ci, !no_nvidia) + } + "build-server-deps" => dependencies::build_server_deps(platform, !no_nvidia), "build-streamer" => { - build::build_streamer(profile, gpl, None, false, profiling, keep_config) + build::build_streamer(profile, gpl, None, common_build_flags, keep_config) } - "build-launcher" => build::build_launcher(profile, false), - "build-server-lib" => build::build_server_lib(profile, None, false), + "build-launcher" => build::build_launcher(profile, common_build_flags), + "build-server-lib" => build::build_server_lib(profile, None, common_build_flags), "build-client" => build::build_android_client(profile), "build-client-lib" => { build::build_android_client_core_lib(profile, link_stdcpp, all_targets) @@ -242,13 +266,13 @@ fn main() { } "run-streamer" => { if !no_rebuild { - build::build_streamer(profile, gpl, None, false, profiling, keep_config); + build::build_streamer(profile, gpl, None, common_build_flags, keep_config); } run_streamer(); } "run-launcher" => { if !no_rebuild { - build::build_launcher(profile, false); + build::build_launcher(profile, common_build_flags); } run_launcher(); } @@ -264,9 +288,9 @@ fn main() { "bump" => version::bump_version(version, is_nightly), "clippy" => { if for_ci { - ci::clippy_ci() + ci::clippy_ci(); } else { - clippy() + clippy(); } } "check-msrv" => version::check_msrv(), diff --git a/alvr/xtask/src/packaging.rs b/alvr/xtask/src/packaging.rs index eeea148ffa..aeb6d324d5 100644 --- a/alvr/xtask/src/packaging.rs +++ b/alvr/xtask/src/packaging.rs @@ -61,7 +61,16 @@ pub fn package_streamer( dependencies::prepare_server_deps(platform, skip_admin_priv, enable_nvenc); - build::build_streamer(Profile::Distribution, gpl, root, true, false, false); + build::build_streamer( + Profile::Distribution, + gpl, + root, + crate::CommonBuildFlags { + locked: true, + ..Default::default() + }, + false, + ); include_licenses(&afs::streamer_build_dir(), gpl); @@ -77,7 +86,13 @@ pub fn package_launcher() { sh.remove_path(afs::launcher_build_dir()).ok(); - build::build_launcher(Profile::Distribution, true); + build::build_launcher( + Profile::Distribution, + crate::CommonBuildFlags { + locked: true, + ..Default::default() + }, + ); include_licenses(&afs::launcher_build_dir(), false); @@ -108,7 +123,7 @@ pub fn package_client_openxr(flavor: ReleaseFlavor, skip_admin_priv: bool) { ReleaseFlavor::PicoStore => OpenXRLoadersSelection::OnlyPico, }; - dependencies::build_android_deps(skip_admin_priv, false, openxr_selection); + dependencies::android::build_deps(skip_admin_priv, false, openxr_selection); if !matches!(flavor, ReleaseFlavor::GitHub) { replace_client_openxr_manifest( diff --git a/wiki/Building-From-Source.md b/wiki/Building-From-Source.md index 6ade045b35..b4fa755c7b 100644 --- a/wiki/Building-From-Source.md +++ b/wiki/Building-From-Source.md @@ -58,8 +58,8 @@ cargo xtask prepare-deps --platform [your platform] [--gpl] [--no-nvidia] ``` * Replace `[your platform]` with your computer OS, either `windows` or `linux` -* **Windows only:** Use the `--gpl` flag if you want to download, build and bundle FFmpeg inside the ALVR streamer. Keep in mind that this is only needed for software encoding. As the name suggests, if you use this flag you can only redistribute the final package as GPLv2.0 licensed; because of the x264 encoder. -* **Linux only:** Use the `--no-nvidia` flag if you have a AMD gpu. +* Use the `--gpl` flag if you want to download, build and bundle FFmpeg, x264 inside the ALVR streamer. As the name suggests, if you use this flag you can only redistribute the final package as GPLv2.0 licensed. +* **Linux only:** Use the `--no-nvidia` flag if you have a AMD or Intel gpu. Next up is the proper build of the streamer. Run the following: @@ -67,7 +67,7 @@ Next up is the proper build of the streamer. Run the following: cargo xtask build-streamer --release [--gpl] ``` -**Windows only:** Again, the `--gpl` flag is needed only if you want to bundle FFmpeg. +Again, the `--gpl` flag is needed only if you want to bundle FFmpeg and x264. You can find the resulting package in `build/alvr_streamer_[your platform]`