forked from MeloNX/MeloNX
add performance hints
This commit is contained in:
parent
61ba5e7bff
commit
ff3099e4a1
src
LibRyujinx
Ryujinx.Graphics.Vulkan
RyujinxAndroid/app/src/main
@ -36,6 +36,12 @@ namespace LibRyujinx
|
|||||||
[DllImport("libryujinxjni")]
|
[DllImport("libryujinxjni")]
|
||||||
private extern static JStringLocalRef createString(JEnvRef jEnv, IntPtr ch);
|
private extern static JStringLocalRef createString(JEnvRef jEnv, IntPtr ch);
|
||||||
|
|
||||||
|
[DllImport("libryujinxjni")]
|
||||||
|
internal extern static void setRenderingThread();
|
||||||
|
|
||||||
|
[DllImport("libryujinxjni")]
|
||||||
|
internal extern static void onFrameEnd(double time);
|
||||||
|
|
||||||
public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance);
|
public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance);
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "JNI_OnLoad")]
|
[UnmanagedCallersOnly(EntryPoint = "JNI_OnLoad")]
|
||||||
@ -279,6 +285,11 @@ namespace LibRyujinx
|
|||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererRunLoop")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererRunLoop")]
|
||||||
public static void JniRunLoopNative(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniRunLoopNative(JEnvRef jEnv, JObjectLocalRef jObj)
|
||||||
{
|
{
|
||||||
|
SetSwapBuffersCallback(() =>
|
||||||
|
{
|
||||||
|
var time = SwitchDevice.EmulationContext.Statistics.GetGameFrameTime();
|
||||||
|
onFrameEnd(time);
|
||||||
|
});
|
||||||
RunLoop();
|
RunLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +165,11 @@ namespace LibRyujinx
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic)
|
||||||
|
{
|
||||||
|
setRenderingThread();
|
||||||
|
}
|
||||||
|
|
||||||
if (device.WaitFifo())
|
if (device.WaitFifo())
|
||||||
{
|
{
|
||||||
device.Statistics.RecordFifoStart();
|
device.Statistics.RecordFifoStart();
|
||||||
|
@ -477,6 +477,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
|
_gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//While this does nothing in most cases, it's useful to notify the end of the frame.
|
||||||
|
swapBuffersCallback?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetAntiAliasing(AntiAliasing effect)
|
public override void SetAntiAliasing(AntiAliasing effect)
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
|
android:appCategory="game"
|
||||||
android:theme="@style/Theme.RyujinxAndroid"
|
android:theme="@style/Theme.RyujinxAndroid"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
|
@ -36,4 +36,10 @@ void* _ryujinxNative = NULL;
|
|||||||
// Ryujinx imported functions
|
// Ryujinx imported functions
|
||||||
bool (*initialize)(char*) = NULL;
|
bool (*initialize)(char*) = NULL;
|
||||||
|
|
||||||
|
long _renderingThreadId = 0;
|
||||||
|
long _currentRenderingThreadId = 0;
|
||||||
|
JavaVM* _vm = nullptr;
|
||||||
|
jobject _mainActivity = nullptr;
|
||||||
|
jclass _mainActivityClass = nullptr;
|
||||||
|
|
||||||
#endif //RYUJINXNATIVE_RYUIJNX_H
|
#endif //RYUJINXNATIVE_RYUIJNX_H
|
||||||
|
@ -17,6 +17,31 @@
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
#include "ryuijnx.h"
|
#include "ryuijnx.h"
|
||||||
|
#include "pthread.h"
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
jmethodID _updateFrameTime;
|
||||||
|
JNIEnv* _rendererEnv = nullptr;
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint;
|
||||||
|
|
||||||
|
JNIEnv* getEnv(bool isRenderer){
|
||||||
|
JNIEnv* env;
|
||||||
|
if(isRenderer){
|
||||||
|
env = _rendererEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(env != nullptr)
|
||||||
|
return env;
|
||||||
|
|
||||||
|
auto result = _vm->AttachCurrentThread(&env, NULL);
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachEnv(){
|
||||||
|
auto result = _vm->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@ -128,4 +153,39 @@ jstring createString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_org_ryujinx_android_MainActivity_getRenderingThreadId(JNIEnv *env, jobject thiz) {
|
||||||
|
return _currentRenderingThreadId;
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
void setRenderingThread(){
|
||||||
|
auto currentId = pthread_self();
|
||||||
|
|
||||||
|
_currentRenderingThreadId = currentId;
|
||||||
|
_renderingThreadId = currentId;
|
||||||
|
|
||||||
|
_currentTimePoint = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_ryujinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) {
|
||||||
|
JavaVM* vm = nullptr;
|
||||||
|
auto success = env->GetJavaVM(&vm);
|
||||||
|
_vm = vm;
|
||||||
|
_mainActivity = thiz;
|
||||||
|
_mainActivityClass = env->GetObjectClass(thiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void onFrameEnd(double time){
|
||||||
|
auto env = getEnv(true);
|
||||||
|
auto cl = env->FindClass("org/ryujinx/android/MainActivity");
|
||||||
|
_updateFrameTime = env->GetStaticMethodID( cl , "updateRenderSessionPerformance", "(J)V");
|
||||||
|
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(now-_currentTimePoint).count();
|
||||||
|
env->CallStaticVoidMethod(cl, _updateFrameTime,
|
||||||
|
nano);
|
||||||
}
|
}
|
@ -1,13 +1,10 @@
|
|||||||
package org.ryujinx.android
|
package org.ryujinx.android
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.Build
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.ryujinx.android.viewmodels.GameModel
|
import org.ryujinx.android.viewmodels.GameModel
|
||||||
import org.ryujinx.android.viewmodels.MainViewModel
|
import org.ryujinx.android.viewmodels.MainViewModel
|
||||||
import org.ryujinx.android.viewmodels.QuickSettings
|
import org.ryujinx.android.viewmodels.QuickSettings
|
||||||
@ -15,6 +12,7 @@ import kotlin.concurrent.thread
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class GameHost(context: Context?, val controller: GameController, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
|
class GameHost(context: Context?, val controller: GameController, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
|
||||||
|
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
|
||||||
private var _updateThread: Thread? = null
|
private var _updateThread: Thread? = null
|
||||||
@ -168,7 +166,25 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun runGame() : Unit{
|
private fun runGame() : Unit{
|
||||||
|
// RenderingThreadWatcher
|
||||||
|
_renderingThreadWatcher = thread(start = true) {
|
||||||
|
var threadId = 0L;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
mainViewModel.performanceManager?.enable()
|
||||||
|
while (_isStarted) {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
var newthreadId = mainViewModel.activity.getRenderingThreadId()
|
||||||
|
|
||||||
|
if (threadId != newthreadId) {
|
||||||
|
mainViewModel.performanceManager?.closeCurrentRenderingSession()
|
||||||
|
}
|
||||||
|
threadId = newthreadId;
|
||||||
|
if (threadId != 0L) {
|
||||||
|
mainViewModel.performanceManager?.initializeRenderingSession(threadId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_nativeRyujinx.graphicsRendererRunLoop()
|
_nativeRyujinx.graphicsRendererRunLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -33,23 +33,39 @@ import org.ryujinx.android.views.MainView
|
|||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
var physicalControllerManager: PhysicalControllerManager
|
var physicalControllerManager: PhysicalControllerManager
|
||||||
private var mainViewModel: MainViewModel? = null
|
|
||||||
private var _isInit: Boolean = false
|
private var _isInit: Boolean = false
|
||||||
var storageHelper: SimpleStorageHelper? = null
|
var storageHelper: SimpleStorageHelper? = null
|
||||||
companion object {
|
companion object {
|
||||||
|
var mainViewModel: MainViewModel? = null
|
||||||
var AppPath : String?
|
var AppPath : String?
|
||||||
var StorageHelper: SimpleStorageHelper? = null
|
var StorageHelper: SimpleStorageHelper? = null
|
||||||
init {
|
init {
|
||||||
AppPath = ""
|
AppPath = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun updateRenderSessionPerformance(gameTime : Long)
|
||||||
|
{
|
||||||
|
if(gameTime <= 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
mainViewModel?.performanceManager?.updateRenderingSessionTime(gameTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
physicalControllerManager = PhysicalControllerManager(this)
|
physicalControllerManager = PhysicalControllerManager(this)
|
||||||
storageHelper = SimpleStorageHelper(this)
|
storageHelper = SimpleStorageHelper(this)
|
||||||
StorageHelper = storageHelper
|
StorageHelper = storageHelper
|
||||||
|
System.loadLibrary("ryujinxjni")
|
||||||
|
initVm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
external fun getRenderingThreadId() : Long
|
||||||
|
external fun initVm()
|
||||||
|
|
||||||
fun setFullScreen() :Unit {
|
fun setFullScreen() :Unit {
|
||||||
requestedOrientation =
|
requestedOrientation =
|
||||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.ryujinx.android
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.PerformanceHintManager
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
|
||||||
|
class PerformanceManager(val performanceHintManager: PerformanceHintManager) {
|
||||||
|
private var _isEnabled: Boolean = false
|
||||||
|
private var renderingSession: PerformanceHintManager.Session? = null
|
||||||
|
val DEFAULT_TARGET_NS = 16666666L
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.S)
|
||||||
|
fun initializeRenderingSession(threadId : Long){
|
||||||
|
if(!_isEnabled || renderingSession != null)
|
||||||
|
return
|
||||||
|
|
||||||
|
var threads = IntArray(1)
|
||||||
|
threads[0] = threadId.toInt()
|
||||||
|
renderingSession = performanceHintManager.createHintSession(threads, DEFAULT_TARGET_NS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.S)
|
||||||
|
fun closeCurrentRenderingSession() {
|
||||||
|
if (_isEnabled)
|
||||||
|
renderingSession?.apply {
|
||||||
|
renderingSession = null
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enable(){
|
||||||
|
_isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.S)
|
||||||
|
fun updateRenderingSessionTime(newTime : Long){
|
||||||
|
if(!_isEnabled)
|
||||||
|
return
|
||||||
|
|
||||||
|
var effectiveTime = newTime
|
||||||
|
|
||||||
|
if(newTime < DEFAULT_TARGET_NS)
|
||||||
|
effectiveTime = DEFAULT_TARGET_NS
|
||||||
|
|
||||||
|
renderingSession?.apply {
|
||||||
|
this.reportActualWorkDuration(effectiveTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,33 @@
|
|||||||
package org.ryujinx.android.viewmodels
|
package org.ryujinx.android.viewmodels
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.PerformanceHintManager
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import org.ryujinx.android.GameHost
|
import org.ryujinx.android.GameHost
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
|
import org.ryujinx.android.PerformanceManager
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
class MainViewModel(val activity: MainActivity) {
|
class MainViewModel(val activity: MainActivity) {
|
||||||
|
var performanceManager: PerformanceManager? = null
|
||||||
var selected: GameModel? = null
|
var selected: GameModel? = null
|
||||||
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
|
private var navController : NavHostController? = null
|
||||||
|
|
||||||
var homeViewModel: HomeViewModel = HomeViewModel(activity, this,)
|
var homeViewModel: HomeViewModel = HomeViewModel(activity, this)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
var hintService =
|
||||||
|
activity.getSystemService(Context.PERFORMANCE_HINT_SERVICE) as PerformanceHintManager
|
||||||
|
performanceManager = PerformanceManager(hintService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadGame(game:GameModel) {
|
fun loadGame(game:GameModel) {
|
||||||
var controller = navController?: return;
|
var controller = navController?: return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user