Merge pull request #8452 from JosJuice/android-emulationactivity-rotation-crash

Android: Replace emulation rotation crash workaround with proper fix
This commit is contained in:
Connor McLaughlin 2019-11-08 10:45:21 +10:00 committed by GitHub
commit 18ba1fd723
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 36 additions and 24 deletions

View File

@ -400,6 +400,8 @@ public final class NativeLibrary
*/ */
public static native void StopEmulation(); public static native void StopEmulation();
public static native void WaitUntilDoneBooting();
/** /**
* Returns true if emulation is running (or is paused). * Returns true if emulation is running (or is paused).
*/ */

View File

@ -235,10 +235,6 @@ public final class EmulationActivity extends AppCompatActivity
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// Find the EmulationFragment
mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
.findFragmentById(R.id.frame_emulation_fragment);
if (savedInstanceState == null) if (savedInstanceState == null)
{ {
// Get params we were passed // Get params we were passed
@ -251,9 +247,7 @@ public final class EmulationActivity extends AppCompatActivity
} }
else else
{ {
// Could have recreated the activity(rotate) before creating the fragment. If the fragment activityRecreated = true;
// doesn't exist, treat this as a new start.
activityRecreated = mEmulationFragment != null;
restoreState(savedInstanceState); restoreState(savedInstanceState);
} }
@ -311,9 +305,10 @@ public final class EmulationActivity extends AppCompatActivity
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} }
if (!(mDeviceHasTouchScreen && lockLandscape && // Find or create the EmulationFragment
getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) && mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
mEmulationFragment == null) .findFragmentById(R.id.frame_emulation_fragment);
if (mEmulationFragment == null)
{ {
mEmulationFragment = EmulationFragment.newInstance(mPaths); mEmulationFragment = EmulationFragment.newInstance(mPaths);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()

View File

@ -316,8 +316,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
state = State.PAUSED; state = State.PAUSED;
Log.debug("[EmulationFragment] Pausing emulation."); Log.debug("[EmulationFragment] Pausing emulation.");
// Release the surface before pausing, since emulation has to be running for that.
NativeLibrary.SurfaceDestroyed();
NativeLibrary.PauseEmulation(); NativeLibrary.PauseEmulation();
} }
else else
@ -381,19 +379,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mSurface = null; mSurface = null;
Log.debug("[EmulationFragment] Surface destroyed."); Log.debug("[EmulationFragment] Surface destroyed.");
if (state == State.RUNNING) if (state != State.STOPPED)
{ {
NativeLibrary.SurfaceDestroyed(); // In order to avoid dereferencing nullptr, we must not destroy the surface while booting
state = State.PAUSED; // the core, so wait here if necessary. An easy (but not 100% consistent) way to reach
} // this method while the core is booting is by having landscape orientation lock enabled
else if (state == State.PAUSED) // and starting emulation while the phone is in portrait mode, leading to the activity
{ // being recreated very soon after NativeLibrary.Run has been called.
Log.warning("[EmulationFragment] Surface cleared while emulation paused."); NativeLibrary.WaitUntilDoneBooting();
}
else
{
Log.warning("[EmulationFragment] Surface cleared while emulation stopped.");
} }
NativeLibrary.SurfaceDestroyed();
} }
} }

View File

@ -197,6 +197,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulati
jobject obj); jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv* env, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv* env,
jobject obj); jobject obj);
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_WaitUntilDoneBooting(JNIEnv* env, jobject obj);
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env, JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env,
jobject obj); jobject obj);
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent( JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
@ -283,6 +285,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulatio
s_update_main_frame_event.Set(); // Kick the waiting event s_update_main_frame_event.Set(); // Kick the waiting event
} }
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_WaitUntilDoneBooting(JNIEnv* env, jobject obj)
{
Core::WaitUntilDoneBooting();
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env, JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env,
jobject obj) jobject obj)
{ {

View File

@ -94,6 +94,7 @@ static bool s_is_stopping = false;
static bool s_hardware_initialized = false; static bool s_hardware_initialized = false;
static bool s_is_started = false; static bool s_is_started = false;
static Common::Flag s_is_booting; static Common::Flag s_is_booting;
static Common::Event s_done_booting;
static std::thread s_emu_thread; static std::thread s_emu_thread;
static StateChangedCallbackFunc s_on_state_changed_callback; static StateChangedCallbackFunc s_on_state_changed_callback;
@ -236,6 +237,8 @@ bool Init(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
g_video_backend->PrepareWindow(wsi); g_video_backend->PrepareWindow(wsi);
// Start the emu thread // Start the emu thread
s_done_booting.Reset();
s_is_booting.Set();
s_emu_thread = std::thread(EmuThread, std::move(boot), wsi); s_emu_thread = std::thread(EmuThread, std::move(boot), wsi);
return true; return true;
} }
@ -412,11 +415,11 @@ static void FifoPlayerThread(const std::optional<std::string>& savestate_path,
static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi) static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi)
{ {
const SConfig& core_parameter = SConfig::GetInstance(); const SConfig& core_parameter = SConfig::GetInstance();
s_is_booting.Set();
if (s_on_state_changed_callback) if (s_on_state_changed_callback)
s_on_state_changed_callback(State::Starting); s_on_state_changed_callback(State::Starting);
Common::ScopeGuard flag_guard{[] { Common::ScopeGuard flag_guard{[] {
s_is_booting.Clear(); s_is_booting.Clear();
s_done_booting.Set();
s_is_started = false; s_is_started = false;
s_is_stopping = false; s_is_stopping = false;
s_wants_determinism = false; s_wants_determinism = false;
@ -539,6 +542,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
// The hardware is initialized. // The hardware is initialized.
s_hardware_initialized = true; s_hardware_initialized = true;
s_is_booting.Clear(); s_is_booting.Clear();
s_done_booting.Set();
// Set execution state to known values (CPU/FIFO/Audio Paused) // Set execution state to known values (CPU/FIFO/Audio Paused)
CPU::Break(); CPU::Break();
@ -662,6 +666,12 @@ State GetState()
return State::Uninitialized; return State::Uninitialized;
} }
void WaitUntilDoneBooting()
{
if (s_is_booting.IsSet() || !s_hardware_initialized)
s_done_booting.Wait();
}
static std::string GenerateScreenshotFolderPath() static std::string GenerateScreenshotFolderPath()
{ {
const std::string& gameId = SConfig::GetInstance().GetGameID(); const std::string& gameId = SConfig::GetInstance().GetGameID();

View File

@ -56,6 +56,7 @@ bool WantsDeterminism();
// [NOT THREADSAFE] For use by Host only // [NOT THREADSAFE] For use by Host only
void SetState(State state); void SetState(State state);
State GetState(); State GetState();
void WaitUntilDoneBooting();
void SaveScreenShot(bool wait_for_completion = false); void SaveScreenShot(bool wait_for_completion = false);
void SaveScreenShot(std::string_view name, bool wait_for_completion = false); void SaveScreenShot(std::string_view name, bool wait_for_completion = false);