211 lines
5.3 KiB
C++
211 lines
5.3 KiB
C++
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "build.h"
|
|
#if defined(__ANDROID__) && !defined(LIBRETRO) && HOST_CPU == CPU_ARM64
|
|
#include "adreno.h"
|
|
#include <dlfcn.h>
|
|
#include "cfg/option.h"
|
|
#include <adrenotools/driver.h>
|
|
#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<PFN_vkGetInstanceProcAddr>(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
|