mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 16:01:52 +01:00
Add utilities for reading and installing gpu driver packages
This commit is contained in:
parent
f3dd3e53c1
commit
1815199d2b
@ -129,6 +129,7 @@ target_link_libraries_system(shader_recompiler Boost::intrusive Boost::container
|
||||
|
||||
# Skyline
|
||||
add_library(skyline SHARED
|
||||
${source_DIR}/driver_jni.cpp
|
||||
${source_DIR}/emu_jni.cpp
|
||||
${source_DIR}/loader_jni.cpp
|
||||
${source_DIR}/skyline/common.cpp
|
||||
|
36
app/src/main/cpp/driver_jni.cpp
Normal file
36
app/src/main/cpp/driver_jni.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <jni.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#include "skyline/common/signal.h"
|
||||
#include "skyline/common/utils.h"
|
||||
|
||||
extern "C" JNIEXPORT jobjectArray JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_getSystemDriverInfo(JNIEnv *env, jobject) {
|
||||
auto libvulkanHandle{dlopen("libvulkan.so", RTLD_NOW)};
|
||||
|
||||
vk::raii::Context vkContext{reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(libvulkanHandle, "vkGetInstanceProcAddr"))};
|
||||
vk::raii::Instance vkInstance{vkContext, vk::InstanceCreateInfo{}};
|
||||
vk::raii::PhysicalDevice physicalDevice{std::move(vk::raii::PhysicalDevices(vkInstance).front())}; // Get the first device as we aren't expecting multiple GPUs
|
||||
|
||||
auto deviceProperties2{physicalDevice.getProperties2<vk::PhysicalDeviceProperties2, vk::PhysicalDeviceDriverProperties>()};
|
||||
auto properties{deviceProperties2.get<vk::PhysicalDeviceProperties2>().properties};
|
||||
|
||||
auto driverId{vk::to_string(deviceProperties2.get<vk::PhysicalDeviceDriverProperties>().driverID)};
|
||||
auto driverVersion{skyline::util::Format("{}.{}.{}", VK_API_VERSION_MAJOR(properties.driverVersion), VK_API_VERSION_MINOR(properties.driverVersion), VK_API_VERSION_PATCH(properties.driverVersion))};
|
||||
|
||||
auto array = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
|
||||
env->SetObjectArrayElement(array, 0, env->NewStringUTF(driverId.c_str()));
|
||||
env->SetObjectArrayElement(array, 1, env->NewStringUTF(driverVersion.c_str()));
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_supportsCustomDriverLoading(JNIEnv *env, jobject instance) {
|
||||
// Check if the Qualcomm KGSL (Kernel Graphics Support Layer) device exists, this indicates that custom drivers can be loaded
|
||||
constexpr auto KgslPath{"/dev/kgsl-3d0"};
|
||||
|
||||
return access(KgslPath, F_OK) == 0;
|
||||
}
|
61
app/src/main/java/emu/skyline/data/DriverPackageMetadata.kt
Normal file
61
app/src/main/java/emu/skyline/data/DriverPackageMetadata.kt
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.*
|
||||
import java.io.File
|
||||
|
||||
data class GpuDriverMetadata(
|
||||
val name : String,
|
||||
val author : String,
|
||||
val packageVersion : String,
|
||||
val vendor : String,
|
||||
val driverVersion : String,
|
||||
val minApi : Int,
|
||||
val description : String,
|
||||
val libraryName : String,
|
||||
) {
|
||||
private constructor(metadataV1 : GpuDriverMetadataV1) : this(
|
||||
name = metadataV1.name,
|
||||
author = metadataV1.author,
|
||||
packageVersion = metadataV1.packageVersion,
|
||||
vendor = metadataV1.vendor,
|
||||
driverVersion = metadataV1.driverVersion,
|
||||
minApi = metadataV1.minApi,
|
||||
description = metadataV1.description,
|
||||
libraryName = metadataV1.libraryName,
|
||||
)
|
||||
|
||||
val label get() = "${name}-v${packageVersion}"
|
||||
|
||||
companion object {
|
||||
private const val SCHEMA_VERSION_V1 = 1
|
||||
|
||||
fun deserialize(metadataFile : File) : GpuDriverMetadata {
|
||||
val metadataJson = Json.parseToJsonElement(metadataFile.readText())
|
||||
|
||||
return when (metadataJson.jsonObject["schemaVersion"]?.jsonPrimitive?.intOrNull) {
|
||||
SCHEMA_VERSION_V1 -> GpuDriverMetadata(Json.decodeFromJsonElement<GpuDriverMetadataV1>(metadataJson))
|
||||
else -> throw SerializationException("Unsupported metadata version")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class GpuDriverMetadataV1(
|
||||
val schemaVersion : Int,
|
||||
val name : String,
|
||||
val author : String,
|
||||
val packageVersion : String,
|
||||
val vendor : String,
|
||||
val driverVersion : String,
|
||||
val minApi : Int,
|
||||
val description : String,
|
||||
val libraryName : String,
|
||||
)
|
225
app/src/main/java/emu/skyline/utils/GpuDriverHelper.kt
Normal file
225
app/src/main/java/emu/skyline/utils/GpuDriverHelper.kt
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import emu.skyline.R
|
||||
import emu.skyline.data.GpuDriverMetadata
|
||||
import emu.skyline.getPublicFilesDir
|
||||
import kotlinx.serialization.SerializationException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
private const val GPU_DRIVER_DIRECTORY = "gpu_drivers"
|
||||
private const val GPU_DRIVER_FILE_REDIRECT_DIR = "gpu/vk_file_redirect"
|
||||
private const val GPU_DRIVER_INSTALL_TEMP_DIR = "driver_temp"
|
||||
private const val GPU_DRIVER_META_FILE = "meta.json"
|
||||
private const val TAG = "GPUDriverHelper"
|
||||
|
||||
interface GpuDriverHelper {
|
||||
companion object {
|
||||
/**
|
||||
* Returns information about the system GPU driver.
|
||||
* @return An array containing the driver vendor and the driver version, in this order, or `null` if an error occurred
|
||||
*/
|
||||
private external fun getSystemDriverInfo() : Array<String>?
|
||||
|
||||
/**
|
||||
* Queries the driver for custom driver loading support.
|
||||
* @return `true` if the device supports loading custom drivers, `false` otherwise
|
||||
*/
|
||||
external fun supportsCustomDriverLoading() : Boolean
|
||||
|
||||
/**
|
||||
* Returns the list of installed gpu drivers.
|
||||
* @return A map from the folder the driver is installed to the metadata of the driver
|
||||
*/
|
||||
fun getInstalledDrivers(context : Context) : Map<File, GpuDriverMetadata> {
|
||||
val gpuDriverDir = getDriversDirectory(context)
|
||||
|
||||
// A map between the driver location and its metadata
|
||||
val driverMap = mutableMapOf<File, GpuDriverMetadata>()
|
||||
|
||||
gpuDriverDir.listFiles()?.forEach { entry ->
|
||||
// Delete any files that aren't a directory
|
||||
if (!entry.isDirectory) {
|
||||
entry.delete()
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val metadataFile = File(entry.canonicalPath, GPU_DRIVER_META_FILE)
|
||||
// Delete entries without metadata
|
||||
if (!metadataFile.exists()) {
|
||||
entry.delete()
|
||||
return@forEach
|
||||
}
|
||||
|
||||
try {
|
||||
driverMap[entry] = GpuDriverMetadata.deserialize(metadataFile)
|
||||
} catch (e : SerializationException) {
|
||||
Log.w(TAG, "Failed to load gpu driver metadata for ${entry.name}, skipping\n${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
return driverMap
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches metadata about the system driver.
|
||||
* @return A [GpuDriverMetadata] object containing data about the system driver
|
||||
*/
|
||||
fun getSystemDriverMetadata(context : Context) : GpuDriverMetadata {
|
||||
val systemDriverInfo = getSystemDriverInfo()
|
||||
|
||||
return GpuDriverMetadata(
|
||||
name = context.getString(R.string.system_driver),
|
||||
author = "",
|
||||
packageVersion = "",
|
||||
vendor = systemDriverInfo?.get(0) ?: "",
|
||||
driverVersion = systemDriverInfo?.get(1) ?: "",
|
||||
minApi = 0,
|
||||
description = context.getString(R.string.system_driver_desc),
|
||||
libraryName = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given driver to the emulator's drivers directory.
|
||||
* @param stream The input stream to read the driver from
|
||||
* @return The exit status of the installation process
|
||||
*/
|
||||
fun installDriver(context : Context, stream : InputStream) : GpuDriverInstallResult {
|
||||
val installTempDir = File(context.cacheDir.canonicalPath, GPU_DRIVER_INSTALL_TEMP_DIR).apply {
|
||||
deleteRecursively()
|
||||
}
|
||||
|
||||
try {
|
||||
ZipUtils.unzip(stream, installTempDir)
|
||||
} catch (e : Exception) {
|
||||
e.printStackTrace()
|
||||
installTempDir.deleteRecursively()
|
||||
return GpuDriverInstallResult.INVALID_ARCHIVE
|
||||
}
|
||||
|
||||
return installUnpackedDriver(context, installTempDir)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given driver to the emulator's drivers directory.
|
||||
* @param file The file to read the driver from
|
||||
* @return The exit status of the installation process
|
||||
*/
|
||||
fun installDriver(context : Context, file : File) : GpuDriverInstallResult {
|
||||
val installTempDir = File(context.cacheDir.canonicalPath, GPU_DRIVER_INSTALL_TEMP_DIR).apply {
|
||||
deleteRecursively()
|
||||
}
|
||||
|
||||
try {
|
||||
ZipUtils.unzip(file, installTempDir)
|
||||
} catch (e : Exception) {
|
||||
e.printStackTrace()
|
||||
installTempDir.deleteRecursively()
|
||||
return GpuDriverInstallResult.INVALID_ARCHIVE
|
||||
}
|
||||
|
||||
return installUnpackedDriver(context, installTempDir)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given unpacked driver to the emulator's drivers directory.
|
||||
* @param unpackDir The location of the unpacked driver
|
||||
* @return The exit status of the installation process
|
||||
*/
|
||||
private fun installUnpackedDriver(context : Context, unpackDir : File) : GpuDriverInstallResult {
|
||||
val cleanup = {
|
||||
unpackDir.deleteRecursively()
|
||||
}
|
||||
|
||||
// Check that the metadata file exists
|
||||
val metadataFile = File(unpackDir, GPU_DRIVER_META_FILE)
|
||||
if (!metadataFile.isFile) {
|
||||
cleanup()
|
||||
return GpuDriverInstallResult.MISSING_METADATA
|
||||
}
|
||||
|
||||
// Check that the driver metadata is valid
|
||||
val driverMetadata = try {
|
||||
GpuDriverMetadata.deserialize(metadataFile)
|
||||
} catch (e : SerializationException) {
|
||||
cleanup()
|
||||
return GpuDriverInstallResult.INVALID_METADATA
|
||||
}
|
||||
|
||||
// Check that the device satisfies the driver's minimum Android version requirements
|
||||
if (Build.VERSION.SDK_INT < driverMetadata.minApi) {
|
||||
cleanup()
|
||||
return GpuDriverInstallResult.UNSUPPORTED_ANDROID_VERSION
|
||||
}
|
||||
|
||||
// Check that the driver is not already installed
|
||||
val installedDrivers = getInstalledDrivers(context)
|
||||
val finalInstallDir = File(getDriversDirectory(context), driverMetadata.label)
|
||||
if (installedDrivers[finalInstallDir] != null) {
|
||||
cleanup()
|
||||
return GpuDriverInstallResult.ALREADY_INSTALLED
|
||||
}
|
||||
|
||||
// Move the driver files to the final location
|
||||
if (!unpackDir.renameTo(finalInstallDir)) {
|
||||
cleanup()
|
||||
throw IOException("Failed to create directory ${finalInstallDir.name}")
|
||||
}
|
||||
|
||||
return GpuDriverInstallResult.SUCCESS
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the library name from the driver metadata for the given driver.
|
||||
*/
|
||||
fun getLibraryName(context : Context, driverLabel : String) : String {
|
||||
val driverDir = File(getDriversDirectory(context), driverLabel)
|
||||
val metadataFile = File(driverDir, GPU_DRIVER_META_FILE)
|
||||
return try {
|
||||
GpuDriverMetadata.deserialize(metadataFile).libraryName
|
||||
} catch (e : SerializationException) {
|
||||
Log.w(TAG, "Failed to load library name for driver ${driverLabel}, driver may not exist or have invalid metadata")
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureFileRedirectDir(context : Context) {
|
||||
File(context.getPublicFilesDir(), GPU_DRIVER_FILE_REDIRECT_DIR).apply {
|
||||
if (!isDirectory) {
|
||||
delete()
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drivers' folder from
|
||||
*/
|
||||
private fun getDriversDirectory(context : Context) = File(context.filesDir.canonicalPath, GPU_DRIVER_DIRECTORY).apply {
|
||||
// Create the directory if it doesn't exist
|
||||
if (!isDirectory) {
|
||||
delete()
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class GpuDriverInstallResult {
|
||||
SUCCESS,
|
||||
INVALID_ARCHIVE,
|
||||
MISSING_METADATA,
|
||||
INVALID_METADATA,
|
||||
UNSUPPORTED_ANDROID_VERSION,
|
||||
ALREADY_INSTALLED,
|
||||
}
|
Loading…
Reference in New Issue
Block a user