Android: Decouple SurfaceView initialization and emulation start.

This commit is contained in:
Eder Bastos 2015-05-24 12:04:32 -04:00
parent ad9dae30a8
commit 4faff3cf62
3 changed files with 127 additions and 8 deletions

View File

@ -4,7 +4,11 @@ import android.app.Fragment;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
@ -14,7 +18,7 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.emulation.overlay.InputOverlay;
public final class EmulationFragment extends Fragment
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback
{
public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".emulation_fragment";
@ -22,8 +26,19 @@ public final class EmulationFragment extends Fragment
private SharedPreferences mPreferences;
private SurfaceView mSurfaceView;
private Surface mSurface;
private InputOverlay mInputOverlay;
private Thread mEmulationThread;
private String mPath;
private boolean mEmulationStarted;
private boolean mEmulationRunning;
public static EmulationFragment newInstance(String path)
{
EmulationFragment fragment = new EmulationFragment();
@ -43,6 +58,9 @@ public final class EmulationFragment extends Fragment
{
super.onCreate(savedInstanceState);
// So this fragment doesn't restart on configuration changes; i.e. rotation.
setRetainInstance(true);
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
}
@ -52,14 +70,15 @@ public final class EmulationFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
String path = getArguments().getString(ARGUMENT_GAME_PATH);
mPath = getArguments().getString(ARGUMENT_GAME_PATH);
NativeLibrary.SetFilename(mPath);
View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
mSurfaceView = (SurfaceView) contents.findViewById(R.id.surface_emulation);
mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay);
NativeLibrary.SetFilename(path);
mSurfaceView.getHolder().addCallback(this);
// If the input overlay was previously disabled, then don't show it.
if (!mPreferences.getBoolean("showInputOverlay", true))
@ -67,6 +86,18 @@ public final class EmulationFragment extends Fragment
mInputOverlay.setVisibility(View.GONE);
}
if (savedInstanceState == null)
{
mEmulationThread = new Thread(mEmulationRunner);
}
else
{
// Likely a rotation occurred.
// TODO Pass native code the Surface, which will have been recreated, from surfaceChanged()
// TODO Also, write the native code that will get the video backend to accept the new Surface as one of its own.
}
return contents;
}
@ -74,21 +105,24 @@ public final class EmulationFragment extends Fragment
public void onStart()
{
super.onStart();
NativeLibrary.UnPauseEmulation();
startEmulation();
}
@Override
public void onStop()
{
super.onStop();
NativeLibrary.PauseEmulation();
pauseEmulation();
}
@Override
public void onDestroyView()
{
super.onDestroyView();
NativeLibrary.StopEmulation();
if (getActivity().isFinishing())
{
NativeLibrary.StopEmulation();
}
}
public void toggleInputOverlayVisibility()
@ -111,4 +145,78 @@ public final class EmulationFragment extends Fragment
editor.apply();
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
Log.d("DolphinEmu", "Surface created.");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.d("DolphinEmu", "Surface changed. Resolution: " + width + "x" + height);
mSurface = holder.getSurface();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.d("DolphinEmu", "Surface destroyed.");
if (mEmulationRunning)
{
pauseEmulation();
}
}
private void startEmulation()
{
if (!mEmulationStarted)
{
Log.d("DolphinEmu", "Starting emulation thread.");
mEmulationThread.start();
}
else
{
Log.d("DolphinEmu", "Resuming emulation.");
NativeLibrary.UnPauseEmulation();
}
mEmulationRunning = true;
}
private void pauseEmulation()
{
Log.d("DolphinEmu", "Pausing emulation.");
NativeLibrary.PauseEmulation();
mEmulationRunning = false;
}
private Runnable mEmulationRunner = new Runnable()
{
@Override
public void run()
{
mEmulationRunning = true;
mEmulationStarted = true;
// Loop until onSurfaceCreated succeeds
while (mSurface == null)
{
if (!mEmulationRunning)
{
// So that if the user quits before this gets a surface, we don't loop infinitely.
return;
}
}
Log.i("DolphinEmu", "Starting emulation: " + mSurface);
// Start emulation using the provided Surface.
NativeLibrary.Run(mSurface);
}
};
}

View File

@ -5,7 +5,7 @@
tools:context="org.dolphinemu.dolphinemu.fragments.EmulationFragment">
<!-- This is what everything is rendered to during emulation -->
<org.dolphinemu.dolphinemu.emulation.NativeGLSurfaceView
<SurfaceView
android:id="@+id/surface_emulation"
android:layout_height="match_parent"
android:layout_width="match_parent"

View File

@ -561,8 +561,16 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDi
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj, jobject _surf)
{
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", g_filename.c_str());
surf = ANativeWindow_fromSurface(env, _surf);
if (surf == nullptr)
{
__android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null.");
return;
}
// Install our callbacks
OSD::AddCallback(OSD::OSD_INIT, ButtonManager::Init);
OSD::AddCallback(OSD::OSD_SHUTDOWN, ButtonManager::Shutdown);
@ -574,8 +582,11 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
// No use running the loop when booting fails
if ( BootManager::BootCore( g_filename.c_str() ) )
{
PowerPC::Start();
while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN)
updateMainFrameEvent.Wait();
}
UICommon::Shutdown();
ANativeWindow_release(surf);