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,
JObjectLocalRef jObj,
JArrayLocalRef extensionsArray,
JLong surfacePtr,
JLong driverHandle)
{
if (Renderer != null)
@ -254,10 +253,6 @@ namespace LibRyujinx
extensions.Add(GetString(jEnv, ext));
}
_surfaceEvent.Set();
_surfacePtr = surfacePtr;
if((long)driverHandle != 0)
{
VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle);

View File

@ -59,9 +59,6 @@ android {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
jniLibs {
keepDebugSymbols += '**/libryujinx.so'
}
}
externalNativeBuild {
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) {
val isStarted = _isStarted
start(holder)
if(isStarted && (_width != width || _height != height))
if(_width != width || _height != height)
{
val nativeHelpers = NativeHelpers()
val window = nativeHelpers.getNativeWindow(holder.surface)
@ -62,85 +60,14 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie
}
private fun start(surfaceHolder: SurfaceHolder) {
val game = gameModel ?: return
val path = game.getPath() ?: return
if (_isStarted)
return
var surface = surfaceHolder.surface
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)
if(_isStarted)
return;
_nativeRyujinx.inputInitialize(width, height)
val settings = QuickSettings(mainViewModel.activity)
if(!settings.useVirtualController){
mainViewModel.controller?.setVisible(false)
}
@ -158,7 +85,7 @@ class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceVie
_guestThread = thread(start = true) {
runGame()
}
_isStarted = success
_isStarted = true
_updateThread = thread(start = true) {
var c = 0

View File

@ -25,7 +25,6 @@ class RyujinxNative {
external fun graphicsInitialize(configuration: GraphicsConfiguration): Boolean
external fun graphicsInitializeRenderer(
extensions: Array<String>,
surface: Long,
driver: Long
): Boolean
@ -48,5 +47,5 @@ class RyujinxNative {
external fun inputSetButtonReleased(button: Int, id: Int): Unit
external fun inputConnectGamepad(index: Int): Int
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 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
import org.ryujinx.android.MainActivity
import org.ryujinx.android.NativeGraphicsInterop
import org.ryujinx.android.NativeHelpers
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")
class MainViewModel(val activity: MainActivity) {
@ -19,7 +30,7 @@ class MainViewModel(val activity: MainActivity) {
private var gameTimeState: MutableState<Double>? = null
private var gameFpsState: MutableState<Double>? = null
private var fifoState: MutableState<Double>? = null
private var navController : NavHostController? = null
var navController : NavHostController? = null
var homeViewModel: HomeViewModel = HomeViewModel(activity, this)
@ -31,15 +42,96 @@ class MainViewModel(val activity: MainActivity) {
}
}
fun loadGame(game:GameModel) {
val controller = navController?: return
activity.setFullScreen()
suspend fun loadGame(game:GameModel) : Boolean {
GameHost.gameModel = game
controller.navigate("game")
var nativeRyujinx = RyujinxNative()
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)
}
fun setNavController(controller: NavHostController) {
navController = controller
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(

View File

@ -32,6 +32,7 @@ import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.zIndex
import androidx.navigation.NavHostController
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.R
import org.ryujinx.android.viewmodels.GameModel
@ -143,14 +148,16 @@ class HomeViews {
Column {
TextButton(onClick = {
navController.navigate("settings")
}, modifier = Modifier.fillMaxWidth()
}, modifier = Modifier
.fillMaxWidth()
.align(Alignment.Start),
) {
Icon(
Icons.Filled.Settings,
contentDescription = "Settings"
)
Text(text = "Settings", modifier = Modifier.padding(16.dp)
Text(text = "Settings", modifier = Modifier
.padding(16.dp)
.align(Alignment.CenterVertically))
}
}
@ -167,6 +174,7 @@ class HomeViews {
val sheetState = rememberModalBottomSheetState()
val scope = rememberCoroutineScope()
val showBottomSheet = remember { mutableStateOf(false) }
val showLoading = remember { mutableStateOf(false) }
Scaffold(
modifier = Modifier.fillMaxSize(),
@ -198,12 +206,31 @@ class HomeViews {
items(list) {
it.titleName?.apply {
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) {
ModalBottomSheet(onDismissRequest = {
showBottomSheet.value = false
@ -264,15 +291,32 @@ class HomeViews {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GameItem(gameModel: GameModel, viewModel: HomeViewModel, showSheet : MutableState<Boolean>) {
Card(shape = MaterialTheme.shapes.medium,
fun GameItem(
gameModel: GameModel,
viewModel: HomeViewModel,
showSheet: MutableState<Boolean>,
showLoading: MutableState<Boolean>
) {
Surface(shape = MaterialTheme.shapes.medium,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.combinedClickable(
onClick = {
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 = {

View File

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