VideoBackends:Vulkan: Allow loading custom drivers on Android

... using libadrenotools
This commit is contained in:
Robin Kertels 2023-06-01 00:05:06 +02:00
parent 35bb663c2a
commit 23bebc5270
No known key found for this signature in database
GPG Key ID: 3824904F14D40757
24 changed files with 399 additions and 29 deletions

3
.gitmodules vendored
View File

@ -54,3 +54,6 @@
[submodule "Externals/rcheevos/rcheevos"] [submodule "Externals/rcheevos/rcheevos"]
path = Externals/rcheevos/rcheevos path = Externals/rcheevos/rcheevos
url = https://github.com/RetroAchievements/rcheevos.git url = https://github.com/RetroAchievements/rcheevos.git
[submodule "Externals/libadrenotools"]
path = Externals/libadrenotools
url = https://github.com/bylaws/libadrenotools.git

View File

@ -728,6 +728,11 @@ if(ENABLE_VULKAN)
if(APPLE AND USE_BUNDLED_MOLTENVK) if(APPLE AND USE_BUNDLED_MOLTENVK)
add_subdirectory(Externals/MoltenVK) add_subdirectory(Externals/MoltenVK)
endif() endif()
if (ANDROID AND _M_ARM_64)
add_subdirectory(Externals/libadrenotools)
endif()
endif() endif()
if(NOT WIN32 OR (NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64"))) if(NOT WIN32 OR (NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")))

1
Externals/libadrenotools vendored Submodule

@ -0,0 +1 @@
Subproject commit f4ce3c9618e7ecfcdd238b17dad9a0b888f5de90

View File

@ -1,6 +1,7 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version "1.7.20"
} }
task copyProfile (type: Copy) { task copyProfile (type: Copy) {
@ -110,6 +111,7 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path "../../../CMakeLists.txt" path "../../../CMakeLists.txt"
version "3.22.1+"
} }
} }
namespace 'org.dolphinemu.dolphinemu' namespace 'org.dolphinemu.dolphinemu'
@ -122,7 +124,7 @@ android {
abiFilters "arm64-v8a", "x86_64" //, "armeabi-v7a", "x86" abiFilters "arm64-v8a", "x86_64" //, "armeabi-v7a", "x86"
// Remove the line below if you want to build the C++ unit tests // Remove the line below if you want to build the C++ unit tests
targets "main" //targets "main", "hook_impl", "main_hook", "gsl_alloc_hook", "file_redirect_hook"
} }
} }
} }
@ -160,6 +162,9 @@ dependencies {
// For loading game covers from disk and GameTDB // For loading game covers from disk and GameTDB
implementation 'io.coil-kt:coil:2.2.2' implementation 'io.coil-kt:coil:2.2.2'
// For loading custom GPU drivers
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation 'com.nononsenseapps:filepicker:4.2.1' implementation 'com.nononsenseapps:filepicker:4.2.1'
} }

View File

@ -41,7 +41,8 @@
android:supportsRtl="true" android:supportsRtl="true"
android:isGame="true" android:isGame="true"
android:banner="@drawable/banner_tv" android:banner="@drawable/banner_tv"
android:hasFragileUserData="true"> android:hasFragileUserData="true"
android:extractNativeLibs="true">
<meta-data <meta-data
android:name="android.max_aspect" android:name="android.max_aspect"
android:value="2.1"/> android:value="2.1"/>
@ -82,6 +83,13 @@
android:theme="@style/Theme.Dolphin.Main" android:theme="@style/Theme.Dolphin.Main"
android:label="@string/settings"/> android:label="@string/settings"/>
<activity
android:name=".features.settings.ui.GpuDriverActivity"
android:exported="false"
android:configChanges="orientation|screenSize"
android:theme="@style/Theme.Dolphin.Main"
android:label="@string/settings"/>
<activity <activity
android:name=".features.cheats.ui.CheatsActivity" android:name=".features.cheats.ui.CheatsActivity"
android:exported="false" android:exported="false"

View File

@ -62,6 +62,12 @@ enum class StringSetting(
Settings.SECTION_GFX_ENHANCEMENTS, Settings.SECTION_GFX_ENHANCEMENTS,
"PostProcessingShader", "PostProcessingShader",
"" ""
),
GFX_DRIVER_LIB_NAME(
Settings.FILE_GFX,
Settings.SECTION_GFX_SETTINGS,
"DriverLibName",
""
); );
override val isOverridden: Boolean override val isOverridden: Boolean

View File

@ -18,18 +18,17 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
/** /**
* A class that spawns its own thread in order perform initialization. * A class that spawns its own thread in order perform initialization.
* *
@ -46,6 +45,7 @@ public final class DirectoryInitialization
private static volatile boolean areDirectoriesAvailable = false; private static volatile boolean areDirectoriesAvailable = false;
private static String userPath; private static String userPath;
private static String sysPath; private static String sysPath;
private static String driverPath;
private static boolean isUsingLegacyUserDirectory = false; private static boolean isUsingLegacyUserDirectory = false;
public enum DirectoryInitializationState public enum DirectoryInitializationState
@ -88,8 +88,7 @@ public final class DirectoryInitialization
directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED); directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED);
} }
@Nullable @Nullable private static File getLegacyUserDirectoryPath()
private static File getLegacyUserDirectoryPath()
{ {
File externalPath = Environment.getExternalStorageDirectory(); File externalPath = Environment.getExternalStorageDirectory();
if (externalPath == null) if (externalPath == null)
@ -98,8 +97,7 @@ public final class DirectoryInitialization
return new File(externalPath, "dolphin-emu"); return new File(externalPath, "dolphin-emu");
} }
@Nullable @Nullable public static File getUserDirectoryPath(Context context)
public static File getUserDirectoryPath(Context context)
{ {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
return null; return null;
@ -107,8 +105,8 @@ public final class DirectoryInitialization
isUsingLegacyUserDirectory = isUsingLegacyUserDirectory =
preferLegacyUserDirectory(context) && PermissionsHandler.hasWriteAccess(context); preferLegacyUserDirectory(context) && PermissionsHandler.hasWriteAccess(context);
return isUsingLegacyUserDirectory ? return isUsingLegacyUserDirectory ? getLegacyUserDirectoryPath() :
getLegacyUserDirectoryPath() : context.getExternalFilesDir(null); context.getExternalFilesDir(null);
} }
private static boolean setDolphinUserDirectory(Context context) private static boolean setDolphinUserDirectory(Context context)
@ -153,6 +151,19 @@ public final class DirectoryInitialization
// Let the native code know where the Sys directory is. // Let the native code know where the Sys directory is.
sysPath = sysDirectory.getPath(); sysPath = sysDirectory.getPath();
SetSysDirectory(sysPath); SetSysDirectory(sysPath);
File driverDirectory = new File(context.getFilesDir(), "GPUDrivers");
driverDirectory.mkdirs();
File driverExtractedDir = new File(driverDirectory, "Extracted");
driverExtractedDir.mkdirs();
File driverTmpDir = new File(driverDirectory, "Tmp");
driverTmpDir.mkdirs();
File driverFileRedirectDir = new File(driverDirectory, "FileRedirect");
driverFileRedirectDir.mkdirs();
SetGpuDriverDirectories(driverDirectory.getPath(),
context.getApplicationInfo().nativeLibraryDir);
DirectoryInitialization.driverPath = driverExtractedDir.getAbsolutePath();
} }
private static void deleteDirectoryRecursively(@NonNull final File file) private static void deleteDirectoryRecursively(@NonNull final File file)
@ -213,6 +224,16 @@ public final class DirectoryInitialization
return sysPath; return sysPath;
} }
public static String getExtractedDriverDirectory()
{
if (!areDirectoriesAvailable)
{
throw new IllegalStateException(
"DirectoryInitialization must run before accessing the driver directory!");
}
return driverPath;
}
public static File getGameListCache(Context context) public static File getGameListCache(Context context)
{ {
return new File(context.getExternalCacheDir(), "gamelist.cache"); return new File(context.getExternalCacheDir(), "gamelist.cache");
@ -235,16 +256,14 @@ public final class DirectoryInitialization
} }
catch (IOException e) catch (IOException e)
{ {
Log.error("[DirectoryInitialization] Failed to copy asset file: " + asset + Log.error("[DirectoryInitialization] Failed to copy asset file: " + asset + e.getMessage());
e.getMessage());
} }
return false; return false;
} }
private static void copyAssetFolder(String assetFolder, File outputFolder, Context context) private static void copyAssetFolder(String assetFolder, File outputFolder, Context context)
{ {
Log.verbose("[DirectoryInitialization] Copying Folder " + assetFolder + " to " + Log.verbose("[DirectoryInitialization] Copying Folder " + assetFolder + " to " + outputFolder);
outputFolder);
try try
{ {
@ -267,8 +286,7 @@ public final class DirectoryInitialization
} }
createdFolder = true; createdFolder = true;
} }
copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), context);
context);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), context); copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), context);
} }
} }
@ -340,8 +358,8 @@ public final class DirectoryInitialization
private static boolean preferLegacyUserDirectory(Context context) private static boolean preferLegacyUserDirectory(Context context)
{ {
return PermissionsHandler.isExternalStorageLegacy() && return PermissionsHandler.isExternalStorageLegacy() &&
!PermissionsHandler.isWritePermissionDenied() && !PermissionsHandler.isWritePermissionDenied() && isExternalFilesDirEmpty(context) &&
isExternalFilesDirEmpty(context) && legacyUserDirectoryExists(); legacyUserDirectoryExists();
} }
public static boolean isUsingLegacyUserDirectory() public static boolean isUsingLegacyUserDirectory()
@ -389,4 +407,6 @@ public final class DirectoryInitialization
} }
private static native void SetSysDirectory(String path); private static native void SetSysDirectory(String path);
private static native void SetGpuDriverDirectories(String path, String libPath);
} }

View File

@ -841,6 +841,18 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="about_github"><a href="https://github.com/dolphin-emu/dolphin">GitHub</a></string> <string name="about_github"><a href="https://github.com/dolphin-emu/dolphin">GitHub</a></string>
<string name="about_support"><a href="https://forums.dolphin-emu.org/">Support</a></string> <string name="about_support"><a href="https://forums.dolphin-emu.org/">Support</a></string>
<string name="about_copyright_warning">\u00A9 20032015+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.</string> <string name="about_copyright_warning">\u00A9 20032015+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.</string>
<string name="system_driver">System driver</string>
<string name="system_driver_desc">The GPU driver that is part of the OS.</string>
<!-- Custom GPU drivers -->
<string name="gpu_driver_submenu">GPU driver</string>
<string name="gpu_driver_install_inprogress">Installing the GPU driver…</string>
<string name="gpu_driver_install_success">GPU driver installed successfully</string>
<string name="gpu_driver_install_invalid_archive">Failed to unzip the provided driver package</string>
<string name="gpu_driver_install_missing_metadata">The supplied driver package is invalid due to missing metadata</string>
<string name="gpu_driver_install_invalid_metadata">The supplied driver package contains invalid metadata, it may be corrupted</string>
<string name="gpu_driver_install_unsupported_android_version">Your device doesn\'t meet the minimum Android version requirements for the supplied driver</string>
<string name="gpu_driver_install_already_installed">The supplied driver package is already installled</string>
<!-- Emulated USB Devices --> <!-- Emulated USB Devices -->
<string name="emulated_usb_devices">Emulated USB Devices</string> <string name="emulated_usb_devices">Emulated USB Devices</string>

View File

@ -30,6 +30,7 @@ add_library(main SHARED
RiivolutionPatches.cpp RiivolutionPatches.cpp
SkylanderConfig.cpp SkylanderConfig.cpp
WiiUtils.cpp WiiUtils.cpp
GpuDriver.cpp
) )
target_link_libraries(main target_link_libraries(main
@ -50,6 +51,12 @@ PRIVATE
"-Wl,--no-whole-archive" "-Wl,--no-whole-archive"
) )
target_include_directories(main
PRIVATE
${CMAKE_SOURCE_DIR}/Externals/libadrenotools/include
${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include
)
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/) file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/)
file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/) file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/)
file(COPY ${CMAKE_SOURCE_DIR}/Data/Sys DESTINATION ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/) file(COPY ${CMAKE_SOURCE_DIR}/Data/Sys DESTINATION ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/)

View File

@ -0,0 +1,147 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on: Skyline Emulator Project
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <jni.h>
#include "Common/IniFile.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
#include <dlfcn.h>
#include <fcntl.h>
#include <jni.h>
#include <unistd.h>
#include "adrenotools/driver.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoBackends/Vulkan/VulkanLoader.h"
extern "C" {
#if defined(_M_ARM_64)
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_getSystemDriverInfo(JNIEnv* env,
jobject)
{
if (!Vulkan::LoadVulkanLibrary(true))
{
return nullptr;
}
u32 vk_api_version = 0;
VkInstance instance = Vulkan::VulkanContext::CreateVulkanInstance(WindowSystemType::Headless,
false, false, &vk_api_version);
if (!instance)
{
return nullptr;
}
if (!Vulkan::LoadVulkanInstanceFunctions(instance))
{
vkDestroyInstance(instance, nullptr);
return nullptr;
}
Vulkan::VulkanContext::GPUList gpu_list = Vulkan::VulkanContext::EnumerateGPUs(instance);
if (gpu_list.empty())
{
vkDestroyInstance(instance, nullptr);
Vulkan::UnloadVulkanLibrary();
return nullptr;
}
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(gpu_list.front(), &properties);
std::string driverId;
if (vkGetPhysicalDeviceProperties2 && vk_api_version >= VK_VERSION_1_1)
{
VkPhysicalDeviceDriverProperties driverProperties;
driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
driverProperties.pNext = nullptr;
VkPhysicalDeviceProperties2 properties2;
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
properties2.pNext = &driverProperties;
vkGetPhysicalDeviceProperties2(gpu_list.front(), &properties2);
driverId = fmt::format("{}", driverProperties.driverID);
}
else
{
driverId = "Unknown";
}
std::string driverVersion =
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(properties.driverVersion),
VK_API_VERSION_MINOR(properties.driverVersion),
VK_API_VERSION_PATCH(properties.driverVersion));
vkDestroyInstance(instance, nullptr);
Vulkan::UnloadVulkanLibrary();
auto array = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
env->SetObjectArrayElement(array, 0, ToJString(env, driverId));
env->SetObjectArrayElement(array, 1, ToJString(env, driverVersion));
return array;
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_supportsCustomDriverLoading(
JNIEnv* env, jobject instance)
{
// If the KGSL device exists custom drivers can be loaded using adrenotools
return Vulkan::SupportsCustomDriver();
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_supportsForceMaxGpuClocks(
JNIEnv* env, jobject instance)
{
// If the KGSL device exists adrenotools can be used to set GPU turbo mode
return Vulkan::SupportsCustomDriver();
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_forceMaxGpuClocks(
JNIEnv* env, jobject instance, jboolean enable)
{
adrenotools_set_turbo(enable);
}
#else
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_getSystemDriverInfo(
JNIEnv* env, jobject instance)
{
auto array = env->NewObjectArray(0, env->FindClass("java/lang/String"), nullptr);
return array;
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_supportsCustomDriverLoading(
JNIEnv* env, jobject instance)
{
return false;
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_supportsForceMaxGpuClocks(
JNIEnv* env, jobject instance)
{
return false;
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_utils_GpuDriverHelper_00024Companion_forceMaxGpuClocks(
JNIEnv* env, jobject instance, jboolean enable)
{
}
#endif
}

View File

@ -371,6 +371,15 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_DirectoryInitializat
File::SetSysDirectory(path); File::SetSysDirectory(path);
} }
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_utils_DirectoryInitialization_SetGpuDriverDirectories(
JNIEnv* env, jclass, jstring jPath, jstring jLibPath)
{
const std::string path = GetJString(env, jPath);
const std::string lib_path = GetJString(env, jLibPath);
File::SetGpuDriverDirectories(path, lib_path);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory( JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory(
JNIEnv* env, jclass, jstring jDirectory) JNIEnv* env, jclass, jstring jDirectory)
{ {

View File

@ -163,3 +163,10 @@
// Subdirs in Config // Subdirs in Config
#define GRAPHICSMOD_CONFIG_DIR "GraphicMods" #define GRAPHICSMOD_CONFIG_DIR "GraphicMods"
// GPU drivers
#define GPU_DRIVERS "GpuDrivers"
#define GPU_DRIVERS_EXTRACTED "Extracted"
#define GPU_DRIVERS_TMP "Tmp"
#define GPU_DRIVERS_HOOK "Hook"
#define GPU_DRIVERS_FILE_REDIRECT "FileRedirect"

View File

@ -24,11 +24,22 @@ DynamicLibrary::DynamicLibrary(const char* filename)
Open(filename); Open(filename);
} }
DynamicLibrary::DynamicLibrary(void* handle)
{
m_handle = handle;
}
DynamicLibrary::~DynamicLibrary() DynamicLibrary::~DynamicLibrary()
{ {
Close(); Close();
} }
DynamicLibrary& DynamicLibrary::operator=(void* handle)
{
m_handle = handle;
return *this;
}
std::string DynamicLibrary::GetUnprefixedFilename(const char* filename) std::string DynamicLibrary::GetUnprefixedFilename(const char* filename)
{ {
#if defined(_WIN32) #if defined(_WIN32)

View File

@ -21,6 +21,8 @@ public:
// Automatically loads the specified library. Call IsOpen() to check validity before use. // Automatically loads the specified library. Call IsOpen() to check validity before use.
DynamicLibrary(const char* filename); DynamicLibrary(const char* filename);
DynamicLibrary(void* handle);
// Closes the library. // Closes the library.
~DynamicLibrary(); ~DynamicLibrary();
@ -30,6 +32,8 @@ public:
DynamicLibrary& operator=(const DynamicLibrary&) = delete; DynamicLibrary& operator=(const DynamicLibrary&) = delete;
DynamicLibrary& operator=(DynamicLibrary&&) = delete; DynamicLibrary& operator=(DynamicLibrary&&) = delete;
DynamicLibrary& operator=(void*);
// Returns the specified library name with the platform-specific suffix added. // Returns the specified library name with the platform-specific suffix added.
static std::string GetUnprefixedFilename(const char* filename); static std::string GetUnprefixedFilename(const char* filename);

View File

@ -66,6 +66,8 @@ namespace File
{ {
#ifdef ANDROID #ifdef ANDROID
static std::string s_android_sys_directory; static std::string s_android_sys_directory;
static std::string s_android_driver_directory;
static std::string s_android_lib_directory;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
@ -796,6 +798,34 @@ void SetSysDirectory(const std::string& path)
s_android_sys_directory); s_android_sys_directory);
s_android_sys_directory = path; s_android_sys_directory = path;
} }
void SetGpuDriverDirectories(const std::string& path, const std::string& lib_path)
{
INFO_LOG_FMT(COMMON, "Setting Driver directory to {} and library path to {}", path, lib_path);
ASSERT_MSG(COMMON, s_android_driver_directory.empty(), "Driver directory already set to {}",
s_android_driver_directory);
ASSERT_MSG(COMMON, s_android_lib_directory.empty(), "Library directory already set to {}",
s_android_lib_directory);
s_android_driver_directory = path;
s_android_lib_directory = lib_path;
}
const std::string GetGpuDriverDirectory(unsigned int dir_index)
{
switch (dir_index)
{
case D_GPU_DRIVERS_EXTRACTED:
return s_android_driver_directory + DIR_SEP GPU_DRIVERS_EXTRACTED DIR_SEP;
case D_GPU_DRIVERS_TMP:
return s_android_driver_directory + DIR_SEP GPU_DRIVERS_TMP DIR_SEP;
case D_GPU_DRIVERS_HOOKS:
return s_android_lib_directory;
case D_GPU_DRIVERS_FILE_REDIRECT:
return s_android_driver_directory + DIR_SEP GPU_DRIVERS_FILE_REDIRECT DIR_SEP;
}
return "";
}
#endif #endif
static std::string s_user_paths[NUM_PATH_INDICES]; static std::string s_user_paths[NUM_PATH_INDICES];

View File

@ -67,6 +67,10 @@ enum
D_GBAUSER_IDX, D_GBAUSER_IDX,
D_GBASAVES_IDX, D_GBASAVES_IDX,
D_WIISDCARDSYNCFOLDER_IDX, D_WIISDCARDSYNCFOLDER_IDX,
D_GPU_DRIVERS_EXTRACTED,
D_GPU_DRIVERS_TMP,
D_GPU_DRIVERS_HOOKS,
D_GPU_DRIVERS_FILE_REDIRECT,
FIRST_FILE_USER_PATH_IDX, FIRST_FILE_USER_PATH_IDX,
F_DOLPHINCONFIG_IDX = FIRST_FILE_USER_PATH_IDX, F_DOLPHINCONFIG_IDX = FIRST_FILE_USER_PATH_IDX,
F_GCPADCONFIG_IDX, F_GCPADCONFIG_IDX,
@ -228,6 +232,8 @@ const std::string& GetSysDirectory();
#ifdef ANDROID #ifdef ANDROID
void SetSysDirectory(const std::string& path); void SetSysDirectory(const std::string& path);
void SetGpuDriverDirectories(const std::string& path, const std::string& lib_path);
const std::string GetGpuDriverDirectory(unsigned int dir_index);
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__

View File

@ -111,6 +111,8 @@ const Info<bool> GFX_PREFER_GLES{{System::GFX, "Settings", "PreferGLES"}, false}
const Info<bool> GFX_MODS_ENABLE{{System::GFX, "Settings", "EnableMods"}, false}; const Info<bool> GFX_MODS_ENABLE{{System::GFX, "Settings", "EnableMods"}, false};
const Info<std::string> GFX_DRIVER_LIB_NAME{{System::GFX, "Settings", "DriverLibName"}, ""};
// Graphics.Enhancements // Graphics.Enhancements
const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING{ const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING{
@ -171,4 +173,5 @@ const Info<bool> GFX_HACK_NO_MIPMAPPING{{System::GFX, "Hacks", "NoMipmapping"},
// Graphics.GameSpecific // Graphics.GameSpecific
const Info<bool> GFX_PERF_QUERIES_ENABLE{{System::GFX, "GameSpecific", "PerfQueriesEnable"}, false}; const Info<bool> GFX_PERF_QUERIES_ENABLE{{System::GFX, "GameSpecific", "PerfQueriesEnable"}, false};
} // namespace Config } // namespace Config

View File

@ -148,4 +148,8 @@ extern const Info<bool> GFX_HACK_NO_MIPMAPPING;
extern const Info<bool> GFX_PERF_QUERIES_ENABLE; extern const Info<bool> GFX_PERF_QUERIES_ENABLE;
// Android custom GPU drivers
extern const Info<std::string> GFX_DRIVER_LIB_NAME;
} // namespace Config } // namespace Config

View File

@ -48,11 +48,19 @@ PRIVATE
xxhash xxhash
) )
if (ANDROID AND _M_ARM_64)
target_link_libraries(videovulkan
PRIVATE
adrenotools
)
endif()
# Only include the Vulkan headers when building the Vulkan backend # Only include the Vulkan headers when building the Vulkan backend
target_include_directories(videovulkan target_include_directories(videovulkan
PRIVATE PRIVATE
${CMAKE_SOURCE_DIR}/Externals/Vulkan/Include ${CMAKE_SOURCE_DIR}/Externals/Vulkan/Include
${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include ${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include
${CMAKE_SOURCE_DIR}/Externals/libadrenotools/include
) )
if(MSVC) if(MSVC)

View File

@ -143,7 +143,18 @@ VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool ena
{ {
// The device itself may not support 1.1, so we check that before using any 1.1 functionality. // The device itself may not support 1.1, so we check that before using any 1.1 functionality.
app_info.apiVersion = VK_MAKE_VERSION(1, 1, 0); app_info.apiVersion = VK_MAKE_VERSION(1, 1, 0);
WARN_LOG_FMT(HOST_GPU, "Using Vulkan 1.1, supported: {}.{}",
VK_VERSION_MAJOR(supported_api_version),
VK_VERSION_MINOR(supported_api_version));
} }
else
{
WARN_LOG_FMT(HOST_GPU, "Using Vulkan 1.0");
}
}
else
{
WARN_LOG_FMT(HOST_GPU, "Using Vulkan 1.0");
} }
*out_vk_api_version = app_info.apiVersion; *out_vk_api_version = app_info.apiVersion;

View File

@ -8,11 +8,18 @@
#include <cstdarg> #include <cstdarg>
#include <cstdlib> #include <cstdlib>
#if defined(ANDROID)
#include <adrenotools/driver.h>
#include <dlfcn.h>
#endif
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/DynamicLibrary.h" #include "Common/DynamicLibrary.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "VideoCommon/VideoConfig.h"
#define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name name; #define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name name;
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) PFN_##name name; #define VULKAN_INSTANCE_ENTRY_POINT(name, required) PFN_##name name;
#define VULKAN_DEVICE_ENTRY_POINT(name, required) PFN_##name name; #define VULKAN_DEVICE_ENTRY_POINT(name, required) PFN_##name name;
@ -36,9 +43,9 @@ static void ResetVulkanLibraryFunctionPointers()
static Common::DynamicLibrary s_vulkan_module; static Common::DynamicLibrary s_vulkan_module;
static bool OpenVulkanLibrary() static bool OpenVulkanLibrary(bool force_system_library)
{ {
#ifdef __APPLE__ #if defined(__APPLE__)
// Check if a path to a specific Vulkan library has been specified. // Check if a path to a specific Vulkan library has been specified.
char* libvulkan_env = getenv("LIBVULKAN_PATH"); char* libvulkan_env = getenv("LIBVULKAN_PATH");
if (libvulkan_env && s_vulkan_module.Open(libvulkan_env)) if (libvulkan_env && s_vulkan_module.Open(libvulkan_env))
@ -48,6 +55,35 @@ static bool OpenVulkanLibrary()
std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libMoltenVK.dylib"; std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libMoltenVK.dylib";
return s_vulkan_module.Open(filename.c_str()); return s_vulkan_module.Open(filename.c_str());
#else #else
#if defined(ANDROID) && _M_ARM_64
const std::string& driver_lib_name = g_Config.customDriverLibraryName;
if (!force_system_library && !driver_lib_name.empty() && SupportsCustomDriver())
{
std::string tmp_dir = File::GetGpuDriverDirectory(D_GPU_DRIVERS_TMP);
std::string hook_dir = File::GetGpuDriverDirectory(D_GPU_DRIVERS_HOOKS);
std::string file_redirect_dir = File::GetGpuDriverDirectory(D_GPU_DRIVERS_FILE_REDIRECT);
std::string driver_dir = File::GetGpuDriverDirectory(D_GPU_DRIVERS_EXTRACTED);
INFO_LOG_FMT(HOST_GPU, "Loading driver: {}", driver_lib_name);
s_vulkan_module = adrenotools_open_libvulkan(
RTLD_NOW, ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM, tmp_dir.c_str(),
hook_dir.c_str(), driver_dir.c_str(), driver_lib_name.c_str(), file_redirect_dir.c_str(),
nullptr);
if (s_vulkan_module.IsOpen())
{
INFO_LOG_FMT(HOST_GPU, "Successfully loaded driver: {}", driver_lib_name);
return true;
}
else
{
WARN_LOG_FMT(HOST_GPU, "Loading driver {} failed.", driver_lib_name);
}
}
#endif
WARN_LOG_FMT(HOST_GPU, "Loading system driver");
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
if (s_vulkan_module.Open(filename.c_str())) if (s_vulkan_module.Open(filename.c_str()))
return true; return true;
@ -58,9 +94,9 @@ static bool OpenVulkanLibrary()
#endif #endif
} }
bool LoadVulkanLibrary() bool LoadVulkanLibrary(bool force_system_library)
{ {
if (!s_vulkan_module.IsOpen() && !OpenVulkanLibrary()) if (!s_vulkan_module.IsOpen() && !OpenVulkanLibrary(force_system_library))
return false; return false;
#define VULKAN_MODULE_ENTRY_POINT(name, required) \ #define VULKAN_MODULE_ENTRY_POINT(name, required) \
@ -91,7 +127,7 @@ bool LoadVulkanInstanceFunctions(VkInstance instance)
*func_ptr = vkGetInstanceProcAddr(instance, name); *func_ptr = vkGetInstanceProcAddr(instance, name);
if (!(*func_ptr) && is_required) if (!(*func_ptr) && is_required)
{ {
ERROR_LOG_FMT(VIDEO, "Vulkan: Failed to load required instance function {}", name); ERROR_LOG_FMT(HOST_GPU, "Vulkan: Failed to load required instance function {}", name);
required_functions_missing = true; required_functions_missing = true;
} }
}; };
@ -111,7 +147,7 @@ bool LoadVulkanDeviceFunctions(VkDevice device)
*func_ptr = vkGetDeviceProcAddr(device, name); *func_ptr = vkGetDeviceProcAddr(device, name);
if (!(*func_ptr) && is_required) if (!(*func_ptr) && is_required)
{ {
ERROR_LOG_FMT(VIDEO, "Vulkan: Failed to load required device function {}", name); ERROR_LOG_FMT(HOST_GPU, "Vulkan: Failed to load required device function {}", name);
required_functions_missing = true; required_functions_missing = true;
} }
}; };
@ -212,4 +248,18 @@ void LogVulkanResult(Common::Log::LogLevel level, const char* func_name, VkResul
static_cast<int>(res), VkResultToString(res)); static_cast<int>(res), VkResultToString(res));
} }
#ifdef ANDROID
static bool CheckKgslPresent()
{
constexpr auto KgslPath{"/dev/kgsl-3d0"};
return access(KgslPath, F_OK) == 0;
}
bool SupportsCustomDriver()
{
return android_get_device_api_level() >= 28 && CheckKgslPresent();
}
#endif
} // namespace Vulkan } // namespace Vulkan

View File

@ -23,6 +23,10 @@
#include "vulkan/vulkan.h" #include "vulkan/vulkan.h"
#ifdef ANDROID
#include <unistd.h>
#endif
// Currently, exclusive fullscreen is only supported on Windows. // Currently, exclusive fullscreen is only supported on Windows.
#if defined(WIN32) #if defined(WIN32)
#define SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN 1 #define SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN 1
@ -78,11 +82,15 @@
namespace Vulkan namespace Vulkan
{ {
bool LoadVulkanLibrary(); bool LoadVulkanLibrary(bool force_system_library = false);
bool LoadVulkanInstanceFunctions(VkInstance instance); bool LoadVulkanInstanceFunctions(VkInstance instance);
bool LoadVulkanDeviceFunctions(VkDevice device); bool LoadVulkanDeviceFunctions(VkDevice device);
void UnloadVulkanLibrary(); void UnloadVulkanLibrary();
#ifdef ANDROID
bool SupportsCustomDriver();
#endif
const char* VkResultToString(VkResult res); const char* VkResultToString(VkResult res);
void LogVulkanResult(Common::Log::LogLevel level, const char* func_name, VkResult res, void LogVulkanResult(Common::Log::LogLevel level, const char* func_name, VkResult res,
const char* msg); const char* msg);

View File

@ -172,6 +172,8 @@ void VideoConfig::Refresh()
bPerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE); bPerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE);
bGraphicMods = Config::Get(Config::GFX_MODS_ENABLE); bGraphicMods = Config::Get(Config::GFX_MODS_ENABLE);
customDriverLibraryName = Config::Get(Config::GFX_DRIVER_LIB_NAME);
} }
void VideoConfig::VerifyValidity() void VideoConfig::VerifyValidity()

View File

@ -214,6 +214,9 @@ struct VideoConfig final
int iShaderCompilerThreads = 0; int iShaderCompilerThreads = 0;
int iShaderPrecompilerThreads = 0; int iShaderPrecompilerThreads = 0;
// Loading custom drivers on Android
std::string customDriverLibraryName;
// Static config per API // Static config per API
// TODO: Move this out of VideoConfig // TODO: Move this out of VideoConfig
struct struct