forked from MeloNX/MeloNX
separate game loading from surface creation
This commit is contained in:
parent
398fb78a31
commit
a050e5c6c0
@ -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);
|
||||
|
@ -59,9 +59,6 @@ android {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
jniLibs {
|
||||
keepDebugSymbols += '**/libryujinx.so'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
||||
fun setNavController(controller: NavHostController) {
|
||||
navController = controller
|
||||
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)
|
||||
}
|
||||
|
||||
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(
|
||||
|
@ -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,11 +206,30 @@ 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 = {
|
||||
@ -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 = {
|
||||
|
@ -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) }
|
||||
|
Loading…
x
Reference in New Issue
Block a user