diff --git a/src/android/app/src/androidTest/java/org/citra/citra_emu/ExampleInstrumentedTest.java b/src/android/app/src/androidTest/java/org/citra/citra_emu/ExampleInstrumentedTest.java
new file mode 100644
index 000000000..671fb4b30
--- /dev/null
+++ b/src/android/app/src/androidTest/java/org/citra/citra_emu/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package org.citra.citra_emu;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("org.citra.citra_emu", appContext.getPackageName());
+ }
+}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
index 9baeeb5e4..464336592 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
@@ -172,8 +172,6 @@ public final class NativeLibrary {
public static native void SurfaceDestroyed();
- public static native void DoFrame();
-
/**
* Unpauses emulation from a paused state.
*/
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.java
index 445faa047..1057e4c16 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.java
@@ -6,7 +6,6 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
-import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -31,7 +30,7 @@ import org.citra.citra_emu.utils.DirectoryStateReceiver;
import org.citra.citra_emu.utils.EmulationMenuSettings;
import org.citra.citra_emu.utils.Log;
-public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback {
+public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback {
private static final String KEY_GAMEPATH = "gamepath";
private static final Handler perfStatsUpdateHandler = new Handler();
@@ -115,7 +114,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
@Override
public void onResume() {
super.onResume();
- Choreographer.getInstance().postFrameCallback(this);
if (DirectoryInitialization.areCitraDirectoriesReady()) {
mEmulationState.run(activity.isActivityRecreated());
} else {
@@ -130,11 +128,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
directoryStateReceiver = null;
}
- if (mEmulationState.isRunning()) {
+ if (mEmulationState.isRunning())
mEmulationState.pause();
- }
-
- Choreographer.getInstance().removeFrameCallback(this);
super.onPause();
}
@@ -232,12 +227,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mEmulationState.clearSurface();
}
- @Override
- public void doFrame(long frameTimeNanos) {
- Choreographer.getInstance().postFrameCallback(this);
- NativeLibrary.DoFrame();
- }
-
public void stopEmulation() {
mEmulationState.stop();
}
@@ -353,9 +342,9 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private void runWithValidSurface() {
mRunWhenSurfaceIsValid = false;
if (state == State.STOPPED) {
- NativeLibrary.SurfaceChanged(mSurface);
Thread mEmulationThread = new Thread(() ->
{
+ NativeLibrary.SurfaceChanged(mSurface);
Log.debug("[EmulationFragment] Starting emulation thread.");
NativeLibrary.Run(mGamePath);
}, "NativeEmulation");
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp
index 068018b69..9ba9605ac 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.cpp
+++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp
@@ -79,7 +79,6 @@ static void UpdateLandscapeScreenLayout() {
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
render_window = surface;
- StopPresenting();
}
bool EmuWindow_Android::OnTouchEvent(int x, int y, bool pressed) {
@@ -220,6 +219,7 @@ void EmuWindow_Android::DestroyContext() {
}
EmuWindow_Android::~EmuWindow_Android() {
+ StopPresenting();
DestroyWindowSurface();
DestroyContext();
}
@@ -228,26 +228,34 @@ std::unique_ptr EmuWindow_Android::CreateSharedContex
return std::make_unique(egl_display, egl_config, egl_context);
}
-void EmuWindow_Android::StopPresenting() {
- if (presenting_state == PresentingState::Running) {
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- presenting_state = PresentingState::Stopped;
+void EmuWindow_Android::StartPresenting() {
+ ASSERT(!presentation_thread);
+ is_presenting = true;
+ presentation_thread =
+ std::make_unique([emu_window{this}] { emu_window->Present(); });
}
-void EmuWindow_Android::TryPresenting() {
- if (presenting_state != PresentingState::Running) {
- if (presenting_state == PresentingState::Initial) {
- eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- presenting_state = PresentingState::Running;
- } else {
- return;
- }
+void EmuWindow_Android::StopPresenting() {
+ is_presenting = false;
+ if (presentation_thread) {
+ presentation_thread->join();
+ presentation_thread.reset();
}
+}
+
+bool EmuWindow_Android::IsPresenting() const {
+ return is_presenting;
+}
+
+void EmuWindow_Android::Present() {
+ eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
- VideoCore::g_renderer->TryPresent(100);
- eglSwapBuffers(egl_display, egl_surface);
+ while (IsPresenting()) {
+ VideoCore::g_renderer->TryPresent(100);
+ eglSwapBuffers(egl_display, egl_surface);
+ }
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
void EmuWindow_Android::PollEvents() {
@@ -258,10 +266,14 @@ void EmuWindow_Android::PollEvents() {
host_window = render_window;
render_window = nullptr;
+ if (IsPresenting()) {
+ StopPresenting();
+ }
+
DestroyWindowSurface();
CreateWindowSurface();
OnFramebufferSizeChanged();
- presenting_state = PresentingState::Initial;
+ StartPresenting();
}
void EmuWindow_Android::MakeCurrent() {
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h
index 10a293c96..f27b462ff 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.h
+++ b/src/android/app/src/main/jni/emu_window/emu_window.h
@@ -4,6 +4,7 @@
#pragma once
+#include
#include
#include
@@ -50,8 +51,9 @@ public:
void MakeCurrent() override;
void DoneCurrent() override;
- void TryPresenting();
+ void StartPresenting();
void StopPresenting();
+ bool IsPresenting() const;
std::unique_ptr CreateSharedContext() const override;
@@ -74,10 +76,7 @@ private:
std::unique_ptr core_context;
- enum class PresentingState {
- Initial,
- Running,
- Stopped,
- };
- PresentingState presenting_state{};
+ std::unique_ptr presentation_thread;
+
+ bool is_presenting{};
};
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 8875444d7..be513d1ef 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -102,6 +102,7 @@ static void TryShutdown() {
return;
}
+ window->StopPresenting();
window->DoneCurrent();
Core::System::GetInstance().Shutdown();
window.reset();
@@ -166,6 +167,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
is_running = true;
pause_emulation = false;
+ window->StartPresenting();
+
SCOPE_EXIT({ TryShutdown(); });
// Audio stretching on Android is only useful with lower framerates, disable it when fullspeed
@@ -193,7 +196,6 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
std::unique_lock pause_lock(paused_mutex);
running_cv.wait(pause_lock, [] { return !pause_emulation || !is_running; });
- window->PollEvents();
}
}
@@ -223,13 +225,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
}
}
-void Java_org_citra_citra_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {
- if (!is_running || pause_emulation) {
- return;
- }
- window->TryPresenting();
-}
-
void Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jint layout_option,
@@ -307,7 +302,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
[[maybe_unused]] jclass clazz) {
is_running = false;
pause_emulation = false;
- window->StopPresenting();
running_cv.notify_all();
}
diff --git a/src/android/app/src/test/java/org/citra/citra_emu/ExampleUnitTest.java b/src/android/app/src/test/java/org/citra/citra_emu/ExampleUnitTest.java
new file mode 100644
index 000000000..4e4bb317f
--- /dev/null
+++ b/src/android/app/src/test/java/org/citra/citra_emu/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package org.citra.citra_emu;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/src/android/build.gradle b/src/android/build.gradle
index 92b0e7081..6f6ecaf8e 100644
--- a/src/android/build.gradle
+++ b/src/android/build.gradle
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath 'com.android.tools.build:gradle:3.6.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files