mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	android: frontend: Add support for GPU driver selection.
This commit is contained in:
		@@ -27,6 +27,7 @@
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:isGame="true"
 | 
			
		||||
        android:banner="@mipmap/ic_launcher"
 | 
			
		||||
        android:extractNativeLibs="true"
 | 
			
		||||
        android:requestLegacyExternalStorage="true">
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
 
 | 
			
		||||
@@ -181,7 +181,7 @@ public final class NativeLibrary {
 | 
			
		||||
 | 
			
		||||
    public static native void SetAppDirectory(String directory);
 | 
			
		||||
 | 
			
		||||
    public static native void SetGpuDriverParameters(String hookLibDir, String customDriverDir, String customDriverName, String fileRedirectDir);
 | 
			
		||||
    public static native void InitializeGpuDriver(String hookLibDir, String customDriverDir, String customDriverName, String fileRedirectDir);
 | 
			
		||||
 | 
			
		||||
    public static native boolean ReloadKeys();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import android.os.Build;
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GameDatabase;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DocumentsTree;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper;
 | 
			
		||||
 | 
			
		||||
public class YuzuApplication extends Application {
 | 
			
		||||
    public static GameDatabase databaseHelper;
 | 
			
		||||
@@ -43,7 +44,7 @@ public class YuzuApplication extends Application {
 | 
			
		||||
        documentsTree = new DocumentsTree();
 | 
			
		||||
 | 
			
		||||
        DirectoryInitialization.start(getApplicationContext());
 | 
			
		||||
 | 
			
		||||
        GpuDriverHelper.initializeDriverParameters(getApplicationContext());
 | 
			
		||||
        NativeLibrary.LogDeviceInfo();
 | 
			
		||||
 | 
			
		||||
        // TODO(bunnei): Disable notifications until we support app suspension.
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import android.view.MenuItem;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.appcompat.app.AlertDialog;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
 | 
			
		||||
@@ -22,6 +23,7 @@ import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileBrowserHelper;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.PicassoUtils;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.StartupHandler;
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ThemeUtil;
 | 
			
		||||
@@ -128,6 +130,41 @@ public final class MainActivity extends AppCompatActivity implements MainView {
 | 
			
		||||
                        MainPresenter.REQUEST_INSTALL_KEYS,
 | 
			
		||||
                        R.string.install_keys);
 | 
			
		||||
                break;
 | 
			
		||||
            case MainPresenter.REQUEST_SELECT_GPU_DRIVER:
 | 
			
		||||
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
 | 
			
		||||
 | 
			
		||||
                // Get the driver name for the dialog message.
 | 
			
		||||
                String driverName = GpuDriverHelper.getCustomDriverName();
 | 
			
		||||
                if (driverName == null) {
 | 
			
		||||
                    driverName = getString(R.string.system_gpu_driver);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Set the dialog message and title.
 | 
			
		||||
                builder.setTitle(getString(R.string.select_gpu_driver_title));
 | 
			
		||||
                builder.setMessage(driverName);
 | 
			
		||||
 | 
			
		||||
                // Cancel button is a no-op.
 | 
			
		||||
                builder.setNegativeButton(android.R.string.cancel, null);
 | 
			
		||||
 | 
			
		||||
                // Select the default system driver.
 | 
			
		||||
                builder.setPositiveButton(R.string.select_gpu_driver_default, (dialogInterface, i) ->
 | 
			
		||||
                {
 | 
			
		||||
                    GpuDriverHelper.installDefaultDriver(this);
 | 
			
		||||
                    Toast.makeText(this, R.string.select_gpu_driver_use_default, Toast.LENGTH_SHORT).show();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Use the file picker to install a custom driver.
 | 
			
		||||
                builder.setNeutralButton(R.string.select_gpu_driver_install, (dialogInterface, i) -> {
 | 
			
		||||
                    FileBrowserHelper.openFilePicker(this,
 | 
			
		||||
                            MainPresenter.REQUEST_SELECT_GPU_DRIVER,
 | 
			
		||||
                            R.string.select_gpu_driver);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Show the dialog.
 | 
			
		||||
                AlertDialog alertDialog = builder.create();
 | 
			
		||||
                alertDialog.show();
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -163,12 +200,26 @@ public final class MainActivity extends AppCompatActivity implements MainView {
 | 
			
		||||
                            Toast.makeText(this, R.string.install_keys_success, Toast.LENGTH_SHORT).show();
 | 
			
		||||
                            refreshFragment();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            Toast.makeText(this, R.string.install_keys_failure, Toast.LENGTH_SHORT).show();
 | 
			
		||||
                            Toast.makeText(this, R.string.install_keys_failure, Toast.LENGTH_LONG).show();
 | 
			
		||||
                            launchFileListActivity(MainPresenter.REQUEST_INSTALL_KEYS);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case MainPresenter.REQUEST_SELECT_GPU_DRIVER:
 | 
			
		||||
                if (resultCode == MainActivity.RESULT_OK) {
 | 
			
		||||
                    int takeFlags = (Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
 | 
			
		||||
                    getContentResolver().takePersistableUriPermission(Uri.parse(result.getDataString()), takeFlags);
 | 
			
		||||
                    GpuDriverHelper.installCustomDriver(this, result.getData());
 | 
			
		||||
                    String driverName = GpuDriverHelper.getCustomDriverName();
 | 
			
		||||
                    if (driverName != null) {
 | 
			
		||||
                        Toast.makeText(this, getString(R.string.select_gpu_driver_install_success) + " " + driverName, Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Toast.makeText(this, R.string.select_gpu_driver_error, Toast.LENGTH_LONG).show();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
 | 
			
		||||
public final class MainPresenter {
 | 
			
		||||
    public static final int REQUEST_ADD_DIRECTORY = 1;
 | 
			
		||||
    public static final int REQUEST_INSTALL_KEYS = 2;
 | 
			
		||||
    public static final int REQUEST_SELECT_GPU_DRIVER = 3;
 | 
			
		||||
    private final MainView mView;
 | 
			
		||||
    private String mDirToAdd;
 | 
			
		||||
    private long mLastClickTime = 0;
 | 
			
		||||
@@ -51,6 +52,10 @@ public final class MainPresenter {
 | 
			
		||||
            case R.id.button_install_keys:
 | 
			
		||||
                launchFileListActivity(REQUEST_INSTALL_KEYS);
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            case R.id.button_select_gpu_driver:
 | 
			
		||||
                launchFileListActivity(REQUEST_SELECT_GPU_DRIVER);
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,130 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.utils;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.zip.ZipEntry;
 | 
			
		||||
import java.util.zip.ZipInputStream;
 | 
			
		||||
 | 
			
		||||
public class GpuDriverHelper {
 | 
			
		||||
    private static final String META_JSON_FILENAME = "meta.json";
 | 
			
		||||
    private static final String DRIVER_INTERNAL_FILENAME = "gpu_driver.zip";
 | 
			
		||||
    private static String fileRedirectionPath;
 | 
			
		||||
    private static String driverInstallationPath;
 | 
			
		||||
    private static String hookLibPath;
 | 
			
		||||
 | 
			
		||||
    private static void unzip(String zipFilePath, String destDir) throws IOException {
 | 
			
		||||
        File dir = new File(destDir);
 | 
			
		||||
 | 
			
		||||
        // Create output directory if it doesn't exist
 | 
			
		||||
        if (!dir.exists()) dir.mkdirs();
 | 
			
		||||
 | 
			
		||||
        // Unpack the files.
 | 
			
		||||
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
 | 
			
		||||
        byte[] buffer = new byte[1024];
 | 
			
		||||
        ZipEntry ze = zis.getNextEntry();
 | 
			
		||||
        while (ze != null) {
 | 
			
		||||
            String fileName = ze.getName();
 | 
			
		||||
            File newFile = new File(destDir + fileName);
 | 
			
		||||
            newFile.getParentFile().mkdirs();
 | 
			
		||||
            FileOutputStream fos = new FileOutputStream(newFile);
 | 
			
		||||
            int len;
 | 
			
		||||
            while ((len = zis.read(buffer)) > 0) {
 | 
			
		||||
                fos.write(buffer, 0, len);
 | 
			
		||||
            }
 | 
			
		||||
            fos.close();
 | 
			
		||||
            zis.closeEntry();
 | 
			
		||||
            ze = zis.getNextEntry();
 | 
			
		||||
        }
 | 
			
		||||
        zis.closeEntry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void initializeDriverParameters(Context context) {
 | 
			
		||||
        try {
 | 
			
		||||
            // Initialize the file redirection directory.
 | 
			
		||||
            fileRedirectionPath = context.getExternalFilesDir(null).getCanonicalPath() + "/gpu/vk_file_redirect/";
 | 
			
		||||
 | 
			
		||||
            // Initialize the driver installation directory.
 | 
			
		||||
            driverInstallationPath = context.getFilesDir().getCanonicalPath() + "/gpu_driver/";
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Initialize directories.
 | 
			
		||||
        initializeDirectories();
 | 
			
		||||
 | 
			
		||||
        // Initialize hook libraries directory.
 | 
			
		||||
        hookLibPath = context.getApplicationInfo().nativeLibraryDir + "/";
 | 
			
		||||
 | 
			
		||||
        // Initialize GPU driver.
 | 
			
		||||
        NativeLibrary.InitializeGpuDriver(hookLibPath, driverInstallationPath, getCustomDriverLibraryName(), fileRedirectionPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void installDefaultDriver(Context context) {
 | 
			
		||||
        // Removing the installed driver will result in the backend using the default system driver.
 | 
			
		||||
        File driverInstallationDir = new File(driverInstallationPath);
 | 
			
		||||
        deleteRecursive(driverInstallationDir);
 | 
			
		||||
        initializeDriverParameters(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void installCustomDriver(Context context, Uri driverPathUri) {
 | 
			
		||||
        // Revert to system default in the event the specified driver is bad.
 | 
			
		||||
        installDefaultDriver(context);
 | 
			
		||||
 | 
			
		||||
        // Ensure we have directories.
 | 
			
		||||
        initializeDirectories();
 | 
			
		||||
 | 
			
		||||
        // Copy the zip file URI into our private storage.
 | 
			
		||||
        FileUtil.copyUriToInternalStorage(context, driverPathUri, driverInstallationPath, DRIVER_INTERNAL_FILENAME);
 | 
			
		||||
 | 
			
		||||
        // Unzip the driver.
 | 
			
		||||
        try {
 | 
			
		||||
            unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Initialize the driver parameters.
 | 
			
		||||
        initializeDriverParameters(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getCustomDriverName() {
 | 
			
		||||
        // Parse the custom driver metadata to retrieve the name.
 | 
			
		||||
        GpuDriverMetadata metadata = new GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME);
 | 
			
		||||
        return metadata.name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getCustomDriverLibraryName() {
 | 
			
		||||
        // Parse the custom driver metadata to retrieve the library name.
 | 
			
		||||
        GpuDriverMetadata metadata = new GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME);
 | 
			
		||||
        return metadata.libraryName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initializeDirectories() {
 | 
			
		||||
        // Ensure the file redirection directory exists.
 | 
			
		||||
        File fileRedirectionDir = new File(fileRedirectionPath);
 | 
			
		||||
        if (!fileRedirectionDir.exists()) {
 | 
			
		||||
            fileRedirectionDir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
        // Ensure the driver installation directory exists.
 | 
			
		||||
        File driverInstallationDir = new File(driverInstallationPath);
 | 
			
		||||
        if (!driverInstallationDir.exists()) {
 | 
			
		||||
            driverInstallationDir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void deleteRecursive(File fileOrDirectory) {
 | 
			
		||||
        if (fileOrDirectory.isDirectory()) {
 | 
			
		||||
            for (File child : fileOrDirectory.listFiles()) {
 | 
			
		||||
                deleteRecursive(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fileOrDirectory.delete();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.utils;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.nio.file.Files;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.nio.file.Paths;
 | 
			
		||||
 | 
			
		||||
public class GpuDriverMetadata {
 | 
			
		||||
 | 
			
		||||
    public String name;
 | 
			
		||||
    public String description;
 | 
			
		||||
    public String author;
 | 
			
		||||
    public String vendor;
 | 
			
		||||
    public String driverVersion;
 | 
			
		||||
    public int minApi;
 | 
			
		||||
    public String libraryName;
 | 
			
		||||
 | 
			
		||||
    public GpuDriverMetadata(String metadataFilePath) {
 | 
			
		||||
        try {
 | 
			
		||||
            JSONObject json = new JSONObject(getStringFromFile(metadataFilePath));
 | 
			
		||||
            name = json.getString("name");
 | 
			
		||||
            description = json.getString("description");
 | 
			
		||||
            author = json.getString("author");
 | 
			
		||||
            vendor = json.getString("vendor");
 | 
			
		||||
            driverVersion = json.getString("driverVersion");
 | 
			
		||||
            minApi = json.getInt("minApi");
 | 
			
		||||
            libraryName = json.getString("libraryName");
 | 
			
		||||
        } catch (JSONException e) {
 | 
			
		||||
            // JSON is malformed, ignore and treat as unsupported metadata.
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            // File is inaccessible, ignore and treat as unsupported metadata.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getStringFromFile(String filePath) throws IOException {
 | 
			
		||||
        Path path = Paths.get(filePath);
 | 
			
		||||
        byte[] bytes = Files.readAllBytes(path);
 | 
			
		||||
        return new String(bytes, StandardCharsets.UTF_8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -18,6 +18,11 @@
 | 
			
		||||
                android:icon="@drawable/ic_install"
 | 
			
		||||
                android:title="@string/install_keys"
 | 
			
		||||
                app:showAsAction="ifRoom" />
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/button_select_gpu_driver"
 | 
			
		||||
                android:icon="@drawable/ic_settings_core"
 | 
			
		||||
                android:title="@string/select_gpu_driver"
 | 
			
		||||
                app:showAsAction="ifRoom" />
 | 
			
		||||
        </menu>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,16 @@
 | 
			
		||||
    <string name="install_keys_success">Keys successfully installed</string>
 | 
			
		||||
    <string name="install_keys_failure">Keys file (prod.keys) is invalid</string>
 | 
			
		||||
 | 
			
		||||
    <!-- GPU driver installation -->
 | 
			
		||||
    <string name="select_gpu_driver">Select GPU driver</string>
 | 
			
		||||
    <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
 | 
			
		||||
    <string name="select_gpu_driver_install">Install</string>
 | 
			
		||||
    <string name="select_gpu_driver_default">Default</string>
 | 
			
		||||
    <string name="select_gpu_driver_install_success">Installed</string>
 | 
			
		||||
    <string name="select_gpu_driver_use_default">Using default GPU driver</string>
 | 
			
		||||
    <string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
 | 
			
		||||
    <string name="system_gpu_driver">System GPU driver</string>
 | 
			
		||||
 | 
			
		||||
    <!-- Preferences Screen -->
 | 
			
		||||
    <string name="preferences_settings">Settings</string>
 | 
			
		||||
    <string name="preferences_general">General</string>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user