1
0
forked from MeloNX/MeloNX

add performance hints

This commit is contained in:
Emmanuel Hansen 2023-07-20 13:35:07 +00:00
parent 61ba5e7bff
commit ff3099e4a1
10 changed files with 189 additions and 7 deletions
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;