From 270f430f70d9325cf4dc3520ad359f14b9da6880 Mon Sep 17 00:00:00 2001 From: Charles Lombardo <clombardo169@gmail.com> Date: Sat, 12 Aug 2023 19:09:30 -0400 Subject: [PATCH 1/3] android: Create custom game icon loader --- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 24 +----- .../org/yuzu/yuzu_emu/utils/GameIconUtils.kt | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index e91277d359..13359ef369 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -3,8 +3,6 @@ package org.yuzu.yuzu_emu.adapters -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.net.Uri import android.text.TextUtils import android.view.LayoutInflater @@ -15,23 +13,20 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import coil.load -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.HomeNavigationDirections -import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel +import org.yuzu.yuzu_emu.utils.GameIconUtils class GameAdapter(private val activity: AppCompatActivity) : ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), @@ -98,12 +93,7 @@ class GameAdapter(private val activity: AppCompatActivity) : this.game = game binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP - activity.lifecycleScope.launch { - val bitmap = decodeGameIcon(game.path) - binding.imageGameScreen.load(bitmap) { - error(R.drawable.default_icon) - } - } + GameIconUtils.loadGameIcon(game, binding.imageGameScreen) binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") @@ -126,14 +116,4 @@ class GameAdapter(private val activity: AppCompatActivity) : return oldItem == newItem } } - - private fun decodeGameIcon(uri: String): Bitmap? { - val data = NativeLibrary.getIcon(uri) - return BitmapFactory.decodeByteArray( - data, - 0, - data.size, - BitmapFactory.Options() - ) - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt new file mode 100644 index 0000000000..c0fe596d7d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.widget.ImageView +import androidx.core.graphics.drawable.toDrawable +import coil.ImageLoader +import coil.decode.DataSource +import coil.fetch.DrawableResult +import coil.fetch.FetchResult +import coil.fetch.Fetcher +import coil.key.Keyer +import coil.memory.MemoryCache +import coil.request.ImageRequest +import coil.request.Options +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.model.Game + +class GameIconFetcher( + private val game: Game, + private val options: Options +) : Fetcher { + override suspend fun fetch(): FetchResult { + return DrawableResult( + drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources), + isSampled = false, + dataSource = DataSource.DISK + ) + } + + private fun decodeGameIcon(uri: String): Bitmap? { + val data = NativeLibrary.getIcon(uri) + return BitmapFactory.decodeByteArray( + data, + 0, + data.size, + BitmapFactory.Options() + ) + } + + class Factory : Fetcher.Factory<Game> { + override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher = + GameIconFetcher(data, options) + } +} + +class GameIconKeyer : Keyer<Game> { + override fun key(data: Game, options: Options): String = data.path +} + +object GameIconUtils { + private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext) + .components { + add(GameIconKeyer()) + add(GameIconFetcher.Factory()) + } + .memoryCache { + MemoryCache.Builder(YuzuApplication.appContext) + .maxSizePercent(0.25) + .build() + } + .build() + + fun loadGameIcon(game: Game, imageView: ImageView) { + val request = ImageRequest.Builder(YuzuApplication.appContext) + .data(game) + .target(imageView) + .error(R.drawable.default_icon) + .build() + imageLoader.enqueue(request) + } +} From b0a96d5216d49395b9b205d0669d071e6bd5647e Mon Sep 17 00:00:00 2001 From: Charles Lombardo <clombardo169@gmail.com> Date: Sat, 12 Aug 2023 20:11:59 -0400 Subject: [PATCH 2/3] android: Game loading/shutting down indicators --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 22 +++- .../yuzu_emu/activities/EmulationActivity.kt | 15 +++ .../DiskShaderCacheProgress.kt | 50 ++++---- .../ShaderProgressViewModel.kt | 31 ----- .../ui/ShaderProgressDialogFragment.kt | 103 ---------------- .../yuzu_emu/fragments/EmulationFragment.kt | 110 +++++++++++++----- .../yuzu/yuzu_emu/model/EmulationViewModel.kt | 59 ++++++++++ src/android/app/src/main/jni/id_cache.cpp | 14 +++ src/android/app/src/main/jni/id_cache.h | 2 + src/android/app/src/main/jni/native.cpp | 16 +++ .../main/res/layout/fragment_emulation.xml | 83 ++++++++++++- .../app/src/main/res/values-de/strings.xml | 1 - .../app/src/main/res/values-es/strings.xml | 1 - .../app/src/main/res/values-fr/strings.xml | 1 - .../app/src/main/res/values-it/strings.xml | 1 - .../app/src/main/res/values-ja/strings.xml | 1 - .../app/src/main/res/values-ko/strings.xml | 1 - .../app/src/main/res/values-nb/strings.xml | 1 - .../app/src/main/res/values-pl/strings.xml | 1 - .../src/main/res/values-pt-rBR/strings.xml | 1 - .../src/main/res/values-pt-rPT/strings.xml | 1 - .../app/src/main/res/values-ru/strings.xml | 1 - .../app/src/main/res/values-uk/strings.xml | 1 - .../src/main/res/values-zh-rCN/strings.xml | 1 - .../src/main/res/values-zh-rTW/strings.xml | 1 - .../app/src/main/res/values/strings.xml | 2 +- 26 files changed, 311 insertions(+), 210 deletions(-) delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 5a7cf4ed73..c8706d7a63 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri -import org.yuzu.yuzu_emu.utils.Log.error -import org.yuzu.yuzu_emu.utils.Log.verbose -import org.yuzu.yuzu_emu.utils.Log.warning +import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable /** @@ -465,7 +463,7 @@ object NativeLibrary { val emulationActivity = sEmulationActivity.get() if (emulationActivity == null) { - warning("[NativeLibrary] EmulationActivity is null, can't exit.") + Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.") return } @@ -490,15 +488,27 @@ object NativeLibrary { } fun setEmulationActivity(emulationActivity: EmulationActivity?) { - verbose("[NativeLibrary] Registering EmulationActivity.") + Log.verbose("[NativeLibrary] Registering EmulationActivity.") sEmulationActivity = WeakReference(emulationActivity) } fun clearEmulationActivity() { - verbose("[NativeLibrary] Unregistering EmulationActivity.") + Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") sEmulationActivity.clear() } + @Keep + @JvmStatic + fun onEmulationStarted() { + sEmulationActivity.get()!!.onEmulationStarted() + } + + @Keep + @JvmStatic + fun onEmulationStopped(status: Int) { + sEmulationActivity.get()!!.onEmulationStopped(status) + } + /** * Logs the Yuzu version, Android version and, CPU. */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index dbd602a1d0..bbd328c717 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -28,6 +28,7 @@ import android.view.Surface import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.Toast +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -41,6 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings +import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper import org.yuzu.yuzu_emu.utils.ForegroundService @@ -70,8 +72,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { private val actionMute = "ACTION_EMULATOR_MUTE" private val actionUnmute = "ACTION_EMULATOR_UNMUTE" + private val emulationViewModel: EmulationViewModel by viewModels() + override fun onDestroy() { stopForegroundService(this) + emulationViewModel.clear() super.onDestroy() } @@ -416,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } } + fun onEmulationStarted() { + emulationViewModel.setEmulationStarted(true) + } + + fun onEmulationStopped(status: Int) { + if (status == 0) { + finish() + } + } + private fun startMotionSensorListener() { val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt index a18efef198..6f4b5b13fd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt @@ -4,43 +4,43 @@ package org.yuzu.yuzu_emu.disk_shader_cache import androidx.annotation.Keep +import androidx.lifecycle.ViewModelProvider import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment +import org.yuzu.yuzu_emu.activities.EmulationActivity +import org.yuzu.yuzu_emu.model.EmulationViewModel +import org.yuzu.yuzu_emu.utils.Log @Keep object DiskShaderCacheProgress { - val finishLock = Object() - private lateinit var fragment: ShaderProgressDialogFragment + private lateinit var emulationViewModel: EmulationViewModel - 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() } + private fun prepareViewModel() { + emulationViewModel = + ViewModelProvider( + NativeLibrary.sEmulationActivity.get() as EmulationActivity + )[EmulationViewModel::class.java] } @JvmStatic fun loadProgress(stage: Int, progress: Int, max: Int) { val emulationActivity = NativeLibrary.sEmulationActivity.get() - ?: error("[DiskShaderCacheProgress] EmulationActivity not present") + if (emulationActivity == null) { + Log.error("[DiskShaderCacheProgress] EmulationActivity not present") + return + } - when (LoadCallbackStage.values()[stage]) { - LoadCallbackStage.Prepare -> prepareDialog() - LoadCallbackStage.Build -> fragment.onUpdateProgress( - emulationActivity.getString(R.string.building_shaders), - progress, - max - ) - LoadCallbackStage.Complete -> fragment.dismiss() + emulationActivity.runOnUiThread { + when (LoadCallbackStage.values()[stage]) { + LoadCallbackStage.Prepare -> prepareViewModel() + LoadCallbackStage.Build -> emulationViewModel.updateProgress( + emulationActivity.getString(R.string.building_shaders), + progress, + max + ) + + LoadCallbackStage.Complete -> {} + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt deleted file mode 100644 index bf6f0366d8..0000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt +++ /dev/null @@ -1,31 +0,0 @@ -// 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) - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt deleted file mode 100644 index 8a8e0a6e89..0000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt +++ /dev/null @@ -1,103 +0,0 @@ -// 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 - } - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 53f19c4f84..944ae652e4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -24,8 +24,9 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import androidx.core.view.isVisible +import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.utils.* @@ -66,6 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private lateinit var game: Game + private val emulationViewModel: EmulationViewModel by activityViewModels() + private var isInFoldableLayout = false override fun onAttach(context: Context) { @@ -130,9 +134,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.showFpsText.setTextColor(Color.YELLOW) binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } - // Setup overlay. - updateShowFpsOverlay() - + binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = game.title binding.inGameMenu.setNavigationItemSelectedListener { @@ -174,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { R.id.menu_exit -> { emulationState.stop() - requireActivity().finish() + emulationViewModel.setIsEmulationStopping(true) + binding.drawerLayout.close() + binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) true } @@ -188,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { requireActivity(), object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { + if (!NativeLibrary.isRunning()) { + return + } + if (binding.drawerLayout.isOpen) { binding.drawerLayout.close() } else { @@ -204,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } } } + + GameIconUtils.loadGameIcon(game, binding.loadingImage) + binding.loadingTitle.text = game.title + binding.loadingTitle.isSelected = true + binding.loadingText.isSelected = true + + emulationViewModel.shaderProgress.observe(viewLifecycleOwner) { + if (it > 0 && it != emulationViewModel.totalShaders.value!!) { + binding.loadingProgressIndicator.isIndeterminate = false + + if (it < binding.loadingProgressIndicator.max) { + binding.loadingProgressIndicator.progress = it + } + } + + if (it == emulationViewModel.totalShaders.value!!) { + binding.loadingText.setText(R.string.loading) + binding.loadingProgressIndicator.isIndeterminate = true + } + } + emulationViewModel.totalShaders.observe(viewLifecycleOwner) { + binding.loadingProgressIndicator.max = it + } + emulationViewModel.shaderMessage.observe(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.loadingText.text = it + } + } + + emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started -> + if (started) { + binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + ViewUtils.showView(binding.surfaceInputOverlay) + ViewUtils.hideView(binding.loadingIndicator) + + // Setup overlay + updateShowFpsOverlay() + } + } + + emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) { + if (it) { + binding.loadingText.setText(R.string.shutting_down) + ViewUtils.showView(binding.loadingIndicator) + ViewUtils.hideView(binding.inputContainer) + ViewUtils.hideView(binding.showFpsText) + } + } } override fun onConfigurationChanged(newConfig: Configuration) { @@ -213,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.drawerLayout.close() } if (EmulationMenuSettings.showOverlay) { - binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } + binding.surfaceInputOverlay.post { + binding.surfaceInputOverlay.visibility = View.VISIBLE + } } } else { - if (EmulationMenuSettings.showOverlay) { - binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } + if (EmulationMenuSettings.showOverlay && + emulationViewModel.emulationStarted.value == true + ) { + binding.surfaceInputOverlay.post { + binding.surfaceInputOverlay.visibility = View.VISIBLE + } + } else { + binding.surfaceInputOverlay.post { + binding.surfaceInputOverlay.visibility = View.INVISIBLE + } } if (!isInFoldableLayout) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { @@ -226,9 +292,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE } } - if (!binding.surfaceInputOverlay.isInEditMode) { - refreshInputOverlay() - } } } @@ -260,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { super.onDetach() } - private fun refreshInputOverlay() { - binding.surfaceInputOverlay.refreshControls() - } - private fun resetInputOverlay() { preferences.edit() .remove(Settings.PREF_CONTROL_SCALE) @@ -281,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val FRAMETIME = 2 val SPEED = 3 perfStatsUpdater = { - val perfStats = NativeLibrary.getPerfStats() - if (perfStats[FPS] > 0 && _binding != null) { - binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) - } - - if (!emulationState.isStopped) { + if (emulationViewModel.emulationStarted.value == true) { + val perfStats = NativeLibrary.getPerfStats() + if (perfStats[FPS] > 0 && _binding != null) { + binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) + } perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) } } perfStatsUpdateHandler.post(perfStatsUpdater!!) - binding.showFpsText.text = resources.getString(R.string.emulation_game_loading) binding.showFpsText.visibility = View.VISIBLE } else { if (perfStatsUpdater != null) { @@ -349,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { isInFoldableLayout = true binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE - refreshInputOverlay() } } it.isSeparating @@ -437,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { .apply() } .setPositiveButton(android.R.string.ok) { _, _ -> - refreshInputOverlay() + binding.surfaceInputOverlay.refreshControls() } .setNegativeButton(android.R.string.cancel, null) .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } @@ -461,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { R.id.menu_show_overlay -> { it.isChecked = !it.isChecked EmulationMenuSettings.showOverlay = it.isChecked - refreshInputOverlay() + binding.surfaceInputOverlay.refreshControls() true } @@ -567,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { preferences.edit() .putInt(Settings.PREF_CONTROL_SCALE, scale) .apply() - refreshInputOverlay() + binding.surfaceInputOverlay.refreshControls() } private fun setControlOpacity(opacity: Int) { preferences.edit() .putInt(Settings.PREF_CONTROL_OPACITY, opacity) .apply() - refreshInputOverlay() + binding.surfaceInputOverlay.refreshControls() } private fun setInsets() { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt new file mode 100644 index 0000000000..e35f51bc3a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.model + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class EmulationViewModel : ViewModel() { + private val _emulationStarted = MutableLiveData(false) + val emulationStarted: LiveData<Boolean> get() = _emulationStarted + + private val _isEmulationStopping = MutableLiveData(false) + val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping + + private val _shaderProgress = MutableLiveData(0) + val shaderProgress: LiveData<Int> get() = _shaderProgress + + private val _totalShaders = MutableLiveData(0) + val totalShaders: LiveData<Int> get() = _totalShaders + + private val _shaderMessage = MutableLiveData("") + val shaderMessage: LiveData<String> get() = _shaderMessage + + fun setEmulationStarted(started: Boolean) { + _emulationStarted.postValue(started) + } + + fun setIsEmulationStopping(value: Boolean) { + _isEmulationStopping.value = value + } + + fun setShaderProgress(progress: Int) { + _shaderProgress.value = progress + } + + fun setTotalShaders(max: Int) { + _totalShaders.value = max + } + + fun setShaderMessage(msg: String) { + _shaderMessage.value = msg + } + + fun updateProgress(msg: String, progress: Int, max: Int) { + setShaderMessage(msg) + setShaderProgress(progress) + setTotalShaders(max) + } + + fun clear() { + _emulationStarted.value = false + _isEmulationStopping.value = false + _shaderProgress.value = 0 + _totalShaders.value = 0 + _shaderMessage.value = "" + } +} diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 9cbbf23a33..960abf95ad 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class; static jclass s_load_callback_stage_class; static jmethodID s_exit_emulation_activity; static jmethodID s_disk_cache_load_progress; +static jmethodID s_on_emulation_started; +static jmethodID s_on_emulation_stopped; static constexpr jint JNI_VERSION = JNI_VERSION_1_6; @@ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() { return s_disk_cache_load_progress; } +jmethodID GetOnEmulationStarted() { + return s_on_emulation_started; +} + +jmethodID GetOnEmulationStopped() { + return s_on_emulation_stopped; +} + } // namespace IDCache #ifdef __cplusplus @@ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); s_disk_cache_load_progress = env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); + s_on_emulation_started = + env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); + s_on_emulation_stopped = + env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); // Initialize Android Storage Common::FS::Android::RegisterCallbacks(env, s_native_library_class); diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index be535fe1e3..b761589281 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h @@ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass(); jclass GetDiskCacheLoadCallbackStageClass(); jmethodID GetExitEmulationActivity(); jmethodID GetDiskCacheLoadProgress(); +jmethodID GetOnEmulationStarted(); +jmethodID GetOnEmulationStopped(); } // namespace IDCache diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b2adfdedaa..83e88010fa 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -335,6 +335,8 @@ public: // Tear down the render window. m_window.reset(); + + OnEmulationStopped(m_load_result); } void PauseEmulation() { @@ -376,6 +378,8 @@ public: m_system.InitializeDebugger(); } + OnEmulationStarted(); + while (true) { { [[maybe_unused]] std::unique_lock lock(m_mutex); @@ -511,6 +515,18 @@ private: static_cast<jint>(progress), static_cast<jint>(max)); } + static void OnEmulationStarted() { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), + IDCache::GetOnEmulationStarted()); + } + + static void OnEmulationStopped(Core::SystemResultStatus result) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), + IDCache::GetOnEmulationStopped(), static_cast<jint>(result)); + } + private: static EmulationSession s_instance; diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index e54a10e8ff..da97d85c13 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -26,6 +26,81 @@ android:focusable="false" android:focusableInTouchMode="false" /> + <com.google.android.material.card.MaterialCardView + android:id="@+id/loading_indicator" + style="?attr/materialCardViewOutlinedStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:focusable="false"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/loading_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_horizontal"> + + <ImageView + android:id="@+id/loading_image" + android:layout_width="wrap_content" + android:layout_height="0dp" + android:adjustViewBounds="true" + app:layout_constraintBottom_toBottomOf="@+id/linearLayout" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/linearLayout" + tools:src="@drawable/default_icon" /> + + <LinearLayout + android:id="@+id/linearLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="24dp" + android:paddingVertical="36dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/loading_image" + app:layout_constraintTop_toTopOf="parent"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/loading_title" + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:requiresFadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + tools:text="@string/games" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/loading_text" + style="@style/TextAppearance.Material3.TitleSmall" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:requiresFadingEdge="horizontal" + android:singleLine="true" + android:text="@string/loading" + android:textAlignment="viewStart" /> + + <com.google.android.material.progressindicator.LinearProgressIndicator + android:id="@+id/loading_progress_indicator" + android:layout_width="192dp" + android:layout_height="wrap_content" + android:layout_marginTop="12dp" + android:indeterminate="true" + app:trackCornerRadius="8dp" /> + + </LinearLayout> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </com.google.android.material.card.MaterialCardView> + </FrameLayout> <FrameLayout @@ -41,11 +116,12 @@ android:layout_height="match_parent" android:layout_gravity="center" android:focusable="true" - android:focusableInTouchMode="true" /> + android:focusableInTouchMode="true" + android:visibility="invisible" /> <Button - style="@style/Widget.Material3.Button.ElevatedButton" android:id="@+id/done_control_config" + style="@style/Widget.Material3.Button.ElevatedButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" @@ -81,6 +157,7 @@ android:layout_height="match_parent" android:layout_gravity="start|bottom" app:headerLayout="@layout/header_in_game" - app:menu="@menu/menu_in_game" /> + app:menu="@menu/menu_in_game" + tools:visibility="gone" /> </androidx.drawerlayout.widget.DrawerLayout> diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 0c1d91264a..daaa7ffdea 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -209,7 +209,6 @@ <string name="emulation_pause">Emulation pausieren</string> <string name="emulation_unpause">Emulation fortsetzen</string> <string name="emulation_input_overlay">Overlay-Optionen</string> - <string name="emulation_game_loading">Spiel lädt…</string> <string name="load_settings">Lädt Einstellungen...</string> diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 357f956d19..e9129cb00d 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Pausar Emulación</string> <string name="emulation_unpause">Reanudar Emulación</string> <string name="emulation_input_overlay">Opciones de pantalla </string> - <string name="emulation_game_loading">Cargando juego...</string> <string name="load_settings">Cargando configuración...</string> diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index dfca1c8306..2d99d618e0 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Mettre en pause l\'émulation</string> <string name="emulation_unpause">Reprendre l\'émulation</string> <string name="emulation_input_overlay">Options de l\'overlay</string> - <string name="emulation_game_loading">Chargement du jeu...</string> <string name="load_settings">Chargement des paramètres…</string> diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 089d93ed68..d9c3de3853 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Metti in pausa l\'emulazione</string> <string name="emulation_unpause">Riprendi Emulazione</string> <string name="emulation_input_overlay">Impostazioni Overlay</string> - <string name="emulation_game_loading">Caricamento del gioco...</string> <string name="load_settings">Caricamento delle impostazioni...</string> diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 39b590bee5..7a226cd5c4 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -211,7 +211,6 @@ <string name="emulation_pause">エミュレーションを一時停止</string> <string name="emulation_unpause">エミュレーションを再開</string> <string name="emulation_input_overlay">オーバーレイオプション</string> - <string name="emulation_game_loading">ロード中…</string> <string name="load_settings">設定をロード中…</string> diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index cbcb2873f5..427b6e5a02 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">에뮬레이션 일시 중지</string> <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> <string name="emulation_input_overlay">오버레이 옵션</string> - <string name="emulation_game_loading">게임 불러오기 중...</string> <string name="load_settings">설정 불러오기 중...</string> diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index e48a4be38f..ce8d7a9e47 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Pause Emulering</string> <string name="emulation_unpause">Opphev pausing av emulering</string> <string name="emulation_input_overlay">Alternativer for overlegg</string> - <string name="emulation_game_loading">Spillet lastes inn...</string> <string name="load_settings">Laster inn innstillinger...</string> diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index bc9c0f7f49..c2c24b48f3 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Wstrzymaj emulację</string> <string name="emulation_unpause">Wznów emulację</string> <string name="emulation_input_overlay">Opcje nakładki</string> - <string name="emulation_game_loading">Wczytywanie gry...</string> <string name="load_settings">Wczytywanie ustawień...</string> diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 75fe0edbf7..04f276108b 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Pausa emulação</string> <string name="emulation_unpause">Retomar emulação</string> <string name="emulation_input_overlay">Opções de sobreposição </string> - <string name="emulation_game_loading">Jogo a carregar...</string> <string name="load_settings">Configurações a carregar...</string> diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 96b040c66b..66a3a1a2ee 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Pausa emulação</string> <string name="emulation_unpause">Retomar emulação</string> <string name="emulation_input_overlay">Opções de sobreposição </string> - <string name="emulation_game_loading">Jogo a carregar...</string> <string name="load_settings">Configurações a carregar...</string> diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 8d954f59eb..f770e954fc 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Пауза эмуляции</string> <string name="emulation_unpause">Возобновление эмуляции</string> <string name="emulation_input_overlay">Настройки оверлея</string> - <string name="emulation_game_loading">Загрузка игры...</string> <string name="load_settings">Загрузка настроек...</string> diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 6c028535b3..ea3ab1b15a 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">Пауза емуляції</string> <string name="emulation_unpause">Відновлення емуляції</string> <string name="emulation_input_overlay">Налаштування оверлея</string> - <string name="emulation_game_loading">Завантаження гри...</string> <string name="load_settings">Завантаження налаштувань...</string> diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index e4ad2ed07e..b45a5a528b 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">暂停模拟</string> <string name="emulation_unpause">继续模拟</string> <string name="emulation_input_overlay">虚拟按键选项</string> - <string name="emulation_game_loading">载入游戏中…</string> <string name="load_settings">正在载入设定…</string> diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 0d32f23df8..3aab889e40 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -213,7 +213,6 @@ <string name="emulation_pause">暫停模擬</string> <string name="emulation_unpause">取消暫停模擬</string> <string name="emulation_input_overlay">覆疊選項</string> - <string name="emulation_game_loading">遊戲正在載入…</string> <string name="load_settings">正在載入設定…</string> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index d43891cecf..b163e6fc1e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -204,6 +204,7 @@ <string name="error_saving">Error saving %1$s.ini: %2$s</string> <string name="unimplemented_menu">Unimplemented Menu</string> <string name="loading">Loading…</string> + <string name="shutting_down">Shutting down…</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_to_default">Reset to default</string> <string name="reset_all_settings">Reset all settings?</string> @@ -262,7 +263,6 @@ <string name="emulation_pause">Pause emulation</string> <string name="emulation_unpause">Unpause emulation</string> <string name="emulation_input_overlay">Overlay options</string> - <string name="emulation_game_loading">Game loading…</string> <string name="load_settings">Loading settings…</string> From 5445e974e0cf164c3295d9eaa658b9d62fe42019 Mon Sep 17 00:00:00 2001 From: Charles Lombardo <clombardo169@gmail.com> Date: Sun, 27 Aug 2023 22:04:04 -0400 Subject: [PATCH 3/3] android: Separate emulation states from emulation mutex Emulation states are repeatedly checked by input and performance stats. During startup and shutdown, this could lead to a long halt on the UI thread because the call to IsRunning will be waiting on the emulation mutex to be unlocked. Using atomics should replace the existing functionality without causing problems. --- src/android/app/src/main/jni/native.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 83e88010fa..0f2a6d9e4e 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -203,12 +203,10 @@ public: } bool IsRunning() const { - std::scoped_lock lock(m_mutex); return m_is_running; } bool IsPaused() const { - std::scoped_lock lock(m_mutex); return m_is_running && m_is_paused; } @@ -544,8 +542,8 @@ private: Core::PerfStatsResults m_perf_stats{}; std::shared_ptr<FileSys::VfsFilesystem> m_vfs; Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; - bool m_is_running{}; - bool m_is_paused{}; + std::atomic<bool> m_is_running = false; + std::atomic<bool> m_is_paused = false; SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;