From 0fc3d692b927bd84f8da14f63532b89b8ea81b6e Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Sat, 11 Apr 2026 20:22:20 +0200 Subject: [PATCH] android: Allow deleting per title disk shader cache (#2032) --- .../java/org/citra/citra_emu/NativeLibrary.kt | 3 + .../citra/citra_emu/adapters/GameAdapter.kt | 58 +++++++++++++++++++ src/android/app/src/main/jni/native.cpp | 42 +++++++++++++- .../src/main/res/layout/dialog_about_game.xml | 15 +++-- .../app/src/main/res/values/strings.xml | 4 ++ 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index f8cb55874..018fea670 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -258,6 +258,9 @@ object NativeLibrary { external fun nativeFileExists(path: String): Boolean + external fun deleteOpenGLShaderCache(titleId: Long) + external fun deleteVulkanShaderCache(titleId: Long) + private var coreErrorAlertResult = false private val coreErrorAlertLock = Object() diff --git a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.kt index d43ea5a60..392f68661 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.kt @@ -36,6 +36,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import android.widget.PopupMenu +import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton @@ -514,6 +515,63 @@ class GameAdapter( showUninstallContextMenu(it, game, bottomSheetDialog) } + bottomSheetView.findViewById(R.id.delete_cache).setOnClickListener { + val options = arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles)) + var selectedIndex = -1 + val dialog = MaterialAlertDialogBuilder(context) + .setTitle(R.string.delete_cache_select_backend) + .setSingleChoiceItems(options, -1) { dialog, which -> + selectedIndex = which + } + .setPositiveButton(android.R.string.ok) {_, _ -> + val progToast = Toast.makeText( + CitraApplication.appContext, + R.string.deleting_shader_cache, + Toast.LENGTH_LONG + ) + progToast.show() + + activity.lifecycleScope.launch(Dispatchers.IO) { + + when (selectedIndex) { + 0 -> { + NativeLibrary.deleteVulkanShaderCache(game.titleId) + } + 1 -> { + NativeLibrary.deleteOpenGLShaderCache(game.titleId) + } + } + + activity.runOnUiThread { + progToast.cancel() + Toast.makeText( + CitraApplication.appContext, + R.string.shader_cache_deleted, + Toast.LENGTH_SHORT + ).show() + } + } + } + .setNegativeButton(android.R.string.cancel) { dialog, _ -> + dialog.dismiss() + } + .create() + + dialog.setOnShowListener { + val positiveButton = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE) + + positiveButton.isEnabled = false + + val listView = dialog.listView + listView.setOnItemClickListener { _, _, position, _ -> + selectedIndex = position + positiveButton.isEnabled = true + } + } + + dialog.show() + } + val bottomSheetBehavior = bottomSheetDialog.getBehavior() bottomSheetBehavior.skipCollapsed = true bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f4a30610c..42ac888d2 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -1130,4 +1130,44 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_nativeFileExists(JNIEnv* env, j return FileUtil::Exists(path); } -} // extern "C" +void Java_org_citra_citra_1emu_NativeLibrary_deleteOpenGLShaderCache(JNIEnv* env, jobject obj, + jlong title_id) { + for (const std::string_view cache_type : {"separable", "conventional"}) { + const std::string path = + fmt::format("{}opengl/precompiled/{}/{:016X}.bin", + FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), cache_type, title_id); + LOG_INFO(Frontend, "Deleting shader file: {}", path); + FileUtil::Delete(path); + } + const std::string path = + fmt::format("{}opengl/transferable/{:016X}.bin", + FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id); + LOG_INFO(Frontend, "Deleting shader file: {}", path); + FileUtil::Delete(path); +} + +void Java_org_citra_citra_1emu_NativeLibrary_deleteVulkanShaderCache(JNIEnv* env, jobject obj, + jlong title_id) { + for (const std::string_view cache_type : {"vs", "fs", "gs", "pl"}) { + const std::string path = + fmt::format("{}vulkan/transferable/{:016X}_{}.vkch", + FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id, cache_type); + LOG_INFO(Frontend, "Deleting shader file: {}", path); + FileUtil::Delete(path); + } + + FileUtil::ForeachDirectoryEntry( + nullptr, + fmt::format("{}vulkan/pipeline", FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)), + [title_id]([[maybe_unused]] u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) { + if (virtual_name.starts_with(fmt::format("{:016X}", title_id))) { + std::string path = directory + DIR_SEP + virtual_name; + LOG_INFO(Frontend, "Deleting shader file: {}", path); + FileUtil::Delete(path); + } + return true; + }); +} + +} // extern "C" \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/dialog_about_game.xml b/src/android/app/src/main/res/layout/dialog_about_game.xml index cb7eea8de..6bcc2a08e 100644 --- a/src/android/app/src/main/res/layout/dialog_about_game.xml +++ b/src/android/app/src/main/res/layout/dialog_about_game.xml @@ -161,7 +161,7 @@ + app:layout_constraintTop_toBottomOf="@+id/horizontal_layout_2"> + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 750677fd5..08e927081 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -586,6 +586,10 @@ Preparing Shaders Building %s + Delete Shader Cache + Select which graphics API to delete the shader cache + Deleting shader cache for title, please wait… + Shader cache deleted Play