Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Implement custom driver loading for ARM64 Android devices #18532

Merged
merged 3 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@
[submodule "ext/libchdr"]
path = ext/libchdr
url = https://github.com/rtissera/libchdr.git
[submodule "ext/libadrenotools"]
path = ext/libadrenotools
url = https://github.com/bylaws/libadrenotools.git
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,12 @@ setup_target_project(Common Common)

target_link_libraries(Common Ext::Snappy cpu_features)

if(ARM64)
if(ANDROID)
target_link_libraries(Common adrenotools)
endif()
endif()

if(USING_GLES2 OR (USING_EGL AND NOT USING_FBDEV))
find_package(X11)
endif()
Expand Down
1 change: 1 addition & 0 deletions Common/File/AndroidStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,5 +313,6 @@ const char *Android_ErrorToString(StorageError error) {
// Very hacky.
std::string g_extFilesDir = "(IF YOU SEE THIS THERE'S A BUG)";
std::string g_externalDir = "(IF YOU SEE THIS THERE'S A BUG (2))";
std::string g_nativeLibDir = "(IF YOU SEE THIS THERE'S A BUG (3))";

#endif
1 change: 1 addition & 0 deletions Common/File/AndroidStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ inline StorageError StorageErrorFromInt(int ival) {

extern std::string g_extFilesDir;
extern std::string g_externalDir;
extern std::string g_nativeLibDir;

#if PPSSPP_PLATFORM(ANDROID) && !defined(__LIBRETRO__)

Expand Down
3 changes: 2 additions & 1 deletion Common/GPU/Vulkan/VulkanContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstring>
#include <iostream>

#include "Core/Config.h"
#include "Common/System/System.h"
#include "Common/System/Display.h"
#include "Common/Log.h"
Expand Down Expand Up @@ -132,7 +133,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
#endif
#endif

if (flags_ & VULKAN_FLAG_VALIDATE) {
if ((flags_ & VULKAN_FLAG_VALIDATE) && g_Config.customDriver.empty()) {
if (IsInstanceExtensionAvailable(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
// Enable the validation layers
for (size_t i = 0; i < ARRAY_SIZE(validationLayers); i++) {
Expand Down
50 changes: 43 additions & 7 deletions Common/GPU/Vulkan/VulkanLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@
#include <string>
#include <cstring>

#include "Core/Config.h"
#include "Common/Data/Format/JSONReader.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/VR/PPSSPPVR.h"
#include "Common/File/FileUtil.h"

#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(SWITCH)
#include <dlfcn.h>
#endif

#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
#include "File/AndroidStorage.h"

#include <adrenotools/driver.h>
#endif

namespace PPSSPP_VK {
PFN_vkCreateInstance vkCreateInstance;
PFN_vkDestroyInstance vkDestroyInstance;
Expand Down Expand Up @@ -289,13 +298,40 @@ static VulkanLibraryHandle VulkanLoadLibrary(const char *logname) {
return LoadLibrary(L"vulkan-1.dll");
#else
void *lib = nullptr;
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
INFO_LOG(G3D, "%s: Library loaded ('%s')", logname, so_names[i]);
break;
}
}

#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
if (!g_Config.customDriver.empty() && g_Config.customDriver != "Default") {
const Path driverPath = g_Config.internalDataDirectory / "drivers" / g_Config.customDriver;

json::JsonReader meta = json::JsonReader((driverPath / "meta.json").c_str());
if (meta.ok()) {
std::string driverLibName = meta.root().get("libraryName")->value.toString();

Path tempDir = g_Config.internalDataDirectory / "temp";
Path fileRedirectDir = g_Config.internalDataDirectory / "vk_file_redirect";

File::CreateDir(tempDir);
File::CreateDir(fileRedirectDir);

lib = adrenotools_open_libvulkan(
RTLD_NOW | RTLD_LOCAL, ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM,
(std::string(tempDir.c_str()) + "/").c_str(),g_nativeLibDir.c_str(),
(std::string(driverPath.c_str()) + "/").c_str(),driverLibName.c_str(),
(std::string(fileRedirectDir.c_str()) + "/").c_str(),nullptr);
}
}
#endif

if (!lib) {
ERROR_LOG(G3D, "Failed to load custom driver");
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
INFO_LOG(G3D, "%s: Library loaded ('%s')", logname, so_names[i]);
break;
}
}
}
return lib;
#endif
}
Expand Down
3 changes: 3 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@ static const ConfigSetting graphicsSettings[] = {
ConfigSetting("CardboardYShift", &g_Config.iCardboardYShift, 0, CfgFlag::PER_GAME),
ConfigSetting("iShowStatusFlags", &g_Config.iShowStatusFlags, 0, CfgFlag::PER_GAME),
ConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT),
#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
ConfigSetting("CustomDriver", &g_Config.customDriver, "", CfgFlag::DEFAULT),
#endif
ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, "", CfgFlag::DEFAULT),
ConfigSetting("DisabledGraphicsBackends", &g_Config.sDisabledGPUBackends, "", CfgFlag::DEFAULT),
ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", CfgFlag::DEFAULT),
Expand Down
1 change: 1 addition & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ struct Config {

// GFX
int iGPUBackend;
std::string customDriver;
std::string sFailedGPUBackends;
std::string sDisabledGPUBackends;
// We have separate device parameters for each backend so it doesn't get erased if you switch backends.
Expand Down
97 changes: 97 additions & 0 deletions UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
#include "UI/RetroAchievementScreens.h"

#include "Common/File/FileUtil.h"
#include "Common/File/VFS/ZipFileReader.h"
#include "Common/Data/Format/JSONReader.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/OSVersion.h"
#include "Common/TimeUtil.h"
Expand Down Expand Up @@ -91,9 +93,20 @@
#if PPSSPP_PLATFORM(ANDROID)

#include "android/jni/AndroidAudio.h"
#include "Common/File/AndroidStorage.h"

extern AndroidAudioState *g_audioState;

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

GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)
Expand Down Expand Up @@ -269,6 +282,27 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings)
// If we're not the first instance, can't save the setting, and it requires a restart, so...
renderingBackendChoice->SetEnabled(false);
}

#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
const Path driverPath = g_Config.internalDataDirectory / "drivers";

std::vector<File::FileInfo> listing;
File::GetFilesInDir(driverPath, &listing);

std::vector<std::string> availableDrivers;
availableDrivers.push_back("Default");

for (auto driver : listing) {
availableDrivers.push_back(driver.name);
}
auto driverChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.customDriver, gr->T("Current GPU Driver"), availableDrivers, I18NCat::NONE, screenManager()));
driverChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCustomDriverChange);

auto customDriverInstallChoice = graphicsSettings->Add(new Choice(gr->T("Install Custom Driver...")));
customDriverInstallChoice->OnClick.Handle(this, &GameSettingsScreen::OnCustomDriverInstall);
}
#endif
#endif

// Backends that don't allow a device choice will only expose one device.
Expand Down Expand Up @@ -1215,6 +1249,69 @@ void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
vrSettings->Add(new PopupMultiChoice(&g_Config.iCameraPitch, vr->T("Camera type"), cameraPitchModes, 0, 3, I18NCat::NONE, screenManager()));
}

UI::EventReturn GameSettingsScreen::OnCustomDriverChange(UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);

screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
if (yes) {
TriggerRestart("GameSettingsScreen::CustomDriverYes");
}
}));
return UI::EVENT_DONE;
}

UI::EventReturn GameSettingsScreen::OnCustomDriverInstall(UI::EventParams &e) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);

System_BrowseForFile(gr->T("Install Custom Driver..."), BrowseFileType::ANY, [this](const std::string &value, int) {
const Path driverPath = g_Config.internalDataDirectory / "drivers";

if (!value.empty()) {
Path zipPath = Path(value);

if (zipPath.GetFileExtension() == ".zip") {
ZipFileReader *zipFileReader = ZipFileReader::Create(zipPath, "");

size_t metaDataSize;
uint8_t *metaData = zipFileReader->ReadFile("meta.json", &metaDataSize);

Path tempMeta = Path(g_Config.internalDataDirectory / "meta.json");

File::CreateEmptyFile(tempMeta);
File::WriteDataToFile(false, metaData, metaDataSize, tempMeta);

delete[] metaData;

json::JsonReader meta = json::JsonReader((g_Config.internalDataDirectory / "meta.json").c_str());
if (meta.ok()) {
std::string driverName = meta.root().get("name")->value.toString();

Path newCustomDriver = driverPath / driverName;
File::CreateFullPath(newCustomDriver);

std::vector<File::FileInfo> zipListing;
zipFileReader->GetFileListing("", &zipListing, nullptr);

for (auto file : zipListing) {
File::CreateEmptyFile(newCustomDriver / file.name);

size_t size;
uint8_t *data = zipFileReader->ReadFile(file.name.c_str(), &size);
File::WriteDataToFile(false, data, size, newCustomDriver / file.name);

delete[] data;
}

File::Delete(tempMeta);

RecreateViews();
}
}
}
});
return UI::EVENT_DONE;
}

UI::EventReturn GameSettingsScreen::OnAutoFrameskip(UI::EventParams &e) {
g_Config.UpdateAfterSettingAutoFrameSkip();
return UI::EVENT_DONE;
Expand Down
2 changes: 2 additions & 0 deletions UI/GameSettingsScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class GameSettingsScreen : public TabbedUIDialogScreenWithGameBackground {
UI::EventReturn OnRenderingBackend(UI::EventParams &e);
UI::EventReturn OnRenderingDevice(UI::EventParams &e);
UI::EventReturn OnInflightFramesChoice(UI::EventParams &e);
UI::EventReturn OnCustomDriverChange(UI::EventParams &e);
UI::EventReturn OnCustomDriverInstall(UI::EventParams &e);
UI::EventReturn OnCameraDeviceChange(UI::EventParams& e);
UI::EventReturn OnMicDeviceChange(UI::EventParams& e);
UI::EventReturn OnAudioDevice(UI::EventParams &e);
Expand Down
3 changes: 2 additions & 1 deletion android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
android:isGame="true"
android:banner="@drawable/tv_banner"
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true">
android:preserveLegacyExternalStorage="true"
android:extractNativeLibs="true">
<meta-data android:name="android.max_aspect" android:value="2.4" />
<activity
android:name=".PpssppActivity"
Expand Down
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ android {
jniLibs {
excludes += ['lib/arm64-v8a/libopenxr_loader.so']
}
jniLibs.useLegacyPackaging = true
}
sourceSets {
main {
Expand Down
15 changes: 15 additions & 0 deletions android/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ RCHEEVOS_FILES := \
${SRC}/ext/rcheevos/src/rhash/hash.c \
${SRC}/ext/rcheevos/src/rhash/md5.c

ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
ADRENOTOOLS_FILES := \
${SRC}/ext/libadrenotools/src/driver.cpp \
${SRC}/ext/libadrenotools/src/hook/hook_impl.cpp \
${SRC}/ext/libadrenotools/src/hook/file_redirect_hook.c \
${SRC}/ext/libadrenotools/src/hook/gsl_alloc_hook.c \
${SRC}/ext/libadrenotools/src/hook/main_hook.c \
${SRC}/ext/libadrenotools/lib/linkernsbypass/android_linker_ns.cpp \
${SRC}/ext/libadrenotools/lib/linkernsbypass/elf_soname_patcher.cpp
endif

VR_FILES := \
$(SRC)/Common/VR/OpenXRLoader.cpp \
$(SRC)/Common/VR/PPSSPPVR.cpp \
Expand Down Expand Up @@ -317,6 +328,10 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/SysError.cpp \
$(SRC)/Common/TimeUtil.cpp

ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
EXEC_AND_LIB_FILES += $(ADRENOTOOLS_FILES)
endif

LOCAL_MODULE := ppsspp_common
LOCAL_SRC_FILES := $(EXEC_AND_LIB_FILES)
include $(BUILD_STATIC_LIBRARY)
Expand Down
2 changes: 2 additions & 0 deletions android/jni/Locals.mk
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@ ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/arm64/lib/libswscale.a
LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/arm64/lib/libavutil.a
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ffmpeg/android/arm64/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ext/libadrenotools/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ext/libadrenotools/lib/linkernsbypass
endif
5 changes: 4 additions & 1 deletion android/jni/app-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ static std::string boardName;

std::string g_externalDir; // Original external dir (root of Android storage).
std::string g_extFilesDir; // App private external dir.
std::string g_nativeLibDir; // App native library dir

static std::vector<std::string> g_additionalStorageDirs;

Expand Down Expand Up @@ -705,7 +706,7 @@ static void parse_args(std::vector<std::string> &args, const std::string value)

extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
(JNIEnv * env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalStorageDir, jstring jexternalFilesDir, jstring jadditionalStorageDirs, jstring jcacheDir, jstring jshortcutParam,
jstring jdataDir, jstring jexternalStorageDir, jstring jexternalFilesDir, jstring jNativeLibDir, jstring jadditionalStorageDirs, jstring jcacheDir, jstring jshortcutParam,
jint jAndroidVersion, jstring jboard) {
SetCurrentThreadName("androidInit");

Expand All @@ -732,9 +733,11 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
std::string externalStorageDir = GetJavaString(env, jexternalStorageDir);
std::string additionalStorageDirsString = GetJavaString(env, jadditionalStorageDirs);
std::string externalFilesDir = GetJavaString(env, jexternalFilesDir);
std::string nativeLibDir = GetJavaString(env, jNativeLibDir);

g_externalDir = externalStorageDir;
g_extFilesDir = externalFilesDir;
g_nativeLibDir = nativeLibDir;

if (!additionalStorageDirsString.empty()) {
SplitString(additionalStorageDirsString, ':', g_additionalStorageDirs);
Expand Down
3 changes: 2 additions & 1 deletion android/src/org/ppsspp/ppsspp/NativeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ public void Initialize() {
String extStorageDir = Environment.getExternalStorageDirectory().getAbsolutePath();
File externalFiles = this.getExternalFilesDir(null);
String externalFilesDir = externalFiles == null ? "" : externalFiles.getAbsolutePath();
String nativeLibDir = getApplicationLibraryDir(appInfo);

Log.i(TAG, "Ext storage: " + extStorageState + " " + extStorageDir);
Log.i(TAG, "Ext files dir: " + externalFilesDir);
Expand Down Expand Up @@ -443,7 +444,7 @@ public void Initialize() {
overrideShortcutParam = null;

NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, externalFilesDir, additionalStorageDirs, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, externalFilesDir, nativeLibDir, additionalStorageDirs, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);

// Allow C++ to tell us to use JavaGL or not.
javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL"));
Expand Down
2 changes: 1 addition & 1 deletion android/src/org/ppsspp/ppsspp/NativeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class NativeApp {
public static final int DEVICE_TYPE_DESKTOP = 2;
public static final int DEVICE_TYPE_VR = 3;

public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String extFilesDir, String additionalStorageDirs, String cacheDir, String shortcutParam, int androidVersion, String board);
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String extFilesDir, String nativeLibDir, String additionalStorageDirs, String cacheDir, String shortcutParam, int androidVersion, String board);
public static native void audioInit();
public static native void audioShutdown();
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
Expand Down
Loading