add closing emulation(starting a new one is still broken), disabled audio

This commit is contained in:
Emmanuel Hansen 2023-07-29 09:40:50 +00:00
parent a050e5c6c0
commit 2999275ed2
13 changed files with 194 additions and 33 deletions

View File

@ -72,7 +72,9 @@ namespace LibRyujinx
var init = Initialize(path, enableDebugLogs); var init = Initialize(path, enableDebugLogs);
AudioDriver = new OboeHardwareDeviceDriver(); // AudioDriver = new OboeHardwareDeviceDriver();
_surfaceEvent?.Set();
_surfaceEvent = new ManualResetEvent(false); _surfaceEvent = new ManualResetEvent(false);
@ -151,6 +153,18 @@ namespace LibRyujinx
return LoadApplication(path); 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")] [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoadDescriptor")]
public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci) public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci)
{ {

View File

@ -1,6 +1,8 @@
using ARMeilleure.Translation; using ARMeilleure.Translation;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input.HLE;
using Silk.NET.Vulkan;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -168,5 +170,41 @@ namespace LibRyujinx
return true; 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;
}
}
} }
} }

View File

@ -3,6 +3,7 @@ using LibRyujinx.Shared;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
@ -21,6 +22,7 @@ namespace LibRyujinx
private static CancellationTokenSource _gpuCancellationTokenSource; private static CancellationTokenSource _gpuCancellationTokenSource;
private static SwapBuffersCallback? _swapBuffersCallback; private static SwapBuffersCallback? _swapBuffersCallback;
private static NativeGraphicsInterop _nativeGraphicsInterop; private static NativeGraphicsInterop _nativeGraphicsInterop;
private static ManualResetEvent _gpuDoneEvent;
public delegate void SwapBuffersCallback(); public delegate void SwapBuffersCallback();
public delegate IntPtr GetProcAddress(string name); public delegate IntPtr GetProcAddress(string name);
@ -146,12 +148,14 @@ namespace LibRyujinx
return; return;
} }
var device = SwitchDevice!.EmulationContext!; var device = SwitchDevice!.EmulationContext!;
_gpuDoneEvent = new ManualResetEvent(true);
device.Gpu.Renderer.Initialize(GraphicsDebugLevel.None); device.Gpu.Renderer.Initialize(GraphicsDebugLevel.None);
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
device.Gpu.Renderer.RunLoop(() => device.Gpu.Renderer.RunLoop(() =>
{ {
_gpuDoneEvent.Reset();
device.Gpu.SetGpuThread(); device.Gpu.SetGpuThread();
device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
Translator.IsReadyForTranslation.Set(); Translator.IsReadyForTranslation.Set();
@ -162,9 +166,11 @@ namespace LibRyujinx
{ {
if (_isStopped) if (_isStopped)
{ {
return; break;
} }
debug_break(1);
if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic) if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic)
{ {
setRenderingThread(); setRenderingThread();
@ -182,6 +188,13 @@ namespace LibRyujinx
device.PresentFrame(() => _swapBuffersCallback?.Invoke()); device.PresentFrame(() => _swapBuffersCallback?.Invoke());
} }
} }
if (device.Gpu.Renderer is ThreadedRenderer threaded)
{
threaded.FlushThreadedCommands();
}
_gpuDoneEvent.Set();
}); });
} }

View File

@ -30,7 +30,7 @@ namespace LibRyujinx
private static VirtualTouchScreenDriver? _touchScreenDriver; private static VirtualTouchScreenDriver? _touchScreenDriver;
private static TouchScreenManager? _touchScreenManager; private static TouchScreenManager? _touchScreenManager;
private static InputManager? _inputManager; private static InputManager? _inputManager;
private static NpadManager _npadManager; private static NpadManager? _npadManager;
private static InputConfig[] _configs; private static InputConfig[] _configs;
public static void InitializeInput(int width, int height) public static void InitializeInput(int width, int height)

View File

@ -641,7 +641,9 @@ namespace LibRyujinx
internal void DisposeContext() internal void DisposeContext()
{ {
EmulationContext?.Dispose(); EmulationContext?.Dispose();
EmulationContext?.DisposeGpu();
EmulationContext = null; EmulationContext = null;
LibRyujinx.Renderer = null;
} }
} }

View File

@ -222,5 +222,6 @@ Java_org_ryujinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz,
extern "C" extern "C"
void debug_break(int code){ void debug_break(int code){
if(code >= 3)
int r = 0; int r = 0;
} }

View File

@ -12,6 +12,7 @@ import java.io.File
import kotlin.concurrent.thread import kotlin.concurrent.thread
class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback { class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
private var _isClosed: Boolean = false
private var _renderingThreadWatcher: Thread? = null private var _renderingThreadWatcher: Thread? = null
private var _height: Int = 0 private var _height: Int = 0
private var _width: 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 _isInit: Boolean = false
private var _isStarted: Boolean = false private var _isStarted: Boolean = false
private var _nativeWindow: Long = 0 private var _nativeWindow: Long = 0
private var _nativeHelper: NativeHelpers = NativeHelpers()
private var _nativeRyujinx: RyujinxNative = RyujinxNative() private var _nativeRyujinx: RyujinxNative = RyujinxNative()
companion object {
var gameModel: GameModel? = null
}
init { init {
holder.addCallback(this) 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) { override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
if(_isClosed)
return
start(holder) start(holder)
if(_width != width || _height != height) 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) if(_isStarted)
return; return;
@ -119,6 +126,7 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie
mainViewModel.performanceManager?.initializeRenderingSession(threadId) mainViewModel.performanceManager?.initializeRenderingSession(threadId)
} }
} }
mainViewModel.performanceManager?.closeCurrentRenderingSession()
} }
} }
_nativeRyujinx.graphicsRendererRunLoop() _nativeRyujinx.graphicsRendererRunLoop()

View File

@ -12,6 +12,7 @@ import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.addCallback
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -62,15 +63,22 @@ class MainActivity : ComponentActivity() {
external fun getRenderingThreadId() : Long external fun getRenderingThreadId() : Long
external fun initVm() external fun initVm()
fun setFullScreen() { fun setFullScreen(fullscreen: Boolean) {
requestedOrientation = requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE if (fullscreen) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_FULL_USER
val insets = WindowCompat.getInsetsController(window, window.decorView) val insets = WindowCompat.getInsetsController(window, window.decorView)
insets.apply { insets.apply {
insets.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars()) if (fullscreen) {
insets.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 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") @SuppressLint("RestrictedApi")
override fun dispatchKeyEvent(event: KeyEvent?): Boolean { override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
event?.apply { event?.apply {
return physicalControllerManager.onKeyEvent(this) if(physicalControllerManager.onKeyEvent(this))
return true;
} }
return super.dispatchKeyEvent(event) return super.dispatchKeyEvent(event)
} }

View File

@ -48,4 +48,6 @@ class RyujinxNative {
external fun inputConnectGamepad(index: Int): Int external fun inputConnectGamepad(index: Int): Int
external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int): Unit external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int): Unit
external fun graphicsSetSurface(surface: Long) external fun graphicsSetSurface(surface: Long)
external fun deviceCloseEmulation()
external fun deviceSignalEmulationClose()
} }

View File

@ -6,10 +6,6 @@ import android.os.Build
import android.os.PerformanceHintManager import android.os.PerformanceHintManager
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.navigation.NavHostController 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.GameController
import org.ryujinx.android.GameHost import org.ryujinx.android.GameHost
import org.ryujinx.android.GraphicsConfiguration import org.ryujinx.android.GraphicsConfiguration
@ -24,6 +20,7 @@ import java.io.File
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
class MainViewModel(val activity: MainActivity) { class MainViewModel(val activity: MainActivity) {
var gameHost: GameHost? = null
var controller: GameController? = null var controller: GameController? = null
var performanceManager: PerformanceManager? = null var performanceManager: PerformanceManager? = null
var selected: GameModel? = null var selected: GameModel? = null
@ -42,9 +39,19 @@ class MainViewModel(val activity: MainActivity) {
} }
} }
suspend fun loadGame(game:GameModel) : Boolean { fun closeGame() {
GameHost.gameModel = game RyujinxNative().deviceSignalEmulationClose()
gameHost?.close()
RyujinxNative().deviceCloseEmulation()
goBack()
activity.setFullScreen(false)
}
fun goBack(){
navController?.popBackStack()
}
fun loadGame(game:GameModel) : Boolean {
var nativeRyujinx = RyujinxNative() var nativeRyujinx = RyujinxNative()
val path = game.getPath() ?: return false val path = game.getPath() ?: return false
@ -129,8 +136,6 @@ class MainViewModel(val activity: MainActivity) {
if(!success) if(!success)
return false return false
activity.physicalControllerManager.connect()
return true return true
} }
@ -163,4 +168,7 @@ class MainViewModel(val activity: MainActivity) {
fun setGameController(controller: GameController) { fun setGameController(controller: GameController) {
this.controller = controller this.controller = controller
} }
fun backCalled() {
}
} }

View File

@ -310,7 +310,7 @@ class HomeViews {
val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
if(success) { if(success) {
launchOnUiThread { launchOnUiThread {
viewModel.mainViewModel?.activity?.setFullScreen() viewModel.mainViewModel?.activity?.setFullScreen(true)
viewModel.mainViewModel?.navController?.navigate("game") viewModel.mainViewModel?.navController?.navigate("game")
} }
} }

View File

@ -1,13 +1,25 @@
package org.ryujinx.android.views 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.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize 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.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.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -71,6 +83,7 @@ class MainView {
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun GameOverlay(mainViewModel: MainViewModel) { fun GameOverlay(mainViewModel: MainViewModel) {
Box(modifier = Modifier.fillMaxSize()) { 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")
}
}
}
}
}
}
}
} }
} }

View File

@ -135,16 +135,6 @@ class SettingViews {
}) })
}) { contentPadding -> }) { contentPadding ->
Column(modifier = Modifier.padding(contentPadding)) { Column(modifier = Modifier.padding(contentPadding)) {
BackHandler {
settingsViewModel.save(
isHostMapped,
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices,
enableShaderCache,
enableTextureRecompression,
resScale,
useVirtualController
)
}
ExpandableView(onCardArrowClick = { }, title = "System") { ExpandableView(onCardArrowClick = { }, title = "System") {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
Row( Row(
@ -462,6 +452,18 @@ class SettingViews {
} }
} }
} }
BackHandler() {
settingsViewModel.save(
isHostMapped,
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices,
enableShaderCache,
enableTextureRecompression,
resScale,
useVirtualController
)
settingsViewModel.navController.popBackStack()
}
} }
} }