/* Copyright 2024 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "build.h" #if defined(__ANDROID__) && !defined(LIBRETRO) && HOST_CPU == CPU_ARM64 #include "adreno.h" #include #include "cfg/option.h" #include #include "json.hpp" using namespace nlohmann; #include "archive/ZipArchive.h" #include "oslib/directory.h" #include "oslib/storage.h" #include "stdclass.h" std::string getNativeLibraryPath(); std::string getFilesPath(); const std::string DRIVER_PATH = "/gpu_driver/"; static void *libvulkanHandle; static json loadDriverMeta() { std::string fullPath = getFilesPath() + DRIVER_PATH + "meta.json"; FILE *f = nowide::fopen(fullPath.c_str(), "rt"); if (f == nullptr) { WARN_LOG(RENDERER, "Can't open %s", fullPath.c_str()); return json{}; } std::string content(4096, '\0'); size_t l = fread(content.data(), 1, content.size(), f); fclose(f); if (l <= 0) { WARN_LOG(RENDERER, "Can't read %s", fullPath.c_str()); return json{}; } content.resize(l); try { return json::parse(content); } catch (const json::exception& e) { WARN_LOG(COMMON, "Corrupted meta.json file: %s", e.what()); return json{}; } } static std::string getLibraryName() { json v = loadDriverMeta(); std::string name; try { v.at("libraryName").get_to(name); } catch (const json::exception& e) { } return name; } PFN_vkGetInstanceProcAddr loadVulkanDriver() { // If the user has selected a custom driver, try to load it if (config::CustomGpuDriver) { std::string libName = getLibraryName(); if (!libName.empty()) { std::string driverPath = getFilesPath() + DRIVER_PATH; std::string tmpLibDir = getFilesPath() + "/tmp/"; mkdir(tmpLibDir.c_str(), 0755); //std::string redirectDir = get_writable_data_path(""); libvulkanHandle = adrenotools_open_libvulkan( RTLD_NOW | RTLD_LOCAL, ADRENOTOOLS_DRIVER_CUSTOM /* | ADRENOTOOLS_DRIVER_FILE_REDIRECT */, tmpLibDir.c_str(), getNativeLibraryPath().c_str(), driverPath.c_str(), libName.c_str(), nullptr, //redirectDir.c_str(), nullptr); if (libvulkanHandle == nullptr) { char *error = dlerror(); WARN_LOG(RENDERER, "Failed to load custom Vulkan driver %s%s: %s", driverPath.c_str(), libName.c_str(), error ? error : ""); } } } if (libvulkanHandle == nullptr) { libvulkanHandle = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); if (libvulkanHandle == nullptr) { char *error = dlerror(); WARN_LOG(RENDERER, "Failed to load system Vulkan driver: %s", error ? error : ""); return nullptr; } } return reinterpret_cast(dlsym(libvulkanHandle, "vkGetInstanceProcAddr")); } void unloadVulkanDriver() { if (libvulkanHandle != nullptr) { dlclose(libvulkanHandle); libvulkanHandle = nullptr; } } bool getCustomGpuDriverInfo(std::string& name, std::string& description, std::string& vendor, std::string& version) { json j = loadDriverMeta(); try { j.at("name").get_to(name); } catch (const json::exception& e) { return false; } try { j.at("description").get_to(description); } catch (const json::exception& e) { description = ""; } try { j.at("vendor").get_to(vendor); } catch (const json::exception& e) { vendor = ""; } try { j.at("driverVersion").get_to(version); } catch (const json::exception& e) { version = ""; } return true; } void uploadCustomGpuDriver(const std::string& zipPath) { FILE *zipf = hostfs::storage().openFile(zipPath, "r"); if (zipf == nullptr) throw FlycastException("Can't open zip file"); ZipArchive archive; if (!archive.Open(zipf)) throw FlycastException("Invalid zip file"); std::string fullPath = getFilesPath() + DRIVER_PATH; flycast::mkdir(fullPath.c_str(), 0755); // Clean driver directory DIR *dir = flycast::opendir(fullPath.c_str()); if (dir != nullptr) { while (true) { dirent *direntry = flycast::readdir(dir); if (direntry == nullptr) break; std::string name = direntry->d_name; if (name == "." || name == "..") continue; name = fullPath + name; unlink(name.c_str()); } } // Extract and save files for (size_t i = 0; ; i++) { ArchiveFile *afile = archive.OpenFileByIndex(i); if (afile == nullptr) break; FILE *f = fopen((fullPath + afile->getName()).c_str(), "wb"); if (f == nullptr) { delete afile; throw FlycastException("Can't save files"); } u8 buf[8_KB]; while (true) { u32 len = afile->Read(buf, sizeof(buf)); if (len < 0) { fclose(f); delete afile; throw FlycastException("Can't read zip"); } if (len == 0) break; if (fwrite(buf, 1, len, f) != len) { fclose(f); delete afile; throw FlycastException("Can't save files"); } } fclose(f); delete afile; } } #endif // __ANDROID__ && !LIBRETRO && arm64