diff --git a/.ci/build-windows-clang-cl.ps1 b/.ci/build-windows-clang-cl.ps1 new file mode 100644 index 0000000000..c7a8a0bbad --- /dev/null +++ b/.ci/build-windows-clang-cl.ps1 @@ -0,0 +1,139 @@ +# .ci/build-windows-clang-cl.ps1 +# Enable strict error handling +$ErrorActionPreference = "Stop" + +trap { + Write-Host "ERROR: $($_.Exception.Message)" + exit 1 +} + +Write-Host "Starting RPCS3 build (PowerShell script)" + +# Automatically find clang_rt.builtins-x86_64.lib +Write-Host "Searching for clang_rt.builtins-x86_64.lib ..." +$clangBuiltinsLibPath = Get-ChildItem -Path "C:/Program Files/LLVM/lib/clang" -Recurse -Filter "clang_rt.builtins-x86_64.lib" -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match "windows\\clang_rt\.builtins-x86_64\.lib$" } | + Select-Object -First 1 + +if (-not $clangBuiltinsLibPath) { + Write-Error "Could not find clang_rt.builtins-x86_64.lib in LLVM installation." + exit 1 +} +function Get-ShortPath([string]$path) { + $fso = New-Object -ComObject Scripting.FileSystemObject + return $fso.GetFolder($path).ShortPath +} + +$clangBuiltinsDir = Split-Path -Parent $clangBuiltinsLibPath.FullName +$clangBuiltinsDirShort = Get-ShortPath $clangBuiltinsDir +$clangBuiltinsLib = Split-Path -Leaf $clangBuiltinsLibPath.FullName +$clangPath = "C:/Program Files/LLVM/bin" + +Write-Host "Found Clang builtins library: $clangBuiltinsLib in $clangBuiltinsDir or short $clangBuiltinsDirShort" + +# Get Windows Kits root from registry +$kitsRoot = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots" -Name "KitsRoot10" +$kitsRootPath = $kitsRoot.KitsRoot10 + +# Search for mt.exe in x64 SDK bin directories +Write-Host "Searching for mt.exe ..." +$mtPath = Get-ChildItem -Path "$kitsRootPath\bin" -Recurse -Filter "mt.exe" -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match "\\x64\\mt\.exe$" } | + Sort-Object FullName -Descending | + Select-Object -First 1 + +if (-not $mtPath) { + Write-Error "Could not find mt.exe in Windows Kits directories." + exit 1 +} + +$mtExePath = $mtPath.FullName + +Write-Host "Found mt.exe at: $mtExePath" + +$VcpkgRoot="$(Get-Location)/vcpkg" +$VcpkgTriplet=$env:VCPKG_TRIPLET +$VcpkgInclude="$VcpkgRoot/installed/$VcpkgTriplet/include" +$VcpkgLib="$VcpkgRoot/installed/$VcpkgTriplet/lib" + +# Configure git safe directory +Write-Host "Configuring git safe directory" +& git config --global --add safe.directory '*' + +# Initialize submodules except certain ones +Write-Host "Initializing submodules" +$excludedSubs = @('llvm','opencv','ffmpeg','FAudio','zlib','libpng','feralinteractive') + +# Get submodule paths excluding those in $excludedSubs +$submodules = Select-String -Path .gitmodules -Pattern 'path = (.+)' | ForEach-Object { + $_.Matches[0].Groups[1].Value +} | Where-Object { + $path = $_ + -not ($excludedSubs | Where-Object { $path -like "*$_*" }) +} + +Write-Host "Updating submodules: $($submodules -join ', ')" +& git submodule update --init --quiet $submodules + +# Create and enter build directory +Write-Host "Creating build directory" +if (!(Test-Path build)) { + New-Item -ItemType Directory -Path build | Out-Null +} +Set-Location build +Write-Host "Changed directory to: $(Get-Location)" + +# Run CMake with Ninja generator and required flags +Write-Host "Running CMake configuration" +& cmake .. ` + -G Ninja ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_C_COMPILER="$clangPath/clang-cl.exe" ` + -DCMAKE_CXX_COMPILER="$clangPath/clang-cl.exe" ` + -DCMAKE_LINKER="$clangPath/lld-link.exe" ` + -DCMAKE_INSTALL_PREFIX=/usr ` + -DCMAKE_TOOLCHAIN_FILE="$VcpkgRoot/scripts/buildsystems/vcpkg.cmake" ` + -DCMAKE_EXE_LINKER_FLAGS="/LIBPATH:$clangBuiltinsDirShort /defaultlib:$clangBuiltinsLib" ` + -DCMAKE_MT="$mtExePath" ` + -DUSE_NATIVE_INSTRUCTIONS=OFF ` + -DUSE_PRECOMPILED_HEADERS=OFF ` + -DVCPKG_TARGET_TRIPLET="$VcpkgTriplet" ` + -DFFMPEG_INCLUDE_DIR="$VcpkgInclude" ` + -DFFMPEG_LIBAVCODEC="$VcpkgLib/avcodec.lib" ` + -DFFMPEG_LIBAVFORMAT="$VcpkgLib/avformat.lib" ` + -DFFMPEG_LIBAVUTIL="$VcpkgLib/avutil.lib" ` + -DFFMPEG_LIBSWSCALE="$VcpkgLib/swscale.lib" ` + -DFFMPEG_LIBSWRESAMPLE="$VcpkgLib/swresample.lib" ` + -DUSE_SYSTEM_CURL=OFF ` + -DUSE_FAUDIO=OFF ` + -DUSE_SDL=ON ` + -DUSE_SYSTEM_SDL=OFF ` + -DUSE_SYSTEM_FFMPEG=ON ` + -DUSE_SYSTEM_OPENCV=ON ` + -DUSE_SYSTEM_OPENAL=OFF ` + -DUSE_SYSTEM_LIBPNG=ON ` + -DUSE_DISCORD_RPC=ON ` + -DOpenGL_GL_PREFERENCE=LEGACY ` + -DWITH_LLVM=ON ` + -DSTATIC_LINK_LLVM=ON ` + -DBUILD_RPCS3_TESTS=OFF ` + -DRUN_RPCS3_TESTS=OFF +Write-Host "CMake configuration complete" + +# Build with ninja +Write-Host "Starting build with Ninja..." +& ninja +if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed with exit code $LASTEXITCODE" + exit 1 +} + +Write-Host "Build succeeded" + +# Go back to root directory +Set-Location .. +Write-Host "Returned to root directory: $(Get-Location)" + +# Deploy if build succeeded +Write-Host "Running deployment script" +& .ci/deploy-windows-clang-cl.sh x86_64 diff --git a/.ci/deploy-windows-clang-cl.sh b/.ci/deploy-windows-clang-cl.sh new file mode 100644 index 0000000000..2e8f556342 --- /dev/null +++ b/.ci/deploy-windows-clang-cl.sh @@ -0,0 +1,29 @@ +#!/bin/sh -ex + +cd build || exit 1 + +CPU_ARCH="${1:-x86_64}" + +echo "Deploying rpcs3 windows clang-cl $CPU_ARCH" + +# BUILD_blablabla is CI specific, so we wrap it for portability +ARTIFACT_DIR=$(cygpath -u "$BUILD_ARTIFACTSTAGINGDIRECTORY") + +# Prepare compatibility and SDL database for packaging +mkdir ./bin/config +mkdir ./bin/config/input_configs +curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt +curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat + +# Package artifacts +7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/* + +# Generate sha256 hashes +# Write to file for GitHub releases +sha256sum "$BUILD" | awk '{ print $1 }' | tee "$BUILD.sha256" +echo "$(cat "$BUILD.sha256");$(stat -c %s "$BUILD")B" > GitHubReleaseMessage.txt + +# Move files to publishing directory +mkdir -p "$ARTIFACT_DIR" +cp -- "$BUILD" "$ARTIFACT_DIR" +cp -- "$BUILD.sha256" "$ARTIFACT_DIR" diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 1981f78edb..54791a5184 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -5,8 +5,6 @@ defaults: shell: bash on: push: - branches: - - master # Only trigger push event on 'master' branch pull_request: workflow_dispatch: @@ -404,6 +402,106 @@ jobs: compression-level: 0 if-no-files-found: error + Windows_Build_ClangCL: + if: always() + name: RPCS3 Windows Clang-CL + runs-on: windows-2025 + strategy: + matrix: + include: + - compiler: clangcl + arch: win64 + env: + CCACHE_DIR: 'C:\ccache' + VCPKG_TRIPLET: x64-windows-release + steps: + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Clone vcpkg + run: git clone https://github.com/microsoft/vcpkg.git + + - name: Bootstrap vcpkg + shell: pwsh + run: .\vcpkg\bootstrap-vcpkg.bat + + - name: Restore vcpkg cache + uses: actions/cache/restore@main + id: restore-vcpkg-cache + with: + path: | + vcpkg/installed + vcpkg/buildtrees + key: vcpkg-${{ runner.os }}-${{ matrix.compiler }}-${{ runner.arch }}-${{ github.run_id }} + restore-keys: vcpkg-${{ runner.os }}-${{ matrix.compiler }}-${{ runner.arch }}- + + - name: Install dependencies with vcpkg + shell: pwsh + run: | + .\vcpkg\vcpkg.exe install ` + ffmpeg[avcodec,avformat,swscale,swresample] ` + llvm ` + opencv ` + qtbase ` + qtsvg ` + qtmultimedia ` + zlib ` + vulkan ` + libpng ` + --triplet $env:VCPKG_TRIPLET ` + --clean-after-build + + - name: Save vcpkg cache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: | + vcpkg/installed + vcpkg/buildtrees + key: ${{ steps.restore-vcpkg-cache.outputs.cache-primary-key }} + + - name: Add LLVM and Ninja to PATH + shell: pwsh + run: | + Add-Content -Path $env:GITHUB_PATH -Value "C:\Program Files\LLVM\bin" + Add-Content -Path $env:GITHUB_PATH -Value "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin" + Add-Content -Path $env:GITHUB_PATH -Value "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" + Add-Content -Path $env:GITHUB_PATH -Value "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja" + + - name: Restore build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{ github.run_id }} + restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- + + - name: Setup CI Variables + run: .ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }} + + - name: Build RPCS3 + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + powershell -ExecutionPolicy Bypass -File .ci/build-windows-clang-cl.ps1 + + - name: Save build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} + + - name: Upload artifacts + uses: actions/upload-artifact@main + with: + name: RPCS3 for Windows (clang-cl) + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + compression-level: 0 + if-no-files-found: error + FreeBSD_Build: # Only run push event on master branch of main repo, but run all PRs if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index d1295886d8..b3d28a1a3a 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -58,9 +58,14 @@ if(WITH_LLVM) else() message(STATUS "Using prebuilt or system LLVM") - if (LLVM_DIR AND NOT IS_ABSOLUTE "${LLVM_DIR}") - # change relative LLVM_DIR to be relative to the source dir - set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR}) + if (LLVM_DIR) + message(STATUS "LLVM_DIR: ${LLVM_DIR}") + + if (NOT IS_ABSOLUTE "${LLVM_DIR}") + # change relative LLVM_DIR to be relative to the source dir + set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR}) + message(STATUS "Changed LLVM_DIR to relative path: ${LLVM_DIR}") + endif() endif() find_package(LLVM CONFIG) diff --git a/3rdparty/opencv/CMakeLists.txt b/3rdparty/opencv/CMakeLists.txt index 1103f3be94..93b9521089 100644 --- a/3rdparty/opencv/CMakeLists.txt +++ b/3rdparty/opencv/CMakeLists.txt @@ -4,7 +4,11 @@ set(OPENCV_TARGET 3rdparty_dummy_lib PARENT_SCOPE) if (USE_SYSTEM_OPENCV) message(STATUS "RPCS3: using system OpenCV") - find_package(OpenCV COMPONENTS core photo) + find_package(OpenCV CONFIG COMPONENTS core photo) + + if(NOT OPENCV_FOUND) + find_package(OpenCV COMPONENTS core photo) + endif() if(OPENCV_FOUND) message(STATUS "RPCS3: found system OpenCV") diff --git a/3rdparty/wolfssl/CMakeLists.txt b/3rdparty/wolfssl/CMakeLists.txt index cf1a66a1f5..ff9e95e4ab 100644 --- a/3rdparty/wolfssl/CMakeLists.txt +++ b/3rdparty/wolfssl/CMakeLists.txt @@ -22,5 +22,17 @@ else() add_subdirectory(wolfssl EXCLUDE_FROM_ALL) - target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H) + target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP WOLFSSL_NO_OPTIONS_H) + + if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # Disable 128-bit Math + set(WOLFSSL_ASM ON CACHE BOOL "" FORCE) + set(WOLFSSL_FAST_MATH OFF CACHE BOOL "" FORCE) + target_compile_definitions(wolfssl PUBLIC WOLFSSL_SP_NO_128BIT FP_MAX_BITS=4096) + + # Disable warnings + target_compile_options(wolfssl PRIVATE /w) + else() + target_compile_definitions(wolfssl PUBLIC FP_MAX_BITS=8192) + endif() endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b2a84ee6..34e535eb2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,9 @@ if(MSVC) message(AUTHOR_WARNING "Debug build currently can not work with static CRT.") endif() endif() - add_compile_options(/MP) + if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(/MP) + endif() endif() if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index c628a90a8a..189773731e 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -525,7 +525,7 @@ void fmt_class_string::format(std::string& out, u64 arg) return; } -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) fmt::append(out, "0x%016llx%016llx", num.hi, num.lo); #else fmt::append(out, "0x%016llx%016llx", static_cast(num >> 64), static_cast(num)); diff --git a/buildfiles/cmake/ConfigureCompiler.cmake b/buildfiles/cmake/ConfigureCompiler.cmake index d59d987b9c..7900c15797 100644 --- a/buildfiles/cmake/ConfigureCompiler.cmake +++ b/buildfiles/cmake/ConfigureCompiler.cmake @@ -1,11 +1,20 @@ # Check and configure compiler options for RPCS3 if(MSVC) - add_compile_options(/Zc:throwingNew- /constexpr:steps16777216) + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + check_cxx_compiler_flag("-msse -msse2 -mcx16" COMPILER_X86) + if (COMPILER_X86) + add_compile_options(-msse -msse2 -mcx16 -mavx -mavx2 -mavx512f -mavx512vbmi -mavx512vl -mavx512dq -mavx512vpopcntdq -maes -mrtm -mpclmul -mmwaitx -mwaitpkg) + endif() + add_link_options(/dynamicbase) + else() + add_compile_options(/Zc:throwingNew- /constexpr:steps16777216) + add_link_options(/DYNAMICBASE) + endif() + add_compile_definitions( _CRT_SECURE_NO_DEPRECATE=1 _CRT_NON_CONFORMING_SWPRINTFS=1 _SCL_SECURE_NO_WARNINGS=1 NOMINMAX _ENABLE_EXTENDED_ALIGNED_STORAGE=1 _HAS_EXCEPTIONS=0) - add_link_options(/DYNAMICBASE) #TODO: Some of these could be cleaned up add_compile_options(/wd4805) # Comparing boolean and int diff --git a/rpcs3/Crypto/aesni.cpp b/rpcs3/Crypto/aesni.cpp index 05bb65fe7f..ba55828a7f 100644 --- a/rpcs3/Crypto/aesni.cpp +++ b/rpcs3/Crypto/aesni.cpp @@ -197,6 +197,21 @@ void aesni_gcm_mult( unsigned char c[16], const unsigned char b[16] ) { #if defined(POLARSSL_HAVE_MSVC_X64_INTRINSICS) +#ifdef __clang__ + __m128i xa, xb, m0, m1, x10, x32, r; + + xa[1] = _byteswap_uint64( *((unsigned __int64*)a + 0) ); + xa[0] = _byteswap_uint64( *((unsigned __int64*)a + 1) ); + xb[1] = _byteswap_uint64( *((unsigned __int64*)b + 0) ); + xb[0] = _byteswap_uint64( *((unsigned __int64*)b + 1) ); + + clmul256( xa, xb, &m0, &m1 ); + sll256( m0, m1, &x10, &x32 ); + r = reducemod128( x10, x32 ); + + *((unsigned __int64*)c + 0) = _byteswap_uint64( r[1] ); + *((unsigned __int64*)c + 1) = _byteswap_uint64( r[0] ); +#else __m128i xa, xb, m0, m1, x10, x32, r; xa.m128i_u64[1] = _byteswap_uint64( *((unsigned __int64*)a + 0) ); @@ -210,6 +225,7 @@ void aesni_gcm_mult( unsigned char c[16], *((unsigned __int64*)c + 0) = _byteswap_uint64( r.m128i_u64[1] ); *((unsigned __int64*)c + 1) = _byteswap_uint64( r.m128i_u64[0] ); +#endif #else unsigned char aa[16], bb[16], cc[16]; size_t i; diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index 14eaa1409d..bccff669bd 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -9,7 +9,7 @@ extern bool g_use_rtm; extern u64 g_rtm_tx_limit1; #ifdef _M_X64 -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) extern "C" { u32 _xbegin(); @@ -242,7 +242,7 @@ namespace utils constexpr u32 popcnt128(const u128& v) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) return popcnt64(v.lo) + popcnt64(v.hi); #else return popcnt64(v) + popcnt64(v >> 64); @@ -274,7 +274,7 @@ namespace utils inline s64 div128(s64 high, s64 low, s64 divisor, s64* remainder = nullptr) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) s64 rem = 0; s64 r = _div128(high, low, divisor, &rem); @@ -296,7 +296,7 @@ namespace utils inline u64 udiv128(u64 high, u64 low, u64 divisor, u64* remainder = nullptr) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) u64 rem = 0; u64 r = _udiv128(high, low, divisor, &rem); @@ -316,7 +316,7 @@ namespace utils return r; } -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) inline u128 operator/(u128 lhs, u64 rhs) { u64 rem = 0; @@ -326,7 +326,7 @@ namespace utils constexpr u32 ctz128(u128 arg) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) if (!arg.lo) return std::countr_zero(arg.hi) + 64u; else @@ -341,7 +341,7 @@ namespace utils constexpr u32 clz128(u128 arg) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) if (arg.hi) return std::countl_zero(arg.hi); else @@ -470,6 +470,6 @@ namespace utils using utils::busy_wait; -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) using utils::operator/; #endif diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index 690f51c0e7..2ef140e941 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -249,13 +249,13 @@ public: } }; -#if defined(ARCH_X64) && !defined(_MSC_VER) +#if defined(ARCH_X64) && !defined(_MSC_VER) && !defined(__m128) using __m128i = long long __attribute__((vector_size(16))); using __m128d = double __attribute__((vector_size(16))); using __m128 = float __attribute__((vector_size(16))); #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) || defined(__clang__) using u128 = __uint128_t; using s128 = __int128_t; #else @@ -266,8 +266,8 @@ extern "C" union __m128i; struct __m128d; - uchar _addcarry_u64(uchar, u64, u64, u64*); - uchar _subborrow_u64(uchar, u64, u64, u64*); + constexpr uchar _addcarry_u64(uchar, u64, u64, u64*); + constexpr uchar _subborrow_u64(uchar, u64, u64, u64*); u64 __shiftleft128(u64, u64, uchar); u64 __shiftright128(u64, u64, uchar); u64 _umul128(u64, u64, u64*); @@ -579,7 +579,7 @@ struct s128 : u128 // Optimization for u64*u64=u128 constexpr u128 u128_from_mul(u64 a, u64 b) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) if (!std::is_constant_evaluated()) { u64 hi;