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(CMakeDependentOption)
include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
@ -19,7 +20,7 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
option(ENABLE_LIBUSB "Enable the use of LibUSB" ON)
option(ENABLE_LIBUSB "Enable the use of LibUSB" "NOT ${ANDROID}")
option(ENABLE_OPENGL "Enable OpenGL" ON)
mark_as_advanced(FORCE ENABLE_OPENGL)
@ -48,7 +49,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
option(YUZU_ROOM "Compile LDN room server" ON)
option(YUZU_ROOM "Compile LDN room server" "NOT ${ANDROID}")
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
@ -60,7 +61,67 @@ option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON)
set(LLVM_VERSION "15.0.6")
# Note: even though libcxx and libcxxabi have separate releases on the project page,
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
# must fetch the source release for the entire llvm tree.
FetchContent_Declare(llvm
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
TLS_VERIFY TRUE
)
FetchContent_MakeAvailable(llvm)
# libcxx has support for most of the range library, but it's gated behind a flag:
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
# Disable standard header inclusion
set(ANDROID_STL "none")
# libcxxabi
set(LIBCXXABI_INCLUDE_TESTS OFF)
set(LIBCXXABI_ENABLE_SHARED FALSE)
set(LIBCXXABI_ENABLE_STATIC TRUE)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
link_libraries(cxxabi_static)
# libcxx
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
set(LIBCXX_CXX_ABI "libcxxabi")
set(LIBCXX_INCLUDE_TESTS OFF)
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
set(LIBCXX_INCLUDE_DOCS OFF)
set(LIBCXX_ENABLE_SHARED FALSE)
set(LIBCXX_ENABLE_STATIC TRUE)
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
link_libraries(cxx_static cxx-headers)
endif()
if (YUZU_USE_BUNDLED_VCPKG)
if (ANDROID)
set(ENV{ANDROID_NDK_HOME} "${ANDROID_NDK}")
list(APPEND VCPKG_MANIFEST_FEATURES "android")
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
set(VCPKG_TARGET_TRIPLET "arm64-android")
# this is to avoid CMake using the host pkg-config to find the host
# libraries when building for Android targets
set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
set(VCPKG_TARGET_TRIPLET "x64-android")
set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
else()
message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}")
endif()
endif()
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
@ -457,7 +518,7 @@ set(FFmpeg_COMPONENTS
avutil
swscale)
if (UNIX AND NOT APPLE)
if (UNIX AND NOT APPLE AND NOT ANDROID)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()

View file

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

View file

@ -7,6 +7,7 @@
# prefix_var: name of a variable which will be set with the path to the extracted contents
function(download_bundled_external remote_path lib_name prefix_var)
set(package_base_url "https://github.com/yuzu-emu/")
set(package_repo "no_platform")
set(package_extension "no_platform")
if (WIN32)
@ -15,10 +16,14 @@ if (WIN32)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(package_repo "ext-linux-bin/raw/main/")
set(package_extension ".tar.xz")
elseif (ANDROID)
set(package_base_url "https://gitlab.com/tertius42/")
set(package_repo "ext-android-bin/-/raw/main/")
set(package_extension ".tar.xz")
else()
message(FATAL_ERROR "No package available for this platform")
endif()
set(package_url "https://github.com/yuzu-emu/${package_repo}")
set(package_url "${package_base_url}${package_repo}")
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
if (NOT EXISTS "${prefix}")

View file

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

View file

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

View file

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
if (NOT WIN32)
if (NOT WIN32 AND NOT ANDROID)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
@ -44,10 +44,12 @@ if (NOT WIN32)
endforeach()
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
if (NOT ANDROID)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
endif()
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
@ -121,6 +123,26 @@ if (NOT WIN32)
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
find_program(BASH_PROGRAM bash REQUIRED)
set(FFmpeg_CROSS_COMPILE_FLAGS "")
if (ANDROID)
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" FFmpeg_HOST_SYSTEM_NAME)
set(TOOLCHAIN "${ANDROID_NDK}/toolchains/llvm/prebuilt/${FFmpeg_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(SYSROOT "${TOOLCHAIN}/sysroot")
set(FFmpeg_CPU "armv8-a")
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--arch=arm64
#--cpu=${FFmpeg_CPU}
--enable-cross-compile
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android-
--sysroot=${SYSROOT}
--target-os=android
--extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
--extra-ldflags="-nostdlib"
)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
@ -129,7 +151,7 @@ if (NOT WIN32)
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avformat
--disable-doc
@ -146,12 +168,14 @@ if (NOT WIN32)
--cc="${FFmpeg_CC}"
--cxx="${FFmpeg_CXX}"
${FFmpeg_HWACCEL_FLAGS}
${FFmpeg_CROSS_COMPILE_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_CC)
unset(FFmpeg_CXX)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_CROSS_COMPILE_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
@ -197,7 +221,38 @@ if (NOT WIN32)
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else(WIN32)
elseif(ANDROID)
# Use yuzu FFmpeg binaries
if (ARCHITECTURE_arm64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-aarch64")
elseif (ARCHITECTURE_x86_64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-x86_64")
else()
message(FATAL_ERROR "Unsupported architecture for Android FFmpeg")
endif()
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/lib" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/libavcodec.so
${FFmpeg_LIBRARY_DIR}/libavdevice.so
${FFmpeg_LIBRARY_DIR}/libavfilter.so
${FFmpeg_LIBRARY_DIR}/libavformat.so
${FFmpeg_LIBRARY_DIR}/libavutil.so
${FFmpeg_LIBRARY_DIR}/libswresample.so
${FFmpeg_LIBRARY_DIR}/libswscale.so
${FFmpeg_LIBRARY_DIR}/libvpx.a
${FFmpeg_LIBRARY_DIR}/libx264.a
CACHE PATH "Paths to FFmpeg libraries" FORCE)
# exported variables
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
elseif(WIN32)
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-5.1.3")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
@ -206,7 +261,6 @@ else(WIN32)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib

View file

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

View file

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

View file

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

View file

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

View file

@ -354,12 +354,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
v.setPadding(left, cutInsets.top, right, 0)
binding.showFpsText.setPadding(
cutInsets.left,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
// Ensure FPS text doesn't get cut off by rounded display corners
val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
if (cutInsets.left == 0) {
binding.showFpsText.setPadding(
sidePadding,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
} else {
binding.showFpsText.setPadding(
cutInsets.left,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
}
windowInsets
}
}

View file

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

View file

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

View file

@ -155,6 +155,14 @@ if (WIN32)
target_link_libraries(common PRIVATE ntdll)
endif()
if(ANDROID)
target_sources(common
PRIVATE
fs/fs_android.cpp
fs/fs_android.h
)
endif()
if(ARCHITECTURE_x86_64)
target_sources(common
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 PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID)
# For ASharedMemory_create
target_link_libraries(common PRIVATE android)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(common PRIVATE precompiled_headers.h)
endif()

View file

@ -22,6 +22,8 @@ DynamicLibrary::DynamicLibrary(const char* filename) {
void(Open(filename));
}
DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {}
DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
: 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.
explicit DynamicLibrary(const char* filename);
/// Initializes the dynamic library with an already opened handle.
explicit DynamicLibrary(void* handle_);
/// Moves the library.
DynamicLibrary(DynamicLibrary&&) noexcept;
DynamicLibrary& operator=(DynamicLibrary&&) noexcept;

View file

@ -30,7 +30,8 @@ std::string NativeErrorToString(int e) {
return ret;
#else
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)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);

View file

@ -5,6 +5,9 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
#include "common/logging/log.h"
#ifdef _WIN32
@ -252,6 +255,23 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
} else {
_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
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif
@ -372,6 +392,23 @@ u64 IOFile::GetSize() const {
// Flush any unwritten buffered data into the file prior to retrieving the file size.
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;
const auto file_size = fs::file_size(file_path, ec);
@ -381,6 +418,7 @@ u64 IOFile::GetSize() const {
PathToUTF8String(file_path), ec.message());
return 0;
}
#endif
return file_size;
}

View file

@ -6,6 +6,9 @@
#include <unordered_map>
#include "common/fs/fs.h"
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
@ -80,9 +83,7 @@ public:
yuzu_paths.insert_or_assign(yuzu_path, new_path);
}
private:
PathManagerImpl() {
fs::path yuzu_path;
void Reinitialize(fs::path yuzu_path = {}) {
fs::path yuzu_path_cache;
fs::path yuzu_path_config;
@ -93,6 +94,10 @@ private:
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_config = yuzu_path / CONFIG_DIR;
#else
@ -122,6 +127,11 @@ private:
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
}
private:
PathManagerImpl() {
Reinitialize();
}
~PathManagerImpl() = default;
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};
}
void SetAppDirectory(const std::string& app_directory) {
PathManagerImpl::GetInstance().Reinitialize(app_directory);
}
const fs::path& GetYuzuPath(YuzuPath 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 path(path_);
#ifdef ANDROID
if (Android::IsContentUri(path)) {
return path;
}
#endif // ANDROID
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';

View file

@ -180,6 +180,14 @@ template <typename Path>
}
#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.
*

View file

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

View file

@ -155,6 +155,26 @@ public:
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;
/**
@ -260,6 +280,9 @@ private:
lambda(static_cast<Backend&>(debugger_backend));
lambda(static_cast<Backend&>(color_console_backend));
lambda(static_cast<Backend&>(file_backend));
#ifdef ANDROID
lambda(static_cast<Backend&>(lc_backend));
#endif
}
static void Deleter(Impl* ptr) {
@ -272,6 +295,9 @@ private:
DebuggerBackend debugger_backend{};
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
#ifdef ANDROID
LogcatBackend lc_backend{};
#endif
MPSCQueue<Entry> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};

View file

@ -8,6 +8,10 @@
#include <windows.h>
#endif
#ifdef ANDROID
#include <android/log.h>
#endif
#include "common/assert.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@ -106,4 +110,35 @@ void PrintColoredMessage(const Entry& entry) {
#undef ESC
#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

View file

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

View file

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

View file

@ -569,6 +569,10 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
}
KeyManager::KeyManager() {
ReloadKeys();
}
void KeyManager::ReloadKeys() {
// Initialize keys
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 {
const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
return !HasKey(key_type, index1, index2);

View file

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

View file

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

View file

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

View file

@ -5,11 +5,14 @@
#include <memory>
#include <utility>
#include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h"
namespace Core::Frontend {
class GraphicsContext;
/// Information for the Graphics Backends signifying what type of screen pointer is in
/// WindowInformation
enum class WindowSystemType {
@ -21,51 +24,6 @@ enum class WindowSystemType {
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
* (e.g. SDL, QGLWidget, GLFW, etc...).

View file

@ -13,7 +13,7 @@ EmulatedConsole::~EmulatedConsole() = default;
void EmulatedConsole::ReloadFromSettings() {
// 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];
motion_params = Common::ParamPackage(player.motions[0]);
motion_params[0] = Common::ParamPackage(player.motions[0]);
ReloadInput();
}
@ -74,14 +74,30 @@ void EmulatedConsole::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
SetTouchParams();
motion_devices = Common::Input::CreateInputDevice(motion_params);
if (motion_devices) {
motion_devices->SetCallback({
motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"};
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 =
[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
std::size_t index = 0;
for (auto& touch_device : touch_devices) {
@ -100,7 +116,9 @@ void EmulatedConsole::ReloadInput() {
}
void EmulatedConsole::UnloadInput() {
motion_devices.reset();
for (auto& motion : motion_devices) {
motion.reset();
}
for (auto& touch : touch_devices) {
touch.reset();
}
@ -133,11 +151,11 @@ void EmulatedConsole::RestoreConfig() {
}
Common::ParamPackage EmulatedConsole::GetMotionParam() const {
return motion_params;
return motion_params[0];
}
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
motion_params = std::move(param);
motion_params[0] = std::move(param);
ReloadInput();
}

View file

@ -29,10 +29,10 @@ struct ConsoleMotionInfo {
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 ConsoleMotionParams = Common::ParamPackage;
using ConsoleMotionParams = std::array<Common::ParamPackage, 2>;
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
using ConsoleMotionValues = ConsoleMotionInfo;

View file

@ -193,6 +193,8 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateInputDevice);
std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
Common::Input::CreateInputDevice);
std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(),
Common::Input::CreateInputDevice);
}
void EmulatedController::LoadTASParams() {
@ -253,6 +255,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
for (auto& param : virtual_stick_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
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::RStick].Set("deadzone", 0.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() {
@ -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;
}
@ -500,6 +523,9 @@ void EmulatedController::UnloadInput() {
for (auto& stick : virtual_stick_devices) {
stick.reset();
}
for (auto& motion : virtual_motion_devices) {
motion.reset();
}
for (auto& camera : camera_devices) {
camera.reset();
}

View file

@ -568,8 +568,10 @@ private:
// Virtual gamepad related variables
ButtonParams virtual_button_params;
StickParams virtual_stick_params;
ControllerMotionParams virtual_motion_params;
ButtonDevices virtual_button_devices;
StickDevices virtual_stick_devices;
ControllerMotionDevices virtual_motion_devices;
mutable std::mutex 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 = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .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, },
#endif
{ .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 = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },

View file

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

View file

@ -73,10 +73,7 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (state != State::WaitingForAmiibo) {
return Info::WrongDeviceState;
}
std::vector<u8> data{};
if (!nfc_file.IsOpen()) {
return Info::UnableToLoad;
@ -101,7 +98,28 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& 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;
memcpy(nfc_data.data(), data.data(), data.size_bytes());
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success;
}

View file

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <span>
#include <string>
#include <vector>
@ -47,6 +48,7 @@ public:
State GetCurrentState() const;
Info LoadAmiibo(const std::string& amiibo_file);
Info LoadAmiibo(std::span<u8> data);
Info ReloadAmiibo();
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);
}
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() {
for (std::size_t i = 0; i < PlayerIndexCount; i++) {
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);
/**
* 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 axis_id the id of the axis to move
* @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,
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
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 (Sirit::Module::*atomic_func)(Id, Id, 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) {
const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::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 (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");
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))};

View file

@ -10,27 +10,6 @@
namespace Shader::Backend::SPIRV {
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>
Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
switch (ctx.stage) {
@ -302,15 +281,26 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
const u32 element{static_cast<u32>(attr) % 4};
if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)};
const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
const auto& generic{ctx.input_generics.at(index)};
if (!ValidId(generic.id)) {
// Attribute is disabled or varying component is not written
return ctx.Const(element == 3 ? 1.0f : 0.0f);
}
const Id generic_id{ctx.input_generics.at(index)};
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
const Id value{ctx.OpLoad(type->id, pointer)};
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
const Id pointer{
AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(element))};
const Id value{ctx.OpLoad(generic.component_type, pointer)};
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) {
case IR::Attribute::PrimitiveId:

View file

@ -17,7 +17,22 @@ Id GetThreadId(EmitContext& ctx) {
Id WarpExtract(EmitContext& ctx, Id value) {
const Id thread_id{GetThreadId(ctx)};
const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
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);
}
}
Id LoadMask(EmitContext& ctx, Id mask) {

View file

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

View file

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

View file

@ -43,6 +43,7 @@ struct Profile {
bool support_gl_variable_aoffi{};
bool support_gl_sparse_textures{};
bool support_gl_derivative_control{};
bool support_scaled_attributes{};
bool warp_size_potentially_larger_than_guest{};
@ -77,6 +78,8 @@ struct Profile {
bool has_gl_bool_ref_bug{};
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
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{};
};

View file

@ -17,6 +17,8 @@ enum class AttributeType : u8 {
Float,
SignedInt,
UnsignedInt,
SignedScaled,
UnsignedScaled,
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 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)
endif()
@ -345,3 +345,7 @@ endif()
if (YUZU_ENABLE_LTO)
set_property(TARGET video_core PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
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() {
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return;
}
#endif
switch (regs.clear_report_value) {
case Regs::ClearReport::ZPassPixelCount:
rasterizer->ResetCounter(QueryType::SamplesPassed);
@ -614,6 +620,12 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
case Regs::ReportSemaphore::Report::Payload:
return regs.report_semaphore.payload;
case Regs::ReportSemaphore::Report::ZPassPixelCount64:
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return 120;
}
#endif
// Deferred.
rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
system.GPU().GetTicks());

View file

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

View file

@ -7,7 +7,7 @@
#include "common/settings.h"
#include "common/thread.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/dma_pusher.h"
#include "video_core/gpu.h"

View file

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

View file

@ -9,7 +9,7 @@
#include "common/common_funcs.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/rasterizer_interface.h"

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// 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"
namespace Null {

View file

@ -4,6 +4,7 @@
#pragma once
#include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
#include "shader_recompiler/frontend/ir/basic_block.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,
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{([&]() {
switch (type) {
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:

View file

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

View file

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

View file

@ -76,10 +76,18 @@ struct ScreenRectVertex {
constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// 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,
0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
-1.f, -1.f, 0.f, 1.f};
#endif // ANDROID
// 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();
swapchain_images != image_count || 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;
#endif
image_count = swapchain_images;
Recreate();
}

View file

@ -303,9 +303,13 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
DescriptorPool& descriptor_pool)
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
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,
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_,
scheduler_, staging_pool_);
quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
@ -442,7 +446,9 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
topology == PrimitiveTopology::QuadStrip);
} else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) {
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) {
// 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;
MemoryCommit null_buffer_commit;
Uint8Pass uint8_pass;
std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass;
};

View file

@ -114,14 +114,16 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
return Shader::AttributeType::Disabled;
case Maxwell::VertexAttribute::Type::SNorm:
case Maxwell::VertexAttribute::Type::UNorm:
case Maxwell::VertexAttribute::Type::UScaled:
case Maxwell::VertexAttribute::Type::SScaled:
case Maxwell::VertexAttribute::Type::Float:
return Shader::AttributeType::Float;
case Maxwell::VertexAttribute::Type::SInt:
return Shader::AttributeType::SignedInt;
case Maxwell::VertexAttribute::Type::UInt:
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;
}
@ -286,14 +288,17 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
texture_cache{texture_cache_}, shader_notify{shader_notify_},
use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.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") {
const auto& float_control{device.FloatControlProperties()};
const VkDriverId driver_id{device.GetDriverID()};
profile = Shader::Profile{
.supported_spirv = device.SupportedSpirvVersion(),
.unified_descriptor_binding = true,
.support_descriptor_aliasing = true,
.support_descriptor_aliasing = device.IsDescriptorAliasingSupported(),
.support_int8 = device.IsInt8Supported(),
.support_int16 = device.IsShaderInt16Supported(),
.support_int64 = device.IsShaderInt64Supported(),
@ -324,6 +329,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.support_derivative_control = true,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(),
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
.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_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
};
.has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY};
host_info = Shader::HostTranslateInfo{
.support_float16 = device.IsFloat16Supported(),
.support_int64 = device.IsShaderInt64Supported(),

View file

@ -4,10 +4,12 @@
#include "common/microprofile.h"
#include "common/settings.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_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_surface.h"
namespace Vulkan {
@ -92,14 +94,17 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat form
} // 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_,
Swapchain& swapchain_)
: render_window{render_window_}, device{device_},
Swapchain& swapchain_, vk::SurfaceKHR& surface_)
: instance{instance_}, render_window{render_window_}, device{device_},
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()},
image_count{swapchain.GetImageCount()} {
image_count{swapchain.GetImageCount()}, last_render_surface{
render_window_.GetWindowInfo().render_surface} {
auto& dld = device.GetLogical();
cmdpool = dld.CreateCommandPool({
@ -290,10 +295,19 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
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();
};
#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
// to account for that.
const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb);

View file

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

View file

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

View file

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

View file

@ -107,16 +107,17 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
u32 width_, u32 height_, bool srgb)
: surface{surface_}, device{device_}, scheduler{scheduler_} {
Create(width_, height_, srgb);
Create(surface_, width_, height_, srgb);
}
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_suboptimal = false;
width = width_;
height = height_;
surface = surface_;
const auto physical_device = device.GetPhysical();
const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
@ -266,7 +267,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
images = swapchain.GetImages();
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;
#endif
}
void Swapchain::CreateSemaphores() {

View file

@ -24,7 +24,7 @@ public:
~Swapchain();
/// 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.
bool AcquireNextImage();
@ -118,7 +118,7 @@ private:
bool NeedsPresentModeUpdate() const;
const VkSurfaceKHR surface;
VkSurfaceKHR surface;
const Device& device;
Scheduler& scheduler;

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
#include <adrenotools/driver.h>
#endif
#include "common/literals.h"
#include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.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()};
while (!stop_token.stop_requested()) {
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
adrenotools_set_turbo(true);
#else
// Reset the fence.
fence.Reset();
@ -209,7 +216,7 @@ void TurboMode::Run(std::stop_token stop_token) {
// Wait for completion.
fence.Wait();
#endif
// Wait for the next graphics queue submission if necessary.
std::unique_lock lk{m_submission_lock};
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};
});
}
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
adrenotools_set_turbo(false);
#endif
}
} // namespace Vulkan

View file

@ -31,7 +31,7 @@ struct DescriptorUpdateEntry {
class UpdateDescriptorQueue final {
// This should be plenty for the vast majority of cases. Most desktop platforms only
// 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 PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;

View file

@ -13,11 +13,39 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
[[maybe_unused]] void* user_data) {
// Skip logging known false-positive validation errors
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 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use
case 0x55362756u: // Descriptor binding and framebuffer attachment overlap
#endif
return VK_FALSE;
default:
break;

View file

@ -18,6 +18,10 @@
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
#include <adrenotools/bcenabler.h>
#endif
namespace Vulkan {
using namespace Common::Literals;
namespace {
@ -262,6 +266,32 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
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,
const std::set<std::string, std::less<>>& exts) {
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 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_amd_driver =
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_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
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) {
LOG_WARNING(Render_Vulkan, "Unsuitable driver is MoltenVK, continuing anyway");
if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
} else if (!is_suitable) {
throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
}
@ -355,6 +389,59 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
CollectPhysicalMemoryInfo();
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) {
const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
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);
}
}
if (extensions.extended_dynamic_state2 && is_radv) {
if (extensions.extended_dynamic_state2 && (is_radv || is_qualcomm)) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING(
@ -415,7 +502,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
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): Confirm if RDNA1 is affected
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");
cant_blit_msaa = true;
}
if (is_intel_anv) {
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
if (is_intel_anv || (is_qualcomm && !is_s8gen2)) {
LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
must_emulate_bgr565 = true;
}
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_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
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;

View file

@ -295,6 +295,11 @@ public:
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.
bool IsFloat16Supported() const {
return features.shader_float16_int8.shaderFloat16;
@ -495,6 +500,10 @@ public:
}
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;
}
@ -551,6 +560,10 @@ public:
return cant_blit_msaa;
}
bool MustEmulateScaledFormats() const {
return must_emulate_scaled_formats;
}
bool MustEmulateBGR565() const {
return must_emulate_bgr565;
}
@ -666,6 +679,7 @@ private:
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
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 dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.

View file

@ -10,29 +10,35 @@
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");
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__
// Check if a path to a specific Vulkan library has been specified.
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.
const auto filename =
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
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
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.
filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
void(library.Open(filename.c_str()));
void(library->Open(filename.c_str()));
}
#endif
return library;
#endif
}
} // namespace Vulkan

View file

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

View file

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

View file

@ -515,8 +515,8 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
auto wsi = QtCommon::GetWindowSystemInfo(window);
vk::InstanceDispatch dld;
const Common::DynamicLibrary library = OpenLibrary();
const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type);
const auto library = OpenLibrary();
const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
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
try {
Vulkan::vk::InstanceDispatch dld;
const Common::DynamicLibrary library = Vulkan::OpenLibrary();
const auto library = Vulkan::OpenLibrary();
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) {
fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what());

View file

@ -318,7 +318,7 @@ anti_aliasing =
fullscreen_mode =
# 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 =
# Anisotropic filtering

View file

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