2020-04-19 23:04:05 +02:00
// SPDX-License-Identifier: MPL-2.0
2020-03-27 20:36:02 +01:00
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
2020-03-26 15:33:19 +01:00
# include <csignal>
2020-11-18 20:47:09 +01:00
# include <pthread.h>
2020-03-26 15:33:19 +01:00
# include <unistd.h>
2020-09-29 14:46:17 +02:00
# include <android/log.h>
2021-03-03 21:35:24 +01:00
# include <android/asset_manager_jni.h>
2021-03-03 21:42:51 +01:00
# include <sys/system_properties.h>
2019-09-24 22:54:27 +02:00
# include "skyline/common.h"
2020-12-05 18:41:52 +01:00
# include "skyline/common/signal.h"
2020-11-03 10:44:09 +01:00
# include "skyline/common/settings.h"
2021-03-20 17:52:08 +01:00
# include "skyline/common/trace.h"
2020-12-05 18:41:52 +01:00
# include "skyline/loader/loader.h"
2021-03-03 21:35:24 +01:00
# include "skyline/vfs/android_asset_filesystem.h"
2019-09-24 22:54:27 +02:00
# include "skyline/os.h"
2019-12-05 16:35:34 +01:00
# include "skyline/jvm.h"
2020-10-28 17:00:39 +01:00
# include "skyline/gpu.h"
2020-04-26 01:34:35 +02:00
# include "skyline/input.h"
2020-11-03 10:44:09 +01:00
# include "skyline/kernel/types/KProcess.h"
2019-09-24 22:54:27 +02:00
2021-06-27 03:26:16 +02:00
jint Fps ; //!< An approximation of the amount of frames being submitted every second
jfloat AverageFrametimeMs ; //!< The average time it takes for a frame to be rendered and presented in milliseconds
jfloat AverageFrametimeDeviationMs ; //!< The average deviation of the average frametimes in milliseconds
2020-11-03 10:44:09 +01:00
std : : weak_ptr < skyline : : kernel : : OS > OsWeak ;
std : : weak_ptr < skyline : : gpu : : GPU > GpuWeak ;
std : : weak_ptr < skyline : : input : : Input > InputWeak ;
2019-12-02 18:40:53 +01:00
2021-03-03 21:42:51 +01:00
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
static std : : string GetTimeZoneName ( ) {
2021-06-27 03:26:16 +02:00
const char * nameEnv = getenv ( " TZ " ) ;
2021-03-03 21:42:51 +01:00
if ( nameEnv )
return std : : string ( nameEnv ) ;
char propBuf [ PROP_VALUE_MAX ] ;
if ( __system_property_get ( " persist.sys.timezone " , propBuf ) ) {
std : : string nameProp ( propBuf ) ;
// Flip -/+, see https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=53;drc=master;bpv=1;bpt=1
if ( nameProp . size ( ) > 2 ) {
if ( nameProp [ 2 ] = = ' - ' )
nameProp [ 2 ] = ' + ' ;
else if ( nameProp [ 2 ] = = ' + ' )
nameProp [ 2 ] = ' - ' ;
}
return nameProp ;
}
// Fallback to GMT
return " GMT " ;
}
2021-03-03 21:35:24 +01:00
extern " C " JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication ( JNIEnv * env , jobject instance , jstring romUriJstring , jint romType , jint romFd , jint preferenceFd , jstring appFilesPathJstring , jobject assetManager ) {
2021-03-04 14:30:14 +01:00
skyline : : signal : : ScopedStackBlocker stackBlocker ; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
2021-06-27 03:26:16 +02:00
Fps = AverageFrametimeMs = AverageFrametimeDeviationMs = 0 ;
2019-12-05 16:35:34 +01:00
2020-10-28 17:00:39 +01:00
pthread_setname_np ( pthread_self ( ) , " EmuMain " ) ;
2019-12-02 18:40:53 +01:00
2020-09-26 07:17:57 +02:00
auto jvmManager { std : : make_shared < skyline : : JvmManager > ( env , instance ) } ;
auto settings { std : : make_shared < skyline : : Settings > ( preferenceFd ) } ;
2020-09-29 14:46:17 +02:00
close ( preferenceFd ) ;
2020-08-08 21:38:51 +02:00
2020-09-26 07:17:57 +02:00
auto appFilesPath { env - > GetStringUTFChars ( appFilesPathJstring , nullptr ) } ;
2021-02-08 05:17:17 +01:00
auto logger { std : : make_shared < skyline : : Logger > ( std : : string ( appFilesPath ) + " skyline.log " , settings - > logLevel ) } ;
2019-12-02 18:40:53 +01:00
2020-09-26 07:17:57 +02:00
auto start { std : : chrono : : steady_clock : : now ( ) } ;
2019-12-02 18:40:53 +01:00
2021-03-11 19:41:12 +01:00
// Initialize tracing
perfetto : : TracingInitArgs args ;
args . backends | = perfetto : : kSystemBackend ;
perfetto : : Tracing : : Initialize ( args ) ;
perfetto : : TrackEvent : : Register ( ) ;
2019-09-24 22:54:27 +02:00
try {
2021-03-03 21:42:51 +01:00
auto os { std : : make_shared < skyline : : kernel : : OS > ( jvmManager , logger , settings , std : : string ( appFilesPath ) , GetTimeZoneName ( ) , std : : make_shared < skyline : : vfs : : AndroidAssetFileSystem > ( AAssetManager_fromJava ( env , assetManager ) ) ) } ;
2020-11-03 10:44:09 +01:00
OsWeak = os ;
GpuWeak = os - > state . gpu ;
InputWeak = os - > state . input ;
2020-08-21 13:14:27 +02:00
jvmManager - > InitializeControllers ( ) ;
2020-08-08 21:38:51 +02:00
env - > ReleaseStringUTFChars ( appFilesPathJstring , appFilesPath ) ;
2020-09-26 07:17:57 +02:00
auto romUri { env - > GetStringUTFChars ( romUriJstring , nullptr ) } ;
2021-03-20 17:52:08 +01:00
logger - > InfoNoPrefix ( " Launching ROM {} " , romUri ) ;
2020-04-03 13:47:32 +02:00
env - > ReleaseStringUTFChars ( romUriJstring , romUri ) ;
2020-04-26 01:34:35 +02:00
2020-11-03 10:44:09 +01:00
os - > Execute ( romFd , static_cast < skyline : : loader : : RomFormat > ( romType ) ) ;
2019-09-24 22:54:27 +02:00
} catch ( std : : exception & e ) {
2021-03-27 22:17:35 +01:00
logger - > ErrorNoPrefix ( " An uncaught exception has occurred: {} " , e . what ( ) ) ;
2020-12-05 18:41:52 +01:00
} catch ( const skyline : : signal : : SignalException & e ) {
2021-03-27 22:17:35 +01:00
logger - > ErrorNoPrefix ( " An uncaught exception has occurred: {} " , e . what ( ) ) ;
2019-09-24 22:54:27 +02:00
} catch ( . . . ) {
2021-03-27 22:17:35 +01:00
logger - > ErrorNoPrefix ( " An unknown uncaught exception has occurred " ) ;
2019-09-24 22:54:27 +02:00
}
2020-04-26 01:34:35 +02:00
2021-03-11 19:41:12 +01:00
perfetto : : TrackEvent : : Flush ( ) ;
2020-11-03 10:44:09 +01:00
InputWeak . reset ( ) ;
2020-08-20 20:31:32 +02:00
2021-03-20 17:52:08 +01:00
logger - > InfoNoPrefix ( " Emulation has ended " ) ;
2019-12-02 18:40:53 +01:00
2020-09-26 07:17:57 +02:00
auto end { std : : chrono : : steady_clock : : now ( ) } ;
2021-03-20 17:52:08 +01:00
logger - > InfoNoPrefix ( " Done in: {} ms " , ( std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( end - start ) . count ( ) ) ) ;
2020-09-29 14:46:17 +02:00
close ( romFd ) ;
2019-09-24 22:54:27 +02:00
}
2019-12-05 16:35:34 +01:00
2020-11-18 20:47:09 +01:00
extern " C " JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation ( JNIEnv * , jobject ) {
2020-11-03 10:44:09 +01:00
auto os { OsWeak . lock ( ) } ;
2020-11-18 20:47:09 +01:00
if ( ! os )
return false ;
2020-11-03 10:44:09 +01:00
auto process { os - > state . process } ;
2020-11-18 20:47:09 +01:00
if ( ! process )
return false ;
2020-11-17 01:48:41 +01:00
process - > Kill ( true , false , true ) ;
2020-11-18 20:47:09 +01:00
return true ;
2019-12-05 16:35:34 +01:00
}
2020-11-18 20:47:09 +01:00
extern " C " JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_setSurface ( JNIEnv * , jobject , jobject surface ) {
2020-11-03 10:44:09 +01:00
auto gpu { GpuWeak . lock ( ) } ;
2020-11-18 20:47:09 +01:00
if ( ! gpu )
return false ;
2020-10-28 17:00:39 +01:00
gpu - > presentation . UpdateSurface ( surface ) ;
2020-11-18 20:47:09 +01:00
return true ;
2019-12-05 16:35:34 +01:00
}
2020-04-18 02:16:09 +02:00
2020-11-18 20:47:09 +01:00
extern " C " JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceStatistics ( JNIEnv * env , jobject thiz ) {
static jclass clazz { } ;
if ( ! clazz )
clazz = env - > GetObjectClass ( thiz ) ;
static jfieldID fpsField { } ;
if ( ! fpsField )
fpsField = env - > GetFieldID ( clazz , " fps " , " I " ) ;
env - > SetIntField ( thiz , fpsField , Fps ) ;
2020-04-18 02:16:09 +02:00
2021-06-27 03:26:16 +02:00
static jfieldID averageFrametimeField { } ;
if ( ! averageFrametimeField )
averageFrametimeField = env - > GetFieldID ( clazz , " averageFrametime " , " F " ) ;
env - > SetFloatField ( thiz , averageFrametimeField , AverageFrametimeMs ) ;
static jfieldID averageFrametimeDeviationField { } ;
if ( ! averageFrametimeDeviationField )
averageFrametimeDeviationField = env - > GetFieldID ( clazz , " averageFrametimeDeviation " , " F " ) ;
env - > SetFloatField ( thiz , averageFrametimeDeviationField , AverageFrametimeDeviationMs ) ;
2020-08-08 21:38:51 +02:00
}
2020-04-26 01:34:35 +02:00
2020-08-15 15:51:23 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController ( JNIEnv * , jobject , jint index , jint type , jint partnerIndex ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-08-20 20:31:32 +02:00
std : : lock_guard guard ( input - > npad . mutex ) ;
2020-08-15 15:51:23 +02:00
input - > npad . controllers [ index ] = skyline : : input : : GuestController { static_cast < skyline : : input : : NpadControllerType > ( type ) , static_cast < skyline : : i8 > ( partnerIndex ) } ;
}
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers ( JNIEnv * , jobject ) {
2020-11-03 10:44:09 +01:00
InputWeak . lock ( ) - > npad . Update ( ) ;
2020-08-15 15:51:23 +02:00
}
2020-08-21 13:14:27 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState ( JNIEnv * , jobject , jint index , jlong mask , jboolean pressed ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss button updates while input hasn't been initialized
auto device { input - > npad . controllers [ index ] . device } ;
if ( device )
device - > SetButtonState ( skyline : : input : : NpadButton { . raw = static_cast < skyline : : u64 > ( mask ) } , pressed ) ;
2020-04-26 01:34:35 +02:00
}
2020-08-15 15:51:23 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue ( JNIEnv * , jobject , jint index , jint axis , jint value ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss axis updates while input hasn't been initialized
auto device { input - > npad . controllers [ index ] . device } ;
if ( device )
device - > SetAxisValue ( static_cast < skyline : : input : : NpadAxisId > ( axis ) , value ) ;
2020-04-26 01:34:35 +02:00
}
2020-09-07 18:39:05 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setTouchState ( JNIEnv * env , jobject , jintArray pointsJni ) {
2020-10-28 17:00:39 +01:00
using Point = skyline : : input : : TouchScreenPoint ;
2020-09-07 18:39:05 +02:00
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss touch updates while input hasn't been initialized
jboolean isCopy { false } ;
2020-09-07 18:39:05 +02:00
2020-10-28 17:00:39 +01:00
skyline : : span < Point > points ( reinterpret_cast < Point * > ( env - > GetIntArrayElements ( pointsJni , & isCopy ) ) , env - > GetArrayLength ( pointsJni ) / ( sizeof ( Point ) / sizeof ( jint ) ) ) ;
input - > touch . SetState ( points ) ;
env - > ReleaseIntArrayElements ( pointsJni , reinterpret_cast < jint * > ( points . data ( ) ) , JNI_ABORT ) ;
2020-09-07 18:39:05 +02:00
}