Vulkan: add support for custom drivers on android

This commit is contained in:
SSimco 2026-05-09 17:03:15 +03:00
parent 4f64e1186f
commit 6705714211
13 changed files with 242 additions and 9 deletions

3
.gitmodules vendored
View File

@ -25,3 +25,6 @@
[submodule "dependencies/xbyak_aarch64"]
path = dependencies/xbyak_aarch64
url = https://github.com/fujitsu/xbyak_aarch64
[submodule "dependencies/libadrenotools"]
path = dependencies/libadrenotools
url = https://github.com/bylaws/libadrenotools

View File

@ -286,4 +286,10 @@ if (NOT ZArchive_FOUND)
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)
endif()
if(ANDROID)
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)")
add_subdirectory("dependencies/libadrenotools" EXCLUDE_FROM_ALL)
endif()
endif()
add_subdirectory(src)

1
dependencies/libadrenotools vendored Submodule

@ -0,0 +1 @@
Subproject commit 8fae8ce254dfc1344527e05301e43f37dea2df80

View File

@ -623,6 +623,9 @@ if(ANDROID)
Filesystem/fscDeviceAndroidSAF.cpp
Filesystem/fscDeviceAndroidSAF.h
)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)")
target_link_libraries(CemuCafe PRIVATE adrenotools)
endif()
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")

View File

@ -275,6 +275,17 @@ bool GameProfile::Load(uint64_t title_id)
}
}
#if BOOST_PLAT_ANDROID
else if (boost::iequals(iniParser.GetCurrentSectionName(), "AndroidDriver"))
{
gameProfile_loadEnumOption(iniParser, "mode", m_driverSetting.mode);
if (m_driverSetting.mode == DriverSettingMode::Custom)
{
m_driverSetting.customPath = iniParser.FindOption("customPath");
}
}
#endif
}
return true;
}
@ -329,6 +340,14 @@ void GameProfile::Save(uint64_t title_id)
fs->writeLine("");
#if BOOST_PLAT_ANDROID
fs->writeLine("[AndroidDriver]");
fs->writeLine(fmt::format("{} = {}", "mode", m_driverSetting.mode).c_str());
if (m_driverSetting.mode == DriverSettingMode::Custom && m_driverSetting.customPath.has_value())
fs->writeLine(fmt::format("{} = {}", "customPath", m_driverSetting.customPath.value()).c_str());
fs->writeLine("");
#endif
#undef WRITE_OPTIONAL_ENTRY
#undef WRITE_ENTRY
#undef WRITE_ENTRY_NUMBERED

View File

@ -43,6 +43,28 @@ public:
[[nodiscard]] const std::array< std::optional<std::string>, 8>& GetControllerProfile() const { return m_controllerProfile; }
#if BOOST_PLAT_ANDROID
struct DriverSetting
{
DriverSettingMode mode = DriverSettingMode::Global;
std::optional<std::string> customPath;
};
[[nodiscard]] DriverSetting GetDriverSetting() const
{
return m_driverSetting;
}
void SetDriverSetting(DriverSetting driverSetting)
{
m_driverSetting = driverSetting;
}
private:
DriverSetting m_driverSetting;
#endif
private:
uint64_t m_title_id = 0;
bool m_is_loaded = false;

View File

@ -144,10 +144,114 @@ bool InitializeDeviceVulkan(VkDevice device)
#else
void* g_vulkan_so = nullptr;
#if BOOST_PLAT_ANDROID
bool SupportsLoadingCustomDriver()
{
#ifdef __aarch64__
std::error_code ec;
return fs::exists("/dev/kgsl-3d0", ec);
#else
return false;
#endif
}
#ifdef __aarch64__
constexpr auto CUSTOM_DRIVER_LIB_NAME = "custom_vulkan.so";
#include <adrenotools/driver.h>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include "config/ActiveSettings.h"
#include "Cafe/GameProfile/GameProfile.h"
std::string get_custom_driver_lib_name(const fs::path& driver_path)
{
static constexpr auto LIB_NAME_MEMBER = "libraryName";
std::ifstream in(driver_path / "meta.json");
if (!in.is_open())
return {};
rapidjson::IStreamWrapper str(in);
rapidjson::Document doc;
doc.ParseStream(str);
if (!doc.HasMember(LIB_NAME_MEMBER) || !doc[LIB_NAME_MEMBER].IsString())
return {};
std::string lib_name = doc[LIB_NAME_MEMBER].GetString();
std::error_code ec;
if (!fs::exists(driver_path / lib_name, ec))
return {};
return lib_name;
}
std::optional<std::string> get_custom_driver_path()
{
const auto& driverSetting = g_current_game_profile->GetDriverSetting();
if (driverSetting.mode == DriverSettingMode::System)
{
return {};
}
std::error_code ec;
if (driverSetting.mode == DriverSettingMode::Custom &&
driverSetting.customPath.has_value() &&
fs::exists(driverSetting.customPath.value(), ec))
{
return driverSetting.customPath;
}
return GetConfig().custom_driver_path;
}
void* load_custom_driver()
{
std::optional<std::string> driver_path = get_custom_driver_path();
if (!driver_path.has_value() || driver_path->empty())
return nullptr;
std::string driver_name = get_custom_driver_lib_name(driver_path.value());
if (driver_name.empty())
return nullptr;
std::error_code ec;
fs::copy(fs::path(driver_path.value()) / driver_name, ActiveSettings::GetInternalPath(CUSTOM_DRIVER_LIB_NAME), fs::copy_options::overwrite_existing, ec);
void* vulkan_so = adrenotools_open_libvulkan(
RTLD_NOW | RTLD_LOCAL,
ADRENOTOOLS_DRIVER_CUSTOM,
nullptr,
(ActiveSettings::GetNativeLibPath().string() + "/").c_str(),
(ActiveSettings::GetInternalPath().string() + "/").c_str(),
CUSTOM_DRIVER_LIB_NAME,
nullptr,
nullptr);
if (!vulkan_so)
{
cemuLog_log(LogType::Force, "Failed to load custom driver");
return nullptr;
}
cemuLog_log(LogType::Force, "Loaded custom driver");
return vulkan_so;
}
#endif // __aarch64__
#endif // BOOST_PLAT_ANDROID
void* dlopen_vulkan_loader()
{
#if BOOST_OS_LINUX || BOOST_OS_BSD
void* vulkan_so = dlopen("libvulkan.so", RTLD_NOW);
static void* vulkan_so = nullptr;
#if BOOST_PLAT_ANDROID && defined(__aarch64__)
vulkan_so = load_custom_driver();
if (vulkan_so)
return vulkan_so;
#endif
vulkan_so = dlopen("libvulkan.so", RTLD_NOW);
if(!vulkan_so)
vulkan_so = dlopen("libvulkan.so.1", RTLD_NOW);
#elif BOOST_OS_MACOS
@ -158,17 +262,19 @@ void* dlopen_vulkan_loader()
bool InitializeGlobalVulkan()
{
void* vulkan_so = dlopen_vulkan_loader();
g_vulkan_so = dlopen_vulkan_loader();
if(g_vulkan_available)
if (g_vulkan_available)
return true;
if (!vulkan_so)
if (!g_vulkan_so)
{
cemuLog_log(LogType::Force, "Vulkan loader not available.");
return false;
}
void* vulkan_so = g_vulkan_so;
#define VKFUNC_INIT
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
@ -177,14 +283,30 @@ bool InitializeGlobalVulkan()
cemuLog_log(LogType::Force, "vkEnumerateInstanceVersion not available. Outdated graphics driver or Vulkan runtime?");
return false;
}
g_vulkan_available = true;
return true;
}
void CleanupGlobalVulkan()
{
if (g_vulkan_so)
{
dlclose(g_vulkan_so);
g_vulkan_so = nullptr;
}
g_vulkan_available = false;
#if BOOST_PLAT_ANDROID && defined(__aarch64__)
std::error_code ec;
fs::remove(ActiveSettings::GetInternalPath(CUSTOM_DRIVER_LIB_NAME), ec);
#endif
}
bool InitializeInstanceVulkan(VkInstance instance)
{
void* vulkan_so = dlopen_vulkan_loader();
void* vulkan_so = g_vulkan_so;
if (!vulkan_so)
return false;
@ -196,7 +318,7 @@ bool InitializeInstanceVulkan(VkInstance instance)
bool InitializeDeviceVulkan(VkDevice device)
{
void* vulkan_so = dlopen_vulkan_loader();
void* vulkan_so = g_vulkan_so;
if (!vulkan_so)
return false;

View File

@ -10,6 +10,11 @@
bool InitializeGlobalVulkan();
bool InitializeInstanceVulkan(VkInstance instance);
bool InitializeDeviceVulkan(VkDevice device);
#if BOOST_PLAT_ANDROID
bool SupportsLoadingCustomDriver();
#endif
extern bool g_vulkan_available;
#endif

View File

@ -2244,10 +2244,10 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor
const VkPipelineStageFlags semWaitStageMask[2] = { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT };
VkSemaphore waitSemArray[2];
submitInfo.waitSemaphoreCount = 0;
if (m_numSubmittedCmdBuffers > 0)
waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit
if (waitSemaphore != VK_NULL_HANDLE)
waitSemArray[submitInfo.waitSemaphoreCount++] = waitSemaphore;
if (m_numSubmittedCmdBuffers > 0)
waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit
submitInfo.pWaitDstStageMask = semWaitStageMask;
submitInfo.pWaitSemaphores = waitSemArray;

View File

@ -301,3 +301,14 @@ fs::path ActiveSettings::GetDefaultMLCPath()
return GetUserDataPath("mlc01");
}
#if BOOST_PLAT_ANDROID
void ActiveSettings::SetNativeLibPath(const fs::path& nativeLibPath)
{
s_native_lib_path = nativeLibPath;
}
void ActiveSettings::SetInternalDir(const fs::path& internalDirPath)
{
s_internal_dir_path = internalDirPath;
}
#endif

View File

@ -56,6 +56,15 @@ public:
[[nodiscard]] static fs::path GetMlcPath();
#if BOOST_PLAT_ANDROID
template <typename ...TArgs>
[[nodiscard]] static fs::path GetNativeLibPath(TArgs&&... args){ return GetPath(s_native_lib_path, std::forward<TArgs>(args)...); };
template <typename ...TArgs>
[[nodiscard]] static fs::path GetInternalPath(TArgs&&... args){ return GetPath(s_internal_dir_path, std::forward<TArgs>(args)...); };
#endif
template <typename ...TArgs>
[[nodiscard]] static fs::path GetMlcPath(TArgs&&... args){ return GetPath(GetMlcPath(), std::forward<TArgs>(args)...); };
static bool IsCustomMlcPath();
@ -73,8 +82,17 @@ private:
inline static fs::path s_data_path;
inline static fs::path s_executable_filename; // cemu.exe
inline static fs::path s_mlc_path;
#if BOOST_PLAT_ANDROID
inline static fs::path s_native_lib_path;
inline static fs::path s_internal_dir_path;
#endif
public:
#if BOOST_PLAT_ANDROID
static void SetNativeLibPath(const fs::path& nativeLibPath);
static void SetInternalDir(const fs::path& internalDirPath);
#endif
// can be called before Init
[[nodiscard]] static bool IsPortableMode();

View File

@ -290,6 +290,11 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
emulated_usb_devices.emulate_infinity_base = usbdevices.get("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base);
emulated_usb_devices.emulate_dimensions_toypad = usbdevices.get("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad);
#if BOOST_PLAT_ANDROID
custom_driver_path = parser.get("custom_driver_path", "");
#endif
return parser;
}
@ -454,6 +459,10 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
usbdevices.set("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base.GetValue());
usbdevices.set("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad.GetValue());
#if BOOST_PLAT_ANDROID
config.set("custom_driver_path", custom_driver_path.GetValue());
#endif
return config;
}

View File

@ -213,6 +213,16 @@ enum class CrashDump
ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled);
#endif
#if BOOST_PLAT_ANDROID
enum class DriverSettingMode
{
Global,
System,
Custom,
};
ENABLE_ENUM_ITERATORS(DriverSettingMode, DriverSettingMode::Global, DriverSettingMode::Custom);
#endif
template <>
struct fmt::formatter<PrecompiledShaderOption> : formatter<string_view> {
template <typename FormatContext>
@ -419,6 +429,10 @@ struct CemuConfig
#undef DISABLE_SCREENSAVER_DEFAULT
ConfigValue<bool> play_boot_sound{false};
#if BOOST_PLAT_ANDROID
ConfigValue<std::string> custom_driver_path{};
#endif
std::vector<std::string> game_paths;
std::mutex game_cache_entries_mutex;
std::vector<GameEntry> game_cache_entries;