separate game loading from surface creation

This commit is contained in:
Emmanuel Hansen 2023-07-28 19:42:34 +00:00
parent 398fb78a31
commit a050e5c6c0
7 changed files with 158 additions and 104 deletions

View File

@ -218,7 +218,6 @@ namespace LibRyujinx
public unsafe static JBoolean JniInitializeGraphicsRendererNative(JEnvRef jEnv, public unsafe static JBoolean JniInitializeGraphicsRendererNative(JEnvRef jEnv,
JObjectLocalRef jObj, JObjectLocalRef jObj,
JArrayLocalRef extensionsArray, JArrayLocalRef extensionsArray,
JLong surfacePtr,
JLong driverHandle) JLong driverHandle)
{ {
if (Renderer != null) if (Renderer != null)
@ -254,10 +253,6 @@ namespace LibRyujinx
extensions.Add(GetString(jEnv, ext)); extensions.Add(GetString(jEnv, ext));
} }
_surfaceEvent.Set();
_surfacePtr = surfacePtr;
if((long)driverHandle != 0) if((long)driverHandle != 0)
{ {
VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle); VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle);

View File

@ -59,9 +59,6 @@ android {
resources { resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}' excludes += '/META-INF/{AL2.0,LGPL2.1}'
} }
jniLibs {
keepDebugSymbols += '**/libryujinx.so'
}
} }
externalNativeBuild { externalNativeBuild {
cmake { cmake {

View File

@ -37,11 +37,9 @@ 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) {
val isStarted = _isStarted
start(holder) start(holder)
if(isStarted && (_width != width || _height != height)) if(_width != width || _height != height)
{ {
val nativeHelpers = NativeHelpers() val nativeHelpers = NativeHelpers()
val window = nativeHelpers.getNativeWindow(holder.surface) val window = nativeHelpers.getNativeWindow(holder.surface)
@ -62,85 +60,14 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie
} }
private fun start(surfaceHolder: SurfaceHolder) { private fun start(surfaceHolder: SurfaceHolder) {
val game = gameModel ?: return
val path = game.getPath() ?: return
if (_isStarted)
return
var surface = surfaceHolder.surface if(_isStarted)
return;
val settings = QuickSettings(mainViewModel.activity)
var success = _nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply {
EnableShaderCache = settings.enableShaderCache
EnableTextureRecompression = settings.enableTextureRecompression
ResScale = settings.resScale
})
val nativeHelpers = NativeHelpers()
val window = nativeHelpers.getNativeWindow(surfaceHolder.surface)
nativeInterop = NativeGraphicsInterop()
nativeInterop!!.VkRequiredExtensions = arrayOf(
"VK_KHR_surface", "VK_KHR_android_surface"
)
nativeInterop!!.VkCreateSurface = nativeHelpers.getCreateSurfacePtr()
nativeInterop!!.SurfaceHandle = window
var driverViewModel = VulkanDriverViewModel(mainViewModel.activity);
var drivers = driverViewModel.getAvailableDrivers()
var driverHandle = 0L;
if(driverViewModel.selected.isNotEmpty()) {
var metaData = drivers.find { it.driverPath == driverViewModel.selected }
metaData?.apply {
var privatePath = mainViewModel.activity.filesDir;
var privateDriverPath = privatePath.canonicalPath + "/driver/"
val pD = File(privateDriverPath)
if(pD.exists())
pD.deleteRecursively()
pD.mkdirs()
var driver = File(driverViewModel.selected)
var parent = driver.parentFile
for (file in parent.walkTopDown()){
if(file.absolutePath == parent.absolutePath)
continue
file.copyTo(File(privateDriverPath + file.name), true)
}
driverHandle = NativeHelpers().loadDriver(mainViewModel.activity.applicationInfo.nativeLibraryDir!! + "/", privateDriverPath, this.libraryName)
}
}
success = _nativeRyujinx.graphicsInitializeRenderer(
nativeInterop!!.VkRequiredExtensions!!,
window,
driverHandle
)
success = _nativeRyujinx.deviceInitialize(
settings.isHostMapped,
settings.useNce,
SystemLanguage.AmericanEnglish.ordinal,
RegionCode.USA.ordinal,
settings.enableVsync,
settings.enableDocked,
settings.enablePtc,
false,
"UTC",
settings.ignoreMissingServices
)
success = _nativeRyujinx.deviceLoad(path)
_nativeRyujinx.inputInitialize(width, height) _nativeRyujinx.inputInitialize(width, height)
val settings = QuickSettings(mainViewModel.activity)
if(!settings.useVirtualController){ if(!settings.useVirtualController){
mainViewModel.controller?.setVisible(false) mainViewModel.controller?.setVisible(false)
} }
@ -158,7 +85,7 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie
_guestThread = thread(start = true) { _guestThread = thread(start = true) {
runGame() runGame()
} }
_isStarted = success _isStarted = true
_updateThread = thread(start = true) { _updateThread = thread(start = true) {
var c = 0 var c = 0

View File

@ -25,7 +25,6 @@ class RyujinxNative {
external fun graphicsInitialize(configuration: GraphicsConfiguration): Boolean external fun graphicsInitialize(configuration: GraphicsConfiguration): Boolean
external fun graphicsInitializeRenderer( external fun graphicsInitializeRenderer(
extensions: Array<String>, extensions: Array<String>,
surface: Long,
driver: Long driver: Long
): Boolean ): Boolean
@ -48,5 +47,5 @@ class RyujinxNative {
external fun inputSetButtonReleased(button: Int, id: Int): Unit external fun inputSetButtonReleased(button: Int, id: Int): Unit
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): String external fun graphicsSetSurface(surface: Long)
} }

View File

@ -6,10 +6,21 @@ 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.MainActivity import org.ryujinx.android.MainActivity
import org.ryujinx.android.NativeGraphicsInterop
import org.ryujinx.android.NativeHelpers
import org.ryujinx.android.PerformanceManager import org.ryujinx.android.PerformanceManager
import org.ryujinx.android.RegionCode
import org.ryujinx.android.RyujinxNative
import org.ryujinx.android.SystemLanguage
import java.io.File
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
class MainViewModel(val activity: MainActivity) { class MainViewModel(val activity: MainActivity) {
@ -19,7 +30,7 @@ class MainViewModel(val activity: MainActivity) {
private var gameTimeState: MutableState<Double>? = null private var gameTimeState: MutableState<Double>? = null
private var gameFpsState: MutableState<Double>? = null private var gameFpsState: MutableState<Double>? = null
private var fifoState: MutableState<Double>? = null private var fifoState: MutableState<Double>? = null
private var navController : NavHostController? = null var navController : NavHostController? = null
var homeViewModel: HomeViewModel = HomeViewModel(activity, this) var homeViewModel: HomeViewModel = HomeViewModel(activity, this)
@ -31,15 +42,96 @@ class MainViewModel(val activity: MainActivity) {
} }
} }
fun loadGame(game:GameModel) { suspend fun loadGame(game:GameModel) : Boolean {
val controller = navController?: return
activity.setFullScreen()
GameHost.gameModel = game GameHost.gameModel = game
controller.navigate("game")
}
fun setNavController(controller: NavHostController) { var nativeRyujinx = RyujinxNative()
navController = controller
val path = game.getPath() ?: return false
val settings = QuickSettings(activity)
var success = nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply {
EnableShaderCache = settings.enableShaderCache
EnableTextureRecompression = settings.enableTextureRecompression
ResScale = settings.resScale
})
if(!success)
return false
val nativeHelpers = NativeHelpers()
var nativeInterop = NativeGraphicsInterop()
nativeInterop!!.VkRequiredExtensions = arrayOf(
"VK_KHR_surface", "VK_KHR_android_surface"
)
nativeInterop!!.VkCreateSurface = nativeHelpers.getCreateSurfacePtr()
nativeInterop!!.SurfaceHandle = 0
var driverViewModel = VulkanDriverViewModel(activity);
var drivers = driverViewModel.getAvailableDrivers()
var driverHandle = 0L;
if (driverViewModel.selected.isNotEmpty()) {
var metaData = drivers.find { it.driverPath == driverViewModel.selected }
metaData?.apply {
var privatePath = activity.filesDir;
var privateDriverPath = privatePath.canonicalPath + "/driver/"
val pD = File(privateDriverPath)
if (pD.exists())
pD.deleteRecursively()
pD.mkdirs()
var driver = File(driverViewModel.selected)
var parent = driver.parentFile
for (file in parent.walkTopDown()) {
if (file.absolutePath == parent.absolutePath)
continue
file.copyTo(File(privateDriverPath + file.name), true)
}
driverHandle = NativeHelpers().loadDriver(
activity.applicationInfo.nativeLibraryDir!! + "/",
privateDriverPath,
this.libraryName
)
}
}
success = nativeRyujinx.graphicsInitializeRenderer(
nativeInterop!!.VkRequiredExtensions!!,
driverHandle
)
if(!success)
return false
success = nativeRyujinx.deviceInitialize(
settings.isHostMapped,
settings.useNce,
SystemLanguage.AmericanEnglish.ordinal,
RegionCode.USA.ordinal,
settings.enableVsync,
settings.enableDocked,
settings.enablePtc,
false,
"UTC",
settings.ignoreMissingServices
)
if(!success)
return false
success = nativeRyujinx.deviceLoad(path)
if(!success)
return false
activity.physicalControllerManager.connect()
return true
} }
fun setStatStates( fun setStatStates(

View File

@ -32,6 +32,7 @@ import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@ -55,10 +56,14 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogWindowProvider import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.anggrayudi.storage.extension.launchOnUiThread
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
import org.ryujinx.android.R import org.ryujinx.android.R
import org.ryujinx.android.viewmodels.GameModel import org.ryujinx.android.viewmodels.GameModel
@ -143,14 +148,16 @@ class HomeViews {
Column { Column {
TextButton(onClick = { TextButton(onClick = {
navController.navigate("settings") navController.navigate("settings")
}, modifier = Modifier.fillMaxWidth() }, modifier = Modifier
.fillMaxWidth()
.align(Alignment.Start), .align(Alignment.Start),
) { ) {
Icon( Icon(
Icons.Filled.Settings, Icons.Filled.Settings,
contentDescription = "Settings" contentDescription = "Settings"
) )
Text(text = "Settings", modifier = Modifier.padding(16.dp) Text(text = "Settings", modifier = Modifier
.padding(16.dp)
.align(Alignment.CenterVertically)) .align(Alignment.CenterVertically))
} }
} }
@ -167,6 +174,7 @@ class HomeViews {
val sheetState = rememberModalBottomSheetState() val sheetState = rememberModalBottomSheetState()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val showBottomSheet = remember { mutableStateOf(false) } val showBottomSheet = remember { mutableStateOf(false) }
val showLoading = remember { mutableStateOf(false) }
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -198,11 +206,30 @@ class HomeViews {
items(list) { items(list) {
it.titleName?.apply { it.titleName?.apply {
if (this.isNotEmpty()) if (this.isNotEmpty())
GameItem(it, viewModel, showBottomSheet) GameItem(it, viewModel, showBottomSheet, showLoading)
} }
} }
} }
} }
if(showLoading.value){
AlertDialog(onDismissRequest = { }) {
Card(modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
shape = MaterialTheme.shapes.medium) {
Column(modifier = Modifier
.padding(16.dp)
.fillMaxWidth()) {
Text(text = "Loading")
LinearProgressIndicator(modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp))
}
}
}
}
if(showBottomSheet.value) { if(showBottomSheet.value) {
ModalBottomSheet(onDismissRequest = { ModalBottomSheet(onDismissRequest = {
@ -264,15 +291,32 @@ class HomeViews {
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun GameItem(gameModel: GameModel, viewModel: HomeViewModel, showSheet : MutableState<Boolean>) { fun GameItem(
Card(shape = MaterialTheme.shapes.medium, gameModel: GameModel,
viewModel: HomeViewModel,
showSheet: MutableState<Boolean>,
showLoading: MutableState<Boolean>
) {
Surface(shape = MaterialTheme.shapes.medium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(8.dp) .padding(8.dp)
.combinedClickable( .combinedClickable(
onClick = { onClick = {
if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") { if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") {
viewModel.mainViewModel?.loadGame(gameModel) runBlocking {
launch {
showLoading.value = true
val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
if(success) {
launchOnUiThread {
viewModel.mainViewModel?.activity?.setFullScreen()
viewModel.mainViewModel?.navController?.navigate("game")
}
}
showLoading.value = false
}
}
} }
}, },
onLongClick = { onLongClick = {

View File

@ -42,7 +42,7 @@ class MainView {
@Composable @Composable
fun Main(mainViewModel: MainViewModel) { fun Main(mainViewModel: MainViewModel) {
val navController = rememberNavController() val navController = rememberNavController()
mainViewModel.setNavController(navController) mainViewModel.navController = navController
NavHost(navController = navController, startDestination = "home") { NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) } composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }