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>
@ -36,25 +37,38 @@
#define LoadLib(a) dlopen(a, RTLD_NOW) #define LoadLib(a) dlopen(a, RTLD_NOW)
void* _ryujinxNative = NULL; 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;
@ -69,11 +83,10 @@ private:
}; };
// Ryujinx imported functions // Ryujinx imported functions
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;
string_helper str_helper = string_helper(); string_helper str_helper = string_helper();

View File

@ -21,21 +21,20 @@
#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;
std::string progressInfo = ""; std::string progressInfo = "";
float progress = -1; float progress = -1;
JNIEnv* getEnv(bool isRenderer){ JNIEnv *getEnv(bool isRenderer) {
JNIEnv* env; JNIEnv *env;
if(isRenderer){ if (isRenderer) {
env = _rendererEnv; env = _rendererEnv;
} }
if(env != nullptr) if (env != nullptr)
return env; return env;
auto result = _vm->AttachCurrentThread(&env, NULL); auto result = _vm->AttachCurrentThread(&env, NULL);
@ -43,23 +42,23 @@ JNIEnv* getEnv(bool isRenderer){
return env; return env;
} }
void detachEnv(){ void detachEnv() {
auto result = _vm->DetachCurrentThread(); auto result = _vm->DetachCurrentThread();
} }
extern "C" extern "C"
{ {
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_org_ryujinx_android_NativeHelpers_getNativeWindow( Java_org_ryujinx_android_NativeHelpers_getNativeWindow(
JNIEnv *env, JNIEnv *env,
jobject instance, jobject instance,
jobject surface) { jobject surface) {
auto nativeWindow = ANativeWindow_fromSurface(env, surface); auto nativeWindow = ANativeWindow_fromSurface(env, surface);
return nativeWindow == NULL ? -1 : (jlong) nativeWindow; return nativeWindow == NULL ? -1 : (jlong) nativeWindow;
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_ryujinx_android_NativeHelpers_releaseNativeWindow( Java_org_ryujinx_android_NativeHelpers_releaseNativeWindow(
JNIEnv *env, JNIEnv *env,
jobject instance, jobject instance,
jlong window) { jlong window) {
@ -67,16 +66,16 @@ extern "C"
if (nativeWindow != NULL) if (nativeWindow != NULL)
ANativeWindow_release(nativeWindow); ANativeWindow_release(nativeWindow);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_ryujinx_android_NativeHelpers_attachCurrentThread( Java_org_ryujinx_android_NativeHelpers_attachCurrentThread(
JNIEnv *env, JNIEnv *env,
jobject instance) { jobject instance) {
JavaVM* jvm = NULL; JavaVM *jvm = NULL;
env->GetJavaVM(&jvm); env->GetJavaVM(&jvm);
if(jvm != NULL) if (jvm != NULL)
jvm->AttachCurrentThread(&env, NULL); jvm->AttachCurrentThread(&env, NULL);
} }
@ -84,41 +83,41 @@ JNIEXPORT void JNICALL
Java_org_ryujinx_android_NativeHelpers_detachCurrentThread( Java_org_ryujinx_android_NativeHelpers_detachCurrentThread(
JNIEnv *env, JNIEnv *env,
jobject instance) { jobject instance) {
JavaVM* jvm = NULL; JavaVM *jvm = NULL;
env->GetJavaVM(&jvm); env->GetJavaVM(&jvm);
if(jvm != NULL) if (jvm != NULL)
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};
info.window = nativeWindow; info.window = nativeWindow;
VK_CHECK(fpCreateAndroidSurfaceKHR(vkInstance, &info, nullptr, &surface)); VK_CHECK(fpCreateAndroidSurfaceKHR(vkInstance, &info, nullptr, &surface));
return (long)surface; return (long) surface;
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_org_ryujinx_android_NativeHelpers_getCreateSurfacePtr( Java_org_ryujinx_android_NativeHelpers_getCreateSurfacePtr(
JNIEnv *env, JNIEnv *env,
jobject instance) { jobject instance) {
return (jlong)createSurface; return (jlong) createSurface;
} }
char* getStringPointer( char *getStringPointer(
JNIEnv *env, JNIEnv *env,
jstring jS) { jstring jS) {
const char *cparam = env->GetStringUTFChars(jS, 0); const char *cparam = env->GetStringUTFChars(jS, 0);
auto len = env->GetStringUTFLength(jS); auto len = env->GetStringUTFLength(jS);
char* s= new char[len]; char *s = new char[len];
strcpy(s, cparam); strcpy(s, cparam);
env->ReleaseStringUTFChars(jS, cparam); env->ReleaseStringUTFChars(jS, cparam);
@ -127,7 +126,7 @@ char* getStringPointer(
jstring createString( jstring createString(
JNIEnv *env, JNIEnv *env,
char* ch) { char *ch) {
auto str = env->NewStringUTF(ch); auto str = env->NewStringUTF(ch);
return str; return str;
@ -144,15 +143,9 @@ jstring createStringFromStdString(
} }
extern "C" extern "C"
JNIEXPORT jlong JNICALL void setRenderingThread() {
Java_org_ryujinx_android_MainActivity_getRenderingThreadId(JNIEnv *env, jobject thiz) {
return _currentRenderingThreadId;
}
extern "C"
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();
@ -160,7 +153,7 @@ void setRenderingThread(){
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_ryujinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) { Java_org_ryujinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) {
JavaVM* vm = nullptr; JavaVM *vm = nullptr;
auto success = env->GetJavaVM(&vm); auto success = env->GetJavaVM(&vm);
_vm = vm; _vm = vm;
_mainActivity = thiz; _mainActivity = thiz;
@ -171,25 +164,26 @@ 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);
progress = progressValue; progress = progressValue;
} }
bool isInitialOrientationFlipped = true; bool isInitialOrientationFlipped = true;
extern "C" extern "C"
void setCurrentTransform(long native_window, int transform){ void setCurrentTransform(long native_window, int transform) {
if(native_window == 0 || native_window == -1) if (native_window == 0 || native_window == -1)
return; return;
auto nativeWindow = (ANativeWindow *) native_window; auto nativeWindow = (ANativeWindow *) native_window;
@ -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"
@ -260,12 +257,12 @@ Java_org_ryujinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz,
delete privateAppsPath; delete privateAppsPath;
delete driverName; delete driverName;
return (jlong)handle; return (jlong) handle;
} }
extern "C" extern "C"
void debug_break(int code){ void debug_break(int code) {
if(code >= 3) if (code >= 3)
int r = 0; int r = 0;
} }
@ -315,14 +312,14 @@ Java_org_ryujinx_android_NativeHelpers_getProgressInfo(JNIEnv *env, jobject thiz
} }
extern "C" extern "C"
long storeString(char* str){ long storeString(char *str) {
return str_helper.store_cstring(str); return str_helper.store_cstring(str);
} }
extern "C" extern "C"
const char* getString(long id){ const char *getString(long id) {
auto str = str_helper.get_stored(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()); ::strcpy(cstr, str.c_str());
return cstr; return cstr;
} }
@ -396,7 +393,7 @@ Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestMessage(JNIEnv *env, j
void UiHandler::setTitle(long storedTitle) { void UiHandler::setTitle(long storedTitle) {
if(title != -1){ if (title != -1) {
str_helper.get_stored(title); str_helper.get_stored(title);
title = -1; title = -1;
} }
@ -405,7 +402,7 @@ void UiHandler::setTitle(long storedTitle) {
} }
void UiHandler::setMessage(long storedMessage) { void UiHandler::setMessage(long storedMessage) {
if(message != -1){ if (message != -1) {
str_helper.get_stored(message); str_helper.get_stored(message);
message = -1; message = -1;
} }
@ -430,7 +427,7 @@ long UiHandler::getMessage() {
} }
void UiHandler::setWatermark(long wm) { void UiHandler::setWatermark(long wm) {
if(watermark != -1){ if (watermark != -1) {
str_helper.get_stored(watermark); str_helper.get_stored(watermark);
watermark = -1; watermark = -1;
} }
@ -453,7 +450,7 @@ long UiHandler::getWatermark() {
} }
void UiHandler::setInitialText(long text) { void UiHandler::setInitialText(long text) {
if(initialText != -1){ if (initialText != -1) {
str_helper.get_stored(watermark); str_helper.get_stored(watermark);
initialText = -1; initialText = -1;
} }
@ -462,7 +459,7 @@ void UiHandler::setInitialText(long text) {
} }
void UiHandler::setSubtitle(long text) { void UiHandler::setSubtitle(long text) {
if(subtitle != -1){ if (subtitle != -1) {
str_helper.get_stored(subtitle); str_helper.get_stored(subtitle);
subtitle = -1; subtitle = -1;
} }

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,24 +28,23 @@ 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 = ""
var StorageHelper: SimpleStorageHelper? = null var StorageHelper: SimpleStorageHelper? = null
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,26 +65,52 @@ 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)
motionSensorManager = MotionSensorManager(this) motionSensorManager = MotionSensorManager(this)
Thread.setDefaultUncaughtExceptionHandler(crashHandler) Thread.setDefaultUncaughtExceptionHandler(crashHandler)
if( if (
!Environment.isExternalStorageManager() !Environment.isExternalStorageManager()
) { ) {
storageHelper?.storage?.requestFullStorageAccess() storageHelper?.storage?.requestFullStorageAccess()
@ -99,8 +120,9 @@ class MainActivity : BaseActivity() {
initialize() initialize()
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.attributes.layoutInDisplayCutoutMode =
WindowCompat.setDecorFitsSystemWindows(window,false) WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
WindowCompat.setDecorFitsSystemWindows(window, false)
mainViewModel = MainViewModel(this) mainViewModel = MainViewModel(this)
mainViewModel!!.physicalControllerManager = physicalControllerManager mainViewModel!!.physicalControllerManager = physicalControllerManager
@ -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
@ -49,16 +46,12 @@ class MainViewModel(val activity: MainActivity) {
field = value field = value
field?.setProgressStates(showLoading, progressValue, progress) field?.setProgressStates(showLoading, progressValue, progress)
} }
var navController : NavHostController? = null var navController: NavHostController? = null
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() {
@ -70,14 +63,14 @@ class MainViewModel(val activity: MainActivity) {
motionSensorManager?.setControllerId(-1) motionSensorManager?.setControllerId(-1)
} }
fun refreshFirmwareVersion(){ fun refreshFirmwareVersion() {
var handle = RyujinxNative.instance.deviceGetInstalledFirmwareVersion() var handle = RyujinxNative.instance.deviceGetInstalledFirmwareVersion()
if(handle != -1L) { if (handle != -1L) {
firmwareVersion = NativeHelpers.instance.getStringJava(handle) firmwareVersion = NativeHelpers.instance.getStringJava(handle)
} }
} }
fun loadGame(game:GameModel) : Boolean { fun loadGame(game: GameModel): Boolean {
val nativeRyujinx = RyujinxNative.instance val nativeRyujinx = RyujinxNative.instance
val descriptor = game.open() val descriptor = game.open()
@ -188,7 +181,7 @@ class MainViewModel(val activity: MainActivity) {
return true return true
} }
fun loadMiiEditor() : Boolean { fun loadMiiEditor(): Boolean {
val nativeRyujinx = RyujinxNative.instance val nativeRyujinx = RyujinxNative.instance
gameModel = null gameModel = null
@ -292,42 +285,42 @@ class MainViewModel(val activity: MainActivity) {
return true return true
} }
fun clearPptcCache(titleId :String){ fun clearPptcCache(titleId: String) {
if(titleId.isNotEmpty()){ if (titleId.isNotEmpty()) {
val basePath = MainActivity.AppPath + "/games/$titleId/cache/cpu" val basePath = MainActivity.AppPath + "/games/$titleId/cache/cpu"
if(File(basePath).exists()){ if (File(basePath).exists()) {
var caches = mutableListOf<String>() var caches = mutableListOf<String>()
val mainCache = basePath + "${File.separator}0" val mainCache = basePath + "${File.separator}0"
File(mainCache).listFiles()?.forEach { File(mainCache).listFiles()?.forEach {
if(it.isFile && it.name.endsWith(".cache")) if (it.isFile && it.name.endsWith(".cache"))
caches.add(it.absolutePath) caches.add(it.absolutePath)
} }
val backupCache = basePath + "${File.separator}1" val backupCache = basePath + "${File.separator}1"
File(backupCache).listFiles()?.forEach { File(backupCache).listFiles()?.forEach {
if(it.isFile && it.name.endsWith(".cache")) if (it.isFile && it.name.endsWith(".cache"))
caches.add(it.absolutePath) caches.add(it.absolutePath)
} }
for(path in caches) for (path in caches)
File(path).delete() File(path).delete()
} }
} }
} }
fun purgeShaderCache(titleId :String) { fun purgeShaderCache(titleId: String) {
if(titleId.isNotEmpty()){ if (titleId.isNotEmpty()) {
val basePath = MainActivity.AppPath + "/games/$titleId/cache/shader" val basePath = MainActivity.AppPath + "/games/$titleId/cache/shader"
if(File(basePath).exists()){ if (File(basePath).exists()) {
var caches = mutableListOf<String>() var caches = mutableListOf<String>()
File(basePath).listFiles()?.forEach { File(basePath).listFiles()?.forEach {
if(!it.isFile) if (!it.isFile)
it.delete() it.delete()
else{ else {
if(it.name.endsWith(".toc") || it.name.endsWith(".data")) if (it.name.endsWith(".toc") || it.name.endsWith(".data"))
caches.add(it.absolutePath) caches.add(it.absolutePath)
} }
} }
for(path in caches) for (path in caches)
File(path).delete() File(path).delete()
} }
} }
@ -347,7 +340,7 @@ class MainViewModel(val activity: MainActivity) {
fifo: Double, fifo: Double,
gameFps: Double, gameFps: Double,
gameTime: Double gameTime: Double
){ ) {
fifoState?.apply { fifoState?.apply {
this.value = fifo this.value = fifo
} }

View File

@ -14,10 +14,11 @@ class QuickSettings(val activity: Activity) {
var isHostMapped: Boolean var isHostMapped: Boolean
var enableShaderCache: Boolean var enableShaderCache: Boolean
var enableTextureRecompression: Boolean var enableTextureRecompression: Boolean
var resScale : Float var resScale: Float
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)
@ -56,7 +59,7 @@ class QuickSettings(val activity: Activity) {
enableTraceLogs = sharedPref.getBoolean("enableStubLogs", false) enableTraceLogs = sharedPref.getBoolean("enableStubLogs", false)
} }
fun save(){ fun save() {
val editor = sharedPref.edit() val editor = sharedPref.edit()
editor.putBoolean("isHostMapped", isHostMapped) editor.putBoolean("isHostMapped", isHostMapped)
@ -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)
@ -255,7 +259,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
} }
} }
fun clearFirmwareSelection(installState: MutableState<FirmwareInstallState>){ fun clearFirmwareSelection(installState: MutableState<FirmwareInstallState>) {
selectedFirmwareFile = null selectedFirmwareFile = null
selectedFirmwareVersion = "" selectedFirmwareVersion = ""
installState.value = FirmwareInstallState.None installState.value = FirmwareInstallState.None
@ -263,7 +267,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
} }
enum class FirmwareInstallState{ enum class FirmwareInstallState {
None, None,
Cancelled, Cancelled,
Verifying, Verifying,

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(
@ -303,9 +309,9 @@ class SettingViews {
} }
} }
if(showFirwmareDialog.value) { if (showFirwmareDialog.value) {
AlertDialog(onDismissRequest = { AlertDialog(onDismissRequest = {
if(firmwareInstallState.value != FirmwareInstallState.Install) { if (firmwareInstallState.value != FirmwareInstallState.Install) {
showFirwmareDialog.value = false showFirwmareDialog.value = false
settingsViewModel.clearFirmwareSelection(firmwareInstallState) settingsViewModel.clearFirmwareSelection(firmwareInstallState)
} }
@ -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 = {
@ -358,9 +366,11 @@ class SettingViews {
firmwareInstallState firmwareInstallState
) )
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,