From 2999275ed206eb374a59dc4de34537ab9d494c33 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Sat, 29 Jul 2023 09:40:50 +0000 Subject: [PATCH] add closing emulation(starting a new one is still broken), disabled audio --- src/LibRyujinx/Android/JniExportedMethods.cs | 16 ++++- src/LibRyujinx/LibRyujinx.Device.cs | 38 +++++++++++ src/LibRyujinx/LibRyujinx.Graphics.cs | 15 ++++- src/LibRyujinx/LibRyujinx.Input.cs | 2 +- src/LibRyujinx/LibRyujinx.cs | 2 + .../app/src/main/cpp/ryujinx.cpp | 1 + .../main/java/org/ryujinx/android/GameHost.kt | 20 ++++-- .../java/org/ryujinx/android/MainActivity.kt | 19 ++++-- .../java/org/ryujinx/android/RyujinxNative.kt | 2 + .../android/viewmodels/MainViewModel.kt | 24 ++++--- .../org/ryujinx/android/views/HomeViews.kt | 2 +- .../org/ryujinx/android/views/MainView.kt | 64 +++++++++++++++++++ .../org/ryujinx/android/views/SettingViews.kt | 22 ++++--- 13 files changed, 194 insertions(+), 33 deletions(-) diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs index fbdfb8a63..f7f04a732 100644 --- a/src/LibRyujinx/Android/JniExportedMethods.cs +++ b/src/LibRyujinx/Android/JniExportedMethods.cs @@ -72,7 +72,9 @@ namespace LibRyujinx var init = Initialize(path, enableDebugLogs); - AudioDriver = new OboeHardwareDeviceDriver(); + // AudioDriver = new OboeHardwareDeviceDriver(); + + _surfaceEvent?.Set(); _surfaceEvent = new ManualResetEvent(false); @@ -151,6 +153,18 @@ namespace LibRyujinx return LoadApplication(path); } + [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceSignalEmulationClose")] + public static void JniSignalEmulationCloseNative(JEnvRef jEnv, JObjectLocalRef jObj) + { + SignalEmulationClose(); + } + + [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceCloseEmulation")] + public static void JniCloseEmulationNative(JEnvRef jEnv, JObjectLocalRef jObj) + { + CloseEmulation(); + } + [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoadDescriptor")] public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci) { diff --git a/src/LibRyujinx/LibRyujinx.Device.cs b/src/LibRyujinx/LibRyujinx.Device.cs index fd7cc16fc..376b7a352 100644 --- a/src/LibRyujinx/LibRyujinx.Device.cs +++ b/src/LibRyujinx/LibRyujinx.Device.cs @@ -1,6 +1,8 @@ using ARMeilleure.Translation; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Input.HLE; +using Silk.NET.Vulkan; using System; using System.IO; using System.Runtime.InteropServices; @@ -168,5 +170,41 @@ namespace LibRyujinx return true; } + + public static void SignalEmulationClose() + { + _isStopped = true; + _isActive = false; + + debug_break(2); + } + + public static void CloseEmulation() + { + if (SwitchDevice == null) + return; + + _npadManager?.Dispose(); + _npadManager = null; + + _touchScreenManager?.Dispose(); + _touchScreenManager = null; + + SwitchDevice?.InputManager?.Dispose(); + SwitchDevice.InputManager = null; + _inputManager = null; + + + _surfaceEvent?.Set(); + + if (Renderer != null) + { + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); + _gpuDoneEvent = null; + SwitchDevice?.DisposeContext(); + Renderer = null; + } + } } } diff --git a/src/LibRyujinx/LibRyujinx.Graphics.cs b/src/LibRyujinx/LibRyujinx.Graphics.cs index b18fab34c..3acd41d39 100644 --- a/src/LibRyujinx/LibRyujinx.Graphics.cs +++ b/src/LibRyujinx/LibRyujinx.Graphics.cs @@ -3,6 +3,7 @@ using LibRyujinx.Shared; using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; @@ -21,6 +22,7 @@ namespace LibRyujinx private static CancellationTokenSource _gpuCancellationTokenSource; private static SwapBuffersCallback? _swapBuffersCallback; private static NativeGraphicsInterop _nativeGraphicsInterop; + private static ManualResetEvent _gpuDoneEvent; public delegate void SwapBuffersCallback(); public delegate IntPtr GetProcAddress(string name); @@ -146,12 +148,14 @@ namespace LibRyujinx return; } var device = SwitchDevice!.EmulationContext!; + _gpuDoneEvent = new ManualResetEvent(true); device.Gpu.Renderer.Initialize(GraphicsDebugLevel.None); _gpuCancellationTokenSource = new CancellationTokenSource(); device.Gpu.Renderer.RunLoop(() => { + _gpuDoneEvent.Reset(); device.Gpu.SetGpuThread(); device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); @@ -162,9 +166,11 @@ namespace LibRyujinx { if (_isStopped) { - return; + break; } + debug_break(1); + if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic) { setRenderingThread(); @@ -182,6 +188,13 @@ namespace LibRyujinx device.PresentFrame(() => _swapBuffersCallback?.Invoke()); } } + + if (device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); }); } diff --git a/src/LibRyujinx/LibRyujinx.Input.cs b/src/LibRyujinx/LibRyujinx.Input.cs index d08b0697f..dc1c1f6d2 100644 --- a/src/LibRyujinx/LibRyujinx.Input.cs +++ b/src/LibRyujinx/LibRyujinx.Input.cs @@ -30,7 +30,7 @@ namespace LibRyujinx private static VirtualTouchScreenDriver? _touchScreenDriver; private static TouchScreenManager? _touchScreenManager; private static InputManager? _inputManager; - private static NpadManager _npadManager; + private static NpadManager? _npadManager; private static InputConfig[] _configs; public static void InitializeInput(int width, int height) diff --git a/src/LibRyujinx/LibRyujinx.cs b/src/LibRyujinx/LibRyujinx.cs index 6431efa2e..2e0657d53 100644 --- a/src/LibRyujinx/LibRyujinx.cs +++ b/src/LibRyujinx/LibRyujinx.cs @@ -641,7 +641,9 @@ namespace LibRyujinx internal void DisposeContext() { EmulationContext?.Dispose(); + EmulationContext?.DisposeGpu(); EmulationContext = null; + LibRyujinx.Renderer = null; } } diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp index f706d0fcd..18d9fb67a 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp +++ b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp @@ -222,5 +222,6 @@ Java_org_ryujinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz, extern "C" void debug_break(int code){ + if(code >= 3) int r = 0; } \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt index c22f34722..38cd171b1 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt @@ -12,6 +12,7 @@ import java.io.File import kotlin.concurrent.thread class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback { + private var _isClosed: Boolean = false private var _renderingThreadWatcher: Thread? = null private var _height: Int = 0 private var _width: Int = 0 @@ -21,14 +22,9 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie private var _isInit: Boolean = false private var _isStarted: Boolean = false private var _nativeWindow: Long = 0 - private var _nativeHelper: NativeHelpers = NativeHelpers() private var _nativeRyujinx: RyujinxNative = RyujinxNative() - companion object { - var gameModel: GameModel? = null - } - init { holder.addCallback(this) } @@ -37,6 +33,8 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie } override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + if(_isClosed) + return start(holder) if(_width != width || _height != height) @@ -59,8 +57,17 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie } - private fun start(surfaceHolder: SurfaceHolder) { + fun close(){ + _isClosed = true + _isInit = false + _isStarted = false + _updateThread?.join() + _renderingThreadWatcher?.join() + } + + private fun start(surfaceHolder: SurfaceHolder) { + mainViewModel.gameHost = this if(_isStarted) return; @@ -119,6 +126,7 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie mainViewModel.performanceManager?.initializeRenderingSession(threadId) } } + mainViewModel.performanceManager?.closeCurrentRenderingSession() } } _nativeRyujinx.graphicsRendererRunLoop() diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt index e0e7c790e..178ee43b6 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt @@ -12,6 +12,7 @@ import android.view.KeyEvent import android.view.MotionEvent import android.view.WindowManager import androidx.activity.ComponentActivity +import androidx.activity.addCallback import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme @@ -62,15 +63,22 @@ class MainActivity : ComponentActivity() { external fun getRenderingThreadId() : Long external fun initVm() - fun setFullScreen() { + fun setFullScreen(fullscreen: Boolean) { requestedOrientation = - ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + if (fullscreen) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_FULL_USER val insets = WindowCompat.getInsetsController(window, window.decorView) insets.apply { - insets.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars()) - insets.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + if (fullscreen) { + insets.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars()) + insets.systemBarsBehavior = + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } else { + insets.show(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars()) + insets.systemBarsBehavior = + WindowInsetsControllerCompat.BEHAVIOR_DEFAULT + } } } @@ -95,7 +103,8 @@ class MainActivity : ComponentActivity() { @SuppressLint("RestrictedApi") override fun dispatchKeyEvent(event: KeyEvent?): Boolean { event?.apply { - return physicalControllerManager.onKeyEvent(this) + if(physicalControllerManager.onKeyEvent(this)) + return true; } return super.dispatchKeyEvent(event) } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt index aeba312eb..c3e54b5d6 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt @@ -48,4 +48,6 @@ class RyujinxNative { external fun inputConnectGamepad(index: Int): Int external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int): Unit external fun graphicsSetSurface(surface: Long) + external fun deviceCloseEmulation() + external fun deviceSignalEmulationClose() } \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt index 33e924915..e8361e455 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt @@ -6,10 +6,6 @@ import android.os.Build import android.os.PerformanceHintManager import androidx.compose.runtime.MutableState import androidx.navigation.NavHostController -import com.anggrayudi.storage.extension.launchOnUiThread -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext import org.ryujinx.android.GameController import org.ryujinx.android.GameHost import org.ryujinx.android.GraphicsConfiguration @@ -24,6 +20,7 @@ import java.io.File @SuppressLint("WrongConstant") class MainViewModel(val activity: MainActivity) { + var gameHost: GameHost? = null var controller: GameController? = null var performanceManager: PerformanceManager? = null var selected: GameModel? = null @@ -42,9 +39,19 @@ class MainViewModel(val activity: MainActivity) { } } - suspend fun loadGame(game:GameModel) : Boolean { - GameHost.gameModel = game + fun closeGame() { + RyujinxNative().deviceSignalEmulationClose() + gameHost?.close() + RyujinxNative().deviceCloseEmulation() + goBack() + activity.setFullScreen(false) + } + fun goBack(){ + navController?.popBackStack() + } + + fun loadGame(game:GameModel) : Boolean { var nativeRyujinx = RyujinxNative() val path = game.getPath() ?: return false @@ -129,8 +136,6 @@ class MainViewModel(val activity: MainActivity) { if(!success) return false - activity.physicalControllerManager.connect() - return true } @@ -163,4 +168,7 @@ class MainViewModel(val activity: MainActivity) { fun setGameController(controller: GameController) { this.controller = controller } + + fun backCalled() { + } } \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt index f52029ab5..d61ac1e6d 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt @@ -310,7 +310,7 @@ class HomeViews { val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false if(success) { launchOnUiThread { - viewModel.mainViewModel?.activity?.setFullScreen() + viewModel.mainViewModel?.activity?.setFullScreen(true) viewModel.mainViewModel?.navController?.navigate("game") } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt index 06a4f8c36..edb60e12a 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt @@ -1,13 +1,25 @@ package org.ryujinx.android.views +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -71,6 +83,7 @@ class MainView { } } + @OptIn(ExperimentalMaterial3Api::class) @Composable fun GameOverlay(mainViewModel: MainViewModel) { Box(modifier = Modifier.fillMaxSize()) { @@ -135,6 +148,57 @@ class MainView { ) } } + + var showBackNotice = remember { + mutableStateOf(false) + } + + BackHandler { + showBackNotice.value = true + } + + if (showBackNotice.value) { + AlertDialog(onDismissRequest = { showBackNotice.value = false }) { + Column { + Surface( + modifier = Modifier + .wrapContentWidth() + .wrapContentHeight(), + shape = MaterialTheme.shapes.large, + tonalElevation = AlertDialogDefaults.TonalElevation + ) { + Column { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text(text = "Are you sure you want to exit the game?") + Text(text = "All unsaved data will be lost!") + } + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Button(onClick = { + mainViewModel.closeGame() + }, modifier = Modifier.padding(16.dp)) { + Text(text = "Exit Game") + } + + Button(onClick = { + showBackNotice.value = false + }, modifier = Modifier.padding(16.dp)) { + Text(text = "Dismiss") + } + } + } + } + } + } + } } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt index b9dee9431..f7cc010c0 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt @@ -135,16 +135,6 @@ class SettingViews { }) }) { contentPadding -> Column(modifier = Modifier.padding(contentPadding)) { - BackHandler { - settingsViewModel.save( - isHostMapped, - useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices, - enableShaderCache, - enableTextureRecompression, - resScale, - useVirtualController - ) - } ExpandableView(onCardArrowClick = { }, title = "System") { Column(modifier = Modifier.fillMaxWidth()) { Row( @@ -462,6 +452,18 @@ class SettingViews { } } } + + BackHandler() { + settingsViewModel.save( + isHostMapped, + useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices, + enableShaderCache, + enableTextureRecompression, + resScale, + useVirtualController + ) + settingsViewModel.navController.popBackStack() + } } }