This commit is contained in:
Rhener Furtado 2026-08-07 13:47:03 +00:00 committed by GitHub
commit d7da74dcec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 212 additions and 17 deletions

View File

@ -204,6 +204,84 @@ jobs:
name: cemu-installer-windows-x64
path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-x64-installer.exe
build-windows-arm64:
continue-on-error: true
runs-on: windows-11-arm
steps:
- name: "Checkout repo"
uses: actions/checkout@v6
with:
submodules: "recursive"
- name: Setup release mode parameters
run: |
echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Build mode is release"
- name: Setup build flags for version
if: ${{ inputs.next_version_major != '' }}
run: |
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
- name: "Ensure clang-cl (LLVM) toolset is installed in Visual Studio"
run: |
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vsPath = & $vswhere -latest -products * -property installationPath
$setup = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe"
$modifyArgs = "modify --installPath `"$vsPath`" --add Microsoft.VisualStudio.Component.VC.Llvm.Clang --add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset --quiet --norestart"
Start-Process -FilePath $setup -Wait -ArgumentList $modifyArgs
- name: "Setup MSVC environment (arm64)"
uses: ilammy/msvc-dev-cmd@v1
with:
arch: arm64
- name: "Setup Ninja"
run: choco install ninja -y
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: |
./dependencies/vcpkg/bootstrap-vcpkg.bat
- name: 'Setup NuGet Credentials for vcpkg'
shell: 'bash'
run: |
`./dependencies/vcpkg/vcpkg.exe fetch nuget | tail -n 1` \
sources add \
-source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
-storepasswordincleartext \
-name "GitHub" \
-username "${{ github.repository_owner }}" \
-password "${{ secrets.GITHUB_TOKEN }}"
`./dependencies/vcpkg/vcpkg.exe fetch nuget | tail -n 1` \
setapikey "${{ secrets.GITHUB_TOKEN }}" \
-source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
- name: "cmake"
run: |
echo "[INFO] BUILD_FLAGS: ${{ env.BUILD_FLAGS }}"
cmake -S . -B build -G Ninja ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_LINKER_TYPE=MSVC -DVCPKG_INSTALL_OPTIONS="--clean-after-build"
- name: "Build Cemu"
run: |
cmake --build build
- name: Prepare artifact
run: Rename-Item bin/Cemu_release.exe Cemu.exe
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: cemu-bin-windows-arm64
path: ./bin/Cemu.exe
build-macos:
runs-on: macos-14
strategy:

View File

@ -42,6 +42,36 @@ Instructions for Visual Studio 2022:
Any other IDE should also work as long as it has CMake and MSVC support. CLion and Visual Studio Code have been confirmed to work.
### Windows on ARM64 (Snapdragon)
Native ARM64 builds are supported and use **clang-cl**. The recompiler uses the
AArch64 backend (`xbyak_aarch64`), so it runs natively rather than under x64
emulation.
Prerequisites (Visual Studio 2022 or Build Tools 2022 with these components):
- MSVC v143 - ARM64/ARM64EC build tools
- C++ Clang Compiler for Windows and *MSBuild support for LLVM (clang-cl) toolset*
- C++ CMake tools for Windows (CMake 3.29 or newer)
- Windows 11 SDK
The Visual Studio CMake generator cannot assemble the codebase, so build from the
command line with Ninja. Open an **"ARM64 Native Tools Command Prompt for VS 2022"**
(or run `vcvarsarm64.bat`), then:
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=release ^
-DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl ^
-DCMAKE_LINKER_TYPE=MSVC
cmake --build build
```
`-DCMAKE_LINKER_TYPE=MSVC` makes clang link with the MSVC `link.exe` (required so
that vcpkg's static libraries link correctly). The resulting binary is
`bin/Cemu_release.exe`. Note: on ARM64 the bundled H.264 decoder (ih264d) is built
as portable C, and libusb is used via the standard vcpkg package.
## Linux
To compile Cemu, a recent enough compiler and STL with C++20 support is required! Clang-15 or higher is what we recommend.

View File

@ -51,7 +51,17 @@ if (ENABLE_VCPKG)
# CONFIG option
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
if (WIN32)
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "")
# Auto-select the vcpkg triplet based on the target architecture.
# Override on the command line with -DVCPKG_TARGET_TRIPLET=...
if (NOT DEFINED VCPKG_TARGET_TRIPLET)
if (CMAKE_GENERATOR_PLATFORM MATCHES "(ARM64|arm64)" OR
CMAKE_SYSTEM_PROCESSOR MATCHES "(ARM64|arm64|aarch64)" OR
"$ENV{VSCMD_ARG_TGT_ARCH}" STREQUAL "arm64")
set(VCPKG_TARGET_TRIPLET "arm64-windows-static" CACHE STRING "")
else()
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "")
endif()
endif()
endif()
endif()
@ -72,9 +82,13 @@ add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# enable link time optimization for release builds
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
# enable link time optimization for release builds.
# Skip it for clang-cl on Windows: IPO forces the LLVM linker (lld-link), which
# cannot consume the MSVC /GL objects inside vcpkg static libraries.
if(NOT (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
endif()
if (MSVC)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT CemuBin)
@ -256,11 +270,34 @@ add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
if (CMAKE_OSX_ARCHITECTURES)
set(CEMU_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
elseif (WIN32 AND (CMAKE_GENERATOR_PLATFORM MATCHES "(ARM64|arm64)" OR "$ENV{VSCMD_ARG_TGT_ARCH}" STREQUAL "arm64"))
# Windows ARM64: the host toolchain / emulated shell can misreport the
# processor, so trust the requested target platform instead.
set(CEMU_ARCHITECTURE "aarch64")
else()
set(CEMU_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
if(MSVC)
# Match the static CRT used by the rest of the project (vcpkg static triplet)
set_property(TARGET xbyak_aarch64 PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# clang-cl emits calls to compiler-rt builtins (e.g. __udivti3 for 128-bit
# integer division) that lld-link does not link automatically. Link the
# clang_rt.builtins library that ships next to the compiler.
get_filename_component(_llvm_bin "${CMAKE_CXX_COMPILER}" DIRECTORY)
get_filename_component(_llvm_root "${_llvm_bin}" DIRECTORY)
file(GLOB _clang_rt_builtins "${_llvm_root}/lib/clang/*/lib/windows/clang_rt.builtins-aarch64.lib")
if(_clang_rt_builtins)
list(GET _clang_rt_builtins 0 _clang_rt_builtins)
link_libraries("${_clang_rt_builtins}")
message(STATUS "Linking compiler-rt builtins: ${_clang_rt_builtins}")
else()
message(WARNING "clang_rt.builtins-aarch64.lib not found; link may fail with undefined __udivti3")
endif()
endif()
endif()
find_package(ZArchive QUIET)

View File

@ -123,7 +123,12 @@ else()
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
# Normalize the architecture string so matching is case-insensitive.
# MSVC/CMake reports "ARM64"/"AMD64" (uppercase) whereas the checks below use
# the lowercase names emitted by GCC/Clang toolchains.
string(TOLOWER "${IH264D_ARCHITECTURE}" IH264D_ARCHITECTURE)
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64")
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE
@ -147,6 +152,18 @@ target_sources(ih264d PRIVATE
"decoder/x86/ih264d_function_selector_ssse3.c"
)
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
if(MSVC)
# Windows on ARM64: build the generic C decoder. The NEON .s files are GAS syntax
# and use ELF-only ":got:" relocations that cannot be assembled for COFF targets,
# so we fall back to portable C (only affects H.264/FMV decode performance).
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
target_sources(ih264d PRIVATE
"common/armv8/ih264_platform_macros.h"
"decoder/arm/ih264d_function_selector.c"
)
target_compile_definitions(ih264d PRIVATE ARMV8 DISABLE_NEON DEFAULT_ARCH=D_ARCH_ARM_NONEON)
else()
enable_language( C CXX ASM )
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
@ -189,6 +206,7 @@ endif()
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
endif()
else()
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
endif()

View File

@ -61,7 +61,7 @@ void ih264d_init_function_ptr(dec_struct_t *ps_codec)
ih264d_init_function_ptr_generic(ps_codec);
switch(e_proc_arch)
{
#if defined(ARMV8)
#if defined(ARMV8) && !defined(DISABLE_NEON)
case ARCH_ARMV8_GENERIC:
default:
ih264d_init_function_ptr_av8(ps_codec);

View File

@ -2,7 +2,7 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges>

View File

@ -668,7 +668,12 @@ endif()
if (ENABLE_LIBUSB)
if (ENABLE_VCPKG)
if(WIN32)
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe")
# pkgconf is a host tool; locate it under whichever host triplet vcpkg
# used (x64-windows on Intel hosts, arm64-windows on ARM64 hosts).
file(GLOB _cemu_pkgconf "${VCPKG_INSTALLED_DIR}/*/tools/pkgconf/pkgconf.exe")
if(_cemu_pkgconf)
list(GET _cemu_pkgconf 0 PKG_CONFIG_EXECUTABLE)
endif()
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)

View File

@ -199,7 +199,7 @@ void debugger_updateMemoryBreakpoint(DebuggerBreakpoint* bp)
{
std::vector<std::thread::native_handle_type> schedulerThreadHandles = coreinit::OSGetSchedulerThreads();
#if BOOST_OS_WINDOWS
#if BOOST_OS_WINDOWS && defined(ARCH_X86_64)
s_debuggerState.activeMemoryBreakpoint = bp;
for (auto& hThreadNH : schedulerThreadHandles)
{

View File

@ -146,7 +146,7 @@ uint32 LatteTexture_CalculateTextureDataHash(LatteTexture* hostTexture)
bool isCompressedFormat = hostTexture->IsCompressedFormat();
if( isCompressedFormat == false )
{
#if BOOST_OS_WINDOWS
#if BOOST_OS_WINDOWS && defined(ARCH_X86_64)
if (g_CPUFeatures.x86.avx2)
{
__m256i h256 = { 0 };

View File

@ -209,6 +209,7 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
// register info
sprintf(dumpLine, "\n");
cemuLog_writePlainToLog(dumpLine);
#if defined(ARCH_X86_64)
sprintf(dumpLine, "RAX=%016I64x RBX=%016I64x RCX=%016I64x RDX=%016I64x\n", context->Rax, context->Rbx, context->Rcx, context->Rdx);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "RSP=%016I64x RBP=%016I64x RDI=%016I64x RSI=%016I64x\n", context->Rsp, context->Rbp, context->Rdi, context->Rsi);
@ -217,6 +218,17 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", context->R12, context->R13, context->R14, context->R15);
cemuLog_writePlainToLog(dumpLine);
#else // AArch64
for (int i = 0; i < 28; i += 4)
{
sprintf(dumpLine, "X%-2d=%016I64x X%-2d=%016I64x X%-2d=%016I64x X%-2d=%016I64x\n",
i, context->X[i], i + 1, context->X[i + 1], i + 2, context->X[i + 2], i + 3, context->X[i + 3]);
cemuLog_writePlainToLog(dumpLine);
}
sprintf(dumpLine, "X28=%016I64x FP =%016I64x LR =%016I64x SP =%016I64x PC =%016I64x\n",
context->X[28], context->Fp, context->Lr, context->Sp, context->Pc);
cemuLog_writePlainToLog(dumpLine);
#endif
CrashLog_SetOutputChannels(false, true);
ExceptionHandler_LogGeneralInfo();
@ -264,11 +276,15 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
if (r != EXCEPTION_CONTINUE_SEARCH)
return r;
#if defined(ARCH_X86_64)
if (GetBits(pExceptionInfo->ContextRecord->Dr6, 0, 1) || GetBits(pExceptionInfo->ContextRecord->Dr6, 1, 1))
debugger_handleSingleStepException(pExceptionInfo->ContextRecord->Dr6);
else if (GetBits(pExceptionInfo->ContextRecord->Dr6, 2, 1) || GetBits(pExceptionInfo->ContextRecord->Dr6, 3, 1))
g_gdbstub->HandleAccessException(pExceptionInfo->ContextRecord->Dr6);
return EXCEPTION_CONTINUE_EXECUTION;
#else // AArch64: hardware debug registers (Dr6) not available; debugger single-step unsupported for now
return EXCEPTION_CONTINUE_SEARCH;
#endif
}
return EXCEPTION_CONTINUE_SEARCH;
}

View File

@ -268,6 +268,15 @@ typedef union _LARGE_INTEGER {
inline T& operator^= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) ^= static_cast<std::underlying_type<T>::type>(b) ); }
#endif
#if defined(_MSC_VER) && defined(_M_ARM64)
// _umul128 is an x86-64 intrinsic and is not provided by <intrin.h> on AArch64
inline uint64 _umul128(uint64 multiplier, uint64 multiplicand, uint64 *highProduct) {
unsigned __int128 x = (unsigned __int128)multiplier * (unsigned __int128)multiplicand;
*highProduct = (uint64)(x >> 64);
return (uint64)(x & 0xFFFFFFFFFFFFFFFF);
}
#endif
template<typename T>
inline T GetBits(T value, uint32 index, uint32 numBits)
{
@ -343,10 +352,10 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor,
FORCE_INLINE int BSF(uint32 v) // returns index of first bit set, counting from LSB. If v is 0 then result is undefined
{
#if defined(_MSC_VER)
#if defined(__GNUC__) || defined(__clang__)
return __builtin_ctz(v); // clang-cl also defines _MSC_VER, so check this first
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
return _tzcnt_u32(v); // TZCNT requires BMI1. But if not supported it will execute as BSF
#elif defined(__GNUC__) || defined(__clang__)
return __builtin_ctz(v);
#else
return std::countr_zero(v);
#endif
@ -610,12 +619,14 @@ inline uint32 GetTitleIdLow(uint64 titleId)
return titleId & 0xFFFFFFFF;
}
#if defined(__GNUC__)
#define memcpy_dwords(__dest, __src, __numDwords) memcpy((__dest), (__src), (__numDwords) * sizeof(uint32))
#define memcpy_qwords(__dest, __src, __numQwords) memcpy((__dest), (__src), (__numQwords) * sizeof(uint64))
#else
// __movsd/__movsq are x86-only MSVC intrinsics; use memcpy everywhere else
// (clang-cl does not define __GNUC__, and AArch64 has no such intrinsics)
#if defined(_MSC_VER) && !defined(__clang__) && defined(ARCH_X86_64)
#define memcpy_dwords(__dest, __src, __numDwords) __movsd((unsigned long*)(__dest), (const unsigned long*)(__src), __numDwords)
#define memcpy_qwords(__dest, __src, __numQwords) __movsq((unsigned long long*)(__dest), (const unsigned long long*)(__src), __numQwords)
#else
#define memcpy_dwords(__dest, __src, __numDwords) memcpy((__dest), (__src), (__numDwords) * sizeof(uint32))
#define memcpy_qwords(__dest, __src, __numQwords) memcpy((__dest), (__src), (__numQwords) * sizeof(uint64))
#endif
// PPC context and memory functions