From bdb38e64019ff6ff99c594130b1b138c1fe90c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Roff=C3=A9?= Date: Fri, 14 Nov 2025 00:07:52 +0100 Subject: [PATCH 1/3] fix: Normalize Windows header includes to lowercase for cross-compilation (#3100) Standardize all Windows API header includes to use lowercase filenames. This ensures compatibility when cross-compiling on case-sensitive filesystems (Linux with MinGW). (cherry picked from commit a99268801f9f49b55841c4dbd06578891757dd68) --- alvr/server_openvr/cpp/alvr_server/Utils.h | 10 +++++----- alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp | 2 +- .../platform/win32/d3d-render-utils/RenderUtils.cpp | 2 +- .../cpp/platform/win32/shared/d3drender.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/alvr/server_openvr/cpp/alvr_server/Utils.h b/alvr/server_openvr/cpp/alvr_server/Utils.h index 68a9dd5e31..d053d694e7 100644 --- a/alvr/server_openvr/cpp/alvr_server/Utils.h +++ b/alvr/server_openvr/cpp/alvr_server/Utils.h @@ -3,18 +3,18 @@ #include #ifdef _WIN32 #pragma warning(disable : 4005) -#include +#include #pragma warning(default : 4005) -#include -#include -#include #include #include #include #include #include +#include +#include +#include #define _USE_MATH_DEFINES -#include +#include #else #include #include diff --git a/alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp b/alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp index caf4979993..cb2f12c914 100644 --- a/alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp +++ b/alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp @@ -2,8 +2,8 @@ #include "../../alvr_server/Logger.h" #include "../../shared/backward.hpp" -#include #include +#include static LONG WINAPI handler(PEXCEPTION_POINTERS ptrs) { backward::StackTrace stacktrace; diff --git a/alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.cpp b/alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.cpp index d1e0cfa1bb..b6b30cc62f 100644 --- a/alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.cpp +++ b/alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.cpp @@ -1,6 +1,6 @@ #include "RenderUtils.h" -#include +#include using namespace std::string_literals; using Microsoft::WRL::ComPtr; diff --git a/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp b/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp index 30fc3c3cf3..49e21956cd 100644 --- a/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp +++ b/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp @@ -1,7 +1,7 @@ //===================== Copyright (c) Valve Corporation. All Rights Reserved. ====================== #include "d3drender.h" -#include -#include +#include +#include #pragma comment( lib, "dxgi.lib" ) #pragma comment( lib, "d3d11.lib" ) From 6641970e6518224391cbda1e01ca035d1c60ce6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Roff=C3=A9?= Date: Tue, 25 Nov 2025 16:32:51 +0100 Subject: [PATCH 2/3] fix: Miscellaneous fixups when building with clang-cl (#3116) * fix(server_openvr): Fix inputColorAdjust type Fixes this build error with clang-cl 19.1.7 cpp/platform/win32/FrameRender.cpp(787,15): error: non-constant-expression cannot be narrowed from type 'int' to 'uint32_t' (aka 'unsigned int') in initializer list [-Wc++11-narrowing] * fix(server_openvr): Remove useless D3D9Types.h include * fix(server_openvr): Normalize Windows link libraries to lowercase for cross-compilation Standardize all Windows API libraries to use lowercase filenames. This ensures compatibility when cross-compiling on case-sensitive filesystems (Linux with MinGW). * fix(server_openvr): Force usage of UuidFromStringA() RPC_CSTR isn't a wide string, so it the A version of UuidFromString() can be called explicitly. Fixes this build error with clang-cl 19.1.7 cpp/platform/win32/shared/d3drender.cpp(93,4): error: no matching function for call to 'UuidFromStringW' 93 | UuidFromString( ( RPC_CSTR ) "8c8f13b1-60eb-4b6a-a433-de86104115ac", &guid ); | ^~~~~~~~~~~~~~ .../kits/10/include/10.0.26100.0/shared/rpcdce.h(2775,24): note: expanded from macro 'UuidFromString' 2775 | #define UuidFromString UuidFromStringW | ^~~~~~~~~~~~~~~ .../kits/10/include/10.0.26100.0/shared/rpcdce.h(2769,1): note: candidate function not viable: no known conversion from 'RPC_CSTR' (aka 'unsigned char *') to 'RPC_WSTR' (aka 'unsigned short *') for 1st argument 2769 | UuidFromStringW ( | ^ 2770 | _In_opt_ RPC_WSTR StringUuid, | ~~~~~~~~~~~~~~~~~~~ (cherry picked from commit aa724975a2b70bc3e00e268de447c9f85ed62ceb) --- alvr/server_openvr/cpp/platform/win32/FrameRender.cpp | 2 +- alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp | 1 - alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp | 4 ++-- .../cpp/shared/amf/public/common/Windows/ThreadWindows.cpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/alvr/server_openvr/cpp/platform/win32/FrameRender.cpp b/alvr/server_openvr/cpp/platform/win32/FrameRender.cpp index 44e6797250..dedd32d7c2 100644 --- a/alvr/server_openvr/cpp/platform/win32/FrameRender.cpp +++ b/alvr/server_openvr/cpp/platform/win32/FrameRender.cpp @@ -674,7 +674,7 @@ bool FrameRender::RenderFrame( m_pD3DRender->GetContext()->OMSetBlendState(m_pBlendState.Get(), NULL, 0xffffffff); } - int inputColorAdjust = 0; + uint32_t inputColorAdjust = 0; if (Settings::Instance().m_enableHdr) { if (SRVDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB || SRVDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB diff --git a/alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp b/alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp index 7ebc1b13d3..49b9a75b05 100644 --- a/alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp +++ b/alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp @@ -14,7 +14,6 @@ #include #endif #include "NvEncoderD3D11.h" -#include #ifndef MAKEFOURCC #define MAKEFOURCC(a,b,c,d) (((unsigned int)a) | (((unsigned int)b)<< 8) | (((unsigned int)c)<<16) | (((unsigned int)d)<<24) ) diff --git a/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp b/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp index 49e21956cd..5cca134c09 100644 --- a/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp +++ b/alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp @@ -5,7 +5,7 @@ #pragma comment( lib, "dxgi.lib" ) #pragma comment( lib, "d3d11.lib" ) -#pragma comment( lib, "Rpcrt4.lib" ) +#pragma comment( lib, "rpcrt4.lib" ) #define Log( ... ) @@ -90,7 +90,7 @@ namespace public: CEventHelper() { - UuidFromString( ( RPC_CSTR ) "8c8f13b1-60eb-4b6a-a433-de86104115ac", &guid ); + UuidFromStringA( ( RPC_CSTR ) "8c8f13b1-60eb-4b6a-a433-de86104115ac", &guid ); EventRegister( &guid, nullptr, nullptr, &handle ); } diff --git a/alvr/server_openvr/cpp/shared/amf/public/common/Windows/ThreadWindows.cpp b/alvr/server_openvr/cpp/shared/amf/public/common/Windows/ThreadWindows.cpp index 94c9da9f76..1bc7b950f3 100644 --- a/alvr/server_openvr/cpp/shared/amf/public/common/Windows/ThreadWindows.cpp +++ b/alvr/server_openvr/cpp/shared/amf/public/common/Windows/ThreadWindows.cpp @@ -345,7 +345,7 @@ amf_pts AMF_CDECL_CALL amf_high_precision_clock() #endif } //------------------------------------------------------------------------------------------------- -#pragma comment (lib, "Winmm.lib") +#pragma comment (lib, "winmm.lib") static amf_uint32 timerPrecision = 1; void AMF_CDECL_CALL amf_increase_timer_precision() From 89ae5e176925493d7cafbdb65100564388b5cbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Roff=C3=A9?= Date: Mon, 24 Nov 2025 18:59:41 +0100 Subject: [PATCH 3/3] Implement cross build for Windows Requires clang-cl and Windows SDK. The implementation introduces an alternative codepath with a lot of code duplication. The goal is to show that seems to be required to make the build system able to cross compile. --- alvr/filesystem/src/lib.rs | 44 ++++++++++--- alvr/server_openvr/build.rs | 82 +++++++++++++++++++++++ alvr/xtask/src/build.rs | 128 +++++++++++++++++++++++++++++++++++- alvr/xtask/src/main.rs | 14 +++- 4 files changed, 257 insertions(+), 11 deletions(-) diff --git a/alvr/filesystem/src/lib.rs b/alvr/filesystem/src/lib.rs index d647aa94b2..79760bcbf3 100644 --- a/alvr/filesystem/src/lib.rs +++ b/alvr/filesystem/src/lib.rs @@ -106,6 +106,8 @@ pub struct Layout { // (linux only) directory where the vulkan layer manifest is saved pub vulkan_layer_manifest_dir: PathBuf, pub launcher_root: Option, + + pub platform: Option<&'static str>, } impl Layout { @@ -154,6 +156,7 @@ impl Layout { .and_then(|p| p.parent()) .and_then(|p| p.parent()) .map(|p| p.to_owned()), + platform: None, } } #[cfg(not(target_os = "linux"))] @@ -170,6 +173,25 @@ impl Layout { ufw_config_dir: root.to_owned(), vulkan_layer_manifest_dir: root.to_owned(), launcher_root: root.parent().and_then(|p| p.parent()).map(|p| p.to_owned()), + platform: None, + } + } + + pub fn new_cross_windows(root: &Path) -> Self { + Self { + executables_dir: root.to_owned(), + libraries_dir: root.to_owned(), + static_resources_dir: root.to_owned(), + config_dir: root.to_owned(), + log_dir: root.to_owned(), + openvr_driver_root_dir: root.to_owned(), + vrcompositor_wrapper_dir: root.to_owned(), + firewall_script_dir: root.to_owned(), + firewalld_config_dir: root.to_owned(), + ufw_config_dir: root.to_owned(), + vulkan_layer_manifest_dir: root.to_owned(), + launcher_root: root.parent().and_then(|p| p.parent()).map(|p| p.to_owned()), + platform: Some("win64"), } } @@ -228,15 +250,19 @@ impl Layout { } pub fn openvr_driver_lib_dir(&self) -> PathBuf { - let platform = if cfg!(windows) { - "win64" - } else if cfg!(target_os = "linux") { - "linux64" - } else if cfg!(target_os = "macos") { - "macos" - } else { - unimplemented!() - }; + let platform = self.platform.unwrap_or_else(|| { + // Those tests aren't "working" while cross compiling + // since this is called in build.rs, which is a host tool + if cfg!(windows) { + "win64" + } else if cfg!(target_os = "linux") { + "linux64" + } else if cfg!(target_os = "macos") { + "macos" + } else { + unimplemented!() + } + }); self.openvr_driver_root_dir.join("bin").join(platform) } diff --git a/alvr/server_openvr/build.rs b/alvr/server_openvr/build.rs index 2573d07e6d..b3fa214327 100644 --- a/alvr/server_openvr/build.rs +++ b/alvr/server_openvr/build.rs @@ -21,8 +21,90 @@ fn get_linux_x264_path() -> PathBuf { alvr_filesystem::deps_dir().join("linux/x264/alvr_build") } +fn cross_windows_build() { + let platform_name = "windows"; + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let platform_subpath = "cpp/platform/win32"; + + let common_iter = walkdir::WalkDir::new("cpp") + .into_iter() + .filter_entry(|entry| { + entry.file_name() != "tools" + && entry.file_name() != "platform" + && (platform_name != "macos" || entry.file_name() != "amf") + && (platform_name != "linux" || entry.file_name() != "amf") + }); + + let platform_iter = walkdir::WalkDir::new(platform_subpath).into_iter(); + + let cpp_paths = common_iter + .chain(platform_iter) + .filter_map(|maybe_entry| maybe_entry.ok()) + .map(|entry| entry.into_path()) + .collect::>(); + + let source_files_paths = cpp_paths.iter().filter(|path| { + path.extension() + .filter(|ext| { + let ext_str = ext.to_string_lossy(); + ext_str == "c" || ext_str == "cpp" + }) + .is_some() + }); + + let mut build = cc::Build::new(); + build + .cpp(true) + .std("c++17") + .files(source_files_paths) + .include(alvr_filesystem::workspace_dir().join("openvr/headers")) + .include("cpp") + .flag("/MD") + .flag("/EHsc") + .debug(false) // This is because we cannot link to msvcrtd (see below) + .flag("/permissive-") + .define("NOMINMAX", None) + .define("_WINSOCKAPI_", None) + .define("_MBCS", None) + .define("_MT", None); + + #[cfg(debug_assertions)] + build.define("ALVR_DEBUG_LOG", None); + + build.compile("bindings"); + + bindgen::builder() + .clang_arg("-xc++") + .header("cpp/alvr_server/bindings.h") + .derive_default(true) + .generate() + .unwrap() + .write_to_file(out_dir.join("bindings.rs")) + .unwrap(); + + println!( + "cargo:rustc-link-search=native={}", + alvr_filesystem::workspace_dir() + .join("openvr/lib/win64") + .to_string_lossy() + ); + println!("cargo:rustc-link-lib=openvr_api"); + + for path in cpp_paths { + println!("cargo:rerun-if-changed={}", path.to_string_lossy()); + } +} + fn main() { let platform_name = env::var("CARGO_CFG_TARGET_OS").unwrap(); + + let cross_to_windows = cfg!(not(target_os = "windows")) && platform_name == "windows"; + if cross_to_windows { + cross_windows_build(); + return; + } + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let platform_subpath = match platform_name.as_str() { diff --git a/alvr/xtask/src/build.rs b/alvr/xtask/src/build.rs index 4ec3ffa2dd..bd2bc1c3e8 100644 --- a/alvr/xtask/src/build.rs +++ b/alvr/xtask/src/build.rs @@ -4,7 +4,7 @@ use std::{ env, fmt::{self, Display, Formatter}, fs, - path::PathBuf, + path::{Path, PathBuf}, vec, }; use xshell::{cmd, Shell}; @@ -71,6 +71,132 @@ pub fn build_server_lib(profile: Profile, root: Option, reproducible: bo cmd!(sh, "cbindgen --output {out}").run().unwrap(); } +pub fn cross_build_windows_streamer( + profile: Profile, + root: Option, + reproducible: bool, + profiling: bool, + keep_config: bool, +) { + let sh = Shell::new().unwrap(); + + let cargo_target = "x86_64-pc-windows-msvc"; + let target_dir = afs::target_dir().join(cargo_target); + + println!("Cross compiling for Windows using target {cargo_target}"); + println!("Target dir: {}", target_dir.display()); + + let mut common_flags = vec!["--target", cargo_target]; + match profile { + Profile::Distribution => { + common_flags.push("--profile"); + common_flags.push("distribution"); + } + Profile::Release => common_flags.push("--release"), + Profile::Debug => (), + } + if reproducible { + common_flags.push("--locked"); + } + + let streamer_build_dir = afs::build_dir().join("alvr_streamer_windows"); + println!("Streamer build dir: {}", streamer_build_dir.display()); + let build_layout = Layout::new_cross_windows(&streamer_build_dir); + + let artifacts_dir = afs::target_dir() + .join(cargo_target) + .join(profile.to_string()); + println!("Artifacts dir: {}", artifacts_dir.display()); + + let common_flags_ref = &common_flags; + + let maybe_config = if keep_config { + fs::read_to_string(build_layout.session()).ok() + } else { + None + }; + + sh.remove_path(afs::streamer_build_dir()).ok(); + sh.create_dir(build_layout.openvr_driver_lib_dir()).unwrap(); + sh.create_dir(&build_layout.executables_dir).unwrap(); + + if let Some(config) = maybe_config { + fs::write(build_layout.session(), config).ok(); + } + + if let Some(root) = root { + sh.set_var("ALVR_ROOT_DIR", root); + } + + // build server + { + let profiling_flag = if profiling { + vec!["--features", "alvr_server_core/trace-performance"] + } else { + vec![] + }; + + let _push_guard = sh.push_dir(afs::crate_dir("server_openvr")); + cmd!(sh, "cargo build {common_flags_ref...} {profiling_flag...}") + .run() + .unwrap(); + + sh.copy_file( + artifacts_dir.join("alvr_server_openvr.dll"), + build_layout + .openvr_driver_lib_dir() + .join("driver_alvr_server.dll"), + ) + .unwrap(); + + sh.copy_file( + artifacts_dir.join("alvr_server_openvr.pdb"), + build_layout + .openvr_driver_lib_dir() + .join("alvr_server_openvr.pdb"), + ) + .unwrap(); + + sh.copy_file( + afs::workspace_dir().join("openvr/bin/win64/openvr_api.dll"), + build_layout.openvr_driver_lib_dir(), + ) + .unwrap(); + + // Bring along the c++ runtime + command::copy_recursive( + &sh, + &afs::crate_dir("server_openvr").join("cpp/bin/windows"), + &build_layout.openvr_driver_lib_dir(), + ) + .unwrap(); + } + + // Build dashboard + { + let _push_guard = sh.push_dir(afs::crate_dir("dashboard")); + cmd!(sh, "cargo build {common_flags_ref...}").run().unwrap(); + + let dashboard_fname = "ALVR Dashboard.exe"; + + sh.copy_file( + artifacts_dir.join("alvr_dashboard.exe"), + build_layout.executables_dir.join(dashboard_fname), + ) + .unwrap(); + } + + // copy static resources + { + // copy driver manifest + sh.copy_file( + afs::crate_dir("xtask").join("resources/driver.vrdrivermanifest"), + build_layout.openvr_driver_manifest(), + ) + .unwrap(); + } +} + pub fn build_streamer( profile: Profile, gpl: bool, diff --git a/alvr/xtask/src/main.rs b/alvr/xtask/src/main.rs index eedc4680bd..50ad36decc 100644 --- a/alvr/xtask/src/main.rs +++ b/alvr/xtask/src/main.rs @@ -193,6 +193,8 @@ fn main() { "android" => BuildPlatform::Android, _ => print_help_and_exit("Unrecognized platform"), }); + let cross_to_windows = + cfg!(not(target_os = "windows")) && matches!(platform, Some(BuildPlatform::Windows)); let version: Option = args.opt_value_from_str("--version").unwrap(); let root: Option = args.opt_value_from_str("--root").unwrap(); @@ -229,7 +231,17 @@ fn main() { } } "build-streamer" => { - build::build_streamer(profile, gpl, None, false, profiling, keep_config) + if cross_to_windows { + build::cross_build_windows_streamer( + profile, + None, + false, + profiling, + keep_config, + ) + } else { + build::build_streamer(profile, gpl, None, false, profiling, keep_config) + } } "build-launcher" => build::build_launcher(profile, false), "build-server-lib" => build::build_server_lib(profile, None, false),