vulkan: allow custom GPU driver loading with libadrenotools
Issue #1471
This commit is contained in:
parent
e8340bfa5e
commit
a6c4530e22
|
@ -32,3 +32,6 @@
|
|||
[submodule "core/deps/discord-rpc"]
|
||||
path = core/deps/discord-rpc
|
||||
url = https://github.com/flyinghead/discord-rpc
|
||||
[submodule "core/deps/libadrenotools"]
|
||||
path = core/deps/libadrenotools
|
||||
url = https://github.com/bylaws/libadrenotools
|
||||
|
|
|
@ -105,9 +105,10 @@ if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
|||
string(REPLACE "-" "." MS_VERSION ${MS_VERSION})
|
||||
string(REGEX REPLACE "\.g[0-9a-f]+" "" MS_VERSION ${MS_VERSION})
|
||||
string(REGEX MATCH "[0-9]+\.[0-9]+\.[0-9]+" VERSION_3PARTS ${MS_VERSION})
|
||||
string(REGEX MATCH "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" VERSION_4PARTS ${MS_VERSION})
|
||||
if(VERSION_3PARTS STREQUAL "")
|
||||
string(APPEND MS_VERSION ".0.0")
|
||||
else()
|
||||
elseif(VERSION_4PARTS STREQUAL "")
|
||||
string(APPEND MS_VERSION ".0")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -1283,6 +1284,11 @@ if(USE_VULKAN)
|
|||
target_compile_options(VulkanMemoryAllocator INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:-Wno-nullability-completeness>)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE GPUOpen::VulkanMemoryAllocator)
|
||||
|
||||
if(ANDROID AND NOT LIBRETRO AND "arm64" IN_LIST ARCHITECTURE)
|
||||
add_subdirectory(core/deps/libadrenotools)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE adrenotools)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_VULKAN HAVE_VULKAN)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/rend/vulkan/oit/oit_buffer.h
|
||||
|
@ -1295,6 +1301,7 @@ if(USE_VULKAN)
|
|||
core/rend/vulkan/oit/oit_renderpass.h
|
||||
core/rend/vulkan/oit/oit_shaders.cpp
|
||||
core/rend/vulkan/oit/oit_shaders.h
|
||||
core/rend/vulkan/adreno.cpp
|
||||
core/rend/vulkan/buffer.cpp
|
||||
core/rend/vulkan/buffer.h
|
||||
core/rend/vulkan/commandpool.cpp
|
||||
|
|
|
@ -47,7 +47,7 @@ ArchiveFile* ZipArchive::OpenFile(const char* name)
|
|||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat(zip, name, 0, &stat);
|
||||
return new ZipArchiveFile(zip_file, stat.size);
|
||||
return new ZipArchiveFile(zip_file, stat.size, stat.name);
|
||||
}
|
||||
|
||||
static zip_file *zip_fopen_by_crc(zip_t *za, u32 crc, int flags, zip_uint64_t& index)
|
||||
|
@ -77,7 +77,7 @@ ArchiveFile* ZipArchive::OpenFileByCrc(u32 crc)
|
|||
zip_stat_t stat;
|
||||
zip_stat_index(zip, index, 0, &stat);
|
||||
|
||||
return new ZipArchiveFile(zip_file, stat.size);
|
||||
return new ZipArchiveFile(zip_file, stat.size, stat.name);
|
||||
}
|
||||
|
||||
u32 ZipArchiveFile::Read(void* buffer, u32 length)
|
||||
|
@ -104,5 +104,15 @@ ArchiveFile *ZipArchive::OpenFirstFile()
|
|||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat_index(zip, 0, 0, &stat);
|
||||
return new ZipArchiveFile(zipFile, stat.size);
|
||||
return new ZipArchiveFile(zipFile, stat.size, stat.name);
|
||||
}
|
||||
|
||||
ArchiveFile *ZipArchive::OpenFileByIndex(size_t index)
|
||||
{
|
||||
zip_file_t *zipFile = zip_fopen_index(zip, index, 0);
|
||||
if (zipFile == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat_index(zip, index, 0, &stat);
|
||||
return new ZipArchiveFile(zipFile, stat.size, stat.name);
|
||||
}
|
||||
|
|
|
@ -31,11 +31,11 @@ public:
|
|||
ArchiveFile* OpenFile(const char* name) override;
|
||||
ArchiveFile* OpenFileByCrc(u32 crc) override;
|
||||
|
||||
bool Open(const void *data, size_t size);
|
||||
ArchiveFile *OpenFirstFile();
|
||||
|
||||
protected:
|
||||
bool Open(FILE *file) override;
|
||||
bool Open(const void *data, size_t size);
|
||||
|
||||
ArchiveFile *OpenFirstFile();
|
||||
ArchiveFile *OpenFileByIndex(size_t index);
|
||||
|
||||
private:
|
||||
zip_t *zip = nullptr;
|
||||
|
@ -44,8 +44,8 @@ private:
|
|||
class ZipArchiveFile : public ArchiveFile
|
||||
{
|
||||
public:
|
||||
ZipArchiveFile(zip_file_t *zip_file, size_t length)
|
||||
: zip_file(zip_file), _length(length) {}
|
||||
ZipArchiveFile(zip_file_t *zip_file, size_t length, const char *name)
|
||||
: zip_file(zip_file), _length(length), name(name) {}
|
||||
~ZipArchiveFile() override {
|
||||
zip_fclose(zip_file);
|
||||
}
|
||||
|
@ -53,8 +53,12 @@ public:
|
|||
size_t length() override {
|
||||
return _length;
|
||||
}
|
||||
const char *getName() override {
|
||||
return name;
|
||||
}
|
||||
|
||||
private:
|
||||
zip_file_t *zip_file;
|
||||
size_t _length;
|
||||
const char *name;
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
virtual ~ArchiveFile() = default;
|
||||
virtual u32 Read(void *buffer, u32 length) = 0;
|
||||
virtual size_t length() = 0;
|
||||
virtual const char *getName() { return nullptr; }
|
||||
};
|
||||
|
||||
class Archive
|
||||
|
|
|
@ -108,6 +108,7 @@ Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
|
|||
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", false);
|
||||
Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
|
||||
Option<bool> FixUpscaleBleedingEdge("rend.FixUpscaleBleedingEdge", true);
|
||||
Option<bool> CustomGpuDriver("rend.CustomGpuDriver", false);
|
||||
#ifdef VIDEO_ROUTING
|
||||
Option<bool, false> VideoRouting("rend.VideoRouting", false);
|
||||
Option<bool, false> VideoRoutingScale("rend.VideoRoutingScale", false);
|
||||
|
|
|
@ -474,6 +474,7 @@ extern Option<bool> DupeFrames;
|
|||
extern Option<bool> NativeDepthInterpolation;
|
||||
extern Option<bool> EmulateFramebuffer;
|
||||
extern Option<bool> FixUpscaleBleedingEdge;
|
||||
extern Option<bool> CustomGpuDriver;
|
||||
#ifdef VIDEO_ROUTING
|
||||
extern Option<bool, false> VideoRouting;
|
||||
extern Option<bool, false> VideoRoutingScale;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5deac9f1ab2bd833ad664bc3386ac1e8998cecb3
|
|
@ -51,6 +51,9 @@
|
|||
|
||||
#ifdef __ANDROID__
|
||||
#include "gui_android.h"
|
||||
#if HOST_CPU == CPU_ARM64 && USE_VULKAN
|
||||
#include "rend/vulkan/adreno.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -2762,6 +2765,75 @@ static void gui_display_settings()
|
|||
ImGui::Text("Driver Name: %s", GraphicsContext::Instance()->getDriverName().c_str());
|
||||
ImGui::Text("Version: %s", GraphicsContext::Instance()->getDriverVersion().c_str());
|
||||
|
||||
#if defined(__ANDROID__) && HOST_CPU == CPU_ARM64 && USE_VULKAN
|
||||
if (isVulkan(config::RendererType))
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(20, 10));
|
||||
if (config::CustomGpuDriver)
|
||||
{
|
||||
std::string name, description, vendor, version;
|
||||
if (getCustomGpuDriverInfo(name, description, vendor, version))
|
||||
{
|
||||
ImGui::Text("Custom Driver:");
|
||||
ImGui::Indent();
|
||||
ImGui::Text("%s - %s", name.c_str(), description.c_str());
|
||||
ImGui::Text("%s - %s", vendor.c_str(), version.c_str());
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Use Default Driver")) {
|
||||
config::CustomGpuDriver = false;
|
||||
ImGui::OpenPopup("Reset Vulkan");
|
||||
}
|
||||
}
|
||||
else if (ImGui::Button("Upload Custom Driver"))
|
||||
ImGui::OpenPopup("Select custom GPU driver");
|
||||
|
||||
static bool driverDirty;
|
||||
const auto& callback = [](bool cancelled, std::string selection) {
|
||||
if (!cancelled) {
|
||||
try {
|
||||
uploadCustomGpuDriver(selection);
|
||||
config::CustomGpuDriver = true;
|
||||
driverDirty = true;
|
||||
} catch (const FlycastException& e) {
|
||||
gui_error(e.what());
|
||||
config::CustomGpuDriver = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
select_file_popup("Select custom GPU driver", callback, true, "zip");
|
||||
|
||||
if (driverDirty) {
|
||||
ImGui::OpenPopup("Reset Vulkan");
|
||||
driverDirty = false;
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ScaledVec2(20, 20));
|
||||
if (ImGui::BeginPopupModal("Reset Vulkan", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
ImGui::Text("Do you want to reset Vulkan to use new driver?");
|
||||
ImGui::NewLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20 * settings.display.uiScale, ImGui::GetStyle().ItemSpacing.y));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(10, 10));
|
||||
if (ImGui::Button("Yes"))
|
||||
{
|
||||
mainui_reinit();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
#endif
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
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 "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 = nowide::fopen(zipPath.c_str(), "rb");
|
||||
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
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "vulkan.h"
|
||||
|
||||
PFN_vkGetInstanceProcAddr loadVulkanDriver();
|
||||
void unloadVulkanDriver();
|
||||
bool getCustomGpuDriverInfo(std::string& name, std::string& description, std::string& vendor, std::string& version);
|
||||
void uploadCustomGpuDriver(const std::string& zipPath);
|
|
@ -33,6 +33,9 @@
|
|||
#include "oslib/oslib.h"
|
||||
#include "vulkan_driver.h"
|
||||
#include "rend/transform_matrix.h"
|
||||
#if defined(__ANDROID__) && HOST_CPU == CPU_ARM64
|
||||
#include "adreno.h"
|
||||
#endif
|
||||
|
||||
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
|
||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
@ -139,8 +142,13 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
|||
try
|
||||
{
|
||||
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
|
||||
#if defined(__ANDROID__) && HOST_CPU == CPU_ARM64
|
||||
vkGetInstanceProcAddr = loadVulkanDriver();
|
||||
#else
|
||||
static vk::DynamicLoader dl;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
#endif
|
||||
if (vkGetInstanceProcAddr == nullptr) {
|
||||
ERROR_LOG(RENDERER, "Vulkan entry point vkGetInstanceProcAddr not found");
|
||||
return false;
|
||||
|
@ -1042,6 +1050,9 @@ void VulkanContext::term()
|
|||
#endif
|
||||
#endif
|
||||
instance.reset();
|
||||
#if defined(__ANDROID__) && HOST_CPU == CPU_ARM64
|
||||
unloadVulkanDriver();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanContext::DoSwapAutomation()
|
||||
|
|
|
@ -73,6 +73,10 @@ android {
|
|||
excludes += ['META-INF/DEPENDENCIES']
|
||||
}
|
||||
}
|
||||
packaging {
|
||||
// This is necessary for libadrenotools custom driver loading
|
||||
jniLibs.useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -415,4 +415,12 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
}
|
||||
|
||||
private static native void register(BaseGLActivity activity);
|
||||
|
||||
public String getNativeLibDir() {
|
||||
return getApplicationContext().getApplicationInfo().nativeLibraryDir;
|
||||
}
|
||||
|
||||
public String getInternalFilesDir() {
|
||||
return getFilesDir().getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -653,3 +653,19 @@ extern "C" void abort_message(const char* format, ...)
|
|||
ERROR_LOG(BOOT, "%s", buffer);
|
||||
abort();
|
||||
}
|
||||
|
||||
std::string getNativeLibraryPath()
|
||||
{
|
||||
JNIEnv *env = jni::env();
|
||||
jmethodID getNativeLibDir = env->GetMethodID(env->GetObjectClass(g_activity), "getNativeLibDir", "()Ljava/lang/String;");
|
||||
jni::String nativeLibDir(jni::env()->CallObjectMethod(g_activity, getNativeLibDir));
|
||||
return nativeLibDir;
|
||||
}
|
||||
|
||||
std::string getFilesPath()
|
||||
{
|
||||
JNIEnv *env = jni::env();
|
||||
jmethodID getInternalFilesDir = env->GetMethodID(env->GetObjectClass(g_activity), "getInternalFilesDir", "()Ljava/lang/String;");
|
||||
jni::String filesDir(jni::env()->CallObjectMethod(g_activity, getInternalFilesDir));
|
||||
return filesDir;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue