android: Allow deleting per title disk shader cache (#2032)

This commit is contained in:
PabloMK7 2026-04-11 20:22:20 +02:00 committed by GitHub
parent 5983a23d38
commit 0fc3d692b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 116 additions and 6 deletions

View File

@ -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()

View File

@ -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<MaterialButton>(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

View File

@ -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"

View File

@ -161,7 +161,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/game_button_tray"
android:id="@+id/horizontal_layout_2"
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -199,19 +199,24 @@
</LinearLayout>
<LinearLayout
android:id="@+id/compress_tray"
android:id="@+id/horizontal_layout_3"
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="start|center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/game_button_tray">
app:layout_constraintTop_toBottomOf="@+id/horizontal_layout_2">
<com.google.android.material.button.MaterialButton
android:id="@+id/delete_cache"
style="@style/Widget.Material3.Button.TonalButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/delete_shader_cache"
android:text="@string/delete_shader_cache" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -586,6 +586,10 @@
<!-- Disk Shader Cache -->
<string name="preparing_shaders">Preparing Shaders</string>
<string name="building_shaders">Building %s</string>
<string name="delete_shader_cache">Delete Shader Cache</string>
<string name="delete_cache_select_backend">Select which graphics API to delete the shader cache</string>
<string name="deleting_shader_cache">Deleting shader cache for title, please wait…</string>
<string name="shader_cache_deleted">Shader cache deleted</string>
<!-- About Game Dialog -->
<string name="play">Play</string>