mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 01:45:45 -04:00
Android: Multiple improvements
- Save/resume state when closing/starting. - Error reporting - hopefully can figure out why it's not starting on some devices. - Reduce startup latency. - Add more options and descriptions to settings.
This commit is contained in:
@ -20,7 +20,14 @@ Log_SetChannel(AndroidHostInterface);
|
||||
static JavaVM* s_jvm;
|
||||
static jclass s_AndroidHostInterface_class;
|
||||
static jmethodID s_AndroidHostInterface_constructor;
|
||||
static jfieldID s_AndroidHostInterface_field_nativePointer;
|
||||
static jfieldID s_AndroidHostInterface_field_mNativePointer;
|
||||
static jmethodID s_AndroidHostInterface_method_reportError;
|
||||
static jmethodID s_AndroidHostInterface_method_reportMessage;
|
||||
static jmethodID s_EmulationActivity_method_reportError;
|
||||
static jmethodID s_EmulationActivity_method_reportMessage;
|
||||
static jmethodID s_EmulationActivity_method_onEmulationStarted;
|
||||
static jmethodID s_EmulationActivity_method_onEmulationStopped;
|
||||
static jmethodID s_EmulationActivity_method_onGameTitleChanged;
|
||||
|
||||
namespace AndroidHelpers {
|
||||
// helper for retrieving the current per-thread jni environment
|
||||
@ -36,7 +43,7 @@ JNIEnv* GetJNIEnv()
|
||||
AndroidHostInterface* GetNativeClass(JNIEnv* env, jobject obj)
|
||||
{
|
||||
return reinterpret_cast<AndroidHostInterface*>(
|
||||
static_cast<uintptr_t>(env->GetLongField(obj, s_AndroidHostInterface_field_nativePointer)));
|
||||
static_cast<uintptr_t>(env->GetLongField(obj, s_AndroidHostInterface_field_mNativePointer)));
|
||||
}
|
||||
|
||||
std::string JStringToString(JNIEnv* env, jstring str)
|
||||
@ -95,12 +102,26 @@ void AndroidHostInterface::RequestExit()
|
||||
|
||||
void AndroidHostInterface::ReportError(const char* message)
|
||||
{
|
||||
HostInterface::ReportError(message);
|
||||
CommonHostInterface::ReportError(message);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring message_jstr = env->NewStringUTF(message);
|
||||
if (m_emulation_activity_object)
|
||||
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_reportError, message_jstr);
|
||||
else
|
||||
env->CallVoidMethod(m_java_object, s_AndroidHostInterface_method_reportError, message_jstr);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::ReportMessage(const char* message)
|
||||
{
|
||||
HostInterface::ReportMessage(message);
|
||||
CommonHostInterface::ReportMessage(message);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring message_jstr = env->NewStringUTF(message);
|
||||
if (m_emulation_activity_object)
|
||||
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_reportMessage, message_jstr);
|
||||
else
|
||||
env->CallVoidMethod(m_java_object, s_AndroidHostInterface_method_reportMessage, message_jstr);
|
||||
}
|
||||
|
||||
std::string AndroidHostInterface::GetStringSettingValue(const char* section, const char* key, const char* default_value)
|
||||
@ -141,22 +162,17 @@ void AndroidHostInterface::UpdateInputMap()
|
||||
CommonHostInterface::UpdateInputMap(m_settings_interface);
|
||||
}
|
||||
|
||||
bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params)
|
||||
bool AndroidHostInterface::StartEmulationThread(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state)
|
||||
{
|
||||
Assert(!IsEmulationThreadRunning());
|
||||
|
||||
emulation_activity = AndroidHelpers::GetJNIEnv()->NewGlobalRef(emulation_activity);
|
||||
|
||||
Log_DevPrintf("Starting emulation thread...");
|
||||
m_emulation_thread_stop_request.store(false);
|
||||
m_emulation_thread =
|
||||
std::thread(&AndroidHostInterface::EmulationThreadEntryPoint, this, initial_surface, std::move(boot_params));
|
||||
m_emulation_thread_started.Wait();
|
||||
if (!m_emulation_thread_start_result.load())
|
||||
{
|
||||
m_emulation_thread.join();
|
||||
Log_ErrorPrint("Failed to start emulation in thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_emulation_thread = std::thread(&AndroidHostInterface::EmulationThreadEntryPoint, this, emulation_activity,
|
||||
initial_surface, std::move(boot_params), resume_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -196,36 +212,48 @@ void AndroidHostInterface::RunOnEmulationThread(std::function<void()> function,
|
||||
m_callback_mutex.unlock();
|
||||
}
|
||||
|
||||
void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params)
|
||||
void AndroidHostInterface::EmulationThreadEntryPoint(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state)
|
||||
{
|
||||
JNIEnv* thread_env;
|
||||
if (s_jvm->AttachCurrentThread(&thread_env, nullptr) != JNI_OK)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to attach JNI to thread");
|
||||
m_emulation_thread_start_result.store(false);
|
||||
m_emulation_thread_started.Signal();
|
||||
ReportError("Failed to attach JNI to thread");
|
||||
return;
|
||||
}
|
||||
|
||||
CreateImGuiContext();
|
||||
m_surface = initial_surface;
|
||||
ApplySettings();
|
||||
m_emulation_activity_object = emulation_activity;
|
||||
ApplySettings(true);
|
||||
|
||||
// Boot system.
|
||||
if (!BootSystem(boot_params))
|
||||
bool boot_result = false;
|
||||
if (resume_state)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to boot system on emulation thread (file:%s).", boot_params.filename.c_str());
|
||||
if (boot_params.filename.empty())
|
||||
boot_result = ResumeSystemFromMostRecentState();
|
||||
else
|
||||
boot_result = ResumeSystemFromState(boot_params.filename.c_str(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
boot_result = BootSystem(boot_params);
|
||||
}
|
||||
|
||||
if (!boot_result)
|
||||
{
|
||||
ReportFormattedError("Failed to boot system on emulation thread (file:%s).", boot_params.filename.c_str());
|
||||
DestroyImGuiContext();
|
||||
m_emulation_thread_start_result.store(false);
|
||||
m_emulation_thread_started.Signal();
|
||||
thread_env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStopped);
|
||||
thread_env->DeleteGlobalRef(m_emulation_activity_object);
|
||||
m_emulation_activity_object = {};
|
||||
s_jvm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
|
||||
// System is ready to go.
|
||||
m_emulation_thread_start_result.store(true);
|
||||
m_emulation_thread_started.Signal();
|
||||
|
||||
thread_env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStarted);
|
||||
while (!m_emulation_thread_stop_request.load())
|
||||
{
|
||||
// run any events
|
||||
@ -264,8 +292,11 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf
|
||||
}
|
||||
}
|
||||
|
||||
DestroySystem();
|
||||
thread_env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStopped);
|
||||
PowerOffSystem();
|
||||
DestroyImGuiContext();
|
||||
thread_env->DeleteGlobalRef(m_emulation_activity_object);
|
||||
m_emulation_activity_object = {};
|
||||
s_jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
@ -308,9 +339,27 @@ void AndroidHostInterface::ReleaseHostDisplay()
|
||||
m_display.reset();
|
||||
}
|
||||
|
||||
void AndroidHostInterface::OnSystemDestroyed()
|
||||
{
|
||||
CommonHostInterface::OnSystemDestroyed();
|
||||
ClearOSDMessages();
|
||||
}
|
||||
|
||||
void AndroidHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
if (m_emulation_activity_object)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring title_string = env->NewStringUTF(System::GetRunningTitle().c_str());
|
||||
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onGameTitleChanged, title_string);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidHostInterface::SurfaceChanged(ANativeWindow* surface, int format, int width, int height)
|
||||
{
|
||||
Log_InfoPrintf("SurfaceChanged %p %d %d %d", surface, format, width, height);
|
||||
ReportFormattedMessage("SurfaceChanged %p %d %d %d", surface, format, width, height);
|
||||
if (m_surface == surface)
|
||||
{
|
||||
if (m_display)
|
||||
@ -412,10 +461,11 @@ void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidat
|
||||
m_game_list->Refresh(invalidate_cache, invalidate_database);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::ApplySettings()
|
||||
void AndroidHostInterface::ApplySettings(bool display_osd_messages)
|
||||
{
|
||||
Settings old_settings = std::move(g_settings);
|
||||
CommonHostInterface::LoadSettings(m_settings_interface);
|
||||
CommonHostInterface::FixIncompatibleSettings(display_osd_messages);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
@ -439,10 +489,26 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((s_AndroidHostInterface_constructor = env->GetMethodID(s_AndroidHostInterface_class, "<init>", "()V")) ==
|
||||
nullptr ||
|
||||
(s_AndroidHostInterface_field_nativePointer =
|
||||
env->GetFieldID(s_AndroidHostInterface_class, "nativePointer", "J")) == nullptr)
|
||||
jclass emulation_activity_class;
|
||||
if ((s_AndroidHostInterface_constructor =
|
||||
env->GetMethodID(s_AndroidHostInterface_class, "<init>", "(Landroid/content/Context;)V")) == nullptr ||
|
||||
(s_AndroidHostInterface_field_mNativePointer =
|
||||
env->GetFieldID(s_AndroidHostInterface_class, "mNativePointer", "J")) == nullptr ||
|
||||
(s_AndroidHostInterface_method_reportError =
|
||||
env->GetMethodID(s_AndroidHostInterface_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
||||
(s_AndroidHostInterface_method_reportMessage =
|
||||
env->GetMethodID(s_AndroidHostInterface_class, "reportMessage", "(Ljava/lang/String;)V")) == nullptr ||
|
||||
(emulation_activity_class = env->FindClass("com/github/stenzek/duckstation/EmulationActivity")) == nullptr ||
|
||||
(s_EmulationActivity_method_reportError =
|
||||
env->GetMethodID(emulation_activity_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
||||
(s_EmulationActivity_method_reportMessage =
|
||||
env->GetMethodID(emulation_activity_class, "reportMessage", "(Ljava/lang/String;)V")) == nullptr ||
|
||||
(s_EmulationActivity_method_onEmulationStarted =
|
||||
env->GetMethodID(emulation_activity_class, "onEmulationStarted", "()V")) == nullptr ||
|
||||
(s_EmulationActivity_method_onEmulationStopped =
|
||||
env->GetMethodID(emulation_activity_class, "onEmulationStopped", "()V")) == nullptr ||
|
||||
(s_EmulationActivity_method_onGameTitleChanged =
|
||||
env->GetMethodID(emulation_activity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr)
|
||||
{
|
||||
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
||||
return -1;
|
||||
@ -457,12 +523,13 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
#define DEFINE_JNI_ARGS_METHOD(return_type, name, ...) \
|
||||
extern "C" JNIEXPORT return_type JNICALL Java_com_github_stenzek_duckstation_##name(JNIEnv* env, __VA_ARGS__)
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_create, jobject unused, jobject context_object, jstring user_directory)
|
||||
DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_create, jobject unused, jobject context_object,
|
||||
jstring user_directory)
|
||||
{
|
||||
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
||||
|
||||
// initialize the java side
|
||||
jobject java_obj = env->NewObject(s_AndroidHostInterface_class, s_AndroidHostInterface_constructor);
|
||||
jobject java_obj = env->NewObject(s_AndroidHostInterface_class, s_AndroidHostInterface_constructor, context_object);
|
||||
if (!java_obj)
|
||||
{
|
||||
Log_ErrorPrint("Failed to create Java AndroidHostInterface");
|
||||
@ -483,7 +550,7 @@ DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_create, jobject unused, job
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
env->SetLongField(java_obj, s_AndroidHostInterface_field_nativePointer,
|
||||
env->SetLongField(java_obj, s_AndroidHostInterface_field_mNativePointer,
|
||||
static_cast<long>(reinterpret_cast<uintptr_t>(cpp_obj)));
|
||||
|
||||
return java_obj;
|
||||
@ -494,8 +561,8 @@ DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_isEmulationThreadRunning,
|
||||
return AndroidHelpers::GetNativeClass(env, obj)->IsEmulationThreadRunning();
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_startEmulationThread, jobject obj, jobject surface,
|
||||
jstring filename, jstring state_filename)
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_startEmulationThread, jobject obj, jobject emulationActivity,
|
||||
jobject surface, jstring filename, jboolean resume_state, jstring state_filename)
|
||||
{
|
||||
ANativeWindow* native_surface = ANativeWindow_fromSurface(env, surface);
|
||||
if (!native_surface)
|
||||
@ -509,7 +576,8 @@ DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_startEmulationThread, jobj
|
||||
SystemBootParameters boot_params;
|
||||
boot_params.filename = AndroidHelpers::JStringToString(env, filename);
|
||||
|
||||
return AndroidHelpers::GetNativeClass(env, obj)->StartEmulationThread(native_surface, std::move(boot_params));
|
||||
return AndroidHelpers::GetNativeClass(env, obj)->StartEmulationThread(emulationActivity, native_surface,
|
||||
std::move(boot_params), resume_state);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_stopEmulationThread, jobject obj)
|
||||
@ -526,7 +594,8 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_surfaceChanged, jobject obj, j
|
||||
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread(
|
||||
[hi, native_surface, format, width, height]() { hi->SurfaceChanged(native_surface, format, width, height); }, true);
|
||||
[hi, native_surface, format, width, height]() { hi->SurfaceChanged(native_surface, format, width, height); },
|
||||
false);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerType, jobject obj, jint index, jstring controller_type)
|
||||
@ -629,11 +698,11 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
if (hi->IsEmulationThreadRunning())
|
||||
{
|
||||
hi->RunOnEmulationThread([hi]() { hi->ApplySettings(); });
|
||||
hi->RunOnEmulationThread([hi]() { hi->ApplySettings(false); });
|
||||
}
|
||||
else
|
||||
{
|
||||
hi->ApplySettings();
|
||||
hi->ApplySettings(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,8 @@ public:
|
||||
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override;
|
||||
|
||||
bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); }
|
||||
bool StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params);
|
||||
bool StartEmulationThread(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state);
|
||||
void RunOnEmulationThread(std::function<void()> function, bool blocking = false);
|
||||
void StopEmulationThread();
|
||||
|
||||
@ -46,7 +47,7 @@ public:
|
||||
void SetControllerAxisState(u32 index, s32 button_code, float value);
|
||||
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database);
|
||||
void ApplySettings();
|
||||
void ApplySettings(bool display_osd_messages);
|
||||
|
||||
protected:
|
||||
void SetUserDirectory() override;
|
||||
@ -56,13 +57,18 @@ protected:
|
||||
bool AcquireHostDisplay() override;
|
||||
void ReleaseHostDisplay() override;
|
||||
|
||||
void OnSystemDestroyed() override;
|
||||
void OnRunningGameChanged() override;
|
||||
|
||||
private:
|
||||
void EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params);
|
||||
void EmulationThreadEntryPoint(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state);
|
||||
|
||||
void CreateImGuiContext();
|
||||
void DestroyImGuiContext();
|
||||
|
||||
jobject m_java_object = {};
|
||||
jobject m_emulation_activity_object = {};
|
||||
|
||||
AndroidSettingsInterface m_settings_interface;
|
||||
|
||||
@ -73,8 +79,6 @@ private:
|
||||
|
||||
std::thread m_emulation_thread;
|
||||
std::atomic_bool m_emulation_thread_stop_request{false};
|
||||
std::atomic_bool m_emulation_thread_start_result{false};
|
||||
Common::Event m_emulation_thread_started;
|
||||
};
|
||||
|
||||
namespace AndroidHelpers {
|
||||
|
Reference in New Issue
Block a user