1
0
forked from MeloNX/MeloNX

fix audio rebase

remove redundant sdk performance session usage

remove debug code and formatting
This commit is contained in:
Emmanuel Hansen 2024-02-25 22:06:54 +00:00
parent d40dc00769
commit 06a7c0217f
15 changed files with 273 additions and 619 deletions

View File

@ -3,7 +3,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -13,17 +13,17 @@ using System.Threading.Tasks;
namespace LibRyujinx.Android namespace LibRyujinx.Android
{ {
internal class AndroidUiHandler : IHostUiHandler, IDisposable internal class AndroidUIHandler : IHostUIHandler, IDisposable
{ {
public IHostUiTheme HostUiTheme => throw new NotImplementedException();
public ManualResetEvent _waitEvent; public ManualResetEvent _waitEvent;
public ManualResetEvent _responseEvent; public ManualResetEvent _responseEvent;
private bool _isDisposed; private bool _isDisposed;
private bool _isOkPressed; private bool _isOkPressed;
private long _input; private long _input;
public AndroidUiHandler() public IHostUITheme HostUITheme => throw new NotImplementedException();
public AndroidUIHandler()
{ {
_waitEvent = new ManualResetEvent(false); _waitEvent = new ManualResetEvent(false);
_responseEvent = new ManualResetEvent(false); _responseEvent = new ManualResetEvent(false);
@ -47,7 +47,7 @@ namespace LibRyujinx.Android
return _isOkPressed; return _isOkPressed;
} }
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
{ {
LibRyujinx.setUiHandlerTitle(LibRyujinx.storeString("Software Keyboard")); LibRyujinx.setUiHandlerTitle(LibRyujinx.storeString("Software Keyboard"));
LibRyujinx.setUiHandlerMessage(LibRyujinx.storeString(args.HeaderText ?? "")); LibRyujinx.setUiHandlerMessage(LibRyujinx.storeString(args.HeaderText ?? ""));
@ -81,7 +81,7 @@ namespace LibRyujinx.Android
return _isOkPressed; return _isOkPressed;
} }
public bool DisplayMessageDialog(ControllerAppletUiArgs args) public bool DisplayMessageDialog(ControllerAppletUIArgs args)
{ {
string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}";

View File

@ -1,18 +0,0 @@
namespace LibRyujinx.Shared.Audio.Oboe
{
internal class OboeAudioBuffer
{
public readonly ulong DriverIdentifier;
public readonly ulong SampleCount;
public readonly byte[] Data;
public ulong SamplePlayed;
public OboeAudioBuffer(ulong driverIdentifier, byte[] data, ulong sampleCount)
{
DriverIdentifier = driverIdentifier;
Data = data;
SampleCount = sampleCount;
SamplePlayed = 0;
}
}
}

View File

@ -1,105 +0,0 @@
using Ryujinx.Audio;
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace LibRyujinx.Shared.Audio.Oboe
{
internal class OboeHardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<OboeHardwareDeviceSession, byte> _sessions;
public OboeHardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
_sessions = new ConcurrentDictionary<OboeHardwareDeviceSession, byte>();
}
public static bool IsSupported => true;
public ManualResetEvent GetUpdateRequiredEvent()
{
return _updateRequiredEvent;
}
public ManualResetEvent GetPauseEvent()
{
return _pauseEvent;
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
{
if (channelCount == 0)
{
channelCount = 2;
}
if (sampleRate == 0)
{
sampleRate = Constants.TargetSampleRate;
}
if (direction != Direction.Output)
{
throw new NotImplementedException("Input direction is currently not implemented on Oboe backend!");
}
OboeHardwareDeviceSession session = new OboeHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
_sessions.TryAdd(session, 0);
return session;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (OboeHardwareDeviceSession session in _sessions.Keys)
{
session.Dispose();
}
_pauseEvent.Dispose();
_sessions.Clear();
}
}
public bool SupportsSampleRate(uint sampleRate)
{
return true;
}
public bool SupportsSampleFormat(SampleFormat sampleFormat)
{
return sampleFormat != SampleFormat.Adpcm;
}
public bool SupportsChannelCount(uint channelCount)
{
return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6;
}
public bool SupportsDirection(Direction direction)
{
return direction == Direction.Output;
}
}
}

View File

@ -1,189 +0,0 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
using System.Threading;
namespace LibRyujinx.Shared.Audio.Oboe
{
internal class OboeHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private OboeHardwareDeviceDriver _driver;
private bool _isClosed;
private bool _isWorkerActive;
private Queue<OboeAudioBuffer> _queuedBuffers;
private bool _isActive;
private ulong _playedSampleCount;
private Thread _workerThread;
private ManualResetEvent _updateRequiredEvent;
private IntPtr _session;
private object _queueLock = new object();
private object _trackLock = new object();
public OboeHardwareDeviceSession(OboeHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{
_driver = driver;
_isActive = false;
_playedSampleCount = 0;
_isWorkerActive = true;
_queuedBuffers = new Queue<OboeAudioBuffer>();
_updateRequiredEvent = driver.GetUpdateRequiredEvent();
_session = OboeInterop.CreateSession((int)requestedSampleFormat, requestedSampleRate, requestedChannelCount);
_workerThread = new Thread(Update);
_workerThread.Name = $"HardwareDeviceSession.Android.Track";
_workerThread.Start();
SetVolume(requestedVolume);
}
public override void UnregisterBuffer(AudioBuffer buffer) { }
public unsafe void Update(object ignored)
{
while (_isWorkerActive)
{
bool needUpdate = false;
bool hasBuffer;
OboeAudioBuffer buffer;
lock (_queueLock)
{
hasBuffer = _queuedBuffers.TryPeek(out buffer);
}
while (hasBuffer)
{
StartIfNotPlaying();
if (_isClosed)
break;
fixed(byte* ptr = buffer.Data)
OboeInterop.WriteToSession(_session, (ulong)ptr, buffer.SampleCount);
lock (_queueLock)
{
_playedSampleCount += buffer.SampleCount;
_queuedBuffers.TryDequeue(out _);
}
needUpdate = true;
lock (_queueLock)
{
hasBuffer = _queuedBuffers.TryPeek(out buffer);
}
}
if (needUpdate)
{
_updateRequiredEvent.Set();
}
// No work
Thread.Sleep(5);
}
}
public override void Dispose()
{
if (_session == 0)
return;
PrepareToClose();
OboeInterop.CloseSession(_session);
_session = 0;
}
public override void PrepareToClose()
{
_isClosed = true;
_isWorkerActive = false;
_workerThread?.Join();
Stop();
}
private void StartIfNotPlaying()
{
lock (_trackLock)
{
if (_isClosed)
return;
if (OboeInterop.IsPlaying(_session) == 0)
{
Start();
}
}
}
public override void QueueBuffer(AudioBuffer buffer)
{
lock (_queueLock)
{
OboeAudioBuffer driverBuffer = new OboeAudioBuffer(buffer.DataPointer, buffer.Data, GetSampleCount(buffer));
_queuedBuffers.Enqueue(driverBuffer);
if (_isActive)
{
StartIfNotPlaying();
}
}
}
public override float GetVolume()
{
return OboeInterop.GetSessionVolume(_session);
}
public override ulong GetPlayedSampleCount()
{
lock (_queueLock)
{
return _playedSampleCount;
}
}
public override void SetVolume(float volume)
{
volume = 1;
OboeInterop.SetSessionVolume(_session, volume);
}
public override void Start()
{
if (_isClosed)
return;
OboeInterop.StartSession(_session);
}
public override void Stop()
{
OboeInterop.StopSession(_session);
}
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
{
lock (_queueLock)
{
if (!_queuedBuffers.TryPeek(out OboeAudioBuffer driverBuffer))
{
return true;
}
return driverBuffer.DriverIdentifier != buffer.DataPointer;
}
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LibRyujinx.Shared.Audio.Oboe
{
internal static partial class OboeInterop
{
private const string InteropLib = "libryujinxjni";
[LibraryImport(InteropLib, EntryPoint = "create_session")]
public static partial IntPtr CreateSession(int sample_format,
uint sample_rate,
uint channel_count);
[LibraryImport(InteropLib, EntryPoint = "start_session")]
public static partial void StartSession(IntPtr session);
[LibraryImport(InteropLib, EntryPoint = "stop_session")]
public static partial void StopSession(IntPtr session);
[LibraryImport(InteropLib, EntryPoint = "close_session")]
public static partial void CloseSession(IntPtr session);
[LibraryImport(InteropLib, EntryPoint = "set_session_volume")]
public static partial void SetSessionVolume(IntPtr session, float volume);
[LibraryImport(InteropLib, EntryPoint = "get_session_volume")]
public static partial float GetSessionVolume(IntPtr session);
[LibraryImport(InteropLib, EntryPoint = "is_playing")]
public static partial int IsPlaying(IntPtr session);
[LibraryImport(InteropLib, EntryPoint = "write_to_session")]
public static partial void WriteToSession(IntPtr session, ulong data, ulong samples);
}
}

View File

@ -3,7 +3,6 @@ using LibRyujinx.Jni.Pointers;
using LibRyujinx.Jni.Primitives; using LibRyujinx.Jni.Primitives;
using LibRyujinx.Jni.References; using LibRyujinx.Jni.References;
using LibRyujinx.Jni.Values; using LibRyujinx.Jni.Values;
using LibRyujinx.Shared.Audio.Oboe;
using Rxmxnx.PInvoke; using Rxmxnx.PInvoke;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Common; using Ryujinx.Common;
@ -233,8 +232,6 @@ namespace LibRyujinx
Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
var list = GetDlcContentList(GetStoredString(pathPtr), (ulong)(long)titleId); var list = GetDlcContentList(GetStoredString(pathPtr), (ulong)(long)titleId);
debug_break(4);
return CreateStringArray(jEnv, list); return CreateStringArray(jEnv, list);
} }

View File

@ -4,6 +4,7 @@
#ifndef RYUJINXNATIVE_RYUIJNX_H #ifndef RYUJINXNATIVE_RYUIJNX_H
#define RYUJINXNATIVE_RYUIJNX_H #define RYUJINXNATIVE_RYUIJNX_H
#include <stdlib.h> #include <stdlib.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <string.h> #include <string.h>
@ -41,20 +42,33 @@ void* _ryujinxNative = NULL;
class UiHandler { class UiHandler {
public: public:
void setTitle(long storedTitle); void setTitle(long storedTitle);
void setMessage(long storedMessage); void setMessage(long storedMessage);
void setWatermark(long wm); void setWatermark(long wm);
void setType(int t); void setType(int t);
void setMode(int t); void setMode(int t);
void setMinLength(int t); void setMinLength(int t);
void setMaxLength(int t); void setMaxLength(int t);
void setInitialText(long text); void setInitialText(long text);
void setSubtitle(long text); void setSubtitle(long text);
long getTitle(); long getTitle();
long getMessage(); long getMessage();
long getWatermark(); long getWatermark();
long getInitialText(); long getInitialText();
long getSubtitle(); long getSubtitle();
int type = 0; int type = 0;
int keyboardMode = 0; int keyboardMode = 0;
int min_length = -1; int min_length = -1;
@ -72,7 +86,6 @@ private:
bool (*initialize)(char *) = NULL; bool (*initialize)(char *) = NULL;
long _renderingThreadId = 0; long _renderingThreadId = 0;
long _currentRenderingThreadId = 0;
JavaVM *_vm = nullptr; JavaVM *_vm = nullptr;
jobject _mainActivity = nullptr; jobject _mainActivity = nullptr;
jclass _mainActivityClass = nullptr; jclass _mainActivityClass = nullptr;

View File

@ -21,7 +21,6 @@
#include <chrono> #include <chrono>
#include <csignal> #include <csignal>
jmethodID _updateFrameTime;
JNIEnv *_rendererEnv = nullptr; JNIEnv *_rendererEnv = nullptr;
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint; std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint;
@ -91,13 +90,13 @@ Java_org_ryujinx_android_NativeHelpers_detachCurrentThread(
jvm->DetachCurrentThread(); jvm->DetachCurrentThread();
} }
long createSurface(long native_surface, long instance) long createSurface(long native_surface, long instance) {
{
auto nativeWindow = (ANativeWindow *) native_surface; auto nativeWindow = (ANativeWindow *) native_surface;
VkSurfaceKHR surface; VkSurfaceKHR surface;
auto vkInstance = (VkInstance) instance; auto vkInstance = (VkInstance) instance;
auto fpCreateAndroidSurfaceKHR = auto fpCreateAndroidSurfaceKHR =
reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(vkInstance, "vkCreateAndroidSurfaceKHR")); reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(vkInstance,
"vkCreateAndroidSurfaceKHR"));
if (!fpCreateAndroidSurfaceKHR) if (!fpCreateAndroidSurfaceKHR)
return -1; return -1;
VkAndroidSurfaceCreateInfoKHR info = {VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR}; VkAndroidSurfaceCreateInfoKHR info = {VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
@ -142,17 +141,11 @@ jstring createStringFromStdString(
} }
}
extern "C"
JNIEXPORT jlong JNICALL
Java_org_ryujinx_android_MainActivity_getRenderingThreadId(JNIEnv *env, jobject thiz) {
return _currentRenderingThreadId;
} }
extern "C" extern "C"
void setRenderingThread() { void setRenderingThread() {
auto currentId = pthread_self(); auto currentId = pthread_self();
_currentRenderingThreadId = currentId;
_renderingThreadId = currentId; _renderingThreadId = currentId;
_currentTimePoint = std::chrono::high_resolution_clock::now(); _currentTimePoint = std::chrono::high_resolution_clock::now();
@ -171,14 +164,15 @@ extern "C"
void onFrameEnd(double time) { void onFrameEnd(double time) {
auto env = getEnv(true); auto env = getEnv(true);
auto cl = env->FindClass("org/ryujinx/android/MainActivity"); auto cl = env->FindClass("org/ryujinx/android/MainActivity");
_updateFrameTime = env->GetStaticMethodID(cl, "updateRenderSessionPerformance", "(J)V"); jmethodID frameEnd = env->GetStaticMethodID(cl, "frameEnded", "(J)V");
auto now = std::chrono::high_resolution_clock::now(); auto now = std::chrono::high_resolution_clock::now();
auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>( auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(
now - _currentTimePoint).count(); now - _currentTimePoint).count();
env->CallStaticVoidMethod(cl, _updateFrameTime, env->CallStaticVoidMethod(cl, frameEnd,
nano); nano);
} }
extern "C" extern "C"
void setProgressInfo(char *info, float progressValue) { void setProgressInfo(char *info, float progressValue) {
progressInfo = std::string(info); progressInfo = std::string(info);
@ -206,7 +200,9 @@ void setCurrentTransform(long native_window, int transform){
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90; nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90;
break; break;
case 0x4: case 0x4:
nativeTransform = isInitialOrientationFlipped ? ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY : ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_180; nativeTransform = isInitialOrientationFlipped
? ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY
: ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_180;
break; break;
case 0x8: case 0x8:
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270; nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270;
@ -232,7 +228,8 @@ void setCurrentTransform(long native_window, int transform){
break; break;
} }
nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, static_cast<int32_t>(nativeTransform)); nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
static_cast<int32_t>(nativeTransform));
} }
extern "C" extern "C"

View File

@ -2,7 +2,6 @@ package org.ryujinx.android
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Build
import android.view.SurfaceHolder import android.view.SurfaceHolder
import android.view.SurfaceView import android.view.SurfaceView
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
@ -130,7 +129,8 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
if (c >= 1000) { if (c >= 1000) {
if (helper.getProgressValue() == -1f) if (helper.getProgressValue() == -1f)
progress?.apply { progress?.apply {
this.value = "Loading ${if(mainViewModel.isMiiEditorLaunched) "Mii Editor" else game!!.titleName}" this.value =
"Loading ${if (mainViewModel.isMiiEditorLaunched) "Mii Editor" else game!!.titleName}"
} }
c = 0 c = 0
mainViewModel.updateStats( mainViewModel.updateStats(
@ -144,26 +144,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
} }
private fun runGame() { private fun runGame() {
// 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)
val newthreadId = mainViewModel.activity.getRenderingThreadId()
if (threadId != newthreadId) {
mainViewModel.performanceManager?.closeCurrentRenderingSession()
}
threadId = newthreadId
if (threadId != 0L) {
mainViewModel.performanceManager?.initializeRenderingSession(threadId)
}
}
mainViewModel.performanceManager?.closeCurrentRenderingSession()
}
}
thread { thread {
mainViewModel.activity.uiHandler.listen() mainViewModel.activity.uiHandler.listen()

View File

@ -1,9 +1,7 @@
package org.ryujinx.android package org.ryujinx.android
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.view.KeyEvent import android.view.KeyEvent
@ -22,7 +20,6 @@ import org.ryujinx.android.ui.theme.RyujinxAndroidTheme
import org.ryujinx.android.viewmodels.MainViewModel import org.ryujinx.android.viewmodels.MainViewModel
import org.ryujinx.android.viewmodels.QuickSettings import org.ryujinx.android.viewmodels.QuickSettings
import org.ryujinx.android.views.MainView import org.ryujinx.android.views.MainView
import kotlin.math.abs
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
@ -31,8 +28,10 @@ class MainActivity : BaseActivity() {
private lateinit var motionSensorManager: MotionSensorManager private lateinit var motionSensorManager: MotionSensorManager
private var _isInit: Boolean = false private var _isInit: Boolean = false
var isGameRunning = false var isGameRunning = false
var isActive = false
var storageHelper: SimpleStorageHelper? = null var storageHelper: SimpleStorageHelper? = null
lateinit var uiHandler: UiHandler lateinit var uiHandler: UiHandler
companion object { companion object {
var mainViewModel: MainViewModel? = null var mainViewModel: MainViewModel? = null
var AppPath: String = "" var AppPath: String = ""
@ -40,15 +39,12 @@ class MainActivity : BaseActivity() {
val performanceMonitor = PerformanceMonitor() val performanceMonitor = PerformanceMonitor()
@JvmStatic @JvmStatic
fun updateRenderSessionPerformance(gameTime : Long) fun frameEnded(gameTime: Long) {
{ mainViewModel?.activity?.apply {
if(gameTime <= 0) if (isActive && QuickSettings(this).enablePerformanceMode) {
return mainViewModel?.performanceManager?.setTurboMode(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mainViewModel?.performanceManager?.updateRenderingSessionTime(gameTime)
} }
mainViewModel?.gameHost?.hideProgressIndicator() mainViewModel?.gameHost?.hideProgressIndicator()
} }
} }
@ -60,7 +56,6 @@ class MainActivity : BaseActivity() {
initVm() initVm()
} }
external fun getRenderingThreadId() : Long
private external fun initVm() private external fun initVm()
private fun initialize() { private fun initialize() {
@ -70,19 +65,45 @@ class MainActivity : BaseActivity() {
val appPath: String = AppPath val appPath: String = AppPath
var quickSettings = QuickSettings(this) var quickSettings = QuickSettings(this)
RyujinxNative.instance.loggingSetEnabled(LogLevel.Debug.ordinal, quickSettings.enableDebugLogs) RyujinxNative.instance.loggingSetEnabled(
RyujinxNative.instance.loggingSetEnabled(LogLevel.Info.ordinal, quickSettings.enableInfoLogs) LogLevel.Debug.ordinal,
RyujinxNative.instance.loggingSetEnabled(LogLevel.Stub.ordinal, quickSettings.enableStubLogs) quickSettings.enableDebugLogs
RyujinxNative.instance.loggingSetEnabled(LogLevel.Warning.ordinal, quickSettings.enableWarningLogs) )
RyujinxNative.instance.loggingSetEnabled(LogLevel.Error.ordinal, quickSettings.enableErrorLogs) RyujinxNative.instance.loggingSetEnabled(
RyujinxNative.instance.loggingSetEnabled(LogLevel.AccessLog.ordinal, quickSettings.enableAccessLogs) LogLevel.Info.ordinal,
RyujinxNative.instance.loggingSetEnabled(LogLevel.Guest.ordinal, quickSettings.enableGuestLogs) quickSettings.enableInfoLogs
RyujinxNative.instance.loggingSetEnabled(LogLevel.Trace.ordinal, quickSettings.enableTraceLogs) )
val success = RyujinxNative.instance.initialize(NativeHelpers.instance.storeStringJava(appPath)) RyujinxNative.instance.loggingSetEnabled(
LogLevel.Stub.ordinal,
quickSettings.enableStubLogs
)
RyujinxNative.instance.loggingSetEnabled(
LogLevel.Warning.ordinal,
quickSettings.enableWarningLogs
)
RyujinxNative.instance.loggingSetEnabled(
LogLevel.Error.ordinal,
quickSettings.enableErrorLogs
)
RyujinxNative.instance.loggingSetEnabled(
LogLevel.AccessLog.ordinal,
quickSettings.enableAccessLogs
)
RyujinxNative.instance.loggingSetEnabled(
LogLevel.Guest.ordinal,
quickSettings.enableGuestLogs
)
RyujinxNative.instance.loggingSetEnabled(
LogLevel.Trace.ordinal,
quickSettings.enableTraceLogs
)
val success =
RyujinxNative.instance.initialize(NativeHelpers.instance.storeStringJava(appPath))
uiHandler = UiHandler() uiHandler = UiHandler()
_isInit = success _isInit = success
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -99,7 +120,8 @@ class MainActivity : BaseActivity() {
initialize() initialize()
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
mainViewModel = MainViewModel(this) mainViewModel = MainViewModel(this)
@ -133,25 +155,6 @@ class MainActivity : BaseActivity() {
storageHelper?.onRestoreInstanceState(savedInstanceState) storageHelper?.onRestoreInstanceState(savedInstanceState)
} }
// Game Stuff
private fun force60HzRefreshRate(enable: Boolean) {
// Hack for MIUI devices since they don't support the standard Android APIs
try {
val setFpsIntent = Intent("com.miui.powerkeeper.SET_ACTIVITY_FPS")
setFpsIntent.putExtra("package_name", "org.ryujinx.android")
setFpsIntent.putExtra("isEnter", enable)
sendBroadcast(setFpsIntent)
} catch (_: Exception) {
}
if (enable)
display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) }
?.let { window.attributes.preferredDisplayModeId = it.modeId }
else
display?.supportedModes?.maxByOrNull { it.refreshRate }
?.let { window.attributes.preferredDisplayModeId = it.modeId }
}
fun setFullScreen(fullscreen: Boolean) { fun setFullScreen(fullscreen: Boolean) {
requestedOrientation = requestedOrientation =
if (fullscreen) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_FULL_USER if (fullscreen) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_FULL_USER
@ -190,20 +193,19 @@ class MainActivity : BaseActivity() {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
isActive = false
if (isGameRunning) { if (isGameRunning) {
NativeHelpers.instance.setTurboMode(false) mainViewModel?.performanceManager?.setTurboMode(false)
force60HzRefreshRate(false)
} }
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
isActive = true
if (isGameRunning) { if (isGameRunning) {
setFullScreen(true) setFullScreen(true)
NativeHelpers.instance.setTurboMode(true)
force60HzRefreshRate(true)
if (QuickSettings(this).enableMotion) if (QuickSettings(this).enableMotion)
motionSensorManager.register() motionSensorManager.register()
} }
@ -211,10 +213,10 @@ class MainActivity : BaseActivity() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
isActive = true
if (isGameRunning) { if (isGameRunning) {
NativeHelpers.instance.setTurboMode(false) mainViewModel?.performanceManager?.setTurboMode(false)
force60HzRefreshRate(false)
} }
motionSensorManager.unregister() motionSensorManager.unregister()

View File

@ -1,49 +1,31 @@
package org.ryujinx.android package org.ryujinx.android
import android.os.Build import android.content.Intent
import android.os.PerformanceHintManager import kotlin.math.abs
import androidx.annotation.RequiresApi
class PerformanceManager(private val performanceHintManager: PerformanceHintManager) { class PerformanceManager(private val activity: MainActivity) {
private var _isEnabled: Boolean = false companion object {
private var renderingSession: PerformanceHintManager.Session? = null fun force60HzRefreshRate(enable: Boolean, activity: MainActivity) {
private val DEFAULT_TARGET_NS = 16666666L // Hack for MIUI devices since they don't support the standard Android APIs
try {
@RequiresApi(Build.VERSION_CODES.S) val setFpsIntent = Intent("com.miui.powerkeeper.SET_ACTIVITY_FPS")
fun initializeRenderingSession(threadId : Long){ setFpsIntent.putExtra("package_name", "org.ryujinx.android")
if(!_isEnabled || renderingSession != null) setFpsIntent.putExtra("isEnter", enable)
return activity.sendBroadcast(setFpsIntent)
} catch (_: Exception) {
val threads = IntArray(1)
threads[0] = threadId.toInt()
renderingSession = performanceHintManager.createHintSession(threads, DEFAULT_TARGET_NS)
} }
@RequiresApi(Build.VERSION_CODES.S) if (enable)
fun closeCurrentRenderingSession() { activity.display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) }
if (_isEnabled) ?.let { activity.window.attributes.preferredDisplayModeId = it.modeId }
renderingSession?.apply { else
renderingSession = null activity.display?.supportedModes?.maxByOrNull { it.refreshRate }
this.close() ?.let { activity.window.attributes.preferredDisplayModeId = it.modeId }
} }
} }
fun enable(){ fun setTurboMode(enable: Boolean) {
_isEnabled = true NativeHelpers.instance.setTurboMode(enable)
} force60HzRefreshRate(enable, activity)
@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)
}
} }
} }

View File

@ -1,9 +1,6 @@
package org.ryujinx.android.viewmodels package org.ryujinx.android.viewmodels
import android.annotation.SuppressLint 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 com.anggrayudi.storage.extension.launchOnUiThread import com.anggrayudi.storage.extension.launchOnUiThread
@ -54,11 +51,7 @@ class MainViewModel(val activity: MainActivity) {
var homeViewModel: HomeViewModel = HomeViewModel(activity, this) var homeViewModel: HomeViewModel = HomeViewModel(activity, this)
init { init {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { performanceManager = PerformanceManager(activity)
val hintService =
activity.getSystemService(Context.PERFORMANCE_HINT_SERVICE) as PerformanceHintManager
performanceManager = PerformanceManager(hintService)
}
} }
fun closeGame() { fun closeGame() {

View File

@ -18,6 +18,7 @@ class QuickSettings(val activity: Activity) {
var isGrid: Boolean var isGrid: Boolean
var useSwitchLayout: Boolean var useSwitchLayout: Boolean
var enableMotion: Boolean var enableMotion: Boolean
var enablePerformanceMode: Boolean
// Logs // Logs
var enableDebugLogs: Boolean var enableDebugLogs: Boolean
@ -29,7 +30,8 @@ class QuickSettings(val activity: Activity) {
var enableAccessLogs: Boolean var enableAccessLogs: Boolean
var enableTraceLogs: Boolean var enableTraceLogs: Boolean
private var sharedPref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity) private var sharedPref: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(activity)
init { init {
isHostMapped = sharedPref.getBoolean("isHostMapped", true) isHostMapped = sharedPref.getBoolean("isHostMapped", true)
@ -45,6 +47,7 @@ class QuickSettings(val activity: Activity) {
isGrid = sharedPref.getBoolean("isGrid", true) isGrid = sharedPref.getBoolean("isGrid", true)
useSwitchLayout = sharedPref.getBoolean("useSwitchLayout", true) useSwitchLayout = sharedPref.getBoolean("useSwitchLayout", true)
enableMotion = sharedPref.getBoolean("enableMotion", true) enableMotion = sharedPref.getBoolean("enableMotion", true)
enablePerformanceMode = sharedPref.getBoolean("enablePerformanceMode", true)
enableDebugLogs = sharedPref.getBoolean("enableDebugLogs", false) enableDebugLogs = sharedPref.getBoolean("enableDebugLogs", false)
enableStubLogs = sharedPref.getBoolean("enableStubLogs", false) enableStubLogs = sharedPref.getBoolean("enableStubLogs", false)
@ -72,6 +75,7 @@ class QuickSettings(val activity: Activity) {
editor.putBoolean("isGrid", isGrid) editor.putBoolean("isGrid", isGrid)
editor.putBoolean("useSwitchLayout", useSwitchLayout) editor.putBoolean("useSwitchLayout", useSwitchLayout)
editor.putBoolean("enableMotion", enableMotion) editor.putBoolean("enableMotion", enableMotion)
editor.putBoolean("enablePerformanceMode", enablePerformanceMode)
editor.putBoolean("enableDebugLogs", enableDebugLogs) editor.putBoolean("enableDebugLogs", enableDebugLogs)
editor.putBoolean("enableStubLogs", enableStubLogs) editor.putBoolean("enableStubLogs", enableStubLogs)

View File

@ -56,6 +56,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
isGrid: MutableState<Boolean>, isGrid: MutableState<Boolean>,
useSwitchLayout: MutableState<Boolean>, useSwitchLayout: MutableState<Boolean>,
enableMotion: MutableState<Boolean>, enableMotion: MutableState<Boolean>,
enablePerformanceMode: MutableState<Boolean>,
enableDebugLogs: MutableState<Boolean>, enableDebugLogs: MutableState<Boolean>,
enableStubLogs: MutableState<Boolean>, enableStubLogs: MutableState<Boolean>,
enableInfoLogs: MutableState<Boolean>, enableInfoLogs: MutableState<Boolean>,
@ -80,6 +81,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
isGrid.value = sharedPref.getBoolean("isGrid", true) isGrid.value = sharedPref.getBoolean("isGrid", true)
useSwitchLayout.value = sharedPref.getBoolean("useSwitchLayout", true) useSwitchLayout.value = sharedPref.getBoolean("useSwitchLayout", true)
enableMotion.value = sharedPref.getBoolean("enableMotion", true) enableMotion.value = sharedPref.getBoolean("enableMotion", true)
enablePerformanceMode.value = sharedPref.getBoolean("enablePerformanceMode", false)
enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false) enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false)
enableStubLogs.value = sharedPref.getBoolean("enableStubLogs", false) enableStubLogs.value = sharedPref.getBoolean("enableStubLogs", false)
@ -105,6 +107,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
isGrid: MutableState<Boolean>, isGrid: MutableState<Boolean>,
useSwitchLayout: MutableState<Boolean>, useSwitchLayout: MutableState<Boolean>,
enableMotion: MutableState<Boolean>, enableMotion: MutableState<Boolean>,
enablePerformanceMode: MutableState<Boolean>,
enableDebugLogs: MutableState<Boolean>, enableDebugLogs: MutableState<Boolean>,
enableStubLogs: MutableState<Boolean>, enableStubLogs: MutableState<Boolean>,
enableInfoLogs: MutableState<Boolean>, enableInfoLogs: MutableState<Boolean>,
@ -129,6 +132,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
editor.putBoolean("isGrid", isGrid.value) editor.putBoolean("isGrid", isGrid.value)
editor.putBoolean("useSwitchLayout", useSwitchLayout.value) editor.putBoolean("useSwitchLayout", useSwitchLayout.value)
editor.putBoolean("enableMotion", enableMotion.value) editor.putBoolean("enableMotion", enableMotion.value)
editor.putBoolean("enablePerformanceMode", enablePerformanceMode.value)
editor.putBoolean("enableDebugLogs", enableDebugLogs.value) editor.putBoolean("enableDebugLogs", enableDebugLogs.value)
editor.putBoolean("enableStubLogs", enableStubLogs.value) editor.putBoolean("enableStubLogs", enableStubLogs.value)

View File

@ -57,6 +57,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.extension
import org.ryujinx.android.Helpers import org.ryujinx.android.Helpers
@ -122,6 +123,7 @@ class SettingViews {
val isGrid = remember { mutableStateOf(true) } val isGrid = remember { mutableStateOf(true) }
val useSwitchLayout = remember { mutableStateOf(true) } val useSwitchLayout = remember { mutableStateOf(true) }
val enableMotion = remember { mutableStateOf(true) } val enableMotion = remember { mutableStateOf(true) }
val enablePerformanceMode = remember { mutableStateOf(true) }
val enableDebugLogs = remember { mutableStateOf(true) } val enableDebugLogs = remember { mutableStateOf(true) }
val enableStubLogs = remember { mutableStateOf(true) } val enableStubLogs = remember { mutableStateOf(true) }
@ -144,6 +146,7 @@ class SettingViews {
isGrid, isGrid,
useSwitchLayout, useSwitchLayout,
enableMotion, enableMotion,
enablePerformanceMode,
enableDebugLogs, enableDebugLogs,
enableStubLogs, enableStubLogs,
enableInfoLogs, enableInfoLogs,
@ -177,6 +180,7 @@ class SettingViews {
isGrid, isGrid,
useSwitchLayout, useSwitchLayout,
enableMotion, enableMotion,
enablePerformanceMode,
enableDebugLogs, enableDebugLogs,
enableStubLogs, enableStubLogs,
enableInfoLogs, enableInfoLogs,
@ -192,9 +196,11 @@ class SettingViews {
} }
}) })
}) { contentPadding -> }) { contentPadding ->
Column(modifier = Modifier Column(
modifier = Modifier
.padding(contentPadding) .padding(contentPadding)
.verticalScroll(rememberScrollState())) { .verticalScroll(rememberScrollState())
) {
ExpandableView(onCardArrowClick = { }, title = "App") { ExpandableView(onCardArrowClick = { }, title = "App") {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
Row( Row(
@ -327,7 +333,8 @@ class SettingViews {
Text(text = "Select a zip or XCI file to install from.") Text(text = "Select a zip or XCI file to install from.")
Row( Row(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth() modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp) .padding(top = 4.dp)
) { ) {
Button(onClick = { Button(onClick = {
@ -350,7 +357,8 @@ class SettingViews {
Text(text = "Firmware ${settingsViewModel.selectedFirmwareVersion} will be installed. Do you want to continue?") Text(text = "Firmware ${settingsViewModel.selectedFirmwareVersion} will be installed. Do you want to continue?")
Row( Row(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth() modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp) .padding(top = 4.dp)
) { ) {
Button(onClick = { Button(onClick = {
@ -360,7 +368,9 @@ class SettingViews {
if (firmwareInstallState.value == FirmwareInstallState.None) { if (firmwareInstallState.value == FirmwareInstallState.None) {
showFirwmareDialog.value = false showFirwmareDialog.value = false
settingsViewModel.clearFirmwareSelection(firmwareInstallState) settingsViewModel.clearFirmwareSelection(
firmwareInstallState
)
} }
}, modifier = Modifier.padding(horizontal = 8.dp)) { }, modifier = Modifier.padding(horizontal = 8.dp)) {
Text(text = "Yes") Text(text = "Yes")
@ -376,34 +386,32 @@ class SettingViews {
} }
} else if (firmwareInstallState.value == FirmwareInstallState.Install) { } else if (firmwareInstallState.value == FirmwareInstallState.Install) {
Text(text = "Installing Firmware ${settingsViewModel.selectedFirmwareVersion}...") Text(text = "Installing Firmware ${settingsViewModel.selectedFirmwareVersion}...")
LinearProgressIndicator(modifier = Modifier LinearProgressIndicator(
.padding(top = 4.dp)) modifier = Modifier
.padding(top = 4.dp)
)
} else if (firmwareInstallState.value == FirmwareInstallState.Verifying) { } else if (firmwareInstallState.value == FirmwareInstallState.Verifying) {
Text(text = "Verifying selected file...") Text(text = "Verifying selected file...")
LinearProgressIndicator(modifier = Modifier LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) )
} } else if (firmwareInstallState.value == FirmwareInstallState.Done) {
else if (firmwareInstallState.value == FirmwareInstallState.Done) {
Text(text = "Installed Firmware ${settingsViewModel.selectedFirmwareVersion}") Text(text = "Installed Firmware ${settingsViewModel.selectedFirmwareVersion}")
firmwareVersion.value = mainViewModel.firmwareVersion firmwareVersion.value = mainViewModel.firmwareVersion
} } else if (firmwareInstallState.value == FirmwareInstallState.Cancelled) {
else if(firmwareInstallState.value == FirmwareInstallState.Cancelled){
val file = settingsViewModel.selectedFirmwareFile val file = settingsViewModel.selectedFirmwareFile
if (file != null) { if (file != null) {
if (file.extension == "xci" || file.extension == "zip") { if (file.extension == "xci" || file.extension == "zip") {
if (settingsViewModel.selectedFirmwareVersion.isEmpty()) { if (settingsViewModel.selectedFirmwareVersion.isEmpty()) {
Text(text = "Unable to find version in selected file") Text(text = "Unable to find version in selected file")
} } else {
else {
Text(text = "Unknown Error has occurred. Please check logs") Text(text = "Unknown Error has occurred. Please check logs")
} }
} } else {
else {
Text(text = "File type is not supported") Text(text = "File type is not supported")
} }
} } else {
else {
Text(text = "File type is not supported") Text(text = "File type is not supported")
} }
} }
@ -503,6 +511,32 @@ class SettingViews {
ignoreMissingServices.value = !ignoreMissingServices.value ignoreMissingServices.value = !ignoreMissingServices.value
}) })
} }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.align(Alignment.CenterVertically)
) {
Text(
text = "Enable Performance Mode",
)
Text(
text = "Forces CPU and GPU to run at max clocks if available.",
fontSize = 12.sp
)
Text(
text = "OS power settings may override this.",
fontSize = 12.sp
)
}
Switch(checked = enablePerformanceMode.value, onCheckedChange = {
enablePerformanceMode.value = !enablePerformanceMode.value
})
}
val isImporting = remember { val isImporting = remember {
mutableStateOf(false) mutableStateOf(false)
} }
@ -1041,6 +1075,7 @@ class SettingViews {
isGrid, isGrid,
useSwitchLayout, useSwitchLayout,
enableMotion, enableMotion,
enablePerformanceMode,
enableDebugLogs, enableDebugLogs,
enableStubLogs, enableStubLogs,
enableInfoLogs, enableInfoLogs,