From 06a7c0217fe87546d85f70000fe2b7fda8e5993c Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Sun, 25 Feb 2024 22:06:54 +0000 Subject: [PATCH] fix audio rebase remove redundant sdk performance session usage remove debug code and formatting --- src/LibRyujinx/Android/AndroidUiHandler.cs | 14 +- .../Android/Audio/Oboe/OboeAudioBuffer.cs | 18 -- .../Audio/Oboe/OboeHardwareDeviceDriver.cs | 105 ---------- .../Audio/Oboe/OboeHardwareDeviceSession.cs | 189 ------------------ .../Android/Audio/Oboe/OboeInterop.cs | 41 ---- src/LibRyujinx/Android/JniExportedMethods.cs | 3 - src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h | 21 +- .../app/src/main/cpp/ryujinx.cpp | 145 +++++++------- .../main/java/org/ryujinx/android/GameHost.kt | 24 +-- .../java/org/ryujinx/android/MainActivity.kt | 108 +++++----- .../org/ryujinx/android/PerformanceManager.kt | 64 +++--- .../android/viewmodels/MainViewModel.kt | 47 ++--- .../android/viewmodels/QuickSettings.kt | 16 +- .../android/viewmodels/SettingsViewModel.kt | 8 +- .../org/ryujinx/android/views/SettingViews.kt | 89 ++++++--- 15 files changed, 273 insertions(+), 619 deletions(-) delete mode 100644 src/LibRyujinx/Android/Audio/Oboe/OboeAudioBuffer.cs delete mode 100644 src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs delete mode 100644 src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs delete mode 100644 src/LibRyujinx/Android/Audio/Oboe/OboeInterop.cs diff --git a/src/LibRyujinx/Android/AndroidUiHandler.cs b/src/LibRyujinx/Android/AndroidUiHandler.cs index 945feb0f4..197eda652 100644 --- a/src/LibRyujinx/Android/AndroidUiHandler.cs +++ b/src/LibRyujinx/Android/AndroidUiHandler.cs @@ -3,7 +3,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; -using Ryujinx.HLE.Ui; +using Ryujinx.HLE.UI; using System; using System.Collections.Generic; using System.Linq; @@ -13,17 +13,17 @@ using System.Threading.Tasks; namespace LibRyujinx.Android { - internal class AndroidUiHandler : IHostUiHandler, IDisposable + internal class AndroidUIHandler : IHostUIHandler, IDisposable { - public IHostUiTheme HostUiTheme => throw new NotImplementedException(); - public ManualResetEvent _waitEvent; public ManualResetEvent _responseEvent; private bool _isDisposed; private bool _isOkPressed; private long _input; - public AndroidUiHandler() + public IHostUITheme HostUITheme => throw new NotImplementedException(); + + public AndroidUIHandler() { _waitEvent = new ManualResetEvent(false); _responseEvent = new ManualResetEvent(false); @@ -47,7 +47,7 @@ namespace LibRyujinx.Android 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.setUiHandlerMessage(LibRyujinx.storeString(args.HeaderText ?? "")); @@ -81,7 +81,7 @@ namespace LibRyujinx.Android 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}"; diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeAudioBuffer.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeAudioBuffer.cs deleted file mode 100644 index aad74115f..000000000 --- a/src/LibRyujinx/Android/Audio/Oboe/OboeAudioBuffer.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs deleted file mode 100644 index 7377b171e..000000000 --- a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs +++ /dev/null @@ -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 _sessions; - - public OboeHardwareDeviceDriver() - { - _updateRequiredEvent = new ManualResetEvent(false); - _pauseEvent = new ManualResetEvent(true); - _sessions = new ConcurrentDictionary(); - } - - 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; - } - } -} \ No newline at end of file diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs deleted file mode 100644 index 76b41d802..000000000 --- a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs +++ /dev/null @@ -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 _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(); - _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; - } - } - } -} diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeInterop.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeInterop.cs deleted file mode 100644 index 79d9ca43e..000000000 --- a/src/LibRyujinx/Android/Audio/Oboe/OboeInterop.cs +++ /dev/null @@ -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); - } -} diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs index 618da6a41..7f9555674 100644 --- a/src/LibRyujinx/Android/JniExportedMethods.cs +++ b/src/LibRyujinx/Android/JniExportedMethods.cs @@ -3,7 +3,6 @@ using LibRyujinx.Jni.Pointers; using LibRyujinx.Jni.Primitives; using LibRyujinx.Jni.References; using LibRyujinx.Jni.Values; -using LibRyujinx.Shared.Audio.Oboe; using Rxmxnx.PInvoke; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Common; @@ -233,8 +232,6 @@ namespace LibRyujinx Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var list = GetDlcContentList(GetStoredString(pathPtr), (ulong)(long)titleId); - debug_break(4); - return CreateStringArray(jEnv, list); } diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h b/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h index 951d36385..0a22ddd9d 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h +++ b/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h @@ -4,6 +4,7 @@ #ifndef RYUJINXNATIVE_RYUIJNX_H #define RYUJINXNATIVE_RYUIJNX_H + #include #include #include @@ -36,25 +37,38 @@ #define LoadLib(a) dlopen(a, RTLD_NOW) -void* _ryujinxNative = NULL; +void *_ryujinxNative = NULL; class UiHandler { public: void setTitle(long storedTitle); + void setMessage(long storedMessage); + void setWatermark(long wm); + void setType(int t); + void setMode(int t); + void setMinLength(int t); + void setMaxLength(int t); + void setInitialText(long text); + void setSubtitle(long text); long getTitle(); + long getMessage(); + long getWatermark(); + long getInitialText(); + long getSubtitle(); + int type = 0; int keyboardMode = 0; int min_length = -1; @@ -69,11 +83,10 @@ private: }; // Ryujinx imported functions -bool (*initialize)(char*) = NULL; +bool (*initialize)(char *) = NULL; long _renderingThreadId = 0; -long _currentRenderingThreadId = 0; -JavaVM* _vm = nullptr; +JavaVM *_vm = nullptr; jobject _mainActivity = nullptr; jclass _mainActivityClass = nullptr; string_helper str_helper = string_helper(); diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp index da87b8051..e4b4cef78 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp +++ b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp @@ -21,21 +21,20 @@ #include #include -jmethodID _updateFrameTime; -JNIEnv* _rendererEnv = nullptr; +JNIEnv *_rendererEnv = nullptr; std::chrono::time_point _currentTimePoint; std::string progressInfo = ""; float progress = -1; -JNIEnv* getEnv(bool isRenderer){ - JNIEnv* env; - if(isRenderer){ +JNIEnv *getEnv(bool isRenderer) { + JNIEnv *env; + if (isRenderer) { env = _rendererEnv; } - if(env != nullptr) + if (env != nullptr) return env; auto result = _vm->AttachCurrentThread(&env, NULL); @@ -43,82 +42,82 @@ JNIEnv* getEnv(bool isRenderer){ return env; } -void detachEnv(){ +void detachEnv() { auto result = _vm->DetachCurrentThread(); } extern "C" { - JNIEXPORT jlong JNICALL - Java_org_ryujinx_android_NativeHelpers_getNativeWindow( - JNIEnv *env, - jobject instance, - jobject surface) { - auto nativeWindow = ANativeWindow_fromSurface(env, surface); - return nativeWindow == NULL ? -1 : (jlong) nativeWindow; - } +JNIEXPORT jlong JNICALL +Java_org_ryujinx_android_NativeHelpers_getNativeWindow( + JNIEnv *env, + jobject instance, + jobject surface) { + auto nativeWindow = ANativeWindow_fromSurface(env, surface); + return nativeWindow == NULL ? -1 : (jlong) nativeWindow; +} - JNIEXPORT void JNICALL - Java_org_ryujinx_android_NativeHelpers_releaseNativeWindow( - JNIEnv *env, - jobject instance, - jlong window) { - auto nativeWindow = (ANativeWindow *) window; +JNIEXPORT void JNICALL +Java_org_ryujinx_android_NativeHelpers_releaseNativeWindow( + JNIEnv *env, + jobject instance, + jlong window) { + auto nativeWindow = (ANativeWindow *) window; - if (nativeWindow != NULL) - ANativeWindow_release(nativeWindow); - } + if (nativeWindow != NULL) + ANativeWindow_release(nativeWindow); +} JNIEXPORT void JNICALL Java_org_ryujinx_android_NativeHelpers_attachCurrentThread( JNIEnv *env, jobject instance) { - JavaVM* jvm = NULL; - env->GetJavaVM(&jvm); + JavaVM *jvm = NULL; + env->GetJavaVM(&jvm); - if(jvm != NULL) - jvm->AttachCurrentThread(&env, NULL); + if (jvm != NULL) + jvm->AttachCurrentThread(&env, NULL); } JNIEXPORT void JNICALL Java_org_ryujinx_android_NativeHelpers_detachCurrentThread( JNIEnv *env, jobject instance) { - JavaVM* jvm = NULL; + JavaVM *jvm = NULL; env->GetJavaVM(&jvm); - if(jvm != NULL) + if (jvm != NULL) jvm->DetachCurrentThread(); } -long createSurface(long native_surface, long instance) -{ +long createSurface(long native_surface, long instance) { auto nativeWindow = (ANativeWindow *) native_surface; VkSurfaceKHR surface; - auto vkInstance = (VkInstance)instance; + auto vkInstance = (VkInstance) instance; auto fpCreateAndroidSurfaceKHR = - reinterpret_cast(vkGetInstanceProcAddr(vkInstance, "vkCreateAndroidSurfaceKHR")); + reinterpret_cast(vkGetInstanceProcAddr(vkInstance, + "vkCreateAndroidSurfaceKHR")); if (!fpCreateAndroidSurfaceKHR) return -1; - VkAndroidSurfaceCreateInfoKHR info = { VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR }; + VkAndroidSurfaceCreateInfoKHR info = {VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR}; info.window = nativeWindow; VK_CHECK(fpCreateAndroidSurfaceKHR(vkInstance, &info, nullptr, &surface)); - return (long)surface; + return (long) surface; } JNIEXPORT jlong JNICALL Java_org_ryujinx_android_NativeHelpers_getCreateSurfacePtr( JNIEnv *env, jobject instance) { - return (jlong)createSurface; + return (jlong) createSurface; } -char* getStringPointer( +char *getStringPointer( JNIEnv *env, jstring jS) { const char *cparam = env->GetStringUTFChars(jS, 0); auto len = env->GetStringUTFLength(jS); - char* s= new char[len]; + char *s = new char[len]; strcpy(s, cparam); env->ReleaseStringUTFChars(jS, cparam); @@ -127,7 +126,7 @@ char* getStringPointer( jstring createString( JNIEnv *env, - char* ch) { + char *ch) { auto str = env->NewStringUTF(ch); return str; @@ -144,15 +143,9 @@ jstring createStringFromStdString( } extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_MainActivity_getRenderingThreadId(JNIEnv *env, jobject thiz) { - return _currentRenderingThreadId; -} -extern "C" -void setRenderingThread(){ +void setRenderingThread() { auto currentId = pthread_self(); - _currentRenderingThreadId = currentId; _renderingThreadId = currentId; _currentTimePoint = std::chrono::high_resolution_clock::now(); @@ -160,7 +153,7 @@ void setRenderingThread(){ extern "C" JNIEXPORT void JNICALL Java_org_ryujinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) { - JavaVM* vm = nullptr; + JavaVM *vm = nullptr; auto success = env->GetJavaVM(&vm); _vm = vm; _mainActivity = thiz; @@ -171,25 +164,26 @@ 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"); + jmethodID frameEnd = env->GetStaticMethodID(cl, "frameEnded", "(J)V"); auto now = std::chrono::high_resolution_clock::now(); auto nano = std::chrono::duration_cast( now - _currentTimePoint).count(); - env->CallStaticVoidMethod(cl, _updateFrameTime, + env->CallStaticVoidMethod(cl, frameEnd, nano); } + extern "C" -void setProgressInfo(char* info, float progressValue) { - progressInfo = std::string (info); +void setProgressInfo(char *info, float progressValue) { + progressInfo = std::string(info); progress = progressValue; } bool isInitialOrientationFlipped = true; extern "C" -void setCurrentTransform(long native_window, int transform){ - if(native_window == 0 || native_window == -1) +void setCurrentTransform(long native_window, int transform) { + if (native_window == 0 || native_window == -1) return; auto nativeWindow = (ANativeWindow *) native_window; @@ -203,10 +197,12 @@ void setCurrentTransform(long native_window, int transform){ nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY; break; case 0x2: - nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90; + nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90; break; 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; case 0x8: nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270; @@ -232,7 +228,8 @@ void setCurrentTransform(long native_window, int transform){ break; } - nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, static_cast(nativeTransform)); + nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + static_cast(nativeTransform)); } extern "C" @@ -254,19 +251,19 @@ Java_org_ryujinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz, driverName, nullptr, nullptr - ); + ); delete libPath; delete privateAppsPath; delete driverName; - return (jlong)handle; + return (jlong) handle; } extern "C" -void debug_break(int code){ - if(code >= 3) - int r = 0; +void debug_break(int code) { + if (code >= 3) + int r = 0; } extern "C" @@ -278,7 +275,7 @@ Java_org_ryujinx_android_NativeHelpers_setTurboMode(JNIEnv *env, jobject thiz, j extern "C" JNIEXPORT jint JNICALL Java_org_ryujinx_android_NativeHelpers_getMaxSwapInterval(JNIEnv *env, jobject thiz, - jlong native_window) { + jlong native_window) { auto nativeWindow = (ANativeWindow *) native_window; return nativeWindow->maxSwapInterval; @@ -315,14 +312,14 @@ Java_org_ryujinx_android_NativeHelpers_getProgressInfo(JNIEnv *env, jobject thiz } extern "C" -long storeString(char* str){ +long storeString(char *str) { return str_helper.store_cstring(str); } extern "C" -const char* getString(long id){ +const char *getString(long id) { auto str = str_helper.get_stored(id); - auto cstr = (char*)::malloc(str.length() + 1); + auto cstr = (char *) ::malloc(str.length() + 1); ::strcpy(cstr, str.c_str()); return cstr; } @@ -381,7 +378,7 @@ Java_org_ryujinx_android_NativeHelpers_setIsInitialOrientationFlipped(JNIEnv *en extern "C" JNIEXPORT jint JNICALL Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestType(JNIEnv *env, jobject thiz) { - return ui_handler.type; + return ui_handler.type; } extern "C" JNIEXPORT jlong JNICALL @@ -396,7 +393,7 @@ Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestMessage(JNIEnv *env, j void UiHandler::setTitle(long storedTitle) { - if(title != -1){ + if (title != -1) { str_helper.get_stored(title); title = -1; } @@ -405,7 +402,7 @@ void UiHandler::setTitle(long storedTitle) { } void UiHandler::setMessage(long storedMessage) { - if(message != -1){ + if (message != -1) { str_helper.get_stored(message); message = -1; } @@ -430,7 +427,7 @@ long UiHandler::getMessage() { } void UiHandler::setWatermark(long wm) { - if(watermark != -1){ + if (watermark != -1) { str_helper.get_stored(watermark); watermark = -1; } @@ -453,7 +450,7 @@ long UiHandler::getWatermark() { } void UiHandler::setInitialText(long text) { - if(initialText != -1){ + if (initialText != -1) { str_helper.get_stored(watermark); initialText = -1; } @@ -462,7 +459,7 @@ void UiHandler::setInitialText(long text) { } void UiHandler::setSubtitle(long text) { - if(subtitle != -1){ + if (subtitle != -1) { str_helper.get_stored(subtitle); subtitle = -1; } @@ -489,12 +486,12 @@ void UiHandler::setMode(int t) { extern "C" JNIEXPORT jint JNICALL Java_org_ryujinx_android_NativeHelpers_getUiHandlerMinLength(JNIEnv *env, jobject thiz) { - return ui_handler.min_length; + return ui_handler.min_length; } extern "C" JNIEXPORT jint JNICALL Java_org_ryujinx_android_NativeHelpers_getUiHandlerMaxLength(JNIEnv *env, jobject thiz) { - return ui_handler.max_length; + return ui_handler.max_length; } extern "C" diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt index 4b1ed3d56..31f8adb3b 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt @@ -2,7 +2,6 @@ package org.ryujinx.android import android.annotation.SuppressLint import android.content.Context -import android.os.Build import android.view.SurfaceHolder import android.view.SurfaceView import androidx.compose.runtime.MutableState @@ -130,7 +129,8 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su if (c >= 1000) { if (helper.getProgressValue() == -1f) 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 mainViewModel.updateStats( @@ -144,26 +144,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su } 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 { mainViewModel.activity.uiHandler.listen() diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt index a55d4e34d..a91fd140f 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt @@ -1,9 +1,7 @@ package org.ryujinx.android import android.annotation.SuppressLint -import android.content.Intent import android.content.pm.ActivityInfo -import android.os.Build import android.os.Bundle import android.os.Environment 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.QuickSettings import org.ryujinx.android.views.MainView -import kotlin.math.abs class MainActivity : BaseActivity() { @@ -31,24 +28,23 @@ class MainActivity : BaseActivity() { private lateinit var motionSensorManager: MotionSensorManager private var _isInit: Boolean = false var isGameRunning = false + var isActive = false var storageHelper: SimpleStorageHelper? = null lateinit var uiHandler: UiHandler + companion object { var mainViewModel: MainViewModel? = null - var AppPath : String = "" + var AppPath: String = "" var StorageHelper: SimpleStorageHelper? = null val performanceMonitor = PerformanceMonitor() @JvmStatic - fun updateRenderSessionPerformance(gameTime : Long) - { - if(gameTime <= 0) - return - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - mainViewModel?.performanceManager?.updateRenderingSessionTime(gameTime) + fun frameEnded(gameTime: Long) { + mainViewModel?.activity?.apply { + if (isActive && QuickSettings(this).enablePerformanceMode) { + mainViewModel?.performanceManager?.setTurboMode(true); + } } - mainViewModel?.gameHost?.hideProgressIndicator() } } @@ -60,7 +56,6 @@ class MainActivity : BaseActivity() { initVm() } - external fun getRenderingThreadId() : Long private external fun initVm() private fun initialize() { @@ -70,26 +65,52 @@ class MainActivity : BaseActivity() { val appPath: String = AppPath var quickSettings = QuickSettings(this) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Debug.ordinal, quickSettings.enableDebugLogs) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Info.ordinal, quickSettings.enableInfoLogs) - 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)) + RyujinxNative.instance.loggingSetEnabled( + LogLevel.Debug.ordinal, + quickSettings.enableDebugLogs + ) + RyujinxNative.instance.loggingSetEnabled( + LogLevel.Info.ordinal, + quickSettings.enableInfoLogs + ) + 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() _isInit = success } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) motionSensorManager = MotionSensorManager(this) Thread.setDefaultUncaughtExceptionHandler(crashHandler) - if( + if ( !Environment.isExternalStorageManager() ) { storageHelper?.storage?.requestFullStorageAccess() @@ -99,8 +120,9 @@ class MainActivity : BaseActivity() { initialize() - window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - WindowCompat.setDecorFitsSystemWindows(window,false) + window.attributes.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + WindowCompat.setDecorFitsSystemWindows(window, false) mainViewModel = MainViewModel(this) mainViewModel!!.physicalControllerManager = physicalControllerManager @@ -133,25 +155,6 @@ class MainActivity : BaseActivity() { 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) { requestedOrientation = if (fullscreen) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_FULL_USER @@ -190,20 +193,19 @@ class MainActivity : BaseActivity() { override fun onStop() { super.onStop() + isActive = false - if(isGameRunning) { - NativeHelpers.instance.setTurboMode(false) - force60HzRefreshRate(false) + if (isGameRunning) { + mainViewModel?.performanceManager?.setTurboMode(false) } } override fun onResume() { super.onResume() + isActive = true - if(isGameRunning) { + if (isGameRunning) { setFullScreen(true) - NativeHelpers.instance.setTurboMode(true) - force60HzRefreshRate(true) if (QuickSettings(this).enableMotion) motionSensorManager.register() } @@ -211,10 +213,10 @@ class MainActivity : BaseActivity() { override fun onPause() { super.onPause() + isActive = true - if(isGameRunning) { - NativeHelpers.instance.setTurboMode(false) - force60HzRefreshRate(false) + if (isGameRunning) { + mainViewModel?.performanceManager?.setTurboMode(false) } motionSensorManager.unregister() diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceManager.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceManager.kt index 0b1cdbfdd..daa2acc74 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceManager.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceManager.kt @@ -1,49 +1,31 @@ package org.ryujinx.android -import android.os.Build -import android.os.PerformanceHintManager -import androidx.annotation.RequiresApi +import android.content.Intent +import kotlin.math.abs -class PerformanceManager(private val performanceHintManager: PerformanceHintManager) { - private var _isEnabled: Boolean = false - private var renderingSession: PerformanceHintManager.Session? = null - private val DEFAULT_TARGET_NS = 16666666L - - @RequiresApi(Build.VERSION_CODES.S) - fun initializeRenderingSession(threadId : Long){ - if(!_isEnabled || renderingSession != null) - return - - val 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() +class PerformanceManager(private val activity: MainActivity) { + companion object { + fun force60HzRefreshRate(enable: Boolean, activity: MainActivity) { + // 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) + activity.sendBroadcast(setFpsIntent) + } catch (_: Exception) { } - } - 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) + if (enable) + activity.display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) } + ?.let { activity.window.attributes.preferredDisplayModeId = it.modeId } + else + activity.display?.supportedModes?.maxByOrNull { it.refreshRate } + ?.let { activity.window.attributes.preferredDisplayModeId = it.modeId } } } + + fun setTurboMode(enable: Boolean) { + NativeHelpers.instance.setTurboMode(enable) + force60HzRefreshRate(enable, activity) + } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt index bf538287c..fad3402ce 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt @@ -1,9 +1,6 @@ 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.navigation.NavHostController import com.anggrayudi.storage.extension.launchOnUiThread @@ -49,16 +46,12 @@ class MainViewModel(val activity: MainActivity) { field = value field?.setProgressStates(showLoading, progressValue, progress) } - var navController : NavHostController? = null + var navController: NavHostController? = null var homeViewModel: HomeViewModel = HomeViewModel(activity, this) init { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val hintService = - activity.getSystemService(Context.PERFORMANCE_HINT_SERVICE) as PerformanceHintManager - performanceManager = PerformanceManager(hintService) - } + performanceManager = PerformanceManager(activity) } fun closeGame() { @@ -70,14 +63,14 @@ class MainViewModel(val activity: MainActivity) { motionSensorManager?.setControllerId(-1) } - fun refreshFirmwareVersion(){ + fun refreshFirmwareVersion() { var handle = RyujinxNative.instance.deviceGetInstalledFirmwareVersion() - if(handle != -1L) { + if (handle != -1L) { firmwareVersion = NativeHelpers.instance.getStringJava(handle) } } - fun loadGame(game:GameModel) : Boolean { + fun loadGame(game: GameModel): Boolean { val nativeRyujinx = RyujinxNative.instance val descriptor = game.open() @@ -188,7 +181,7 @@ class MainViewModel(val activity: MainActivity) { return true } - fun loadMiiEditor() : Boolean { + fun loadMiiEditor(): Boolean { val nativeRyujinx = RyujinxNative.instance gameModel = null @@ -292,42 +285,42 @@ class MainViewModel(val activity: MainActivity) { return true } - fun clearPptcCache(titleId :String){ - if(titleId.isNotEmpty()){ + fun clearPptcCache(titleId: String) { + if (titleId.isNotEmpty()) { val basePath = MainActivity.AppPath + "/games/$titleId/cache/cpu" - if(File(basePath).exists()){ + if (File(basePath).exists()) { var caches = mutableListOf() val mainCache = basePath + "${File.separator}0" File(mainCache).listFiles()?.forEach { - if(it.isFile && it.name.endsWith(".cache")) + if (it.isFile && it.name.endsWith(".cache")) caches.add(it.absolutePath) } val backupCache = basePath + "${File.separator}1" File(backupCache).listFiles()?.forEach { - if(it.isFile && it.name.endsWith(".cache")) + if (it.isFile && it.name.endsWith(".cache")) caches.add(it.absolutePath) } - for(path in caches) + for (path in caches) File(path).delete() } } } - fun purgeShaderCache(titleId :String) { - if(titleId.isNotEmpty()){ + fun purgeShaderCache(titleId: String) { + if (titleId.isNotEmpty()) { val basePath = MainActivity.AppPath + "/games/$titleId/cache/shader" - if(File(basePath).exists()){ + if (File(basePath).exists()) { var caches = mutableListOf() File(basePath).listFiles()?.forEach { - if(!it.isFile) + if (!it.isFile) it.delete() - else{ - if(it.name.endsWith(".toc") || it.name.endsWith(".data")) + else { + if (it.name.endsWith(".toc") || it.name.endsWith(".data")) caches.add(it.absolutePath) } } - for(path in caches) + for (path in caches) File(path).delete() } } @@ -347,7 +340,7 @@ class MainViewModel(val activity: MainActivity) { fifo: Double, gameFps: Double, gameTime: Double - ){ + ) { fifoState?.apply { this.value = fifo } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/QuickSettings.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/QuickSettings.kt index c608b7f7f..b4fa9eecf 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/QuickSettings.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/QuickSettings.kt @@ -14,10 +14,11 @@ class QuickSettings(val activity: Activity) { var isHostMapped: Boolean var enableShaderCache: Boolean var enableTextureRecompression: Boolean - var resScale : Float - var isGrid : Boolean - var useSwitchLayout : Boolean - var enableMotion : Boolean + var resScale: Float + var isGrid: Boolean + var useSwitchLayout: Boolean + var enableMotion: Boolean + var enablePerformanceMode: Boolean // Logs var enableDebugLogs: Boolean @@ -29,7 +30,8 @@ class QuickSettings(val activity: Activity) { var enableAccessLogs: Boolean var enableTraceLogs: Boolean - private var sharedPref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity) + private var sharedPref: SharedPreferences = + PreferenceManager.getDefaultSharedPreferences(activity) init { isHostMapped = sharedPref.getBoolean("isHostMapped", true) @@ -45,6 +47,7 @@ class QuickSettings(val activity: Activity) { isGrid = sharedPref.getBoolean("isGrid", true) useSwitchLayout = sharedPref.getBoolean("useSwitchLayout", true) enableMotion = sharedPref.getBoolean("enableMotion", true) + enablePerformanceMode = sharedPref.getBoolean("enablePerformanceMode", true) enableDebugLogs = sharedPref.getBoolean("enableDebugLogs", false) enableStubLogs = sharedPref.getBoolean("enableStubLogs", false) @@ -56,7 +59,7 @@ class QuickSettings(val activity: Activity) { enableTraceLogs = sharedPref.getBoolean("enableStubLogs", false) } - fun save(){ + fun save() { val editor = sharedPref.edit() editor.putBoolean("isHostMapped", isHostMapped) @@ -72,6 +75,7 @@ class QuickSettings(val activity: Activity) { editor.putBoolean("isGrid", isGrid) editor.putBoolean("useSwitchLayout", useSwitchLayout) editor.putBoolean("enableMotion", enableMotion) + editor.putBoolean("enablePerformanceMode", enablePerformanceMode) editor.putBoolean("enableDebugLogs", enableDebugLogs) editor.putBoolean("enableStubLogs", enableStubLogs) diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt index f83cb7076..574267894 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt @@ -56,6 +56,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main isGrid: MutableState, useSwitchLayout: MutableState, enableMotion: MutableState, + enablePerformanceMode: MutableState, enableDebugLogs: MutableState, enableStubLogs: MutableState, enableInfoLogs: MutableState, @@ -80,6 +81,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main isGrid.value = sharedPref.getBoolean("isGrid", true) useSwitchLayout.value = sharedPref.getBoolean("useSwitchLayout", true) enableMotion.value = sharedPref.getBoolean("enableMotion", true) + enablePerformanceMode.value = sharedPref.getBoolean("enablePerformanceMode", false) enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false) enableStubLogs.value = sharedPref.getBoolean("enableStubLogs", false) @@ -105,6 +107,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main isGrid: MutableState, useSwitchLayout: MutableState, enableMotion: MutableState, + enablePerformanceMode: MutableState, enableDebugLogs: MutableState, enableStubLogs: MutableState, enableInfoLogs: MutableState, @@ -129,6 +132,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main editor.putBoolean("isGrid", isGrid.value) editor.putBoolean("useSwitchLayout", useSwitchLayout.value) editor.putBoolean("enableMotion", enableMotion.value) + editor.putBoolean("enablePerformanceMode", enablePerformanceMode.value) editor.putBoolean("enableDebugLogs", enableDebugLogs.value) editor.putBoolean("enableStubLogs", enableStubLogs.value) @@ -255,7 +259,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main } } - fun clearFirmwareSelection(installState: MutableState){ + fun clearFirmwareSelection(installState: MutableState) { selectedFirmwareFile = null selectedFirmwareVersion = "" installState.value = FirmwareInstallState.None @@ -263,7 +267,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main } -enum class FirmwareInstallState{ +enum class FirmwareInstallState { None, Cancelled, Verifying, diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt index 2625a0320..acd93f2b3 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.extension import org.ryujinx.android.Helpers @@ -122,6 +123,7 @@ class SettingViews { val isGrid = remember { mutableStateOf(true) } val useSwitchLayout = remember { mutableStateOf(true) } val enableMotion = remember { mutableStateOf(true) } + val enablePerformanceMode = remember { mutableStateOf(true) } val enableDebugLogs = remember { mutableStateOf(true) } val enableStubLogs = remember { mutableStateOf(true) } @@ -144,6 +146,7 @@ class SettingViews { isGrid, useSwitchLayout, enableMotion, + enablePerformanceMode, enableDebugLogs, enableStubLogs, enableInfoLogs, @@ -177,6 +180,7 @@ class SettingViews { isGrid, useSwitchLayout, enableMotion, + enablePerformanceMode, enableDebugLogs, enableStubLogs, enableInfoLogs, @@ -192,9 +196,11 @@ class SettingViews { } }) }) { contentPadding -> - Column(modifier = Modifier - .padding(contentPadding) - .verticalScroll(rememberScrollState())) { + Column( + modifier = Modifier + .padding(contentPadding) + .verticalScroll(rememberScrollState()) + ) { ExpandableView(onCardArrowClick = { }, title = "App") { Column(modifier = Modifier.fillMaxWidth()) { Row( @@ -303,9 +309,9 @@ class SettingViews { } } - if(showFirwmareDialog.value) { + if (showFirwmareDialog.value) { AlertDialog(onDismissRequest = { - if(firmwareInstallState.value != FirmwareInstallState.Install) { + if (firmwareInstallState.value != FirmwareInstallState.Install) { showFirwmareDialog.value = false settingsViewModel.clearFirmwareSelection(firmwareInstallState) } @@ -327,7 +333,8 @@ class SettingViews { Text(text = "Select a zip or XCI file to install from.") Row( horizontalArrangement = Arrangement.End, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .padding(top = 4.dp) ) { Button(onClick = { @@ -350,7 +357,8 @@ class SettingViews { Text(text = "Firmware ${settingsViewModel.selectedFirmwareVersion} will be installed. Do you want to continue?") Row( horizontalArrangement = Arrangement.End, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .padding(top = 4.dp) ) { Button(onClick = { @@ -358,9 +366,11 @@ class SettingViews { firmwareInstallState ) - if(firmwareInstallState.value == FirmwareInstallState.None){ + if (firmwareInstallState.value == FirmwareInstallState.None) { showFirwmareDialog.value = false - settingsViewModel.clearFirmwareSelection(firmwareInstallState) + settingsViewModel.clearFirmwareSelection( + firmwareInstallState + ) } }, modifier = Modifier.padding(horizontal = 8.dp)) { Text(text = "Yes") @@ -376,34 +386,32 @@ class SettingViews { } } else if (firmwareInstallState.value == FirmwareInstallState.Install) { Text(text = "Installing Firmware ${settingsViewModel.selectedFirmwareVersion}...") - LinearProgressIndicator(modifier = Modifier - .padding(top = 4.dp)) + LinearProgressIndicator( + modifier = Modifier + .padding(top = 4.dp) + ) } else if (firmwareInstallState.value == FirmwareInstallState.Verifying) { Text(text = "Verifying selected file...") - LinearProgressIndicator(modifier = Modifier - .fillMaxWidth() - ) - } - else if (firmwareInstallState.value == FirmwareInstallState.Done) { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + ) + } else if (firmwareInstallState.value == FirmwareInstallState.Done) { Text(text = "Installed Firmware ${settingsViewModel.selectedFirmwareVersion}") firmwareVersion.value = mainViewModel.firmwareVersion - } - else if(firmwareInstallState.value == FirmwareInstallState.Cancelled){ + } else if (firmwareInstallState.value == FirmwareInstallState.Cancelled) { val file = settingsViewModel.selectedFirmwareFile - if(file != null){ - if(file.extension == "xci" || file.extension == "zip"){ - if(settingsViewModel.selectedFirmwareVersion.isEmpty()) { + if (file != null) { + if (file.extension == "xci" || file.extension == "zip") { + if (settingsViewModel.selectedFirmwareVersion.isEmpty()) { Text(text = "Unable to find version in selected file") - } - else { + } else { Text(text = "Unknown Error has occurred. Please check logs") } - } - else { + } else { Text(text = "File type is not supported") } - } - else { + } else { Text(text = "File type is not supported") } } @@ -503,6 +511,32 @@ class SettingViews { 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 { mutableStateOf(false) } @@ -1041,6 +1075,7 @@ class SettingViews { isGrid, useSwitchLayout, enableMotion, + enablePerformanceMode, enableDebugLogs, enableStubLogs, enableInfoLogs,