Merge pull request #10008 from JosJuice/android-remove-emulationstate
Android: Remove the EmulationState class
This commit is contained in:
commit
0d8ad5f53a
|
@ -393,6 +393,8 @@ public final class NativeLibrary
|
||||||
|
|
||||||
public static native void SurfaceDestroyed();
|
public static native void SurfaceDestroyed();
|
||||||
|
|
||||||
|
public static native boolean HasSurface();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpauses emulation from a paused state.
|
* Unpauses emulation from a paused state.
|
||||||
*/
|
*/
|
||||||
|
@ -408,6 +410,13 @@ public final class NativeLibrary
|
||||||
*/
|
*/
|
||||||
public static native void StopEmulation();
|
public static native void StopEmulation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that IsRunning will return true from now on until emulation exits.
|
||||||
|
* (If this is not called, IsRunning will start returning true at some point
|
||||||
|
* after calling Run.)
|
||||||
|
*/
|
||||||
|
public static native void SetIsBooting();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if emulation is running (or is paused).
|
* Returns true if emulation is running (or is paused).
|
||||||
*/
|
*/
|
||||||
|
@ -415,6 +424,8 @@ public final class NativeLibrary
|
||||||
|
|
||||||
public static native boolean IsRunningAndStarted();
|
public static native boolean IsRunningAndStarted();
|
||||||
|
|
||||||
|
public static native boolean IsRunningAndUnpaused();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables CPU block profiling
|
* Enables or disables CPU block profiling
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,7 +32,9 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
|
|
||||||
private InputOverlay mInputOverlay;
|
private InputOverlay mInputOverlay;
|
||||||
|
|
||||||
private EmulationState mEmulationState;
|
private String[] mGamePaths;
|
||||||
|
private boolean mRunWhenSurfaceIsValid;
|
||||||
|
private boolean mLoadPreviousTemporaryState;
|
||||||
|
|
||||||
private EmulationActivity activity;
|
private EmulationActivity activity;
|
||||||
|
|
||||||
|
@ -73,8 +75,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|
||||||
String[] gamePaths = getArguments().getStringArray(KEY_GAMEPATHS);
|
mGamePaths = getArguments().getStringArray(KEY_GAMEPATHS);
|
||||||
mEmulationState = new EmulationState(gamePaths, getTemporaryStateFilePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,14 +118,18 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
public void onResume()
|
public void onResume()
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mEmulationState.run(activity.isActivityRecreated());
|
run(activity.isActivityRecreated());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause()
|
public void onPause()
|
||||||
{
|
{
|
||||||
if (mEmulationState.isRunning() && !NativeLibrary.IsShowingAlertMessage())
|
if (NativeLibrary.IsRunningAndUnpaused() && !NativeLibrary.IsShowingAlertMessage())
|
||||||
mEmulationState.pause();
|
{
|
||||||
|
Log.debug("[EmulationFragment] Pausing emulation.");
|
||||||
|
NativeLibrary.PauseEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,18 +178,25 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
|
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
|
||||||
mEmulationState.newSurface(holder.getSurface());
|
NativeLibrary.SurfaceChanged(holder.getSurface());
|
||||||
|
if (mRunWhenSurfaceIsValid)
|
||||||
|
{
|
||||||
|
runWithValidSurface();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder)
|
public void surfaceDestroyed(@NonNull SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
mEmulationState.clearSurface();
|
Log.debug("[EmulationFragment] Surface destroyed.");
|
||||||
|
NativeLibrary.SurfaceDestroyed();
|
||||||
|
mRunWhenSurfaceIsValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopEmulation()
|
public void stopEmulation()
|
||||||
{
|
{
|
||||||
mEmulationState.stop();
|
Log.debug("[EmulationFragment] Stopping emulation.");
|
||||||
|
NativeLibrary.StopEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startConfiguringControls()
|
public void startConfiguringControls()
|
||||||
|
@ -210,173 +222,70 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
return mInputOverlay != null && mInputOverlay.isInEditMode();
|
return mInputOverlay != null && mInputOverlay.isInEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EmulationState
|
private void run(boolean isActivityRecreated)
|
||||||
{
|
{
|
||||||
private enum State
|
if (isActivityRecreated)
|
||||||
{
|
{
|
||||||
STOPPED, RUNNING, PAUSED
|
if (NativeLibrary.IsRunning())
|
||||||
}
|
|
||||||
|
|
||||||
private final String[] mGamePaths;
|
|
||||||
private State state;
|
|
||||||
private Surface mSurface;
|
|
||||||
private boolean mRunWhenSurfaceIsValid;
|
|
||||||
private boolean loadPreviousTemporaryState;
|
|
||||||
private final String temporaryStatePath;
|
|
||||||
|
|
||||||
EmulationState(String[] gamePaths, String temporaryStatePath)
|
|
||||||
{
|
|
||||||
mGamePaths = gamePaths;
|
|
||||||
this.temporaryStatePath = temporaryStatePath;
|
|
||||||
// Starting state is stopped.
|
|
||||||
state = State.STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters for the current state
|
|
||||||
|
|
||||||
public synchronized boolean isStopped()
|
|
||||||
{
|
|
||||||
return state == State.STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isPaused()
|
|
||||||
{
|
|
||||||
return state == State.PAUSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isRunning()
|
|
||||||
{
|
|
||||||
return state == State.RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
// State changing methods
|
|
||||||
|
|
||||||
public synchronized void stop()
|
|
||||||
{
|
|
||||||
if (state != State.STOPPED)
|
|
||||||
{
|
{
|
||||||
Log.debug("[EmulationFragment] Stopping emulation.");
|
mLoadPreviousTemporaryState = false;
|
||||||
state = State.STOPPED;
|
deleteFile(getTemporaryStateFilePath());
|
||||||
NativeLibrary.StopEmulation();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.warning("[EmulationFragment] Stop called while already stopped.");
|
mLoadPreviousTemporaryState = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public synchronized void pause()
|
|
||||||
{
|
{
|
||||||
if (state != State.PAUSED)
|
Log.debug("[EmulationFragment] activity resumed or fresh start");
|
||||||
{
|
mLoadPreviousTemporaryState = false;
|
||||||
state = State.PAUSED;
|
// activity resumed without being killed or this is the first run
|
||||||
Log.debug("[EmulationFragment] Pausing emulation.");
|
deleteFile(getTemporaryStateFilePath());
|
||||||
|
|
||||||
NativeLibrary.PauseEmulation();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.warning("[EmulationFragment] Pause called while already paused.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void run(boolean isActivityRecreated)
|
// If the surface is set, run now. Otherwise, wait for it to get set.
|
||||||
|
if (NativeLibrary.HasSurface())
|
||||||
{
|
{
|
||||||
if (isActivityRecreated)
|
runWithValidSurface();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRunWhenSurfaceIsValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runWithValidSurface()
|
||||||
|
{
|
||||||
|
mRunWhenSurfaceIsValid = false;
|
||||||
|
if (!NativeLibrary.IsRunning())
|
||||||
|
{
|
||||||
|
NativeLibrary.SetIsBooting();
|
||||||
|
|
||||||
|
Thread emulationThread = new Thread(() ->
|
||||||
{
|
{
|
||||||
if (NativeLibrary.IsRunning())
|
if (mLoadPreviousTemporaryState)
|
||||||
{
|
{
|
||||||
loadPreviousTemporaryState = false;
|
Log.debug("[EmulationFragment] Starting emulation thread from previous state.");
|
||||||
state = State.PAUSED;
|
NativeLibrary.Run(mGamePaths, getTemporaryStateFilePath(), true);
|
||||||
deleteFile(temporaryStatePath);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loadPreviousTemporaryState = true;
|
Log.debug("[EmulationFragment] Starting emulation thread.");
|
||||||
|
NativeLibrary.Run(mGamePaths);
|
||||||
}
|
}
|
||||||
}
|
EmulationActivity.stopIgnoringLaunchRequests();
|
||||||
else
|
}, "NativeEmulation");
|
||||||
{
|
emulationThread.start();
|
||||||
Log.debug("[EmulationFragment] activity resumed or fresh start");
|
|
||||||
loadPreviousTemporaryState = false;
|
|
||||||
// activity resumed without being killed or this is the first run
|
|
||||||
deleteFile(temporaryStatePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the surface is set, run now. Otherwise, wait for it to get set.
|
|
||||||
if (mSurface != null)
|
|
||||||
{
|
|
||||||
runWithValidSurface();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mRunWhenSurfaceIsValid = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Surface callbacks
|
|
||||||
public synchronized void newSurface(Surface surface)
|
|
||||||
{
|
{
|
||||||
mSurface = surface;
|
if (!EmulationActivity.getHasUserPausedEmulation() && !NativeLibrary.IsShowingAlertMessage())
|
||||||
if (mRunWhenSurfaceIsValid)
|
|
||||||
{
|
{
|
||||||
runWithValidSurface();
|
Log.debug("[EmulationFragment] Resuming emulation.");
|
||||||
|
NativeLibrary.UnPauseEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clearSurface()
|
|
||||||
{
|
|
||||||
if (mSurface == null)
|
|
||||||
{
|
|
||||||
Log.warning("[EmulationFragment] clearSurface called, but surface already null.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mSurface = null;
|
|
||||||
Log.debug("[EmulationFragment] Surface destroyed.");
|
|
||||||
|
|
||||||
NativeLibrary.SurfaceDestroyed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runWithValidSurface()
|
|
||||||
{
|
|
||||||
mRunWhenSurfaceIsValid = false;
|
|
||||||
if (state == State.STOPPED)
|
|
||||||
{
|
|
||||||
Thread emulationThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
NativeLibrary.SurfaceChanged(mSurface);
|
|
||||||
if (loadPreviousTemporaryState)
|
|
||||||
{
|
|
||||||
Log.debug("[EmulationFragment] Starting emulation thread from previous state.");
|
|
||||||
NativeLibrary.Run(mGamePaths, temporaryStatePath, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.debug("[EmulationFragment] Starting emulation thread.");
|
|
||||||
NativeLibrary.Run(mGamePaths);
|
|
||||||
}
|
|
||||||
EmulationActivity.stopIgnoringLaunchRequests();
|
|
||||||
}, "NativeEmulation");
|
|
||||||
emulationThread.start();
|
|
||||||
}
|
|
||||||
else if (state == State.PAUSED)
|
|
||||||
{
|
|
||||||
NativeLibrary.SurfaceChanged(mSurface);
|
|
||||||
if (!EmulationActivity.getHasUserPausedEmulation() &&
|
|
||||||
!NativeLibrary.IsShowingAlertMessage())
|
|
||||||
{
|
|
||||||
Log.debug("[EmulationFragment] Resuming emulation.");
|
|
||||||
NativeLibrary.UnPauseEmulation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.debug("[EmulationFragment] Bug, run called while already running.");
|
|
||||||
}
|
|
||||||
state = State.RUNNING;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveTemporaryState()
|
public void saveTemporaryState()
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Event.h"
|
#include "Common/Event.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
#include "Common/Logging/LogManager.h"
|
#include "Common/Logging/LogManager.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
@ -81,6 +82,7 @@ Common::Event s_update_main_frame_event;
|
||||||
std::mutex s_surface_lock;
|
std::mutex s_surface_lock;
|
||||||
bool s_need_nonblocking_alert_msg;
|
bool s_need_nonblocking_alert_msg;
|
||||||
|
|
||||||
|
Common::Flag s_is_booting;
|
||||||
bool s_have_wm_user_stop = false;
|
bool s_have_wm_user_stop = false;
|
||||||
bool s_game_metadata_is_valid = false;
|
bool s_game_metadata_is_valid = false;
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
@ -247,9 +249,14 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulatio
|
||||||
s_update_main_frame_event.Set();
|
s_update_main_frame_event.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetIsBooting(JNIEnv*, jclass)
|
||||||
|
{
|
||||||
|
s_is_booting.Set();
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv*, jclass)
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv*, jclass)
|
||||||
{
|
{
|
||||||
return static_cast<jboolean>(Core::IsRunning());
|
return s_is_booting.IsSet() || static_cast<jboolean>(Core::IsRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndStarted(JNIEnv*,
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndStarted(JNIEnv*,
|
||||||
|
@ -258,6 +265,12 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunnin
|
||||||
return static_cast<jboolean>(Core::IsRunningAndStarted());
|
return static_cast<jboolean>(Core::IsRunningAndStarted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndUnpaused(JNIEnv*, jclass)
|
||||||
|
{
|
||||||
|
return static_cast<jboolean>(Core::GetState() == Core::State::Running);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
||||||
JNIEnv* env, jclass, jstring jDevice, jint Button, jint Action)
|
JNIEnv* env, jclass, jstring jDevice, jint Button, jint Action)
|
||||||
{
|
{
|
||||||
|
@ -430,7 +443,25 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
|
||||||
jclass)
|
jclass)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(s_surface_lock);
|
{
|
||||||
|
// If emulation continues running without a valid surface, we will probably crash,
|
||||||
|
// so pause emulation until we get a valid surface again. EmulationFragment handles resuming.
|
||||||
|
|
||||||
|
std::unique_lock host_identity_guard(s_host_identity_lock);
|
||||||
|
|
||||||
|
while (s_is_booting.IsSet())
|
||||||
|
{
|
||||||
|
// Need to wait for boot to finish before we can pause
|
||||||
|
host_identity_guard.unlock();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
host_identity_guard.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Core::GetState() == Core::State::Running)
|
||||||
|
Core::SetState(Core::State::Paused);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard surface_guard(s_surface_lock);
|
||||||
|
|
||||||
if (g_renderer)
|
if (g_renderer)
|
||||||
g_renderer->ChangeSurface(nullptr);
|
g_renderer->ChangeSurface(nullptr);
|
||||||
|
@ -442,6 +473,13 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurface(JNIEnv*, jclass)
|
||||||
|
{
|
||||||
|
std::lock_guard guard(s_surface_lock);
|
||||||
|
|
||||||
|
return s_surf ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAspectRatio(JNIEnv*,
|
JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAspectRatio(JNIEnv*,
|
||||||
jclass)
|
jclass)
|
||||||
{
|
{
|
||||||
|
@ -554,6 +592,7 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s_is_booting.Clear();
|
||||||
s_need_nonblocking_alert_msg = false;
|
s_need_nonblocking_alert_msg = false;
|
||||||
surface_guard.unlock();
|
surface_guard.unlock();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue