Skip to content

Commit

Permalink
Merge pull request #11867 from K0bin/adrenotools
Browse files Browse the repository at this point in the history
Implement loading custom drivers on Android
  • Loading branch information
JMC47 committed Jun 11, 2023
2 parents bcd6e2e + 2da7d16 commit 5c0581e
Show file tree
Hide file tree
Showing 33 changed files with 904 additions and 36 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Expand Up @@ -54,3 +54,6 @@
[submodule "Externals/rcheevos/rcheevos"]
path = Externals/rcheevos/rcheevos
url = https://github.com/RetroAchievements/rcheevos.git
[submodule "Externals/libadrenotools"]
path = Externals/libadrenotools
url = https://github.com/bylaws/libadrenotools.git
5 changes: 5 additions & 0 deletions CMakeLists.txt
Expand Up @@ -728,6 +728,11 @@ if(ENABLE_VULKAN)
if(APPLE AND USE_BUNDLED_MOLTENVK)
add_subdirectory(Externals/MoltenVK)
endif()


if (ANDROID AND _M_ARM_64)
add_subdirectory(Externals/libadrenotools)
endif()
endif()

if(NOT WIN32 OR (NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")))
Expand Down
1 change: 1 addition & 0 deletions Externals/libadrenotools
Submodule libadrenotools added at f4ce3c
7 changes: 6 additions & 1 deletion Source/Android/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version "1.7.20"
}

task copyProfile (type: Copy) {
Expand Down Expand Up @@ -110,6 +111,7 @@ android {
externalNativeBuild {
cmake {
path "../../../CMakeLists.txt"
version "3.22.1+"
}
}
namespace 'org.dolphinemu.dolphinemu'
Expand All @@ -122,7 +124,7 @@ android {
abiFilters "arm64-v8a", "x86_64" //, "armeabi-v7a", "x86"

// Remove the line below if you want to build the C++ unit tests
targets "main"
//targets "main", "hook_impl", "main_hook", "gsl_alloc_hook", "file_redirect_hook"
}
}
}
Expand Down Expand Up @@ -160,6 +162,9 @@ dependencies {
// For loading game covers from disk and GameTDB
implementation 'io.coil-kt:coil:2.2.2'

// For loading custom GPU drivers
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"

implementation 'com.nononsenseapps:filepicker:4.2.1'
}

Expand Down
3 changes: 2 additions & 1 deletion Source/Android/app/src/main/AndroidManifest.xml
Expand Up @@ -41,7 +41,8 @@
android:supportsRtl="true"
android:isGame="true"
android:banner="@drawable/banner_tv"
android:hasFragileUserData="true">
android:hasFragileUserData="true"
android:extractNativeLibs="true">
<meta-data
android:name="android.max_aspect"
android:value="2.1"/>
Expand Down
Expand Up @@ -62,6 +62,12 @@ enum class StringSetting(
Settings.SECTION_GFX_ENHANCEMENTS,
"PostProcessingShader",
""
),
GFX_DRIVER_LIB_NAME(
Settings.FILE_GFX,
Settings.SECTION_GFX_SETTINGS,
"DriverLibName",
""
);

override val isOverridden: Boolean
Expand Down
Expand Up @@ -49,7 +49,8 @@ enum class MenuTag {
WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0),
WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1),
WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2),
WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3);
WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3),
GPU_DRIVERS("gpu_drivers");

var tag: String
private set
Expand Down
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// GPU driver implementation partially based on:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.features.settings.ui

import android.content.Context
Expand All @@ -20,13 +24,15 @@ import androidx.lifecycle.ViewModelProvider
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding
import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
import org.dolphinemu.dolphinemu.utils.InsetsHelper
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint
Expand Down Expand Up @@ -165,8 +171,21 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
super.onActivityResult(requestCode, resultCode, result)

// If the user picked a file, as opposed to just backing out.
if (resultCode == RESULT_OK) {
if (requestCode != MainPresenter.REQUEST_DIRECTORY) {
if (resultCode != RESULT_OK) {
return
}

when (requestCode) {
MainPresenter.REQUEST_DIRECTORY -> {
val path = FileBrowserHelper.getSelectedPath(result)
fragment!!.adapter!!.onFilePickerConfirmation(path!!)
}

MainPresenter.REQUEST_GAME_FILE
or MainPresenter.REQUEST_SD_FILE
or MainPresenter.REQUEST_WAD_FILE
or MainPresenter.REQUEST_WII_SAVE_FILE
or MainPresenter.REQUEST_NAND_BIN_FILE -> {
val uri = canonicalizeIfPossible(result!!.data!!)
val validExtensions: Set<String> =
if (requestCode == MainPresenter.REQUEST_GAME_FILE) FileBrowserHelper.GAME_EXTENSIONS else FileBrowserHelper.RAW_EXTENSION
Expand All @@ -178,9 +197,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
contentResolver.takePersistableUriPermission(uri, takeFlags)
fragment!!.adapter!!.onFilePickerConfirmation(uri.toString())
}
} else {
val path = FileBrowserHelper.getSelectedPath(result)
fragment!!.adapter!!.onFilePickerConfirmation(path!!)
}
}
}
Expand Down
Expand Up @@ -3,21 +3,31 @@
package org.dolphinemu.dolphinemu.features.settings.ui

import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding
import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
import org.dolphinemu.dolphinemu.ui.main.MainActivity
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
import java.util.*
import kotlin.collections.ArrayList
Expand Down Expand Up @@ -111,6 +121,11 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}

override fun loadSubMenu(menuKey: MenuTag) {
if (menuKey == MenuTag.GPU_DRIVERS) {
showGpuDriverDialog()
return
}

activityView!!.showSettingsFragment(
menuKey,
null,
Expand Down Expand Up @@ -170,6 +185,74 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
}

override fun showGpuDriverDialog() {
if (presenter.gpuDriver == null) {
return
}
val msg = "${presenter!!.gpuDriver!!.name} ${presenter!!.gpuDriver!!.driverVersion}"

MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.gpu_driver_dialog_title))
.setMessage(msg)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.gpu_driver_dialog_system) { _: DialogInterface?, _: Int ->
presenter.useSystemDriver()
}
.setPositiveButton(R.string.gpu_driver_dialog_install) { _: DialogInterface?, _: Int ->
askForDriverFile()
}
.show()
}

private fun askForDriverFile() {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
type = "application/zip"
}
startActivityForResult(intent, MainPresenter.REQUEST_GPU_DRIVER)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

// If the user picked a file, as opposed to just backing out.
if (resultCode != AppCompatActivity.RESULT_OK) {
return
}

if (requestCode != MainPresenter.REQUEST_GPU_DRIVER) {
return
}

val uri = data?.data ?: return
presenter.installDriver(uri)
}

override fun onDriverInstallDone(result: GpuDriverInstallResult) {
val view = binding?.root ?: return
Snackbar
.make(view, resolveInstallResultString(result), Snackbar.LENGTH_LONG)
.show()
}

override fun onDriverUninstallDone() {
Toast.makeText(
requireContext(),
R.string.gpu_driver_dialog_uninstall_done,
Toast.LENGTH_SHORT
).show()
}

private fun resolveInstallResultString(result: GpuDriverInstallResult) = when (result) {
GpuDriverInstallResult.Success -> getString(R.string.gpu_driver_install_success)
GpuDriverInstallResult.InvalidArchive -> getString(R.string.gpu_driver_install_invalid_archive)
GpuDriverInstallResult.MissingMetadata -> getString(R.string.gpu_driver_install_missing_metadata)
GpuDriverInstallResult.InvalidMetadata -> getString(R.string.gpu_driver_install_invalid_metadata)
GpuDriverInstallResult.UnsupportedAndroidVersion -> getString(R.string.gpu_driver_install_unsupported_android_version)
GpuDriverInstallResult.AlreadyInstalled -> getString(R.string.gpu_driver_install_already_installed)
GpuDriverInstallResult.FileNotFound -> getString(R.string.gpu_driver_install_file_not_found)
}

companion object {
private const val ARGUMENT_MENU_TAG = "menu_tag"
private const val ARGUMENT_GAME_ID = "game_id"
Expand Down
Expand Up @@ -4,11 +4,16 @@ package org.dolphinemu.dolphinemu.features.settings.ui

import android.content.Context
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.ArraySet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.UserDataActivity
Expand All @@ -25,6 +30,7 @@ import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog
import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter
import org.dolphinemu.dolphinemu.features.settings.model.*
import org.dolphinemu.dolphinemu.features.settings.model.view.*
import org.dolphinemu.dolphinemu.model.GpuDriverMetadata
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.utils.*
import java.util.*
Expand All @@ -45,6 +51,9 @@ class SettingsFragmentPresenter(
private var controllerNumber = 0
private var controllerType = 0

var gpuDriver: GpuDriverMetadata? = null
private val libNameSetting: StringSetting = StringSetting.GFX_DRIVER_LIB_NAME

fun onCreate(menuTag: MenuTag, gameId: String?, extras: Bundle) {
this.gameId = gameId
this.menuTag = menuTag
Expand All @@ -56,6 +65,11 @@ class SettingsFragmentPresenter(
controllerNumber = menuTag.subType
} else if (menuTag.isSerialPort1Menu) {
serialPort1Type = extras.getInt(ARG_SERIALPORT1_TYPE)
} else if (menuTag == MenuTag.GRAPHICS) {
this.gpuDriver =
GpuDriverHelper.getInstalledDriverMetadata() ?: GpuDriverHelper.getSystemDriverMetadata(
context.applicationContext
)
}
}

Expand Down Expand Up @@ -1250,6 +1264,15 @@ class SettingsFragmentPresenter(
MenuTag.ADVANCED_GRAPHICS
)
)

if (GpuDriverHelper.supportsCustomDriverLoading() && this.gpuDriver != null) {
sl.add(
SubmenuSetting(
context,
R.string.gpu_driver_submenu, MenuTag.GPU_DRIVERS
)
)
}
}

private fun addEnhanceSettings(sl: ArrayList<SettingsItem>) {
Expand Down Expand Up @@ -2113,7 +2136,7 @@ class SettingsFragmentPresenter(
profileString: String,
controllerNumber: Int
) {
val profiles = ProfileDialogPresenter(menuTag).getProfileNames(false)
val profiles = ProfileDialogPresenter(menuTag!!).getProfileNames(false)
val profileKey = profileString + "Profile" + (controllerNumber + 1)
sl.add(
StringSingleChoiceSetting(
Expand Down Expand Up @@ -2324,6 +2347,45 @@ class SettingsFragmentPresenter(
)
}

fun installDriver(uri: Uri) {
val context = this.context.applicationContext
CoroutineScope(Dispatchers.IO).launch {
val stream = context.contentResolver.openInputStream(uri)
if (stream == null) {
GpuDriverHelper.uninstallDriver()
withContext(Dispatchers.Main) {
fragmentView.onDriverInstallDone(GpuDriverInstallResult.FileNotFound)
}
return@launch
}

val result = GpuDriverHelper.installDriver(stream)
withContext(Dispatchers.Main) {
with(this@SettingsFragmentPresenter) {
this.gpuDriver = GpuDriverHelper.getInstalledDriverMetadata()
?: GpuDriverHelper.getSystemDriverMetadata(context) ?: return@withContext
this.libNameSetting.setString(this.settings!!, this.gpuDriver!!.libraryName)
}
fragmentView.onDriverInstallDone(result)
}
}
}

fun useSystemDriver() {
CoroutineScope(Dispatchers.IO).launch {
GpuDriverHelper.uninstallDriver()
withContext(Dispatchers.Main) {
with(this@SettingsFragmentPresenter) {
this.gpuDriver =
GpuDriverHelper.getInstalledDriverMetadata()
?: GpuDriverHelper.getSystemDriverMetadata(context.applicationContext)
this.libNameSetting.setString(this.settings!!, "")
}
fragmentView.onDriverUninstallDone()
}
}
}

companion object {
private val LOG_TYPE_NAMES = NativeLibrary.GetLogTypeNames()
const val ARG_CONTROLLER_TYPE = "controller_type"
Expand Down

0 comments on commit 5c0581e

Please sign in to comment.