/*
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