early-access version 3626

This commit is contained in:
pineappleEA 2023-05-31 22:58:29 +02:00
parent 0d71b72e9a
commit a8ad9662f0
80 changed files with 889 additions and 192 deletions

View file

@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
include(DownloadExternals) include(DownloadExternals)
include(CMakeDependentOption) include(CMakeDependentOption)
include(CTest) include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options. # Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON # 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 # 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) 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) option(ENABLE_OPENGL "Enable OpenGL" ON)
mark_as_advanced(FORCE ENABLE_OPENGL) 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_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) 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) 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 (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) if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif() endif()
@ -457,7 +518,7 @@ set(FFmpeg_COMPONENTS
avutil avutil
swscale) swscale)
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE AND NOT ANDROID)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva) pkg_check_modules(LIBVA libva)
endif() endif()

View file

@ -6,5 +6,5 @@ function(copy_yuzu_FFmpeg_deps target_dir)
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/") set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS) file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
string(STRIP "${FFmpeg_REQUIRED_DLLS}" 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) endfunction(copy_yuzu_FFmpeg_deps)

View file

@ -7,6 +7,7 @@
# prefix_var: name of a variable which will be set with the path to the extracted contents # 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) 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_repo "no_platform")
set(package_extension "no_platform") set(package_extension "no_platform")
if (WIN32) if (WIN32)
@ -15,10 +16,14 @@ if (WIN32)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(package_repo "ext-linux-bin/raw/main/") set(package_repo "ext-linux-bin/raw/main/")
set(package_extension ".tar.xz") 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() else()
message(FATAL_ERROR "No package available for this platform") message(FATAL_ERROR "No package available for this platform")
endif() 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}") set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
if (NOT EXISTS "${prefix}") if (NOT EXISTS "${prefix}")

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3625. This is the source code for early-access 3626.
## Legal Notice ## Legal Notice

View file

@ -147,3 +147,9 @@ endif()
add_library(stb stb/stb_dxt.cpp) add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb) target_include_directories(stb PUBLIC ./stb)
if (ANDROID)
if (ARCHITECTURE_arm64)
add_subdirectory(libadrenotools)
endif()
endif()

View file

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project # SPDX-FileCopyrightText: 2021 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
if (NOT WIN32) if (NOT WIN32 AND NOT ANDROID)
# Build FFmpeg from externals # Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals") message(STATUS "Using FFmpeg from externals")
@ -44,10 +44,12 @@ if (NOT WIN32)
endforeach() endforeach()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
if (NOT ANDROID)
pkg_check_modules(LIBVA libva) pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda) pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec) pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau) pkg_check_modules(VDPAU vdpau)
endif()
set(FFmpeg_HWACCEL_LIBRARIES) set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS) set(FFmpeg_HWACCEL_FLAGS)
@ -121,6 +123,26 @@ if (NOT WIN32)
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif() 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 # `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues # `--disable-vdpau` is needed to avoid linking issues
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER}) set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
@ -129,7 +151,7 @@ if (NOT WIN32)
OUTPUT OUTPUT
${FFmpeg_MAKEFILE} ${FFmpeg_MAKEFILE}
COMMAND COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure ${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
--disable-avdevice --disable-avdevice
--disable-avformat --disable-avformat
--disable-doc --disable-doc
@ -146,12 +168,14 @@ if (NOT WIN32)
--cc="${FFmpeg_CC}" --cc="${FFmpeg_CC}"
--cxx="${FFmpeg_CXX}" --cxx="${FFmpeg_CXX}"
${FFmpeg_HWACCEL_FLAGS} ${FFmpeg_HWACCEL_FLAGS}
${FFmpeg_CROSS_COMPILE_FLAGS}
WORKING_DIRECTORY WORKING_DIRECTORY
${FFmpeg_BUILD_DIR} ${FFmpeg_BUILD_DIR}
) )
unset(FFmpeg_CC) unset(FFmpeg_CC)
unset(FFmpeg_CXX) unset(FFmpeg_CXX)
unset(FFmpeg_HWACCEL_FLAGS) 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 # 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. # with context of the jobserver. Also helps ninja users.
@ -197,7 +221,38 @@ if (NOT WIN32)
else() else()
message(FATAL_ERROR "FFmpeg not found") message(FATAL_ERROR "FFmpeg not found")
endif() 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 # Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-5.1.3") set(FFmpeg_EXT_NAME "ffmpeg-5.1.3")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") 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_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_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" 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 set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib ${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib ${FFmpeg_LIBRARY_DIR}/avcodec.lib

View file

@ -195,3 +195,8 @@ endif()
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
add_subdirectory(web_service) add_subdirectory(web_service)
endif() endif()
if (ANDROID)
add_subdirectory(android/app/src/main/jni)
target_include_directories(yuzu-android PRIVATE android/app/src/main)
endif()

View file

@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.features.settings.model.view
class RunnableSetting( class RunnableSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val isRuntimeRunnable: Boolean,
val runnable: () -> Unit val runnable: () -> Unit
) : SettingsItem(null, titleId, descriptionId) { ) : SettingsItem(null, titleId, descriptionId) {
override val type = TYPE_RUNNABLE override val type = TYPE_RUNNABLE

View file

@ -115,7 +115,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add( add(
RunnableSetting( RunnableSetting(
R.string.reset_to_default, R.string.reset_to_default,
0 0,
false
) { ) {
ResetSettingsDialogFragment().show( ResetSettingsDialogFragment().show(
settingsActivity.supportFragmentManager, settingsActivity.supportFragmentManager,

View file

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 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.RunnableSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
@ -25,8 +26,10 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) {
setting.runnable.invoke() setting.runnable.invoke()
} }
}
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
// no-op // no-op

View file

@ -354,12 +354,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
v.setPadding(left, cutInsets.top, right, 0) v.setPadding(left, cutInsets.top, right, 0)
// 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( binding.showFpsText.setPadding(
cutInsets.left, cutInsets.left,
cutInsets.top, cutInsets.top,
cutInsets.right, cutInsets.right,
cutInsets.bottom cutInsets.bottom
) )
}
windowInsets windowInsets
} }
} }

View file

@ -32,11 +32,13 @@
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
android:shadowColor="@android:color/black" android:shadowColor="@android:color/black"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" /> android:textSize="12sp"
tools:ignore="RtlHardcoded" />
<Button <Button
style="@style/Widget.Material3.Button.ElevatedButton" style="@style/Widget.Material3.Button.ElevatedButton"

View file

@ -22,6 +22,7 @@
<item name="colorPrimary">@color/yuzu_primaryContainer</item> <item name="colorPrimary">@color/yuzu_primaryContainer</item>
<item name="colorSurface">@color/yuzu_primaryContainer</item> <item name="colorSurface">@color/yuzu_primaryContainer</item>
<item name="colorSecondary">@color/yuzu_primary</item> <item name="colorSecondary">@color/yuzu_primary</item>
<item name="android:textColorLink">@color/yuzu_primary</item>
<item name="buttonBarPositiveButtonStyle">@style/YuzuButton</item> <item name="buttonBarPositiveButtonStyle">@style/YuzuButton</item>
<item name="buttonBarNegativeButtonStyle">@style/YuzuButton</item> <item name="buttonBarNegativeButtonStyle">@style/YuzuButton</item>
<item name="buttonBarNeutralButtonStyle">@style/YuzuButton</item> <item name="buttonBarNeutralButtonStyle">@style/YuzuButton</item>

View file

@ -155,6 +155,14 @@ if (WIN32)
target_link_libraries(common PRIVATE ntdll) target_link_libraries(common PRIVATE ntdll)
endif() endif()
if(ANDROID)
target_sources(common
PRIVATE
fs/fs_android.cpp
fs/fs_android.h
)
endif()
if(ARCHITECTURE_x86_64) if(ARCHITECTURE_x86_64)
target_sources(common target_sources(common
PRIVATE PRIVATE
@ -196,6 +204,11 @@ create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads) target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID)
# For ASharedMemory_create
target_link_libraries(common PRIVATE android)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS) if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(common PRIVATE precompiled_headers.h) target_precompile_headers(common PRIVATE precompiled_headers.h)
endif() endif()

View file

@ -22,6 +22,8 @@ DynamicLibrary::DynamicLibrary(const char* filename) {
void(Open(filename)); void(Open(filename));
} }
DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {}
DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
: handle{std::exchange(rhs.handle, nullptr)} {} : handle{std::exchange(rhs.handle, nullptr)} {}

View file

@ -20,6 +20,9 @@ public:
/// Automatically loads the specified library. Call IsOpen() to check validity before use. /// Automatically loads the specified library. Call IsOpen() to check validity before use.
explicit DynamicLibrary(const char* filename); explicit DynamicLibrary(const char* filename);
/// Initializes the dynamic library with an already opened handle.
explicit DynamicLibrary(void* handle_);
/// Moves the library. /// Moves the library.
DynamicLibrary(DynamicLibrary&&) noexcept; DynamicLibrary(DynamicLibrary&&) noexcept;
DynamicLibrary& operator=(DynamicLibrary&&) noexcept; DynamicLibrary& operator=(DynamicLibrary&&) noexcept;

View file

@ -30,7 +30,8 @@ std::string NativeErrorToString(int e) {
return ret; return ret;
#else #else
char err_str[255]; char err_str[255];
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) #if defined(ANDROID) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific) // Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str)); const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str); return std::string(str);

View file

@ -5,6 +5,9 @@
#include "common/fs/file.h" #include "common/fs/file.h"
#include "common/fs/fs.h" #include "common/fs/fs.h"
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
#include "common/logging/log.h" #include "common/logging/log.h"
#ifdef _WIN32 #ifdef _WIN32
@ -252,6 +255,23 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
} else { } else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
} }
#elif ANDROID
if (Android::IsContentUri(path)) {
ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) {
file = fdopen(fd, "r");
const auto error_num = errno;
if (error_num != 0 && file == nullptr) {
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
strerror(error_num));
}
} else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
}
} else {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
}
#else #else
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif #endif
@ -372,6 +392,23 @@ u64 IOFile::GetSize() const {
// Flush any unwritten buffered data into the file prior to retrieving the file size. // Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file); std::fflush(file);
#if ANDROID
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);
} else {
std::error_code ec;
file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
}
#else
std::error_code ec; std::error_code ec;
const auto file_size = fs::file_size(file_path, ec); const auto file_size = fs::file_size(file_path, ec);
@ -381,6 +418,7 @@ u64 IOFile::GetSize() const {
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
return 0; return 0;
} }
#endif
return file_size; return file_size;
} }

View file

@ -6,6 +6,9 @@
#include <unordered_map> #include <unordered_map>
#include "common/fs/fs.h" #include "common/fs/fs.h"
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
#include "common/fs/fs_paths.h" #include "common/fs/fs_paths.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -80,9 +83,7 @@ public:
yuzu_paths.insert_or_assign(yuzu_path, new_path); yuzu_paths.insert_or_assign(yuzu_path, new_path);
} }
private: void Reinitialize(fs::path yuzu_path = {}) {
PathManagerImpl() {
fs::path yuzu_path;
fs::path yuzu_path_cache; fs::path yuzu_path_cache;
fs::path yuzu_path_config; fs::path yuzu_path_config;
@ -93,6 +94,10 @@ private:
yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR;
} }
yuzu_path_cache = yuzu_path / CACHE_DIR;
yuzu_path_config = yuzu_path / CONFIG_DIR;
#elif ANDROID
ASSERT(!yuzu_path.empty());
yuzu_path_cache = yuzu_path / CACHE_DIR; yuzu_path_cache = yuzu_path / CACHE_DIR;
yuzu_path_config = yuzu_path / CONFIG_DIR; yuzu_path_config = yuzu_path / CONFIG_DIR;
#else #else
@ -122,6 +127,11 @@ private:
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
} }
private:
PathManagerImpl() {
Reinitialize();
}
~PathManagerImpl() = default; ~PathManagerImpl() = default;
void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
@ -210,6 +220,10 @@ fs::path RemoveTrailingSeparators(const fs::path& path) {
return fs::path{string_path}; return fs::path{string_path};
} }
void SetAppDirectory(const std::string& app_directory) {
PathManagerImpl::GetInstance().Reinitialize(app_directory);
}
const fs::path& GetYuzuPath(YuzuPath yuzu_path) { const fs::path& GetYuzuPath(YuzuPath yuzu_path) {
return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path);
} }
@ -350,6 +364,12 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_); std::string path(path_);
#ifdef ANDROID
if (Android::IsContentUri(path)) {
return path;
}
#endif // ANDROID
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';

View file

@ -180,6 +180,14 @@ template <typename Path>
} }
#endif #endif
/**
* Sets the directory used for application storage. Used on Android where we do not know internal
* storage until informed by the frontend.
*
* @param app_directory Directory to use for application storage.
*/
void SetAppDirectory(const std::string& app_directory);
/** /**
* Gets the filesystem path associated with the YuzuPath enum. * Gets the filesystem path associated with the YuzuPath enum.
* *

View file

@ -11,6 +11,10 @@
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
#ifdef ANDROID
#include <android/sharedmem.h>
#endif
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
@ -366,17 +370,20 @@ public:
} }
// Backing memory initialization // Backing memory initialization
#if defined(__FreeBSD__) && __FreeBSD__ < 13 #ifdef ANDROID
fd = ASharedMemory_create("HostMemory", backing_size);
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
fd = shm_open(SHM_ANON, O_RDWR, 0600); fd = shm_open(SHM_ANON, O_RDWR, 0600);
#else #else
fd = memfd_create("HostMemory", 0); fd = memfd_create("HostMemory", 0);
#endif #endif
if (fd == -1) { if (fd < 0) {
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
#ifndef ANDROID
// Defined to extend the file with zeros // Defined to extend the file with zeros
int ret = ftruncate(fd, backing_size); int ret = ftruncate(fd, backing_size);
if (ret != 0) { if (ret != 0) {
@ -384,6 +391,7 @@ public:
strerror(errno)); strerror(errno));
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
#endif
backing_base = static_cast<u8*>( backing_base = static_cast<u8*>(
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));

View file

@ -155,6 +155,26 @@ public:
void EnableForStacktrace() override {} void EnableForStacktrace() override {}
}; };
#ifdef ANDROID
/**
* Backend that writes to the Android logcat
*/
class LogcatBackend : public Backend {
public:
explicit LogcatBackend() = default;
~LogcatBackend() override = default;
void Write(const Entry& entry) override {
PrintMessageToLogcat(entry);
}
void Flush() override {}
void EnableForStacktrace() override {}
};
#endif
bool initialization_in_progress_suppress_logging = true; bool initialization_in_progress_suppress_logging = true;
/** /**
@ -260,6 +280,9 @@ private:
lambda(static_cast<Backend&>(debugger_backend)); lambda(static_cast<Backend&>(debugger_backend));
lambda(static_cast<Backend&>(color_console_backend)); lambda(static_cast<Backend&>(color_console_backend));
lambda(static_cast<Backend&>(file_backend)); lambda(static_cast<Backend&>(file_backend));
#ifdef ANDROID
lambda(static_cast<Backend&>(lc_backend));
#endif
} }
static void Deleter(Impl* ptr) { static void Deleter(Impl* ptr) {
@ -272,6 +295,9 @@ private:
DebuggerBackend debugger_backend{}; DebuggerBackend debugger_backend{};
ColorConsoleBackend color_console_backend{}; ColorConsoleBackend color_console_backend{};
FileBackend file_backend; FileBackend file_backend;
#ifdef ANDROID
LogcatBackend lc_backend{};
#endif
MPSCQueue<Entry> message_queue{}; MPSCQueue<Entry> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};

View file

@ -8,6 +8,10 @@
#include <windows.h> #include <windows.h>
#endif #endif
#ifdef ANDROID
#include <android/log.h>
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -106,4 +110,35 @@ void PrintColoredMessage(const Entry& entry) {
#undef ESC #undef ESC
#endif #endif
} }
void PrintMessageToLogcat(const Entry& entry) {
#ifdef ANDROID
const auto str = FormatLogMessage(entry);
android_LogPriority android_log_priority;
switch (entry.log_level) {
case Level::Trace:
android_log_priority = ANDROID_LOG_VERBOSE;
break;
case Level::Debug:
android_log_priority = ANDROID_LOG_DEBUG;
break;
case Level::Info:
android_log_priority = ANDROID_LOG_INFO;
break;
case Level::Warning:
android_log_priority = ANDROID_LOG_WARN;
break;
case Level::Error:
android_log_priority = ANDROID_LOG_ERROR;
break;
case Level::Critical:
android_log_priority = ANDROID_LOG_FATAL;
break;
case Level::Count:
UNREACHABLE();
}
__android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str());
#endif
}
} // namespace Common::Log } // namespace Common::Log

View file

@ -15,4 +15,6 @@ std::string FormatLogMessage(const Entry& entry);
void PrintMessage(const Entry& entry); void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored according to the severity level. /// Prints the same message as `PrintMessage`, but colored according to the severity level.
void PrintColoredMessage(const Entry& entry); void PrintColoredMessage(const Entry& entry);
/// Formats and prints a log entry to the android logcat.
void PrintMessageToLogcat(const Entry& entry);
} // namespace Common::Log } // namespace Common::Log

View file

@ -139,6 +139,7 @@ add_library(core STATIC
frontend/emu_window.h frontend/emu_window.h
frontend/framebuffer_layout.cpp frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h frontend/framebuffer_layout.h
frontend/graphics_context.h
hid/emulated_console.cpp hid/emulated_console.cpp
hid/emulated_console.h hid/emulated_console.h
hid/emulated_controller.cpp hid/emulated_controller.cpp

View file

@ -569,6 +569,10 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
} }
KeyManager::KeyManager() { KeyManager::KeyManager() {
ReloadKeys();
}
void KeyManager::ReloadKeys() {
// Initialize keys // Initialize keys
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
@ -702,6 +706,10 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
} }
} }
bool KeyManager::AreKeysLoaded() const {
return !s128_keys.empty() && !s256_keys.empty();
}
bool KeyManager::BaseDeriveNecessary() const { bool KeyManager::BaseDeriveNecessary() const {
const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) { const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
return !HasKey(key_type, index1, index2); return !HasKey(key_type, index1, index2);

View file

@ -267,6 +267,9 @@ public:
bool AddTicketCommon(Ticket raw); bool AddTicketCommon(Ticket raw);
bool AddTicketPersonalized(Ticket raw); bool AddTicketPersonalized(Ticket raw);
void ReloadKeys();
bool AreKeysLoaded() const;
private: private:
KeyManager(); KeyManager();

View file

@ -6,9 +6,15 @@
namespace Core { namespace Core {
#ifdef ANDROID
constexpr size_t VirtualReserveSize = 1ULL << 38;
#else
constexpr size_t VirtualReserveSize = 1ULL << 39;
#endif
DeviceMemory::DeviceMemory() DeviceMemory::DeviceMemory()
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
1ULL << 39} {} VirtualReserveSize} {}
DeviceMemory::~DeviceMemory() = default; DeviceMemory::~DeviceMemory() = default;
} // namespace Core } // namespace Core

View file

@ -6,8 +6,6 @@
namespace Core::Frontend { namespace Core::Frontend {
GraphicsContext::~GraphicsContext() = default;
EmuWindow::EmuWindow() { EmuWindow::EmuWindow() {
// TODO: Find a better place to set this. // TODO: Find a better place to set this.
config.min_client_area_size = config.min_client_area_size =

View file

@ -5,11 +5,14 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
namespace Core::Frontend { namespace Core::Frontend {
class GraphicsContext;
/// Information for the Graphics Backends signifying what type of screen pointer is in /// Information for the Graphics Backends signifying what type of screen pointer is in
/// WindowInformation /// WindowInformation
enum class WindowSystemType { enum class WindowSystemType {
@ -21,51 +24,6 @@ enum class WindowSystemType {
Android, Android,
}; };
/**
* Represents a drawing context that supports graphics operations.
*/
class GraphicsContext {
public:
virtual ~GraphicsContext();
/// Inform the driver to swap the front/back buffers and present the current image
virtual void SwapBuffers() {}
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() {}
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() {}
class Scoped {
public:
[[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
context.MakeCurrent();
}
~Scoped() {
if (active) {
context.DoneCurrent();
}
}
/// In the event that context was destroyed before the Scoped is destroyed, this provides a
/// mechanism to prevent calling a destroyed object's method during the deconstructor
void Cancel() {
active = false;
}
private:
GraphicsContext& context;
bool active{true};
};
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
/// ends
[[nodiscard]] Scoped Acquire() {
return Scoped{*this};
}
};
/** /**
* Abstraction class used to provide an interface between emulation code and the frontend * Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...). * (e.g. SDL, QGLWidget, GLFW, etc...).

View file

@ -13,7 +13,7 @@ EmulatedConsole::~EmulatedConsole() = default;
void EmulatedConsole::ReloadFromSettings() { void EmulatedConsole::ReloadFromSettings() {
// Using first motion device from player 1. No need to assign any unique config at the moment // Using first motion device from player 1. No need to assign any unique config at the moment
const auto& player = Settings::values.players.GetValue()[0]; const auto& player = Settings::values.players.GetValue()[0];
motion_params = Common::ParamPackage(player.motions[0]); motion_params[0] = Common::ParamPackage(player.motions[0]);
ReloadInput(); ReloadInput();
} }
@ -74,14 +74,30 @@ void EmulatedConsole::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function // If you load any device here add the equivalent to the UnloadInput() function
SetTouchParams(); SetTouchParams();
motion_devices = Common::Input::CreateInputDevice(motion_params); motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"};
if (motion_devices) {
motion_devices->SetCallback({ for (std::size_t index = 0; index < motion_devices.size(); ++index) {
motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]);
if (!motion_devices[index]) {
continue;
}
motion_devices[index]->SetCallback({
.on_change = .on_change =
[this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); }, [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
}); });
} }
// Restore motion state
auto& emulated_motion = console.motion_values.emulated;
auto& motion = console.motion_state;
emulated_motion.ResetRotations();
emulated_motion.ResetQuaternion();
motion.accel = emulated_motion.GetAcceleration();
motion.gyro = emulated_motion.GetGyroscope();
motion.rotation = emulated_motion.GetRotations();
motion.orientation = emulated_motion.GetOrientation();
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
// Unique index for identifying touch device source // Unique index for identifying touch device source
std::size_t index = 0; std::size_t index = 0;
for (auto& touch_device : touch_devices) { for (auto& touch_device : touch_devices) {
@ -100,7 +116,9 @@ void EmulatedConsole::ReloadInput() {
} }
void EmulatedConsole::UnloadInput() { void EmulatedConsole::UnloadInput() {
motion_devices.reset(); for (auto& motion : motion_devices) {
motion.reset();
}
for (auto& touch : touch_devices) { for (auto& touch : touch_devices) {
touch.reset(); touch.reset();
} }
@ -133,11 +151,11 @@ void EmulatedConsole::RestoreConfig() {
} }
Common::ParamPackage EmulatedConsole::GetMotionParam() const { Common::ParamPackage EmulatedConsole::GetMotionParam() const {
return motion_params; return motion_params[0];
} }
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
motion_params = std::move(param); motion_params[0] = std::move(param);
ReloadInput(); ReloadInput();
} }

View file

@ -29,10 +29,10 @@ struct ConsoleMotionInfo {
MotionInput emulated{}; MotionInput emulated{};
}; };
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
using ConsoleMotionParams = Common::ParamPackage; using ConsoleMotionParams = std::array<Common::ParamPackage, 2>;
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
using ConsoleMotionValues = ConsoleMotionInfo; using ConsoleMotionValues = ConsoleMotionInfo;

View file

@ -193,6 +193,8 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateInputDevice); Common::Input::CreateInputDevice);
std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
Common::Input::CreateInputDevice); Common::Input::CreateInputDevice);
std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(),
Common::Input::CreateInputDevice);
} }
void EmulatedController::LoadTASParams() { void EmulatedController::LoadTASParams() {
@ -253,6 +255,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
for (auto& param : virtual_stick_params) { for (auto& param : virtual_stick_params) {
param = common_params; param = common_params;
} }
for (auto& param : virtual_stick_params) {
param = common_params;
}
for (auto& param : virtual_motion_params) {
param = common_params;
}
// TODO(german77): Replace this with an input profile or something better // TODO(german77): Replace this with an input profile or something better
virtual_button_params[Settings::NativeButton::A].Set("button", 0); virtual_button_params[Settings::NativeButton::A].Set("button", 0);
@ -284,6 +292,9 @@ void EmulatedController::LoadVirtualGamepadParams() {
virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
virtual_motion_params[Settings::NativeMotion::MotionLeft].Set("motion", 0);
virtual_motion_params[Settings::NativeMotion::MotionRight].Set("motion", 0);
} }
void EmulatedController::ReloadInput() { void EmulatedController::ReloadInput() {
@ -463,6 +474,18 @@ void EmulatedController::ReloadInput() {
}, },
}); });
} }
for (std::size_t index = 0; index < virtual_motion_devices.size(); ++index) {
if (!virtual_motion_devices[index]) {
continue;
}
virtual_motion_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetMotion(callback, index);
},
});
}
turbo_button_state = 0; turbo_button_state = 0;
} }
@ -500,6 +523,9 @@ void EmulatedController::UnloadInput() {
for (auto& stick : virtual_stick_devices) { for (auto& stick : virtual_stick_devices) {
stick.reset(); stick.reset();
} }
for (auto& motion : virtual_motion_devices) {
motion.reset();
}
for (auto& camera : camera_devices) { for (auto& camera : camera_devices) {
camera.reset(); camera.reset();
} }

View file

@ -568,8 +568,10 @@ private:
// Virtual gamepad related variables // Virtual gamepad related variables
ButtonParams virtual_button_params; ButtonParams virtual_button_params;
StickParams virtual_stick_params; StickParams virtual_stick_params;
ControllerMotionParams virtual_motion_params;
ButtonDevices virtual_button_devices; ButtonDevices virtual_button_devices;
StickDevices virtual_stick_devices; StickDevices virtual_stick_devices;
ControllerMotionDevices virtual_motion_devices;
mutable std::mutex mutex; mutable std::mutex mutex;
mutable std::mutex callback_mutex; mutable std::mutex callback_mutex;

View file

@ -25,7 +25,12 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
#ifdef ANDROID
// With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
{ .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
#else
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
#endif
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall }, { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, { .bit_width = 39, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, }, { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },

View file

@ -46,6 +46,7 @@ ProfileManager::ProfileManager() {
// Create an user if none are present // Create an user if none are present
if (user_count == 0) { if (user_count == 0) {
CreateNewUser(UUID::MakeRandom(), "yuzu"); CreateNewUser(UUID::MakeRandom(), "yuzu");
WriteUserSaveFile();
} }
auto current = auto current =

View file

@ -73,10 +73,7 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile}; Common::FS::FileType::BinaryFile};
std::vector<u8> data{};
if (state != State::WaitingForAmiibo) {
return Info::WrongDeviceState;
}
if (!nfc_file.IsOpen()) { if (!nfc_file.IsOpen()) {
return Info::UnableToLoad; return Info::UnableToLoad;
@ -101,7 +98,28 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
} }
file_path = filename; file_path = filename;
return LoadAmiibo(data);
}
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
if (state != State::WaitingForAmiibo) {
return Info::WrongDeviceState;
}
switch (data.size_bytes()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
nfc_data.resize(AmiiboSize);
break;
case MifareSize:
nfc_data.resize(MifareSize);
break;
default:
return Info::NotAnAmiibo;
}
state = State::AmiiboIsOpen; state = State::AmiiboIsOpen;
memcpy(nfc_data.data(), data.data(), data.size_bytes());
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success; return Info::Success;
} }

View file

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <span>
#include <string> #include <string>
#include <vector> #include <vector>
@ -47,6 +48,7 @@ public:
State GetCurrentState() const; State GetCurrentState() const;
Info LoadAmiibo(const std::string& amiibo_file); Info LoadAmiibo(const std::string& amiibo_file);
Info LoadAmiibo(std::span<u8> data);
Info ReloadAmiibo(); Info ReloadAmiibo();
Info CloseAmiibo(); Info CloseAmiibo();

View file

@ -39,6 +39,22 @@ void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axi
SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value); SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value);
} }
void VirtualGamepad::SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x,
float gyro_y, float gyro_z, float accel_x, float accel_y,
float accel_z) {
const auto identifier = GetIdentifier(player_index);
const BasicMotion motion_data{
.gyro_x = gyro_x,
.gyro_y = gyro_y,
.gyro_z = gyro_z,
.accel_x = accel_x,
.accel_y = accel_y,
.accel_z = accel_z,
.delta_timestamp = delta_timestamp,
};
SetMotion(identifier, 0, motion_data);
}
void VirtualGamepad::ResetControllers() { void VirtualGamepad::ResetControllers() {
for (std::size_t i = 0; i < PlayerIndexCount; i++) { for (std::size_t i = 0; i < PlayerIndexCount; i++) {
SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f);

View file

@ -52,7 +52,7 @@ public:
void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value);
/** /**
* Sets the status of all buttons bound with the key to released * Sets the status of a stick to a specific player index
* @param player_index the player number that will take this action * @param player_index the player number that will take this action
* @param axis_id the id of the axis to move * @param axis_id the id of the axis to move
* @param x_value the position of the stick in the x axis * @param x_value the position of the stick in the x axis
@ -62,6 +62,16 @@ public:
void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
float y_value); float y_value);
/**
* Sets the status of the motion sensor to a specific player index
* @param player_index the player number that will take this action
* @param delta_timestamp time passed since last reading
* @param gyro_x,gyro_y,gyro_z the gyro sensor readings
* @param accel_x,accel_y,accel_z the acelerometer reading
*/
void SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, float gyro_y,
float gyro_z, float accel_x, float accel_y, float accel_z);
/// Restores all inputs into the neutral position /// Restores all inputs into the neutral position
void ResetControllers(); void ResetControllers();

View file

@ -69,6 +69,11 @@ Id StorageAtomicU32(EmitContext& ctx, const IR::Value& binding, const IR::Value&
Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id), Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id),
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) { Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV, "Descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U64);
}
if (ctx.profile.support_int64_atomics) { if (ctx.profile.support_int64_atomics) {
const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64, const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
binding, offset, sizeof(u64))}; binding, offset, sizeof(u64))};
@ -86,6 +91,11 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value&
Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) { Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV, "Descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U32[2]);
}
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))}; binding, offset, sizeof(u32[2]))};

View file

@ -10,27 +10,6 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
struct AttrInfo {
Id pointer;
Id id;
bool needs_cast;
};
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
switch (type) {
case AttributeType::Float:
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
case AttributeType::UnsignedInt:
return AttrInfo{ctx.input_u32, ctx.U32[1], true};
case AttributeType::SignedInt:
return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
case AttributeType::Disabled:
return std::nullopt;
}
throw InvalidArgument("Invalid attribute type {}", type);
}
template <typename... Args> template <typename... Args>
Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
switch (ctx.stage) { switch (ctx.stage) {
@ -302,15 +281,26 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
const u32 element{static_cast<u32>(attr) % 4}; const u32 element{static_cast<u32>(attr) % 4};
if (IR::IsGeneric(attr)) { if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; const auto& generic{ctx.input_generics.at(index)};
if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) { if (!ValidId(generic.id)) {
// Attribute is disabled or varying component is not written // Attribute is disabled or varying component is not written
return ctx.Const(element == 3 ? 1.0f : 0.0f); return ctx.Const(element == 3 ? 1.0f : 0.0f);
} }
const Id generic_id{ctx.input_generics.at(index)}; const Id pointer{
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(element))};
const Id value{ctx.OpLoad(type->id, pointer)}; const Id value{ctx.OpLoad(generic.component_type, pointer)};
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; return [&ctx, generic, value]() {
switch (generic.load_op) {
case InputGenericLoadOp::Bitcast:
return ctx.OpBitcast(ctx.F32[1], value);
case InputGenericLoadOp::SToF:
return ctx.OpConvertSToF(ctx.F32[1], value);
case InputGenericLoadOp::UToF:
return ctx.OpConvertUToF(ctx.F32[1], value);
default:
return value;
};
}();
} }
switch (attr) { switch (attr) {
case IR::Attribute::PrimitiveId: case IR::Attribute::PrimitiveId:

View file

@ -17,8 +17,23 @@ Id GetThreadId(EmitContext& ctx) {
Id WarpExtract(EmitContext& ctx, Id value) { Id WarpExtract(EmitContext& ctx, Id value) {
const Id thread_id{GetThreadId(ctx)}; const Id thread_id{GetThreadId(ctx)};
const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))}; const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
if (ctx.profile.has_broken_spirv_subgroup_mask_vector_extract_dynamic) {
const Id c0_sel{ctx.OpSelect(ctx.U32[1], ctx.OpIEqual(ctx.U1, local_index, ctx.Const(0U)),
ctx.OpCompositeExtract(ctx.U32[1], value, 0U), ctx.Const(0U))};
const Id c1_sel{ctx.OpSelect(ctx.U32[1], ctx.OpIEqual(ctx.U1, local_index, ctx.Const(1U)),
ctx.OpCompositeExtract(ctx.U32[1], value, 1U), ctx.Const(0U))};
const Id c2_sel{ctx.OpSelect(ctx.U32[1], ctx.OpIEqual(ctx.U1, local_index, ctx.Const(2U)),
ctx.OpCompositeExtract(ctx.U32[1], value, 2U), ctx.Const(0U))};
const Id c3_sel{ctx.OpSelect(ctx.U32[1], ctx.OpIEqual(ctx.U1, local_index, ctx.Const(3U)),
ctx.OpCompositeExtract(ctx.U32[1], value, 3U), ctx.Const(0U))};
const Id c0_or_c1{ctx.OpBitwiseOr(ctx.U32[1], c0_sel, c1_sel)};
const Id c2_or_c3{ctx.OpBitwiseOr(ctx.U32[1], c2_sel, c3_sel)};
const Id c0_or_c1_or_c2_or_c3{ctx.OpBitwiseOr(ctx.U32[1], c0_or_c1, c2_or_c3)};
return c0_or_c1_or_c2_or_c3;
} else {
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index); return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
} }
}
Id LoadMask(EmitContext& ctx, Id mask) { Id LoadMask(EmitContext& ctx, Id mask) {
const Id value{ctx.OpLoad(ctx.U32[4], mask)}; const Id value{ctx.OpLoad(ctx.U32[4], mask)};

View file

@ -25,12 +25,6 @@ enum class Operation {
FPMax, FPMax,
}; };
struct AttrInfo {
Id pointer;
Id id;
bool needs_cast;
};
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown}; const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]}; const Id type{ctx.F32[1]};
@ -206,23 +200,37 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) {
return ctx.TypeVector(ctx.TypeInt(32, true), 4); return ctx.TypeVector(ctx.TypeInt(32, true), 4);
case AttributeType::UnsignedInt: case AttributeType::UnsignedInt:
return ctx.U32[4]; return ctx.U32[4];
case AttributeType::SignedScaled:
return ctx.profile.support_scaled_attributes ? ctx.F32[4]
: ctx.TypeVector(ctx.TypeInt(32, true), 4);
case AttributeType::UnsignedScaled:
return ctx.profile.support_scaled_attributes ? ctx.F32[4] : ctx.U32[4];
case AttributeType::Disabled: case AttributeType::Disabled:
break; break;
} }
throw InvalidArgument("Invalid attribute type {}", type); throw InvalidArgument("Invalid attribute type {}", type);
} }
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { InputGenericInfo GetAttributeInfo(EmitContext& ctx, AttributeType type, Id id) {
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
switch (type) { switch (type) {
case AttributeType::Float: case AttributeType::Float:
return AttrInfo{ctx.input_f32, ctx.F32[1], false}; return InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None};
case AttributeType::UnsignedInt: case AttributeType::UnsignedInt:
return AttrInfo{ctx.input_u32, ctx.U32[1], true}; return InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::Bitcast};
case AttributeType::SignedInt: case AttributeType::SignedInt:
return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true}; return InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
InputGenericLoadOp::Bitcast};
case AttributeType::SignedScaled:
return ctx.profile.support_scaled_attributes
? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
: InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
InputGenericLoadOp::SToF};
case AttributeType::UnsignedScaled:
return ctx.profile.support_scaled_attributes
? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
: InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::UToF};
case AttributeType::Disabled: case AttributeType::Disabled:
return std::nullopt; return InputGenericInfo{};
} }
throw InvalidArgument("Invalid attribute type {}", type); throw InvalidArgument("Invalid attribute type {}", type);
} }
@ -746,18 +754,29 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
continue; continue;
} }
AddLabel(labels[label_index]); AddLabel(labels[label_index]);
const auto type{AttrTypes(*this, static_cast<u32>(index))}; const auto& generic{input_generics.at(index)};
if (!type) { const Id generic_id{generic.id};
if (!ValidId(generic_id)) {
OpReturnValue(Const(0.0f)); OpReturnValue(Const(0.0f));
++label_index; ++label_index;
continue; continue;
} }
const Id generic_id{input_generics.at(index)}; const Id pointer{
const Id pointer{is_array is_array ? OpAccessChain(generic.pointer_type, generic_id, vertex, masked_index)
? OpAccessChain(type->pointer, generic_id, vertex, masked_index) : OpAccessChain(generic.pointer_type, generic_id, masked_index)};
: OpAccessChain(type->pointer, generic_id, masked_index)}; const Id value{OpLoad(generic.component_type, pointer)};
const Id value{OpLoad(type->id, pointer)}; const Id result{[this, generic, value]() {
const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value}; switch (generic.load_op) {
case InputGenericLoadOp::Bitcast:
return OpBitcast(F32[1], value);
case InputGenericLoadOp::SToF:
return OpConvertSToF(F32[1], value);
case InputGenericLoadOp::UToF:
return OpConvertUToF(F32[1], value);
default:
return value;
};
}()};
OpReturnValue(result); OpReturnValue(result);
++label_index; ++label_index;
} }
@ -1457,7 +1476,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
const Id id{DefineInput(*this, type, true)}; const Id id{DefineInput(*this, type, true)};
Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
Name(id, fmt::format("in_attr{}", index)); Name(id, fmt::format("in_attr{}", index));
input_generics[index] = id; input_generics[index] = GetAttributeInfo(*this, input_type, id);
if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) { if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) {
Decorate(id, spv::Decoration::PassthroughNV); Decorate(id, spv::Decoration::PassthroughNV);

View file

@ -95,6 +95,20 @@ struct StorageDefinitions {
Id U32x4{}; Id U32x4{};
}; };
enum class InputGenericLoadOp {
None,
Bitcast,
SToF,
UToF,
};
struct InputGenericInfo {
Id id;
Id pointer_type;
Id component_type;
InputGenericLoadOp load_op;
};
struct GenericElementInfo { struct GenericElementInfo {
Id id{}; Id id{};
u32 first_element{}; u32 first_element{};
@ -283,7 +297,7 @@ public:
bool need_input_position_indirect{}; bool need_input_position_indirect{};
Id input_position{}; Id input_position{};
std::array<Id, 32> input_generics{}; std::array<InputGenericInfo, 32> input_generics{};
Id output_point_size{}; Id output_point_size{};
Id output_position{}; Id output_position{};

View file

@ -43,6 +43,7 @@ struct Profile {
bool support_gl_variable_aoffi{}; bool support_gl_variable_aoffi{};
bool support_gl_sparse_textures{}; bool support_gl_sparse_textures{};
bool support_gl_derivative_control{}; bool support_gl_derivative_control{};
bool support_scaled_attributes{};
bool warp_size_potentially_larger_than_guest{}; bool warp_size_potentially_larger_than_guest{};
@ -77,6 +78,8 @@ struct Profile {
bool has_gl_bool_ref_bug{}; bool has_gl_bool_ref_bug{};
/// Ignores SPIR-V ordered vs unordered using GLSL semantics /// Ignores SPIR-V ordered vs unordered using GLSL semantics
bool ignore_nan_fp_comparisons{}; bool ignore_nan_fp_comparisons{};
/// Some drivers have broken support for OpVectorExtractDynamic on subgroup mask inputs
bool has_broken_spirv_subgroup_mask_vector_extract_dynamic{};
u32 gl_max_compute_smem_size{}; u32 gl_max_compute_smem_size{};
}; };

View file

@ -17,6 +17,8 @@ enum class AttributeType : u8 {
Float, Float,
SignedInt, SignedInt,
UnsignedInt, UnsignedInt,
SignedScaled,
UnsignedScaled,
Disabled, Disabled,
}; };

View file

@ -281,7 +281,7 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core) target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PUBLIC glad shader_recompiler stb) target_link_libraries(video_core PUBLIC glad shader_recompiler stb)
if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) if (YUZU_USE_BUNDLED_FFMPEG AND NOT (WIN32 OR ANDROID))
add_dependencies(video_core ffmpeg-build) add_dependencies(video_core ffmpeg-build)
endif() endif()
@ -345,3 +345,7 @@ endif()
if (YUZU_ENABLE_LTO) if (YUZU_ENABLE_LTO)
set_property(TARGET video_core PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) set_property(TARGET video_core PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif() endif()
if (ANDROID AND ARCHITECTURE_arm64)
target_link_libraries(video_core PRIVATE adrenotools)
endif()

View file

@ -593,6 +593,12 @@ void Maxwell3D::ProcessQueryCondition() {
} }
void Maxwell3D::ProcessCounterReset() { void Maxwell3D::ProcessCounterReset() {
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return;
}
#endif
switch (regs.clear_report_value) { switch (regs.clear_report_value) {
case Regs::ClearReport::ZPassPixelCount: case Regs::ClearReport::ZPassPixelCount:
rasterizer->ResetCounter(QueryType::SamplesPassed); rasterizer->ResetCounter(QueryType::SamplesPassed);
@ -614,6 +620,12 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
case Regs::ReportSemaphore::Report::Payload: case Regs::ReportSemaphore::Report::Payload:
return regs.report_semaphore.payload; return regs.report_semaphore.payload;
case Regs::ReportSemaphore::Report::ZPassPixelCount64: case Regs::ReportSemaphore::Report::ZPassPixelCount64:
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return 120;
}
#endif
// Deferred. // Deferred.
rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed, rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
system.GPU().GetTicks()); system.GPU().GetTicks());

View file

@ -14,6 +14,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"

View file

@ -7,7 +7,7 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/frontend/emu_window.h" #include "core/frontend/graphics_context.h"
#include "video_core/control/scheduler.h" #include "video_core/control/scheduler.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"

View file

@ -5,6 +5,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace VideoCore { namespace VideoCore {

View file

@ -9,7 +9,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/emu_window.h" #include "core/frontend/framebuffer_layout.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
#include "video_core/renderer_null/renderer_null.h" #include "video_core/renderer_null/renderer_null.h"
namespace Null { namespace Null {

View file

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
#include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/control_flow.h"

View file

@ -347,6 +347,14 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
Maxwell::VertexAttribute::Size size) { Maxwell::VertexAttribute::Size size) {
if (device.MustEmulateScaledFormats()) {
if (type == Maxwell::VertexAttribute::Type::SScaled) {
type = Maxwell::VertexAttribute::Type::SInt;
} else if (type == Maxwell::VertexAttribute::Type::UScaled) {
type = Maxwell::VertexAttribute::Type::UInt;
}
}
const VkFormat format{([&]() { const VkFormat format{([&]() {
switch (type) { switch (type) {
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:

View file

@ -16,7 +16,7 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/telemetry.h" #include "common/telemetry.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/frontend/emu_window.h" #include "core/frontend/graphics_context.h"
#include "core/telemetry_session.h" #include "core/telemetry_session.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
@ -84,8 +84,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
std::unique_ptr<Core::Frontend::GraphicsContext> context_) try std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
: RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
Settings::values.renderer_debug.GetValue())), Settings::values.renderer_debug.GetValue())),
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
surface(CreateSurface(instance, render_window.GetWindowInfo())), surface(CreateSurface(instance, render_window.GetWindowInfo())),
@ -93,7 +93,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
state_tracker(), scheduler(device, state_tracker), state_tracker(), scheduler(device, state_tracker),
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
render_window.GetFramebufferLayout().height, false), render_window.GetFramebufferLayout().height, false),
present_manager(render_window, device, memory_allocator, scheduler, swapchain), present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
surface),
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager, blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
scheduler, screen_info), scheduler, screen_info),
rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,

View file

@ -63,7 +63,7 @@ private:
Core::Memory::Memory& cpu_memory; Core::Memory::Memory& cpu_memory;
Tegra::GPU& gpu; Tegra::GPU& gpu;
Common::DynamicLibrary library; std::shared_ptr<Common::DynamicLibrary> library;
vk::InstanceDispatch dld; vk::InstanceDispatch dld;
vk::Instance instance; vk::Instance instance;

View file

@ -76,10 +76,18 @@ struct ScreenRectVertex {
constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format off // clang-format off
#ifdef ANDROID
// Android renders in portrait, so rotate the matrix.
return { 0.f, 2.f / width, 0.f, 0.f,
-2.f / height, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
1.f, -1.f, 0.f, 1.f};
#else
return { 2.f / width, 0.f, 0.f, 0.f, return { 2.f / width, 0.f, 0.f, 0.f,
0.f, 2.f / height, 0.f, 0.f, 0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f,
-1.f, -1.f, 0.f, 1.f}; -1.f, -1.f, 0.f, 1.f};
#endif // ANDROID
// clang-format on // clang-format on
} }
@ -441,7 +449,12 @@ void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& f
if (const std::size_t swapchain_images = swapchain.GetImageCount(); if (const std::size_t swapchain_images = swapchain.GetImageCount();
swapchain_images != image_count || current_srgb != is_srgb) { swapchain_images != image_count || current_srgb != is_srgb) {
current_srgb = is_srgb; current_srgb = is_srgb;
#ifdef ANDROID
// Android is already ordered the same as Switch.
image_view_format = current_srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
#else
image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
#endif
image_count = swapchain_images; image_count = swapchain_images;
Recreate(); Recreate();
} }

View file

@ -303,9 +303,13 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
DescriptorPool& descriptor_pool) DescriptorPool& descriptor_pool)
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_}, staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
uint8_pass(device, scheduler, descriptor_pool, staging_pool, compute_pass_descriptor_queue),
quad_index_pass(device, scheduler, descriptor_pool, staging_pool, quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue) { compute_pass_descriptor_queue) {
if (device.GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
// TODO: FixMe: Uint8Pass compute shader does not build on some Qualcomm drivers.
uint8_pass = std::make_unique<Uint8Pass>(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue);
}
quad_array_index_buffer = std::make_shared<QuadArrayIndexBuffer>(device_, memory_allocator_, quad_array_index_buffer = std::make_shared<QuadArrayIndexBuffer>(device_, memory_allocator_,
scheduler_, staging_pool_); scheduler_, staging_pool_);
quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_, quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
@ -442,7 +446,9 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
topology == PrimitiveTopology::QuadStrip); topology == PrimitiveTopology::QuadStrip);
} else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) { } else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) {
vk_index_type = VK_INDEX_TYPE_UINT16; vk_index_type = VK_INDEX_TYPE_UINT16;
std::tie(vk_buffer, vk_offset) = uint8_pass.Assemble(num_indices, buffer, offset); if (uint8_pass) {
std::tie(vk_buffer, vk_offset) = uint8_pass->Assemble(num_indices, buffer, offset);
}
} }
if (vk_buffer == VK_NULL_HANDLE) { if (vk_buffer == VK_NULL_HANDLE) {
// Vulkan doesn't support null index buffers. Replace it with our own null buffer. // Vulkan doesn't support null index buffers. Replace it with our own null buffer.

View file

@ -139,7 +139,7 @@ private:
vk::Buffer null_buffer; vk::Buffer null_buffer;
MemoryCommit null_buffer_commit; MemoryCommit null_buffer_commit;
Uint8Pass uint8_pass; std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass; QuadIndexedPass quad_index_pass;
}; };

View file

@ -114,14 +114,16 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
return Shader::AttributeType::Disabled; return Shader::AttributeType::Disabled;
case Maxwell::VertexAttribute::Type::SNorm: case Maxwell::VertexAttribute::Type::SNorm:
case Maxwell::VertexAttribute::Type::UNorm: case Maxwell::VertexAttribute::Type::UNorm:
case Maxwell::VertexAttribute::Type::UScaled:
case Maxwell::VertexAttribute::Type::SScaled:
case Maxwell::VertexAttribute::Type::Float: case Maxwell::VertexAttribute::Type::Float:
return Shader::AttributeType::Float; return Shader::AttributeType::Float;
case Maxwell::VertexAttribute::Type::SInt: case Maxwell::VertexAttribute::Type::SInt:
return Shader::AttributeType::SignedInt; return Shader::AttributeType::SignedInt;
case Maxwell::VertexAttribute::Type::UInt: case Maxwell::VertexAttribute::Type::UInt:
return Shader::AttributeType::UnsignedInt; return Shader::AttributeType::UnsignedInt;
case Maxwell::VertexAttribute::Type::UScaled:
return Shader::AttributeType::UnsignedScaled;
case Maxwell::VertexAttribute::Type::SScaled:
return Shader::AttributeType::SignedScaled;
} }
return Shader::AttributeType::Float; return Shader::AttributeType::Float;
} }
@ -286,14 +288,17 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
texture_cache{texture_cache_}, shader_notify{shader_notify_}, texture_cache{texture_cache_}, shader_notify{shader_notify_},
use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), workers(device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY
? 1
: (std::max(std::thread::hardware_concurrency(), 2U) - 1),
"VkPipelineBuilder"),
serialization_thread(1, "VkPipelineSerialization") { serialization_thread(1, "VkPipelineSerialization") {
const auto& float_control{device.FloatControlProperties()}; const auto& float_control{device.FloatControlProperties()};
const VkDriverId driver_id{device.GetDriverID()}; const VkDriverId driver_id{device.GetDriverID()};
profile = Shader::Profile{ profile = Shader::Profile{
.supported_spirv = device.SupportedSpirvVersion(), .supported_spirv = device.SupportedSpirvVersion(),
.unified_descriptor_binding = true, .unified_descriptor_binding = true,
.support_descriptor_aliasing = true, .support_descriptor_aliasing = device.IsDescriptorAliasingSupported(),
.support_int8 = device.IsInt8Supported(), .support_int8 = device.IsInt8Supported(),
.support_int16 = device.IsShaderInt16Supported(), .support_int16 = device.IsShaderInt16Supported(),
.support_int64 = device.IsShaderInt64Supported(), .support_int64 = device.IsShaderInt64Supported(),
@ -324,6 +329,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.support_derivative_control = true, .support_derivative_control = true,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(),
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
@ -341,7 +347,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.has_broken_signed_operations = false, .has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false, .ignore_nan_fp_comparisons = false,
}; .has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY};
host_info = Shader::HostTranslateInfo{ host_info = Shader::HostTranslateInfo{
.support_float16 = device.IsFloat16Supported(), .support_float16 = device.IsFloat16Supported(),
.support_int64 = device.IsShaderInt64Supported(), .support_int64 = device.IsShaderInt64Supported(),

View file

@ -4,10 +4,12 @@
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/frontend/emu_window.h"
#include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_present_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_surface.h"
namespace Vulkan { namespace Vulkan {
@ -92,14 +94,17 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat form
} // Anonymous namespace } // Anonymous namespace
PresentManager::PresentManager(Core::Frontend::EmuWindow& render_window_, const Device& device_, PresentManager::PresentManager(const vk::Instance& instance_,
Core::Frontend::EmuWindow& render_window_, const Device& device_,
MemoryAllocator& memory_allocator_, Scheduler& scheduler_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
Swapchain& swapchain_) Swapchain& swapchain_, vk::SurfaceKHR& surface_)
: render_window{render_window_}, device{device_}, : instance{instance_}, render_window{render_window_}, device{device_},
memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_},
blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}, surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(),
swapchain.GetImageViewFormat())},
use_present_thread{Settings::values.async_presentation.GetValue()}, use_present_thread{Settings::values.async_presentation.GetValue()},
image_count{swapchain.GetImageCount()} { image_count{swapchain.GetImageCount()}, last_render_surface{
render_window_.GetWindowInfo().render_surface} {
auto& dld = device.GetLogical(); auto& dld = device.GetLogical();
cmdpool = dld.CreateCommandPool({ cmdpool = dld.CreateCommandPool({
@ -290,10 +295,19 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain); MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
const auto recreate_swapchain = [&] { const auto recreate_swapchain = [&] {
swapchain.Create(frame->width, frame->height, frame->is_srgb); swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb);
image_count = swapchain.GetImageCount(); image_count = swapchain.GetImageCount();
}; };
#ifdef ANDROID
// If the frontend recreated the surface, recreate the renderer surface and swapchain.
if (last_render_surface != render_window.GetWindowInfo().render_surface) {
last_render_surface = render_window.GetWindowInfo().render_surface;
surface = CreateSurface(instance, render_window.GetWindowInfo());
recreate_swapchain();
}
#endif
// If the size or colorspace of the incoming frames has changed, recreate the swapchain // If the size or colorspace of the incoming frames has changed, recreate the swapchain
// to account for that. // to account for that.
const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb); const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb);

View file

@ -37,8 +37,9 @@ struct Frame {
class PresentManager { class PresentManager {
public: public:
PresentManager(Core::Frontend::EmuWindow& render_window, const Device& device, PresentManager(const vk::Instance& instance, Core::Frontend::EmuWindow& render_window,
MemoryAllocator& memory_allocator, Scheduler& scheduler, Swapchain& swapchain); const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
Swapchain& swapchain, vk::SurfaceKHR& surface);
~PresentManager(); ~PresentManager();
/// Returns the last used presentation frame /// Returns the last used presentation frame
@ -60,11 +61,13 @@ private:
void CopyToSwapchain(Frame* frame); void CopyToSwapchain(Frame* frame);
private: private:
const vk::Instance& instance;
Core::Frontend::EmuWindow& render_window; Core::Frontend::EmuWindow& render_window;
const Device& device; const Device& device;
MemoryAllocator& memory_allocator; MemoryAllocator& memory_allocator;
Scheduler& scheduler; Scheduler& scheduler;
Swapchain& swapchain; Swapchain& swapchain;
vk::SurfaceKHR& surface;
vk::CommandPool cmdpool; vk::CommandPool cmdpool;
std::vector<Frame> frames; std::vector<Frame> frames;
std::queue<Frame*> present_queue; std::queue<Frame*> present_queue;
@ -77,7 +80,8 @@ private:
std::jthread present_thread; std::jthread present_thread;
bool blit_supported; bool blit_supported;
bool use_present_thread; bool use_present_thread;
std::size_t image_count; std::size_t image_count{};
void* last_render_surface{};
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -188,7 +188,14 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
FlushWork(); FlushWork();
gpu_memory->FlushCaching(); gpu_memory->FlushCaching();
#if ANDROID
if (Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
query_cache.UpdateCounters(); query_cache.UpdateCounters();
}
#else
query_cache.UpdateCounters();
#endif
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
if (!pipeline) { if (!pipeline) {
@ -272,7 +279,14 @@ void RasterizerVulkan::DrawTexture() {
SCOPE_EXIT({ gpu.TickWork(); }); SCOPE_EXIT({ gpu.TickWork(); });
FlushWork(); FlushWork();
#if ANDROID
if (Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
query_cache.UpdateCounters(); query_cache.UpdateCounters();
}
#else
query_cache.UpdateCounters();
#endif
texture_cache.SynchronizeGraphicsDescriptors(); texture_cache.SynchronizeGraphicsDescriptors();
texture_cache.UpdateRenderTargets(false); texture_cache.UpdateRenderTargets(false);
@ -743,7 +757,11 @@ void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_load
} }
void RasterizerVulkan::FlushWork() { void RasterizerVulkan::FlushWork() {
#ifdef ANDROID
static constexpr u32 DRAWS_TO_DISPATCH = 1024;
#else
static constexpr u32 DRAWS_TO_DISPATCH = 4096; static constexpr u32 DRAWS_TO_DISPATCH = 4096;
#endif // ANDROID
// Only check multiples of 8 draws // Only check multiples of 8 draws
static_assert(DRAWS_TO_DISPATCH % 8 == 0); static_assert(DRAWS_TO_DISPATCH % 8 == 0);

View file

@ -239,8 +239,15 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
void Scheduler::AllocateNewContext() { void Scheduler::AllocateNewContext() {
// Enable counters once again. These are disabled when a command buffer is finished. // Enable counters once again. These are disabled when a command buffer is finished.
if (query_cache) { if (query_cache) {
#if ANDROID
if (Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
query_cache->UpdateCounters(); query_cache->UpdateCounters();
} }
#else
query_cache->UpdateCounters();
#endif
}
} }
void Scheduler::InvalidateState() { void Scheduler::InvalidateState() {
@ -250,7 +257,14 @@ void Scheduler::InvalidateState() {
} }
void Scheduler::EndPendingOperations() { void Scheduler::EndPendingOperations() {
#if ANDROID
if (Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
query_cache->DisableStreams(); query_cache->DisableStreams();
}
#else
query_cache->DisableStreams();
#endif
EndRenderPass(); EndRenderPass();
} }

View file

@ -107,16 +107,17 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
u32 width_, u32 height_, bool srgb) u32 width_, u32 height_, bool srgb)
: surface{surface_}, device{device_}, scheduler{scheduler_} { : surface{surface_}, device{device_}, scheduler{scheduler_} {
Create(width_, height_, srgb); Create(surface_, width_, height_, srgb);
} }
Swapchain::~Swapchain() = default; Swapchain::~Swapchain() = default;
void Swapchain::Create(u32 width_, u32 height_, bool srgb) { void Swapchain::Create(VkSurfaceKHR surface_, u32 width_, u32 height_, bool srgb) {
is_outdated = false; is_outdated = false;
is_suboptimal = false; is_suboptimal = false;
width = width_; width = width_;
height = height_; height = height_;
surface = surface_;
const auto physical_device = device.GetPhysical(); const auto physical_device = device.GetPhysical();
const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
@ -266,7 +267,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
images = swapchain.GetImages(); images = swapchain.GetImages();
image_count = static_cast<u32>(images.size()); image_count = static_cast<u32>(images.size());
#ifdef ANDROID
// Android is already ordered the same as Switch.
image_view_format = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
#else
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
#endif
} }
void Swapchain::CreateSemaphores() { void Swapchain::CreateSemaphores() {

View file

@ -24,7 +24,7 @@ public:
~Swapchain(); ~Swapchain();
/// Creates (or recreates) the swapchain with a given size. /// Creates (or recreates) the swapchain with a given size.
void Create(u32 width, u32 height, bool srgb); void Create(VkSurfaceKHR surface, u32 width, u32 height, bool srgb);
/// Acquires the next image in the swapchain, waits as needed. /// Acquires the next image in the swapchain, waits as needed.
bool AcquireNextImage(); bool AcquireNextImage();
@ -118,7 +118,7 @@ private:
bool NeedsPresentModeUpdate() const; bool NeedsPresentModeUpdate() const;
const VkSurfaceKHR surface; VkSurfaceKHR surface;
const Device& device; const Device& device;
Scheduler& scheduler; Scheduler& scheduler;

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
#include <adrenotools/driver.h>
#endif
#include "common/literals.h" #include "common/literals.h"
#include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.h" #include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
@ -144,6 +148,9 @@ void TurboMode::Run(std::stop_token stop_token) {
auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()}; auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()};
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
adrenotools_set_turbo(true);
#else
// Reset the fence. // Reset the fence.
fence.Reset(); fence.Reset();
@ -209,7 +216,7 @@ void TurboMode::Run(std::stop_token stop_token) {
// Wait for completion. // Wait for completion.
fence.Wait(); fence.Wait();
#endif
// Wait for the next graphics queue submission if necessary. // Wait for the next graphics queue submission if necessary.
std::unique_lock lk{m_submission_lock}; std::unique_lock lk{m_submission_lock};
Common::CondvarWait(m_submission_cv, lk, stop_token, [this] { Common::CondvarWait(m_submission_cv, lk, stop_token, [this] {
@ -217,6 +224,9 @@ void TurboMode::Run(std::stop_token stop_token) {
std::chrono::milliseconds{100}; std::chrono::milliseconds{100};
}); });
} }
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
adrenotools_set_turbo(false);
#endif
} }
} // namespace Vulkan } // namespace Vulkan

View file

@ -31,7 +31,7 @@ struct DescriptorUpdateEntry {
class UpdateDescriptorQueue final { class UpdateDescriptorQueue final {
// This should be plenty for the vast majority of cases. Most desktop platforms only // This should be plenty for the vast majority of cases. Most desktop platforms only
// provide up to 3 swapchain images. // provide up to 3 swapchain images.
static constexpr size_t FRAMES_IN_FLIGHT = 5; static constexpr size_t FRAMES_IN_FLIGHT = 7;
static constexpr size_t FRAME_PAYLOAD_SIZE = 0x20000; static constexpr size_t FRAME_PAYLOAD_SIZE = 0x20000;
static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT; static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;

View file

@ -13,11 +13,39 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
[[maybe_unused]] void* user_data) { [[maybe_unused]] void* user_data) {
// Skip logging known false-positive validation errors // Skip logging known false-positive validation errors
switch (static_cast<u32>(data->messageIdNumber)) { switch (static_cast<u32>(data->messageIdNumber)) {
#ifdef ANDROID
case 0xbf9cf353u: // VUID-vkCmdBindVertexBuffers2-pBuffers-04111
// The below are due to incorrect reporting of extendedDynamicState
case 0x1093bebbu: // VUID-vkCmdSetCullMode-None-03384
case 0x9215850fu: // VUID-vkCmdSetDepthTestEnable-None-03352
case 0x86bf18dcu: // VUID-vkCmdSetDepthWriteEnable-None-03354
case 0x0792ad08u: // VUID-vkCmdSetStencilOp-None-03351
case 0x93e1ba4eu: // VUID-vkCmdSetFrontFace-None-03383
case 0xac9c13c5u: // VUID-vkCmdSetStencilTestEnable-None-03350
case 0xc9a2001bu: // VUID-vkCmdSetDepthBoundsTestEnable-None-03349
case 0x8b7159a7u: // VUID-vkCmdSetDepthCompareOp-None-03353
// The below are due to incorrect reporting of extendedDynamicState2
case 0xb13c8036u: // VUID-vkCmdSetDepthBiasEnable-None-04872
case 0xdff2e5c1u: // VUID-vkCmdSetRasterizerDiscardEnable-None-04871
case 0x0cc85f41u: // VUID-vkCmdSetPrimitiveRestartEnable-None-04866
case 0x01257b492: // VUID-vkCmdSetLogicOpEXT-None-0486
// The below are due to incorrect reporting of vertexInputDynamicState
case 0x398e0dabu: // VUID-vkCmdSetVertexInputEXT-None-04790
// The below are due to incorrect reporting of extendedDynamicState3
case 0x970c11a5u: // VUID-vkCmdSetColorWriteMaskEXT-extendedDynamicState3ColorWriteMask-07364
case 0x6b453f78u: // VUID-vkCmdSetColorBlendEnableEXT-extendedDynamicState3ColorBlendEnable-07355
case 0xf66469d0u: // VUID-vkCmdSetColorBlendEquationEXT-extendedDynamicState3ColorBlendEquation-07356
case 0x1d43405eu: // VUID-vkCmdSetLogicOpEnableEXT-extendedDynamicState3LogicOpEnable-07365
case 0x638462e8u: // VUID-vkCmdSetDepthClampEnableEXT-extendedDynamicState3DepthClampEnable-07448
// Misc
case 0xe0a2da61u: // VUID-vkCmdDrawIndexed-format-07753
#else
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0]) case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use
case 0x55362756u: // Descriptor binding and framebuffer attachment overlap case 0x55362756u: // Descriptor binding and framebuffer attachment overlap
#endif
return VK_FALSE; return VK_FALSE;
default: default:
break; break;

View file

@ -18,6 +18,10 @@
#include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
#include <adrenotools/bcenabler.h>
#endif
namespace Vulkan { namespace Vulkan {
using namespace Common::Literals; using namespace Common::Literals;
namespace { namespace {
@ -262,6 +266,32 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
return format_properties; return format_properties;
} }
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
void OverrideBcnFormats(std::unordered_map<VkFormat, VkFormatProperties>& format_properties) {
// These properties are extracted from Adreno driver 512.687.0
constexpr VkFormatFeatureFlags tiling_features{
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
constexpr VkFormatFeatureFlags buffer_features{VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT};
static constexpr std::array bcn_formats{
VK_FORMAT_BC1_RGBA_SRGB_BLOCK, VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_BC2_SRGB_BLOCK,
VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_BC3_SRGB_BLOCK, VK_FORMAT_BC3_UNORM_BLOCK,
VK_FORMAT_BC4_SNORM_BLOCK, VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_BC5_SNORM_BLOCK,
VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_BC6H_SFLOAT_BLOCK, VK_FORMAT_BC6H_UFLOAT_BLOCK,
VK_FORMAT_BC7_SRGB_BLOCK, VK_FORMAT_BC7_UNORM_BLOCK,
};
for (const auto format : bcn_formats) {
format_properties[format].linearTilingFeatures = tiling_features;
format_properties[format].optimalTilingFeatures = tiling_features;
format_properties[format].bufferFeatures = buffer_features;
}
}
#endif
NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
const std::set<std::string, std::less<>>& exts) { const std::set<std::string, std::less<>>& exts) {
if (exts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { if (exts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
@ -302,6 +332,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_suitable = GetSuitability(surface != nullptr); const bool is_suitable = GetSuitability(surface != nullptr);
const VkDriverId driver_id = properties.driver.driverID; const VkDriverId driver_id = properties.driver.driverID;
const auto device_id = properties.properties.deviceID;
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
const bool is_amd_driver = const bool is_amd_driver =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
@ -310,9 +341,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
const bool is_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY; const bool is_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK; const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
const bool is_s8gen2 = device_id == 0x43050a01;
if (is_mvk && !is_suitable) { if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Unsuitable driver is MoltenVK, continuing anyway"); LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
} else if (!is_suitable) { } else if (!is_suitable) {
throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER); throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
} }
@ -355,6 +389,59 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
CollectPhysicalMemoryInfo(); CollectPhysicalMemoryInfo();
CollectToolingInfo(); CollectToolingInfo();
#ifdef ANDROID
if (is_qualcomm || is_turnip) {
LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
extensions.custom_border_color = false;
loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
}
if (is_qualcomm) {
must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
extensions.push_descriptor = false;
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
#ifdef ARCHITECTURE_arm64
// Patch the driver to enable BCn textures.
const auto major = (properties.properties.driverVersion >> 24) << 2;
const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU;
const auto vendor = properties.properties.vendorID;
const auto patch_status = adrenotools_get_bcn_type(major, minor, vendor);
if (patch_status == ADRENOTOOLS_BCN_PATCH) {
LOG_INFO(Render_Vulkan, "Patching Adreno driver to support BCn texture formats");
if (adrenotools_patch_bcn(
reinterpret_cast<void*>(dld.vkGetPhysicalDeviceFormatProperties))) {
OverrideBcnFormats(format_properties);
} else {
LOG_ERROR(Render_Vulkan, "Patch failed! Driver code may now crash");
}
} else if (patch_status == ADRENOTOOLS_BCN_BLOB) {
LOG_INFO(Render_Vulkan, "Adreno driver supports BCn textures without patches");
} else {
LOG_WARNING(Render_Vulkan, "Adreno driver can't be patched to enable BCn textures");
}
#endif // ARCHITECTURE_arm64
}
const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if (is_arm) {
must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
}
#endif // ANDROID
if (is_nvidia) { if (is_nvidia) {
const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
const auto arch = GetNvidiaArchitecture(physical, supported_extensions); const auto arch = GetNvidiaArchitecture(physical, supported_extensions);
@ -388,7 +475,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_radv) { if (extensions.extended_dynamic_state2 && (is_radv || is_qualcomm)) {
const u32 version = (properties.properties.driverVersion << 3) >> 3; const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING( LOG_WARNING(
@ -415,7 +502,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
dynamic_state3_enables = false; dynamic_state3_enables = false;
} }
} }
if (extensions.vertex_input_dynamic_state && is_radv) { if (extensions.vertex_input_dynamic_state && (is_radv || is_qualcomm)) {
// Qualcomm S8gen2 drivers do not properly support vertex_input_dynamic_state.
// TODO(ameerj): Blacklist only offending driver versions // TODO(ameerj): Blacklist only offending driver versions
// TODO(ameerj): Confirm if RDNA1 is affected // TODO(ameerj): Confirm if RDNA1 is affected
const bool is_rdna2 = const bool is_rdna2 =
@ -467,8 +555,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
cant_blit_msaa = true; cant_blit_msaa = true;
} }
if (is_intel_anv) { if (is_intel_anv || (is_qualcomm && !is_s8gen2)) {
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format"); LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
must_emulate_bgr565 = true; must_emulate_bgr565 = true;
} }
if (extensions.push_descriptor && is_intel_anv) { if (extensions.push_descriptor && is_intel_anv) {
@ -633,7 +721,8 @@ bool Device::ShouldBoostClocks() const {
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS || driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS ||
driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA ||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP;
const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;

View file

@ -295,6 +295,11 @@ public:
return features.features.textureCompressionASTC_LDR; return features.features.textureCompressionASTC_LDR;
} }
/// Returns true if descriptor aliasing is natively supported.
bool IsDescriptorAliasingSupported() const {
return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
}
/// Returns true if the device supports float16 natively. /// Returns true if the device supports float16 natively.
bool IsFloat16Supported() const { bool IsFloat16Supported() const {
return features.shader_float16_int8.shaderFloat16; return features.shader_float16_int8.shaderFloat16;
@ -495,6 +500,10 @@ public:
} }
bool HasTimelineSemaphore() const { bool HasTimelineSemaphore() const {
if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
// Timeline semaphores do not work properly on all Qualcomm drivers.
return false;
}
return features.timeline_semaphore.timelineSemaphore; return features.timeline_semaphore.timelineSemaphore;
} }
@ -551,6 +560,10 @@ public:
return cant_blit_msaa; return cant_blit_msaa;
} }
bool MustEmulateScaledFormats() const {
return must_emulate_scaled_formats;
}
bool MustEmulateBGR565() const { bool MustEmulateBGR565() const {
return must_emulate_bgr565; return must_emulate_bgr565;
} }
@ -666,6 +679,7 @@ private:
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3. bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.

View file

@ -10,29 +10,35 @@
namespace Vulkan { namespace Vulkan {
Common::DynamicLibrary OpenLibrary() { std::shared_ptr<Common::DynamicLibrary> OpenLibrary(
[[maybe_unused]] Core::Frontend::GraphicsContext* context) {
LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library"); LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
Common::DynamicLibrary library; #if defined(ANDROID) && defined(ARCHITECTURE_arm64)
// Android manages its Vulkan driver from the frontend.
return context->GetDriverLibrary();
#else
auto library = std::make_shared<Common::DynamicLibrary>();
#ifdef __APPLE__ #ifdef __APPLE__
// Check if a path to a specific Vulkan library has been specified. // Check if a path to a specific Vulkan library has been specified.
char* const libvulkan_env = std::getenv("LIBVULKAN_PATH"); char* const libvulkan_env = std::getenv("LIBVULKAN_PATH");
if (!libvulkan_env || !library.Open(libvulkan_env)) { if (!libvulkan_env || !library->Open(libvulkan_env)) {
// Use the libvulkan.dylib from the application bundle. // Use the libvulkan.dylib from the application bundle.
const auto filename = const auto filename =
Common::FS::GetBundleDirectory() / "Contents/Frameworks/libvulkan.dylib"; Common::FS::GetBundleDirectory() / "Contents/Frameworks/libvulkan.dylib";
void(library.Open(Common::FS::PathToUTF8String(filename).c_str())); void(library->Open(Common::FS::PathToUTF8String(filename).c_str()));
} }
#else #else
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename); LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
if (!library.Open(filename.c_str())) { if (!library->Open(filename.c_str())) {
// Android devices may not have libvulkan.so.1, only libvulkan.so. // Android devices may not have libvulkan.so.1, only libvulkan.so.
filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename); LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
void(library.Open(filename.c_str())); void(library->Open(filename.c_str()));
} }
#endif #endif
return library; return library;
#endif
} }
} // namespace Vulkan } // namespace Vulkan

View file

@ -3,10 +3,14 @@
#pragma once #pragma once
#include <memory>
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
#include "core/frontend/graphics_context.h"
namespace Vulkan { namespace Vulkan {
Common::DynamicLibrary OpenLibrary(); std::shared_ptr<Common::DynamicLibrary> OpenLibrary(
[[maybe_unused]] Core::Frontend::GraphicsContext* context = nullptr);
} // namespace Vulkan } // namespace Vulkan

View file

@ -46,6 +46,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/cpu_manager.h" #include "core/cpu_manager.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/frontend/graphics_context.h"
#include "input_common/drivers/camera.h" #include "input_common/drivers/camera.h"
#include "input_common/drivers/keyboard.h" #include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h" #include "input_common/drivers/mouse.h"

View file

@ -515,8 +515,8 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
auto wsi = QtCommon::GetWindowSystemInfo(window); auto wsi = QtCommon::GetWindowSystemInfo(window);
vk::InstanceDispatch dld; vk::InstanceDispatch dld;
const Common::DynamicLibrary library = OpenLibrary(); const auto library = OpenLibrary();
const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type); const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
vk::SurfaceKHR surface = CreateSurface(instance, wsi); vk::SurfaceKHR surface = CreateSurface(instance, wsi);

View file

@ -25,9 +25,9 @@ void CheckVulkan() {
// Just start the Vulkan loader, this will crash if something is wrong // Just start the Vulkan loader, this will crash if something is wrong
try { try {
Vulkan::vk::InstanceDispatch dld; Vulkan::vk::InstanceDispatch dld;
const Common::DynamicLibrary library = Vulkan::OpenLibrary(); const auto library = Vulkan::OpenLibrary();
const Vulkan::vk::Instance instance = const Vulkan::vk::Instance instance =
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1); Vulkan::CreateInstance(*library, dld, VK_API_VERSION_1_1);
} catch (const Vulkan::vk::Exception& exception) { } catch (const Vulkan::vk::Exception& exception) {
fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what());

View file

@ -318,7 +318,7 @@ anti_aliasing =
fullscreen_mode = fullscreen_mode =
# Aspect ratio # Aspect ratio
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
aspect_ratio = aspect_ratio =
# Anisotropic filtering # Anisotropic filtering

View file

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <utility> #include <utility>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
struct SDL_Window; struct SDL_Window;