diff --git a/.gitmodules b/.gitmodules index 82a4f4bf24c6..7ab10b0ec98a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 520ae2b0a945..20a0b97f950f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 17e973ddb831..fe106f772d04 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -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 diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index b50625e425d2..cb40db0e9ff0 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -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__) diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 05607469c8f8..821671de1d99 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -5,6 +5,7 @@ #include #include +#include "Core/Config.h" #include "Common/System/System.h" #include "Common/System/Display.h" #include "Common/Log.h" @@ -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++) { diff --git a/Common/GPU/Vulkan/VulkanLoader.cpp b/Common/GPU/Vulkan/VulkanLoader.cpp index 3c7069c4a1cc..c54fe1e4b3fe 100644 --- a/Common/GPU/Vulkan/VulkanLoader.cpp +++ b/Common/GPU/Vulkan/VulkanLoader.cpp @@ -20,15 +20,24 @@ #include #include +#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 #endif +#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64) +#include "File/AndroidStorage.h" + +#include +#endif + namespace PPSSPP_VK { PFN_vkCreateInstance vkCreateInstance; PFN_vkDestroyInstance vkDestroyInstance; @@ -289,13 +298,38 @@ 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 (!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); + } + } + + 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 } diff --git a/Core/Config.cpp b/Core/Config.cpp index c7b194a3a35f..fe70e719f38b 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -585,6 +585,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), diff --git a/Core/Config.h b/Core/Config.h index de2d6971cbfe..5051c2076bfb 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -147,6 +147,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. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 881d6ee8bc18..4c4adcde0799 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -91,9 +91,22 @@ #if PPSSPP_PLATFORM(ANDROID) #include "android/jni/AndroidAudio.h" +#include "File/VFS/ZipFileReader.h" +#include "File/AndroidStorage.h" +#include "Data/Format/JSONReader.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) @@ -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 listing; + File::GetFilesInDir(driverPath, &listing); + + std::vector 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. @@ -1213,6 +1247,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 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; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 4c83715cf21f..d3861139f01b 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -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); diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 38853da07c85..cb584faa475c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -47,7 +47,8 @@ android:isGame="true" android:banner="@drawable/tv_banner" android:requestLegacyExternalStorage="true" - android:preserveLegacyExternalStorage="true"> + android:preserveLegacyExternalStorage="true" + android:extractNativeLibs="true"> g_additionalStorageDirs; @@ -705,7 +706,7 @@ static void parse_args(std::vector &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"); @@ -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); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index cc0309af02a5..70a8c1717248 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -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); @@ -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")); diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 0e714be16909..e0c42d884274 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -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); diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 1031f6edf91c..32adc01d2a20 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -39,3 +39,7 @@ if(USE_DISCORD AND NOT IOS AND NOT LIBRETRO) endif() add_subdirectory(libchdr-build) + +if(ANDROID AND ARM64) + add_subdirectory(libadrenotools) +endif() diff --git a/ext/libadrenotools b/ext/libadrenotools new file mode 160000 index 000000000000..deec5f75ee1a --- /dev/null +++ b/ext/libadrenotools @@ -0,0 +1 @@ +Subproject commit deec5f75ee1a8ccbe32c8780b1d17284fc87b0f1