early-access version 4094

This commit is contained in:
pineappleEA 2024-01-28 17:06:37 +01:00
parent 9de78b45a9
commit 292dc9d2a9
13 changed files with 161 additions and 87 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 4093. This is the source code for early-access 4094.
## Legal Notice ## Legal Notice

View file

@ -261,7 +261,7 @@ object NativeLibrary {
/** /**
* Begins emulation. * Begins emulation.
*/ */
external fun run(path: String?) external fun run(path: String?, programIndex: Int = 0)
// Surface Handling // Surface Handling
external fun surfaceChanged(surf: Surface?) external fun surfaceChanged(surf: Surface?)
@ -489,6 +489,12 @@ object NativeLibrary {
sEmulationActivity.get()!!.onEmulationStopped(status) sEmulationActivity.get()!!.onEmulationStopped(status)
} }
@Keep
@JvmStatic
fun onProgramChanged(programIndex: Int) {
sEmulationActivity.get()!!.onProgramChanged(programIndex)
}
/** /**
* Logs the Yuzu version, Android version and, CPU. * Logs the Yuzu version, Android version and, CPU.
*/ */

View file

@ -76,7 +76,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onDestroy() { override fun onDestroy() {
stopForegroundService(this) stopForegroundService(this)
emulationViewModel.clear()
super.onDestroy() super.onDestroy()
} }
@ -446,9 +445,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
} }
fun onEmulationStopped(status: Int) { fun onEmulationStopped(status: Int) {
if (status == 0) { if (status == 0 && emulationViewModel.programChanged.value == -1) {
finish() finish()
} }
emulationViewModel.setEmulationStopped(true)
}
fun onProgramChanged(programIndex: Int) {
emulationViewModel.setProgramChanged(programIndex)
} }
private fun startMotionSensorListener() { private fun startMotionSensorListener() {

View file

@ -424,10 +424,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
} }
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.programChanged.collect {
if (it != 0) {
emulationViewModel.setEmulationStarted(false)
binding.drawerLayout.close()
binding.drawerLayout
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
ViewUtils.hideView(binding.surfaceInputOverlay)
ViewUtils.showView(binding.loadingIndicator)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStopped.collect {
if (it && emulationViewModel.programChanged.value != -1) {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
}
}
}
}
} }
} }
private fun startEmulation() { private fun startEmulation(programIndex: Int = 0) {
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
if (!DirectoryInitialization.areDirectoriesReady) { if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start() DirectoryInitialization.start()
@ -435,7 +463,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateScreenLayout() updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated) emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
} }
} }
@ -833,6 +861,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) { ) {
private var state: State private var state: State
private var surface: Surface? = null private var surface: Surface? = null
lateinit var emulationThread: Thread
init { init {
// Starting state is stopped. // Starting state is stopped.
@ -878,7 +907,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
@Synchronized @Synchronized
fun run(isActivityRecreated: Boolean) { fun run(isActivityRecreated: Boolean, programIndex: Int = 0) {
if (isActivityRecreated) { if (isActivityRecreated) {
if (NativeLibrary.isRunning()) { if (NativeLibrary.isRunning()) {
state = State.PAUSED state = State.PAUSED
@ -889,10 +918,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// If the surface is set, run now. Otherwise, wait for it to get set. // If the surface is set, run now. Otherwise, wait for it to get set.
if (surface != null) { if (surface != null) {
runWithValidSurface() runWithValidSurface(programIndex)
} }
} }
@Synchronized
fun changeProgram(programIndex: Int) {
emulationThread.join()
emulationThread = Thread({
Log.debug("[EmulationFragment] Starting emulation thread.")
NativeLibrary.run(gamePath, programIndex)
}, "NativeEmulation")
emulationThread.start()
}
// Surface callbacks // Surface callbacks
@Synchronized @Synchronized
fun newSurface(surface: Surface?) { fun newSurface(surface: Surface?) {
@ -932,7 +971,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun runWithValidSurface() { private fun runWithValidSurface(programIndex: Int = 0) {
NativeLibrary.surfaceChanged(surface) NativeLibrary.surfaceChanged(surface)
if (!emulationCanStart.invoke()) { if (!emulationCanStart.invoke()) {
return return
@ -940,9 +979,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
when (state) { when (state) {
State.STOPPED -> { State.STOPPED -> {
val emulationThread = Thread({ emulationThread = Thread({
Log.debug("[EmulationFragment] Starting emulation thread.") Log.debug("[EmulationFragment] Starting emulation thread.")
NativeLibrary.run(gamePath) NativeLibrary.run(gamePath, programIndex)
}, "NativeEmulation") }, "NativeEmulation")
emulationThread.start() emulationThread.start()
} }

View file

@ -15,6 +15,12 @@ class EmulationViewModel : ViewModel() {
val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
private val _isEmulationStopping = MutableStateFlow(false) private val _isEmulationStopping = MutableStateFlow(false)
private val _emulationStopped = MutableStateFlow(false)
val emulationStopped = _emulationStopped.asStateFlow()
private val _programChanged = MutableStateFlow(-1)
val programChanged = _programChanged.asStateFlow()
val shaderProgress: StateFlow<Int> get() = _shaderProgress val shaderProgress: StateFlow<Int> get() = _shaderProgress
private val _shaderProgress = MutableStateFlow(0) private val _shaderProgress = MutableStateFlow(0)
@ -35,6 +41,17 @@ class EmulationViewModel : ViewModel() {
_isEmulationStopping.value = value _isEmulationStopping.value = value
} }
fun setEmulationStopped(value: Boolean) {
if (value) {
_emulationStarted.value = false
}
_emulationStopped.value = value
}
fun setProgramChanged(programIndex: Int) {
_programChanged.value = programIndex
}
fun setShaderProgress(progress: Int) { fun setShaderProgress(progress: Int) {
_shaderProgress.value = progress _shaderProgress.value = progress
} }
@ -56,20 +73,4 @@ class EmulationViewModel : ViewModel() {
fun setDrawerOpen(value: Boolean) { fun setDrawerOpen(value: Boolean) {
_drawerOpen.value = value _drawerOpen.value = value
} }
fun clear() {
setEmulationStarted(false)
setIsEmulationStopping(false)
setShaderProgress(0)
setTotalShaders(0)
setShaderMessage("")
}
companion object {
const val KEY_EMULATION_STARTED = "EmulationStarted"
const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
const val KEY_SHADER_PROGRESS = "ShaderProgress"
const val KEY_TOTAL_SHADERS = "TotalShaders"
const val KEY_SHADER_MESSAGE = "ShaderMessage"
}
} }

View file

@ -19,6 +19,7 @@ static jmethodID s_exit_emulation_activity;
static jmethodID s_disk_cache_load_progress; static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started; static jmethodID s_on_emulation_started;
static jmethodID s_on_emulation_stopped; static jmethodID s_on_emulation_stopped;
static jmethodID s_on_program_changed;
static jclass s_game_class; static jclass s_game_class;
static jmethodID s_game_constructor; static jmethodID s_game_constructor;
@ -123,6 +124,10 @@ jmethodID GetOnEmulationStopped() {
return s_on_emulation_stopped; return s_on_emulation_stopped;
} }
jmethodID GetOnProgramChanged() {
return s_on_program_changed;
}
jclass GetGameClass() { jclass GetGameClass() {
return s_game_class; return s_game_class;
} }
@ -306,6 +311,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped = s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));

View file

@ -19,6 +19,7 @@ jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress(); jmethodID GetDiskCacheLoadProgress();
jmethodID GetOnEmulationStarted(); jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped(); jmethodID GetOnEmulationStopped();
jmethodID GetOnProgramChanged();
jclass GetGameClass(); jclass GetGameClass();
jmethodID GetGameConstructor(); jmethodID GetGameConstructor();

View file

@ -215,7 +215,8 @@ void EmulationSession::SetAppletId(int applet_id) {
static_cast<Service::AM::AppletId>(m_applet_id)); static_cast<Service::AM::AppletId>(m_applet_id));
} }
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
const std::size_t program_index) {
std::scoped_lock lock(m_mutex); std::scoped_lock lock(m_mutex);
// Create the render window. // Create the render window.
@ -247,6 +248,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
// Load the ROM. // Load the ROM.
Service::AM::FrontendAppletParameters params{ Service::AM::FrontendAppletParameters params{
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id), .applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
.program_index = static_cast<s32>(program_index),
}; };
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
if (m_load_result != Core::SystemResultStatus::Success) { if (m_load_result != Core::SystemResultStatus::Success) {
@ -258,6 +260,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.GetCpuManager().OnGpuReady(); m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); }); m_system.RegisterExitCallback([&] { HaltEmulation(); });
// Register an ExecuteProgram callback such that Core can execute a sub-program
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
m_next_program_index = program_index_;
EmulationSession::GetInstance().HaltEmulation();
});
OnEmulationStarted(); OnEmulationStarted();
return Core::SystemResultStatus::Success; return Core::SystemResultStatus::Success;
} }
@ -265,6 +273,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
void EmulationSession::ShutdownEmulation() { void EmulationSession::ShutdownEmulation() {
std::scoped_lock lock(m_mutex); std::scoped_lock lock(m_mutex);
if (m_next_program_index != -1) {
ChangeProgram(m_next_program_index);
m_next_program_index = -1;
}
m_is_running = false; m_is_running = false;
// Unload user input. // Unload user input.
@ -415,6 +428,12 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
static_cast<jint>(result)); static_cast<jint>(result));
} }
void EmulationSession::ChangeProgram(std::size_t program_index) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
static_cast<jint>(program_index));
}
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
auto program_id_string = GetJString(env, jprogramId); auto program_id_string = GetJString(env, jprogramId);
try { try {
@ -424,7 +443,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
} }
} }
static Core::SystemResultStatus RunEmulation(const std::string& filepath) { static Core::SystemResultStatus RunEmulation(const std::string& filepath,
const size_t program_index = 0) {
MicroProfileOnThreadCreate("EmuThread"); MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); }); SCOPE_EXIT({ MicroProfileShutdown(); });
@ -437,7 +457,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index);
if (result != Core::SystemResultStatus::Success) { if (result != Core::SystemResultStatus::Success) {
return result; return result;
} }
@ -702,11 +722,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
Settings::LogSettings(); Settings::LogSettings();
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
jstring j_path) { jint j_program_index) {
const std::string path = GetJString(env, j_path); const std::string path = GetJString(env, j_path);
const Core::SystemResultStatus result{RunEmulation(path)}; const Core::SystemResultStatus result{RunEmulation(path, j_program_index)};
if (result != Core::SystemResultStatus::Success) { if (result != Core::SystemResultStatus::Success) {
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetExitEmulationActivity(), static_cast<int>(result)); IDCache::GetExitEmulationActivity(), static_cast<int>(result));

View file

@ -46,7 +46,8 @@ public:
void ConfigureFilesystemProvider(const std::string& filepath); void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem(bool reload); void InitializeSystem(bool reload);
void SetAppletId(int applet_id); void SetAppletId(int applet_id);
Core::SystemResultStatus InitializeEmulation(const std::string& filepath); Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
const std::size_t program_index = 0);
bool IsHandheldOnly(); bool IsHandheldOnly();
void SetDeviceType([[maybe_unused]] int index, int type); void SetDeviceType([[maybe_unused]] int index, int type);
@ -61,6 +62,7 @@ public:
private: private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStopped(Core::SystemResultStatus result); static void OnEmulationStopped(Core::SystemResultStatus result);
static void ChangeProgram(std::size_t program_index);
private: private:
// Window management // Window management
@ -86,4 +88,7 @@ private:
// Synchronization // Synchronization
std::condition_variable_any m_cv; std::condition_variable_any m_cv;
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
// Program index for next boot
std::atomic<s32> m_next_program_index = -1;
}; };

View file

@ -34,8 +34,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:focusable="false"
android:defaultFocusHighlightEnabled="false" android:defaultFocusHighlightEnabled="false"
android:clickable="false"> android:clickable="false"
app:rippleColor="@android:color/transparent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loading_layout" android:id="@+id/loading_layout"

View file

@ -15,25 +15,34 @@ namespace Common {
#if _MSC_VER #if _MSC_VER
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected);
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual);
template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u8>(u8* pointer, u8 value, u8 expected) {
const u8 result = const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected; return result == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u16>(u16* pointer, u16 value, u16 expected) {
const u16 result = const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected; return result == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u32>(u32* pointer, u32 value, u32 expected) {
const u32 result = const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected; return result == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u64>(u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected); value, expected);
return result == expected; return result == expected;
@ -45,28 +54,31 @@ namespace Common {
reinterpret_cast<__int64*>(expected.data())) != 0; reinterpret_cast<__int64*>(expected.data())) != 0;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, template <>
u8& actual) { [[nodiscard]] inline bool AtomicCompareAndSwap<u8>(u8* pointer, u8 value, u8 expected, u8& actual) {
actual = actual =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return actual == expected; return actual == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u16>(u16* pointer, u16 value, u16 expected,
u16& actual) { u16& actual) {
actual = actual =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return actual == expected; return actual == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected, template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u32>(u32* pointer, u32 value, u32 expected,
u32& actual) { u32& actual) {
actual = actual =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return actual == expected; return actual == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected, template <>
[[nodiscard]] inline bool AtomicCompareAndSwap<u64>(u64* pointer, u64 value, u64 expected,
u64& actual) { u64& actual) {
actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value, actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
expected); expected);
@ -91,23 +103,12 @@ namespace Common {
#else #else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected) {
return __sync_bool_compare_and_swap(pointer, expected, value); return __sync_bool_compare_and_swap(pointer, expected, value);
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { [[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a; unsigned __int128 value_a;
unsigned __int128 expected_a; unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128)); std::memcpy(&value_a, value.data(), sizeof(u128));
@ -115,31 +116,13 @@ namespace Common {
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, template <typename T>
u8& actual) { [[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value); actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected; return actual == expected;
} }
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, [[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected,
u16& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
u32& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
u64& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
u128& actual) { u128& actual) {
unsigned __int128 value_a; unsigned __int128 value_a;
unsigned __int128 expected_a; unsigned __int128 expected_a;
@ -151,7 +134,7 @@ namespace Common {
return actual_a == expected_a; return actual_a == expected_a;
} }
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) { [[nodiscard]] inline u128 AtomicLoad128(u64* pointer) {
unsigned __int128 zeros_a = 0; unsigned __int128 zeros_a = 0;
unsigned __int128 result_a = unsigned __int128 result_a =
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a); __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);

View file

@ -221,6 +221,7 @@ void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) {
void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) {
std::shared_ptr<Applet> applet; std::shared_ptr<Applet> applet;
bool should_stop = false;
{ {
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
@ -231,10 +232,17 @@ void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) {
applet = it->second; applet = it->second;
m_applets.erase(it); m_applets.erase(it);
should_stop = m_applets.empty();
} }
// Terminate process. // Terminate process.
applet->process->Terminate(); applet->process->Terminate();
// If there were no applets left, stop emulation.
if (should_stop) {
m_system.Exit();
}
} }
void AppletManager::CreateAndInsertByFrontendAppletParameters( void AppletManager::CreateAndInsertByFrontendAppletParameters(

View file

@ -781,8 +781,7 @@ struct Memory::Impl {
}, },
[&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); });
if (ptr) { if (ptr) {
const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); return Common::AtomicCompareAndSwap(reinterpret_cast<T*>(ptr), data, expected);
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
} }
return true; return true;
} }
@ -796,8 +795,7 @@ struct Memory::Impl {
}, },
[&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(u128)); }); [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(u128)); });
if (ptr) { if (ptr) {
const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); return Common::AtomicCompareAndSwap(reinterpret_cast<u64*>(ptr), data, expected);
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
} }
return true; return true;
} }