early-access version 3626
This commit is contained in:
parent
0d71b72e9a
commit
a8ad9662f0
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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}")
|
||||
|
@ -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
|
||||
|
||||
|
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
@ -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()
|
||||
|
62
externals/ffmpeg/CMakeLists.txt
vendored
62
externals/ffmpeg/CMakeLists.txt
vendored
@ -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)
|
||||
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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -115,7 +115,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
add(
|
||||
RunnableSetting(
|
||||
R.string.reset_to_default,
|
||||
0
|
||||
0,
|
||||
false
|
||||
) {
|
||||
ResetSettingsDialogFragment().show(
|
||||
settingsActivity.supportFragmentManager,
|
||||
|
@ -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,8 +26,10 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) {
|
||||
setting.runnable.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
// no-op
|
||||
|
@ -354,12 +354,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
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(
|
||||
cutInsets.left,
|
||||
cutInsets.top,
|
||||
cutInsets.right,
|
||||
cutInsets.bottom
|
||||
)
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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()
|
||||
|
@ -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)} {}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ? '\\' : '/';
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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));
|
||||
|
@ -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()};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -267,6 +267,9 @@ public:
|
||||
bool AddTicketCommon(Ticket raw);
|
||||
bool AddTicketPersonalized(Ticket raw);
|
||||
|
||||
void ReloadKeys();
|
||||
bool AreKeysLoaded() const;
|
||||
|
||||
private:
|
||||
KeyManager();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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...).
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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, },
|
||||
|
@ -46,6 +46,7 @@ ProfileManager::ProfileManager() {
|
||||
// Create an user if none are present
|
||||
if (user_count == 0) {
|
||||
CreateNewUser(UUID::MakeRandom(), "yuzu");
|
||||
WriteUserSaveFile();
|
||||
}
|
||||
|
||||
auto current =
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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]))};
|
||||
|
@ -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:
|
||||
|
@ -17,8 +17,23 @@ 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))};
|
||||
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) {
|
||||
const Id value{ctx.OpLoad(ctx.U32[4], mask)};
|
||||
|
@ -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);
|
||||
|
@ -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{};
|
||||
|
@ -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{};
|
||||
};
|
||||
|
@ -17,6 +17,8 @@ enum class AttributeType : u8 {
|
||||
Float,
|
||||
SignedInt,
|
||||
UnsignedInt,
|
||||
SignedScaled,
|
||||
UnsignedScaled,
|
||||
Disabled,
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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());
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -239,8 +239,15 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::InvalidateState() {
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -4,7 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/graphics_context.h"
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user