Merge pull request #3494 from Sonicadvance1/android_rotation

[Android] Add support for rotation and minimizing the application
This commit is contained in:
Ryan Houdek 2016-01-11 01:18:37 -05:00
commit 78bb37b29f
10 changed files with 163 additions and 57 deletions

View File

@ -333,10 +333,12 @@ public final class NativeLibrary
/** /**
* Begins emulation. * Begins emulation.
*
* @param surf The surface to render to.
*/ */
public static native void Run(Surface surf); public static native void Run();
// Surface Handling
public static native void SurfaceChanged(Surface surf);
public static native void SurfaceDestroyed();
/** Unpauses emulation from a paused state. */ /** Unpauses emulation from a paused state. */
public static native void UnPauseEmulation(); public static native void UnPauseEmulation();

View File

@ -177,13 +177,16 @@ public final class EmulationActivity extends AppCompatActivity
} }
}); });
// Instantiate an EmulationFragment. if (savedInstanceState == null)
EmulationFragment emulationFragment = EmulationFragment.newInstance(path); {
// Instantiate an EmulationFragment.
EmulationFragment emulationFragment = EmulationFragment.newInstance(path);
// Add fragment to the activity - this triggers all its lifecycle callbacks. // Add fragment to the activity - this triggers all its lifecycle callbacks.
getFragmentManager().beginTransaction() getFragmentManager().beginTransaction()
.add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG)
.commit(); .commit();
}
if (mDeviceHasTouchScreen) if (mDeviceHasTouchScreen)
{ {

View File

@ -115,7 +115,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
public void onStop() public void onStop()
{ {
super.onStop(); super.onStop();
pauseEmulation();
} }
@Override @Override
@ -160,12 +159,14 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
{ {
Log.d("DolphinEmu", "Surface changed. Resolution: " + width + "x" + height); Log.d("DolphinEmu", "Surface changed. Resolution: " + width + "x" + height);
mSurface = holder.getSurface(); mSurface = holder.getSurface();
NativeLibrary.SurfaceChanged(mSurface);
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) public void surfaceDestroyed(SurfaceHolder holder)
{ {
Log.d("DolphinEmu", "Surface destroyed."); Log.d("DolphinEmu", "Surface destroyed.");
NativeLibrary.SurfaceDestroyed();
if (mEmulationRunning) if (mEmulationRunning)
{ {
@ -216,20 +217,14 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mEmulationRunning = true; mEmulationRunning = true;
mEmulationStarted = true; mEmulationStarted = true;
// Loop until onSurfaceCreated succeeds
while (mSurface == null) while (mSurface == null)
{
if (!mEmulationRunning) if (!mEmulationRunning)
{
// So that if the user quits before this gets a surface, we don't loop infinitely.
return; return;
}
}
Log.i("DolphinEmu", "Starting emulation: " + mSurface); Log.i("DolphinEmu", "Starting emulation: " + mSurface);
// Start emulation using the provided Surface. // Start emulation using the provided Surface.
NativeLibrary.Run(mSurface); NativeLibrary.Run();
} }
}; };
} }

View File

@ -18,6 +18,7 @@
#include "Common/CPUDetect.h" #include "Common/CPUDetect.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/GL/GLInterfaceBase.h"
#include "Common/Logging/LogManager.h" #include "Common/Logging/LogManager.h"
#include "Core/BootManager.h" #include "Core/BootManager.h"
@ -380,16 +381,17 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDi
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv *env, jobject obj, jboolean enable); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv *env, jobject obj, jboolean enable);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj, jobject _surf); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv *env, jobject obj, jobject _surf);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv *env, jobject obj)
{ {
PowerPC::Start(); Core::SetState(Core::CORE_RUN);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv *env, jobject obj)
{ {
PowerPC::Pause(); Core::SetState(Core::CORE_PAUSE);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv *env, jobject obj)
@ -596,18 +598,45 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClasses
g_jni_method_end = env->GetStaticMethodID(g_jni_class, "endEmulationActivity", "()V"); g_jni_method_end = env->GetStaticMethodID(g_jni_class, "endEmulationActivity", "()V");
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj, jobject _surf) // Surface Handling
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv *env, jobject obj, jobject _surf)
{
surf = ANativeWindow_fromSurface(env, _surf);
if (surf == nullptr)
__android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null.");
// If GLInterface isn't a thing yet then we don't need to let it know that the surface has changed
if (GLInterface)
{
GLInterface->UpdateHandle(surf);
Renderer::s_ChangedSurface.Reset();
Renderer::s_SurfaceNeedsChanged.Set();
Renderer::s_ChangedSurface.Wait();
}
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv *env, jobject obj)
{
if (surf)
{
ANativeWindow_release(surf);
surf = nullptr;
}
// If GLInterface isn't a thing yet then we don't need to let it know that the surface has changed
if (GLInterface)
{
GLInterface->UpdateHandle(nullptr);
Renderer::s_ChangedSurface.Reset();
Renderer::s_SurfaceNeedsChanged.Set();
Renderer::s_ChangedSurface.Wait();
}
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj)
{ {
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", g_filename.c_str()); __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 // Install our callbacks
OSD::AddCallback(OSD::CallbackType::Initialization, ButtonManager::Init); OSD::AddCallback(OSD::CallbackType::Initialization, ButtonManager::Init);
OSD::AddCallback(OSD::CallbackType::Shutdown, ButtonManager::Shutdown); OSD::AddCallback(OSD::CallbackType::Shutdown, ButtonManager::Shutdown);
@ -627,7 +656,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
Core::Shutdown(); Core::Shutdown();
UICommon::Shutdown(); UICommon::Shutdown();
ANativeWindow_release(surf);
if (surf)
{
ANativeWindow_release(surf);
surf = nullptr;
}
// Execute the Java method. // Execute the Java method.
env->CallStaticVoidMethod(g_jni_class, g_jni_method_end); env->CallStaticVoidMethod(g_jni_class, g_jni_method_end);

View File

@ -11,7 +11,8 @@
// Show the current FPS // Show the current FPS
void cInterfaceEGL::Swap() void cInterfaceEGL::Swap()
{ {
eglSwapBuffers(egl_dpy, egl_surf); if (egl_surf != EGL_NO_SURFACE)
eglSwapBuffers(egl_dpy, egl_surf);
} }
void cInterfaceEGL::SwapInterval(int Interval) void cInterfaceEGL::SwapInterval(int Interval)
{ {
@ -98,10 +99,11 @@ void cInterfaceEGL::DetectMode()
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() // Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceEGL::Create(void *window_handle, bool core) bool cInterfaceEGL::Create(void *window_handle, bool core)
{ {
const char *s;
EGLint egl_major, egl_minor; EGLint egl_major, egl_minor;
egl_dpy = OpenDisplay(); egl_dpy = OpenDisplay();
m_host_window = (EGLNativeWindowType) window_handle;
m_has_handle = !!window_handle;
if (!egl_dpy) if (!egl_dpy)
{ {
@ -116,7 +118,6 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
} }
/* Detection code */ /* Detection code */
EGLConfig config;
EGLint num_configs; EGLint num_configs;
DetectMode(); DetectMode();
@ -154,7 +155,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
break; break;
} }
if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) if (!eglChooseConfig( egl_dpy, attribs, &m_config, 1, &num_configs))
{ {
INFO_LOG(VIDEO, "Error: couldn't get an EGL visual config\n"); INFO_LOG(VIDEO, "Error: couldn't get an EGL visual config\n");
exit(1); exit(1);
@ -165,43 +166,86 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
else else
eglBindAPI(EGL_OPENGL_ES_API); eglBindAPI(EGL_OPENGL_ES_API);
EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; egl_ctx = eglCreateContext(egl_dpy, m_config, EGL_NO_CONTEXT, ctx_attribs );
EGLNativeWindowType native_window = InitializePlatform(host_window, config);
s = eglQueryString(egl_dpy, EGL_VERSION);
INFO_LOG(VIDEO, "EGL_VERSION = %s\n", s);
s = eglQueryString(egl_dpy, EGL_VENDOR);
INFO_LOG(VIDEO, "EGL_VENDOR = %s\n", s);
s = eglQueryString(egl_dpy, EGL_EXTENSIONS);
INFO_LOG(VIDEO, "EGL_EXTENSIONS = %s\n", s);
s = eglQueryString(egl_dpy, EGL_CLIENT_APIS);
INFO_LOG(VIDEO, "EGL_CLIENT_APIS = %s\n", s);
egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs );
if (!egl_ctx) if (!egl_ctx)
{ {
INFO_LOG(VIDEO, "Error: eglCreateContext failed\n"); INFO_LOG(VIDEO, "Error: eglCreateContext failed\n");
exit(1); exit(1);
} }
egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); std::string tmp;
if (!egl_surf) std::istringstream buffer(eglQueryString(egl_dpy, EGL_EXTENSIONS));
while (buffer >> tmp)
{ {
INFO_LOG(VIDEO, "Error: eglCreateWindowSurface failed\n"); if (tmp == "EGL_KHR_surfaceless_context")
exit(1); {
m_supports_surfaceless = true;
break;
}
} }
CreateWindowSurface();
return true; return true;
} }
void cInterfaceEGL::CreateWindowSurface()
{
if (m_has_handle)
{
EGLNativeWindowType native_window = InitializePlatform(m_host_window, m_config);
egl_surf = eglCreateWindowSurface(egl_dpy, m_config, native_window, nullptr);
if (!egl_surf)
{
INFO_LOG(VIDEO, "Error: eglCreateWindowSurface failed\n");
exit(1);
}
}
else if (!m_supports_surfaceless)
{
EGLint attrib_list[] =
{
EGL_NONE,
};
egl_surf = eglCreatePbufferSurface(egl_dpy, m_config, attrib_list);
if (!egl_surf)
{
INFO_LOG(VIDEO, "Error: eglCreatePbufferSurface failed");
exit(2);
}
}
else
{
egl_surf = EGL_NO_SURFACE;
}
}
void cInterfaceEGL::DestroyWindowSurface()
{
if (egl_surf != EGL_NO_SURFACE && !eglDestroySurface(egl_dpy, egl_surf))
NOTICE_LOG(VIDEO, "Could not destroy window surface.");
egl_surf = EGL_NO_SURFACE;
}
bool cInterfaceEGL::MakeCurrent() bool cInterfaceEGL::MakeCurrent()
{ {
return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx); return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx);
} }
void cInterfaceEGL::UpdateHandle(void* window_handle)
{
m_host_window = (EGLNativeWindowType)window_handle;
m_has_handle = !!window_handle;
}
void cInterfaceEGL::UpdateSurface()
{
ClearCurrent();
DestroyWindowSurface();
CreateWindowSurface();
MakeCurrent();
}
bool cInterfaceEGL::ClearCurrent() bool cInterfaceEGL::ClearCurrent()
{ {
return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@ -218,8 +262,7 @@ void cInterfaceEGL::Shutdown()
eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglDestroyContext(egl_dpy, egl_ctx)) if (!eglDestroyContext(egl_dpy, egl_ctx))
NOTICE_LOG(VIDEO, "Could not destroy drawing context."); NOTICE_LOG(VIDEO, "Could not destroy drawing context.");
if (!eglDestroySurface(egl_dpy, egl_surf)) DestroyWindowSurface();
NOTICE_LOG(VIDEO, "Could not destroy window surface.");
if (!eglTerminate(egl_dpy)) if (!eglTerminate(egl_dpy))
NOTICE_LOG(VIDEO, "Could not destroy display connection."); NOTICE_LOG(VIDEO, "Could not destroy display connection.");
egl_ctx = nullptr; egl_ctx = nullptr;

View File

@ -11,6 +11,15 @@
class cInterfaceEGL : public cInterfaceBase class cInterfaceEGL : public cInterfaceBase
{ {
private:
EGLConfig m_config;
bool m_has_handle;
EGLNativeWindowType m_host_window;
bool m_supports_surfaceless = false;
void CreateWindowSurface();
void DestroyWindowSurface();
protected: protected:
void DetectMode(); void DetectMode();
EGLSurface egl_surf; EGLSurface egl_surf;
@ -20,6 +29,7 @@ protected:
virtual EGLDisplay OpenDisplay() = 0; virtual EGLDisplay OpenDisplay() = 0;
virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0;
virtual void ShutdownPlatform() = 0; virtual void ShutdownPlatform() = 0;
public: public:
void Swap() override; void Swap() override;
void SwapInterval(int interval) override; void SwapInterval(int interval) override;
@ -29,4 +39,6 @@ public:
bool MakeCurrent() override; bool MakeCurrent() override;
bool ClearCurrent() override; bool ClearCurrent() override;
void Shutdown() override; void Shutdown() override;
void UpdateHandle(void* window_handle) override;
void UpdateSurface() override;
}; };

View File

@ -42,6 +42,8 @@ public:
virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; }
virtual void Update() { } virtual void Update() { }
virtual bool PeekMessages() { return false; } virtual bool PeekMessages() { return false; }
virtual void UpdateHandle(void* window_handle) {}
virtual void UpdateSurface() {}
}; };
extern std::unique_ptr<cInterfaceBase> GLInterface; extern std::unique_ptr<cInterfaceBase> GLInterface;

View File

@ -1467,6 +1467,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
OSD::DoCallbacks(OSD::CallbackType::OnFrame); OSD::DoCallbacks(OSD::CallbackType::OnFrame);
OSD::DrawMessages(); OSD::DrawMessages();
if (s_SurfaceNeedsChanged.IsSet())
{
GLInterface->UpdateSurface();
s_SurfaceNeedsChanged.Clear();
s_ChangedSurface.Set();
}
// Copy the rendered frame to the real window // Copy the rendered frame to the real window
GLInterface->Swap(); GLInterface->Swap();

View File

@ -58,6 +58,10 @@ Common::Event Renderer::s_screenshotCompleted;
volatile bool Renderer::s_bScreenshot; volatile bool Renderer::s_bScreenshot;
// Final surface changing
Common::Flag Renderer::s_SurfaceNeedsChanged;
Common::Event Renderer::s_ChangedSurface;
// The framebuffer size // The framebuffer size
int Renderer::s_target_width; int Renderer::s_target_width;
int Renderer::s_target_height; int Renderer::s_target_height;

View File

@ -138,6 +138,10 @@ public:
static Common::Event s_screenshotCompleted; static Common::Event s_screenshotCompleted;
// Final surface changing
static Common::Flag s_SurfaceNeedsChanged;
static Common::Event s_ChangedSurface;
protected: protected:
static void CalculateTargetScale(int x, int y, int* scaledX, int* scaledY); static void CalculateTargetScale(int x, int y, int* scaledX, int* scaledY);