forked from suyu/suyu
android: video_core: Add support for disk shader cache. (#64)
This commit is contained in:
parent
6d2e7de2e0
commit
4006468f73
12 changed files with 258 additions and 4 deletions
|
@ -42,6 +42,7 @@ object NativeLibrary {
|
||||||
const val Player8Device = 7
|
const val Player8Device = 7
|
||||||
const val ConsoleDevice = 8
|
const val ConsoleDevice = 8
|
||||||
|
|
||||||
|
@JvmField
|
||||||
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.disk_shader_cache
|
||||||
|
|
||||||
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment
|
||||||
|
|
||||||
|
object DiskShaderCacheProgress {
|
||||||
|
val finishLock = Object()
|
||||||
|
private lateinit var fragment: ShaderProgressDialogFragment
|
||||||
|
|
||||||
|
private fun prepareDialog() {
|
||||||
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()!!
|
||||||
|
emulationActivity.runOnUiThread {
|
||||||
|
fragment = ShaderProgressDialogFragment.newInstance(
|
||||||
|
emulationActivity.getString(R.string.loading),
|
||||||
|
emulationActivity.getString(R.string.preparing_shaders)
|
||||||
|
)
|
||||||
|
fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
|
||||||
|
}
|
||||||
|
synchronized(finishLock) { finishLock.wait() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun loadProgress(stage: Int, progress: Int, max: Int) {
|
||||||
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||||
|
?: error("[DiskShaderCacheProgress] EmulationActivity not present")
|
||||||
|
|
||||||
|
when (LoadCallbackStage.values()[stage]) {
|
||||||
|
LoadCallbackStage.Prepare -> prepareDialog()
|
||||||
|
LoadCallbackStage.Build -> fragment.onUpdateProgress(
|
||||||
|
emulationActivity.getString(R.string.building_shaders),
|
||||||
|
progress,
|
||||||
|
max
|
||||||
|
)
|
||||||
|
LoadCallbackStage.Complete -> fragment.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent to VideoCore::LoadCallbackStage
|
||||||
|
enum class LoadCallbackStage {
|
||||||
|
Prepare, Build, Complete
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.disk_shader_cache
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class ShaderProgressViewModel : ViewModel() {
|
||||||
|
private val _progress = MutableLiveData(0)
|
||||||
|
val progress: LiveData<Int> get() = _progress
|
||||||
|
|
||||||
|
private val _max = MutableLiveData(0)
|
||||||
|
val max: LiveData<Int> get() = _max
|
||||||
|
|
||||||
|
private val _message = MutableLiveData("")
|
||||||
|
val message: LiveData<String> get() = _message
|
||||||
|
|
||||||
|
fun setProgress(progress: Int) {
|
||||||
|
_progress.postValue(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMax(max: Int) {
|
||||||
|
_max.postValue(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMessage(msg: String) {
|
||||||
|
_message.postValue(msg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.disk_shader_cache.ui
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
|
import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress
|
||||||
|
import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel
|
||||||
|
|
||||||
|
class ShaderProgressDialogFragment : DialogFragment() {
|
||||||
|
private var _binding: DialogProgressBarBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
private lateinit var alertDialog: AlertDialog
|
||||||
|
|
||||||
|
private lateinit var shaderProgressViewModel: ShaderProgressViewModel
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
_binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||||
|
shaderProgressViewModel =
|
||||||
|
ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java]
|
||||||
|
|
||||||
|
val title = requireArguments().getString(TITLE)
|
||||||
|
val message = requireArguments().getString(MESSAGE)
|
||||||
|
|
||||||
|
isCancelable = false
|
||||||
|
alertDialog = MaterialAlertDialogBuilder(requireActivity())
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.create()
|
||||||
|
return alertDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress ->
|
||||||
|
binding.progressBar.progress = progress
|
||||||
|
setUpdateText()
|
||||||
|
}
|
||||||
|
shaderProgressViewModel.max.observe(viewLifecycleOwner) { max ->
|
||||||
|
binding.progressBar.max = max
|
||||||
|
setUpdateText()
|
||||||
|
}
|
||||||
|
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
|
||||||
|
alertDialog.setMessage(msg)
|
||||||
|
}
|
||||||
|
synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUpdateProgress(msg: String, progress: Int, max: Int) {
|
||||||
|
shaderProgressViewModel.setProgress(progress)
|
||||||
|
shaderProgressViewModel.setMax(max)
|
||||||
|
shaderProgressViewModel.setMessage(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpdateText() {
|
||||||
|
binding.progressText.text = String.format(
|
||||||
|
"%d/%d",
|
||||||
|
shaderProgressViewModel.progress.value,
|
||||||
|
shaderProgressViewModel.max.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "ProgressDialogFragment"
|
||||||
|
const val TITLE = "title"
|
||||||
|
const val MESSAGE = "message"
|
||||||
|
|
||||||
|
fun newInstance(title: String, message: String): ShaderProgressDialogFragment {
|
||||||
|
val frag = ShaderProgressDialogFragment()
|
||||||
|
val args = Bundle()
|
||||||
|
args.putString(TITLE, title)
|
||||||
|
args.putString(MESSAGE, message)
|
||||||
|
frag.arguments = args
|
||||||
|
return frag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -196,6 +196,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||||
val rendererResolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION)
|
val rendererResolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION)
|
||||||
val rendererAspectRatio =
|
val rendererAspectRatio =
|
||||||
rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASPECT_RATIO)
|
rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASPECT_RATIO)
|
||||||
|
val rendererUseDiskShaderCache =
|
||||||
|
rendererSection.getSetting(SettingsFile.KEY_RENDERER_USE_DISK_SHADER_CACHE)
|
||||||
val rendererForceMaxClocks =
|
val rendererForceMaxClocks =
|
||||||
rendererSection.getSetting(SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK)
|
rendererSection.getSetting(SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK)
|
||||||
val rendererAsynchronousShaders =
|
val rendererAsynchronousShaders =
|
||||||
|
@ -250,6 +252,16 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SwitchSetting(
|
||||||
|
SettingsFile.KEY_RENDERER_USE_DISK_SHADER_CACHE,
|
||||||
|
Settings.SECTION_RENDERER,
|
||||||
|
rendererUseDiskShaderCache,
|
||||||
|
R.string.use_disk_shader_cache,
|
||||||
|
R.string.use_disk_shader_cache_description,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK,
|
SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK,
|
||||||
|
|
|
@ -36,6 +36,7 @@ object SettingsFile {
|
||||||
const val KEY_RENDERER_RESOLUTION = "resolution_setup"
|
const val KEY_RENDERER_RESOLUTION = "resolution_setup"
|
||||||
const val KEY_RENDERER_ASPECT_RATIO = "aspect_ratio"
|
const val KEY_RENDERER_ASPECT_RATIO = "aspect_ratio"
|
||||||
const val KEY_RENDERER_ACCURACY = "gpu_accuracy"
|
const val KEY_RENDERER_ACCURACY = "gpu_accuracy"
|
||||||
|
const val KEY_RENDERER_USE_DISK_SHADER_CACHE = "use_disk_shader_cache"
|
||||||
const val KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders"
|
const val KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders"
|
||||||
const val KEY_RENDERER_FORCE_MAX_CLOCK = "force_max_clock"
|
const val KEY_RENDERER_FORCE_MAX_CLOCK = "force_max_clock"
|
||||||
const val KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit"
|
const val KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit"
|
||||||
|
|
|
@ -226,10 +226,6 @@ void Config::ReadValues() {
|
||||||
ReadSetting("Renderer", Settings::values.bg_green);
|
ReadSetting("Renderer", Settings::values.bg_green);
|
||||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||||
|
|
||||||
// Disable shader cache by default on Android
|
|
||||||
Settings::values.use_disk_shader_cache =
|
|
||||||
config->GetBoolean("Renderer", "use_disk_shader_cache", false);
|
|
||||||
|
|
||||||
// Enable force_max_clock by default on Android
|
// Enable force_max_clock by default on Android
|
||||||
Settings::values.renderer_force_max_clock =
|
Settings::values.renderer_force_max_clock =
|
||||||
config->GetBoolean("Renderer", "force_max_clock", true);
|
config->GetBoolean("Renderer", "force_max_clock", true);
|
||||||
|
|
|
@ -3,13 +3,18 @@
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/fs/fs_android.h"
|
#include "common/fs/fs_android.h"
|
||||||
#include "jni/applets/software_keyboard.h"
|
#include "jni/applets/software_keyboard.h"
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
static JavaVM* s_java_vm;
|
static JavaVM* s_java_vm;
|
||||||
static jclass s_native_library_class;
|
static jclass s_native_library_class;
|
||||||
|
static jclass s_disk_cache_progress_class;
|
||||||
|
static jclass s_load_callback_stage_class;
|
||||||
static jmethodID s_exit_emulation_activity;
|
static jmethodID s_exit_emulation_activity;
|
||||||
|
static jmethodID s_disk_cache_load_progress;
|
||||||
|
|
||||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
|
@ -38,10 +43,22 @@ jclass GetNativeLibraryClass() {
|
||||||
return s_native_library_class;
|
return s_native_library_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jclass GetDiskCacheProgressClass() {
|
||||||
|
return s_disk_cache_progress_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||||
|
return s_load_callback_stage_class;
|
||||||
|
}
|
||||||
|
|
||||||
jmethodID GetExitEmulationActivity() {
|
jmethodID GetExitEmulationActivity() {
|
||||||
return s_exit_emulation_activity;
|
return s_exit_emulation_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jmethodID GetDiskCacheLoadProgress() {
|
||||||
|
return s_disk_cache_load_progress;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -58,8 +75,16 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
// Initialize Java classes
|
// Initialize Java classes
|
||||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
|
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||||
|
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||||
|
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||||
|
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||||
|
|
||||||
|
// Initialize methods
|
||||||
s_exit_emulation_activity =
|
s_exit_emulation_activity =
|
||||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
||||||
|
s_disk_cache_load_progress =
|
||||||
|
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
||||||
|
|
||||||
// Initialize Android Storage
|
// Initialize Android Storage
|
||||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||||
|
@ -79,6 +104,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||||
// UnInitialize Android Storage
|
// UnInitialize Android Storage
|
||||||
Common::FS::Android::UnRegisterCallbacks();
|
Common::FS::Android::UnRegisterCallbacks();
|
||||||
env->DeleteGlobalRef(s_native_library_class);
|
env->DeleteGlobalRef(s_native_library_class);
|
||||||
|
env->DeleteGlobalRef(s_disk_cache_progress_class);
|
||||||
|
env->DeleteGlobalRef(s_load_callback_stage_class);
|
||||||
|
|
||||||
// UnInitialze applets
|
// UnInitialze applets
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
namespace IDCache {
|
namespace IDCache {
|
||||||
|
|
||||||
JNIEnv* GetEnvForThread();
|
JNIEnv* GetEnvForThread();
|
||||||
jclass GetNativeLibraryClass();
|
jclass GetNativeLibraryClass();
|
||||||
|
jclass GetDiskCacheProgressClass();
|
||||||
|
jclass GetDiskCacheLoadCallbackStageClass();
|
||||||
jmethodID GetExitEmulationActivity();
|
jmethodID GetExitEmulationActivity();
|
||||||
|
jmethodID GetDiskCacheLoadProgress();
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "jni/emu_window/emu_window.h"
|
#include "jni/emu_window/emu_window.h"
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -229,6 +230,15 @@ public:
|
||||||
m_is_running = true;
|
m_is_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the disk shader cache.
|
||||||
|
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
|
m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||||
|
LoadDiskCacheProgress);
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void(m_system.Run());
|
void(m_system.Run());
|
||||||
|
|
||||||
if (m_system.DebuggerEnabled()) {
|
if (m_system.DebuggerEnabled()) {
|
||||||
|
@ -295,6 +305,14 @@ private:
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
||||||
|
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
||||||
|
static_cast<jint>(progress), static_cast<jint>(max));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static EmulationSession s_instance;
|
static EmulationSession s_instance;
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,13 @@
|
||||||
android:layout_margin="24dp"
|
android:layout_margin="24dp"
|
||||||
app:trackCornerRadius="4dp" />
|
app:trackCornerRadius="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/progress_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:gravity="end" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, which will reduce stutter but may introduce glitches.</string>
|
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, which will reduce stutter but may introduce glitches.</string>
|
||||||
<string name="renderer_debug">Enable graphics debugging</string>
|
<string name="renderer_debug">Enable graphics debugging</string>
|
||||||
<string name="renderer_debug_description">When checked, the graphics API enters a slower debugging mode.</string>
|
<string name="renderer_debug_description">When checked, the graphics API enters a slower debugging mode.</string>
|
||||||
|
<string name="use_disk_shader_cache">Use disk shader cache</string>
|
||||||
|
<string name="use_disk_shader_cache_description">Reduce stuttering by storing and loading generated shaders to disk.</string>
|
||||||
|
|
||||||
<!-- Audio settings strings -->
|
<!-- Audio settings strings -->
|
||||||
<string name="audio_volume">Volume</string>
|
<string name="audio_volume">Volume</string>
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
<string name="ini_saved">Saved settings</string>
|
<string name="ini_saved">Saved settings</string>
|
||||||
<string name="gameid_saved">Saved settings for %1$s</string>
|
<string name="gameid_saved">Saved settings for %1$s</string>
|
||||||
<string name="error_saving">Error saving %1$s.ini: %2$s</string>
|
<string name="error_saving">Error saving %1$s.ini: %2$s</string>
|
||||||
|
<string name="loading">Loading...</string>
|
||||||
|
|
||||||
<!-- Game Grid Screen-->
|
<!-- Game Grid Screen-->
|
||||||
<string name="grid_menu_core_settings">Settings</string>
|
<string name="grid_menu_core_settings">Settings</string>
|
||||||
|
@ -183,4 +186,8 @@
|
||||||
<string name="gamepad_home">Home</string>
|
<string name="gamepad_home">Home</string>
|
||||||
<string name="gamepad_screenshot">Screenshot</string>
|
<string name="gamepad_screenshot">Screenshot</string>
|
||||||
|
|
||||||
|
<!-- Disk shader cache -->
|
||||||
|
<string name="preparing_shaders">Preparing shaders</string>
|
||||||
|
<string name="building_shaders">Building shaders</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue