diff --git a/CMakeLists.txt b/CMakeLists.txt index 50a4cf08d..fe9417f37 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul include(DownloadExternals) include(CMakeDependentOption) include(CTest) +include(FetchContent) # Set bundled sdl2/qt as dependent options. # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON @@ -19,7 +20,7 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON # On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF) -option(ENABLE_LIBUSB "Enable the use of LibUSB" ON) +option(ENABLE_LIBUSB "Enable the use of LibUSB" "NOT ${ANDROID}") option(ENABLE_OPENGL "Enable OpenGL" ON) mark_as_advanced(FORCE ENABLE_OPENGL) @@ -48,7 +49,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}") option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) -option(YUZU_ROOM "Compile LDN room server" ON) +option(YUZU_ROOM "Compile LDN room server" "NOT ${ANDROID}") CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF) @@ -60,7 +61,67 @@ option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) +# On Android, fetch and compile libcxx before doing anything else +if (ANDROID) + set(CMAKE_SKIP_INSTALL_RULES ON) + set(LLVM_VERSION "15.0.6") + + # Note: even though libcxx and libcxxabi have separate releases on the project page, + # the separated releases cannot be compiled. Only in-tree builds work. Therefore we + # must fetch the source release for the entire llvm tree. + FetchContent_Declare(llvm + URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz" + URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92 + TLS_VERIFY TRUE + ) + FetchContent_MakeAvailable(llvm) + + # libcxx has support for most of the range library, but it's gated behind a flag: + add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL) + + # Disable standard header inclusion + set(ANDROID_STL "none") + + # libcxxabi + set(LIBCXXABI_INCLUDE_TESTS OFF) + set(LIBCXXABI_ENABLE_SHARED FALSE) + set(LIBCXXABI_ENABLE_STATIC TRUE) + set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE) + add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi") + link_libraries(cxxabi_static) + + # libcxx + set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE) + set(LIBCXX_CXX_ABI "libcxxabi") + set(LIBCXX_INCLUDE_TESTS OFF) + set(LIBCXX_INCLUDE_BENCHMARKS OFF) + set(LIBCXX_INCLUDE_DOCS OFF) + set(LIBCXX_ENABLE_SHARED FALSE) + set(LIBCXX_ENABLE_STATIC TRUE) + set(LIBCXX_ENABLE_ASSERTIONS FALSE) + add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx") + set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}") + link_libraries(cxx_static cxx-headers) +endif() + if (YUZU_USE_BUNDLED_VCPKG) + if (ANDROID) + set(ENV{ANDROID_NDK_HOME} "${ANDROID_NDK}") + list(APPEND VCPKG_MANIFEST_FEATURES "android") + + if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + set(VCPKG_TARGET_TRIPLET "arm64-android") + # this is to avoid CMake using the host pkg-config to find the host + # libraries when building for Android targets + set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE) + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") + set(VCPKG_TARGET_TRIPLET "x64-android") + set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE) + else() + message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}") + endif() + endif() + if (YUZU_TESTS) list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") endif() @@ -457,7 +518,7 @@ set(FFmpeg_COMPONENTS avutil swscale) -if (UNIX AND NOT APPLE) +if (UNIX AND NOT APPLE AND NOT ANDROID) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) endif() diff --git a/CMakeModules/CopyYuzuFFmpegDeps.cmake b/CMakeModules/CopyYuzuFFmpegDeps.cmake index a74fbb9ee..117faa122 100755 --- a/CMakeModules/CopyYuzuFFmpegDeps.cmake +++ b/CMakeModules/CopyYuzuFFmpegDeps.cmake @@ -6,5 +6,5 @@ function(copy_yuzu_FFmpeg_deps target_dir) set(DLL_DEST "$/") file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS) string(STRIP "${FFmpeg_REQUIRED_DLLS}" FFmpeg_REQUIRED_DLLS) - windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS}) + windows_copy_files(${target_dir} ${FFmpeg_LIBRARY_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS}) endfunction(copy_yuzu_FFmpeg_deps) diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index d3dd83126..cfd1dab0f 100755 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -7,6 +7,7 @@ # prefix_var: name of a variable which will be set with the path to the extracted contents function(download_bundled_external remote_path lib_name prefix_var) +set(package_base_url "https://github.com/yuzu-emu/") set(package_repo "no_platform") set(package_extension "no_platform") if (WIN32) @@ -15,10 +16,14 @@ if (WIN32) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(package_repo "ext-linux-bin/raw/main/") set(package_extension ".tar.xz") +elseif (ANDROID) + set(package_base_url "https://gitlab.com/tertius42/") + set(package_repo "ext-android-bin/-/raw/main/") + set(package_extension ".tar.xz") else() message(FATAL_ERROR "No package available for this platform") endif() -set(package_url "https://github.com/yuzu-emu/${package_repo}") +set(package_url "${package_base_url}${package_repo}") set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") if (NOT EXISTS "${prefix}") diff --git a/README.md b/README.md index ac5f10c0c..03e3cc60a 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3625. +This is the source code for early-access 3626. ## Legal Notice diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9ad30d427..d81ea2f69 100755 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -147,3 +147,9 @@ endif() add_library(stb stb/stb_dxt.cpp) target_include_directories(stb PUBLIC ./stb) + +if (ANDROID) + if (ARCHITECTURE_arm64) + add_subdirectory(libadrenotools) + endif() +endif() diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 7e696375a..cf82222c3 100755 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2021 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -if (NOT WIN32) +if (NOT WIN32 AND NOT ANDROID) # Build FFmpeg from externals message(STATUS "Using FFmpeg from externals") @@ -44,10 +44,12 @@ if (NOT WIN32) endforeach() find_package(PkgConfig REQUIRED) - pkg_check_modules(LIBVA libva) - pkg_check_modules(CUDA cuda) - pkg_check_modules(FFNVCODEC ffnvcodec) - pkg_check_modules(VDPAU vdpau) + if (NOT ANDROID) + pkg_check_modules(LIBVA libva) + pkg_check_modules(CUDA cuda) + pkg_check_modules(FFNVCODEC ffnvcodec) + pkg_check_modules(VDPAU vdpau) + endif() set(FFmpeg_HWACCEL_LIBRARIES) set(FFmpeg_HWACCEL_FLAGS) @@ -121,6 +123,26 @@ if (NOT WIN32) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau) endif() + find_program(BASH_PROGRAM bash REQUIRED) + + set(FFmpeg_CROSS_COMPILE_FLAGS "") + if (ANDROID) + string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" FFmpeg_HOST_SYSTEM_NAME) + set(TOOLCHAIN "${ANDROID_NDK}/toolchains/llvm/prebuilt/${FFmpeg_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}") + set(SYSROOT "${TOOLCHAIN}/sysroot") + set(FFmpeg_CPU "armv8-a") + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS + --arch=arm64 + #--cpu=${FFmpeg_CPU} + --enable-cross-compile + --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- + --sysroot=${SYSROOT} + --target-os=android + --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" + --extra-ldflags="-nostdlib" + ) + endif() + # `configure` parameters builds only exactly what yuzu needs from FFmpeg # `--disable-vdpau` is needed to avoid linking issues set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER}) @@ -129,7 +151,7 @@ if (NOT WIN32) OUTPUT ${FFmpeg_MAKEFILE} COMMAND - /bin/bash ${FFmpeg_PREFIX}/configure + ${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure --disable-avdevice --disable-avformat --disable-doc @@ -146,12 +168,14 @@ if (NOT WIN32) --cc="${FFmpeg_CC}" --cxx="${FFmpeg_CXX}" ${FFmpeg_HWACCEL_FLAGS} + ${FFmpeg_CROSS_COMPILE_FLAGS} WORKING_DIRECTORY ${FFmpeg_BUILD_DIR} ) unset(FFmpeg_CC) unset(FFmpeg_CXX) unset(FFmpeg_HWACCEL_FLAGS) + unset(FFmpeg_CROSS_COMPILE_FLAGS) # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child # with context of the jobserver. Also helps ninja users. @@ -197,7 +221,38 @@ if (NOT WIN32) else() message(FATAL_ERROR "FFmpeg not found") endif() -else(WIN32) +elseif(ANDROID) + # Use yuzu FFmpeg binaries + if (ARCHITECTURE_arm64) + set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-aarch64") + elseif (ARCHITECTURE_x86_64) + set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-x86_64") + else() + message(FATAL_ERROR "Unsupported architecture for Android FFmpeg") + endif() + set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") + download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "") + set(FFmpeg_FOUND YES) + set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE) + set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/lib" CACHE PATH "Path to FFmpeg library directory" FORCE) + set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE) + set(FFmpeg_LIBRARIES + ${FFmpeg_LIBRARY_DIR}/libavcodec.so + ${FFmpeg_LIBRARY_DIR}/libavdevice.so + ${FFmpeg_LIBRARY_DIR}/libavfilter.so + ${FFmpeg_LIBRARY_DIR}/libavformat.so + ${FFmpeg_LIBRARY_DIR}/libavutil.so + ${FFmpeg_LIBRARY_DIR}/libswresample.so + ${FFmpeg_LIBRARY_DIR}/libswscale.so + ${FFmpeg_LIBRARY_DIR}/libvpx.a + ${FFmpeg_LIBRARY_DIR}/libx264.a + CACHE PATH "Paths to FFmpeg libraries" FORCE) + # exported variables + set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE) + set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE) + set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE) + set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE) +elseif(WIN32) # Use yuzu FFmpeg binaries set(FFmpeg_EXT_NAME "ffmpeg-5.1.3") set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") @@ -206,7 +261,6 @@ else(WIN32) set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE) set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE) set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE) - set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE) set(FFmpeg_LIBRARIES ${FFmpeg_LIBRARY_DIR}/swscale.lib ${FFmpeg_LIBRARY_DIR}/avcodec.lib diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0e573327..0305f2baa 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,3 +195,8 @@ endif() if (ENABLE_WEB_SERVICE) add_subdirectory(web_service) endif() + +if (ANDROID) + add_subdirectory(android/app/src/main/jni) + target_include_directories(yuzu-android PRIVATE android/app/src/main) +endif() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt index dc89b5774..caaab50d8 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt @@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.features.settings.model.view class RunnableSetting( titleId: Int, descriptionId: Int, + val isRuntimeRunnable: Boolean, val runnable: () -> Unit ) : SettingsItem(null, titleId, descriptionId) { override val type = TYPE_RUNNABLE diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index d9abc358a..7d55c5b2f 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -115,7 +115,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) add( RunnableSetting( R.string.reset_to_default, - 0 + 0, + false ) { ResetSettingsDialogFragment().show( settingsActivity.supportFragmentManager, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 0c0bb93fc..5dad5945f 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View +import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem @@ -25,7 +26,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA } override fun onClick(clicked: View) { - setting.runnable.invoke() + if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) { + setting.runnable.invoke() + } } override fun onLongClick(clicked: View): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index ce3f2639a..ea44733c1 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -354,12 +354,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { v.setPadding(left, cutInsets.top, right, 0) - binding.showFpsText.setPadding( - cutInsets.left, - cutInsets.top, - cutInsets.right, - cutInsets.bottom - ) + // Ensure FPS text doesn't get cut off by rounded display corners + val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge) + if (cutInsets.left == 0) { + binding.showFpsText.setPadding( + sidePadding, + cutInsets.top, + cutInsets.right, + cutInsets.bottom + ) + } else { + binding.showFpsText.setPadding( + cutInsets.left, + cutInsets.top, + cutInsets.right, + cutInsets.bottom + ) + } windowInsets } } diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 648b5aaed..74ec512af 100755 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -32,11 +32,13 @@ android:id="@+id/show_fps_text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="left" android:clickable="false" android:focusable="false" android:shadowColor="@android:color/black" android:textColor="@android:color/white" - android:textSize="12sp" /> + android:textSize="12sp" + tools:ignore="RtlHardcoded" />