diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 71fbbd93e6..209d59919e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -333,10 +333,12 @@ public final class NativeLibrary /** * 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. */ public static native void UnPauseEmulation(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 04b5149a0e..5415b65b7d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -177,13 +177,16 @@ public final class EmulationActivity extends AppCompatActivity } }); - // Instantiate an EmulationFragment. - EmulationFragment emulationFragment = EmulationFragment.newInstance(path); + if (savedInstanceState == null) + { + // Instantiate an EmulationFragment. + EmulationFragment emulationFragment = EmulationFragment.newInstance(path); - // Add fragment to the activity - this triggers all its lifecycle callbacks. - getFragmentManager().beginTransaction() - .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) - .commit(); + // Add fragment to the activity - this triggers all its lifecycle callbacks. + getFragmentManager().beginTransaction() + .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) + .commit(); + } if (mDeviceHasTouchScreen) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index 1eb1684531..ab79e62a68 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -115,7 +115,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C public void onStop() { super.onStop(); - pauseEmulation(); } @Override @@ -160,12 +159,14 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { Log.d("DolphinEmu", "Surface changed. Resolution: " + width + "x" + height); mSurface = holder.getSurface(); + NativeLibrary.SurfaceChanged(mSurface); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d("DolphinEmu", "Surface destroyed."); + NativeLibrary.SurfaceDestroyed(); if (mEmulationRunning) { @@ -216,20 +217,14 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C 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); + NativeLibrary.Run(); } }; } diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 2cff486721..ffa0eca40a 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -18,6 +18,7 @@ #include "Common/CPUDetect.h" #include "Common/Event.h" #include "Common/FileUtil.h" +#include "Common/GL/GLInterfaceBase.h" #include "Common/Logging/LogManager.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_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_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) { - PowerPC::Start(); + Core::SetState(Core::CORE_RUN); } 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) @@ -596,18 +598,45 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClasses 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()); - 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::CallbackType::Initialization, ButtonManager::Init); OSD::AddCallback(OSD::CallbackType::Shutdown, ButtonManager::Shutdown); @@ -627,7 +656,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv * Core::Shutdown(); UICommon::Shutdown(); - ANativeWindow_release(surf); + + if (surf) + { + ANativeWindow_release(surf); + surf = nullptr; + } // Execute the Java method. env->CallStaticVoidMethod(g_jni_class, g_jni_method_end); diff --git a/Source/Core/Common/GL/GLInterface/EGL.cpp b/Source/Core/Common/GL/GLInterface/EGL.cpp index 202568b2e9..e61627cbdf 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.cpp +++ b/Source/Core/Common/GL/GLInterface/EGL.cpp @@ -11,7 +11,8 @@ // Show the current FPS void cInterfaceEGL::Swap() { - eglSwapBuffers(egl_dpy, egl_surf); + if (egl_surf != EGL_NO_SURFACE) + eglSwapBuffers(egl_dpy, egl_surf); } void cInterfaceEGL::SwapInterval(int Interval) { @@ -98,10 +99,11 @@ void cInterfaceEGL::DetectMode() // Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() bool cInterfaceEGL::Create(void *window_handle, bool core) { - const char *s; EGLint egl_major, egl_minor; egl_dpy = OpenDisplay(); + m_host_window = (EGLNativeWindowType) window_handle; + m_has_handle = !!window_handle; if (!egl_dpy) { @@ -116,7 +118,6 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) } /* Detection code */ - EGLConfig config; EGLint num_configs; DetectMode(); @@ -154,7 +155,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) 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"); exit(1); @@ -165,43 +166,86 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) else eglBindAPI(EGL_OPENGL_ES_API); - EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; - 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 ); + egl_ctx = eglCreateContext(egl_dpy, m_config, EGL_NO_CONTEXT, ctx_attribs ); if (!egl_ctx) { INFO_LOG(VIDEO, "Error: eglCreateContext failed\n"); exit(1); } - egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); - if (!egl_surf) + std::string tmp; + std::istringstream buffer(eglQueryString(egl_dpy, EGL_EXTENSIONS)); + while (buffer >> tmp) { - INFO_LOG(VIDEO, "Error: eglCreateWindowSurface failed\n"); - exit(1); + if (tmp == "EGL_KHR_surfaceless_context") + { + m_supports_surfaceless = true; + break; + } } + + CreateWindowSurface(); 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() { 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() { 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); if (!eglDestroyContext(egl_dpy, egl_ctx)) NOTICE_LOG(VIDEO, "Could not destroy drawing context."); - if (!eglDestroySurface(egl_dpy, egl_surf)) - NOTICE_LOG(VIDEO, "Could not destroy window surface."); + DestroyWindowSurface(); if (!eglTerminate(egl_dpy)) NOTICE_LOG(VIDEO, "Could not destroy display connection."); egl_ctx = nullptr; diff --git a/Source/Core/Common/GL/GLInterface/EGL.h b/Source/Core/Common/GL/GLInterface/EGL.h index 21f563f861..8811aa0c97 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.h +++ b/Source/Core/Common/GL/GLInterface/EGL.h @@ -11,6 +11,15 @@ 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: void DetectMode(); EGLSurface egl_surf; @@ -20,6 +29,7 @@ protected: virtual EGLDisplay OpenDisplay() = 0; virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; virtual void ShutdownPlatform() = 0; + public: void Swap() override; void SwapInterval(int interval) override; @@ -29,4 +39,6 @@ public: bool MakeCurrent() override; bool ClearCurrent() override; void Shutdown() override; + void UpdateHandle(void* window_handle) override; + void UpdateSurface() override; }; diff --git a/Source/Core/Common/GL/GLInterfaceBase.h b/Source/Core/Common/GL/GLInterfaceBase.h index d7885635bb..34834403e5 100644 --- a/Source/Core/Common/GL/GLInterfaceBase.h +++ b/Source/Core/Common/GL/GLInterfaceBase.h @@ -42,6 +42,8 @@ public: virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } virtual void Update() { } virtual bool PeekMessages() { return false; } + virtual void UpdateHandle(void* window_handle) {} + virtual void UpdateSurface() {} }; extern std::unique_ptr GLInterface; diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index a5ee56ca1f..ce332c0b98 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1467,6 +1467,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co OSD::DoCallbacks(OSD::CallbackType::OnFrame); OSD::DrawMessages(); + if (s_SurfaceNeedsChanged.IsSet()) + { + GLInterface->UpdateSurface(); + s_SurfaceNeedsChanged.Clear(); + s_ChangedSurface.Set(); + } + // Copy the rendered frame to the real window GLInterface->Swap(); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 76136c88aa..b569c378a3 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -58,6 +58,10 @@ Common::Event Renderer::s_screenshotCompleted; volatile bool Renderer::s_bScreenshot; +// Final surface changing +Common::Flag Renderer::s_SurfaceNeedsChanged; +Common::Event Renderer::s_ChangedSurface; + // The framebuffer size int Renderer::s_target_width; int Renderer::s_target_height; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index ab471d71dd..fb3668286a 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -138,6 +138,10 @@ public: static Common::Event s_screenshotCompleted; + // Final surface changing + static Common::Flag s_SurfaceNeedsChanged; + static Common::Event s_ChangedSurface; + protected: static void CalculateTargetScale(int x, int y, int* scaledX, int* scaledY);