diff --git a/Common/GPU/GraphicsContext.h b/Common/GPU/GraphicsContext.h index ca05a20a17..a4b2667a41 100644 --- a/Common/GPU/GraphicsContext.h +++ b/Common/GPU/GraphicsContext.h @@ -54,10 +54,6 @@ public: // Called from the emu thread, so be ready for that. virtual void NotifyEmuThreadExit() {} - // The API context that queued-up commands were recorded against is gone (or isn't current on the - // thread about to drain them), so they must not be executed for real. Only meaningful on Android GL. - virtual void NotifyContextLost() {} - // Useful for checks that need to be performed every frame. // Should strive to get rid of these. virtual void Poll() {} diff --git a/Common/GPU/OpenGL/OpenGLGraphicsContext.h b/Common/GPU/OpenGL/OpenGLGraphicsContext.h index 2214830045..76895759e4 100644 --- a/Common/GPU/OpenGL/OpenGLGraphicsContext.h +++ b/Common/GPU/OpenGL/OpenGLGraphicsContext.h @@ -33,12 +33,6 @@ public: renderManager_->ThreadEnd(); } - void NotifyContextLost() override { - if (renderManager_) { - renderManager_->SetSkipGLCalls(); - } - } - // Call from emu thread void NotifyEmuThreadExit() override { renderManager_->SetSkipGLCalls(); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 2e99af395f..7ea72f7bd4 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -8,7 +8,7 @@ using namespace PPSSPP_VK; -// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper PPSSPPSizeManager +// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper PPSSPPSizeManager static void MergeRenderAreaRectInto(VkRect2D *dest, const VkRect2D &src) { if (dest->offset.x > src.offset.x) { diff --git a/Core/Config.cpp b/Core/Config.cpp index df0a205302..a82c9cc8c5 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -704,7 +704,6 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("Smart2DTexFiltering", SETTING(g_Config, bSmart2DTexFiltering), false, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("InternalResolution", SETTING(g_Config, iInternalResolution), &DefaultInternalResolution, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("AndroidHwScale", SETTING(g_Config, iAndroidHwScale), 0, CfgFlag::DEFAULT), - ConfigSetting("AndroidNativeEGL", SETTING(g_Config, bAndroidNativeEGL), false, CfgFlag::DEFAULT), ConfigSetting("HighQualityDepth", SETTING(g_Config, bHighQualityDepth), true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("FrameSkip", SETTING(g_Config, iFrameSkip), 0, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), IsVREnabled(), CfgFlag::PER_GAME | CfgFlag::REPORT), diff --git a/Core/Config.h b/Core/Config.h index b369dadc5e..ca21fb0658 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -569,12 +569,6 @@ public: // Use the hardware scaler to scale up the image to save fillrate. Similar to Windows' window size, really. int iAndroidHwScale; // 0 = device resolution. 1 = 480x272 (extended to correct aspect), 2 = 960x544 etc. - // Android, OpenGL backend only: own EGL and the render thread in C++ instead of letting - // Java's GLSurfaceView do it. Temporary, while the new path is being shaken out on devices - - // the intent is for it to become the only way and for the JavaGL path to go away. - // Only read when the activity is created, so changing it needs an app restart. - bool bAndroidNativeEGL; - // Risky JIT optimizations bool bDiscardRegsOnJRRA; diff --git a/UI/DeveloperToolsScreen.cpp b/UI/DeveloperToolsScreen.cpp index 5a46cc1ba0..c570b671ae 100644 --- a/UI/DeveloperToolsScreen.cpp +++ b/UI/DeveloperToolsScreen.cpp @@ -539,17 +539,6 @@ void DeveloperToolsScreen::CreateGraphicsTab(UI::LinearLayout *list) { }); } -#if PPSSPP_PLATFORM(ANDROID) - if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { - list->Add(new ItemHeader(dev->T("OpenGL"))); - // Temporary, while the EGL path is being tested on devices - see Core/Config.h. - list->Add(new CheckBox(&g_Config.bAndroidNativeEGL, dev->T("Use native EGL instead of GLSurfaceView"), ""))->OnClick.Add([](UI::EventParams &e) { - // TODO: Not translating yet, same as the multi-threaded rendering checkbox above. - g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required"); - }); - } -#endif - if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) { auto driverChoice = list->Add(new Choice(gr->T("AdrenoTools driver manager"))); driverChoice->OnClick.Add([=](UI::EventParams &e) { diff --git a/android/jni/AndroidEGLGraphicsContext.cpp b/android/jni/AndroidEGLGraphicsContext.cpp index 6bc5d2d533..962a093609 100644 --- a/android/jni/AndroidEGLGraphicsContext.cpp +++ b/android/jni/AndroidEGLGraphicsContext.cpp @@ -19,6 +19,7 @@ #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/System/OSD.h" #include "Common/VR/PPSSPPVR.h" #include "android/jni/AndroidEGLGraphicsContext.h" @@ -66,7 +67,7 @@ struct ConfigAttempt { // Stencil is not optional in practice: without it, non-buffered rendering has nothing to work with. // // The last entry is the fallback: ask for the bare minimum and take whatever eglChooseConfig puts -// first, which is roughly what GLSurfaceView's default chooser does today. +// first, which is roughly what GLSurfaceView's default chooser used to do. const ConfigAttempt kConfigAttempts[] = { { "RGBA8888 D24S8", 8, 8, 8, 8, 24, 8 }, { "RGBA8888 D16S8", 8, 8, 8, 8, 16, 8 }, @@ -139,9 +140,9 @@ bool AndroidEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, vo INFO_LOG(Log::G3D, "EGL %d.%d initialized (vendor: %s)", eglMajor, eglMinor, eglQueryString(display_, EGL_VENDOR)); - // Mirrors NativeRenderer's setEGLContextClientVersion(isVRDevice() ? 3 : 2). Asking for 2 is - // not a cap: GLES3 is detected from GL_VERSION at runtime (see GLFeatures.cpp), which is how - // the Java path has always ended up using GLES3 on devices that have it. + // Mirrors what NativeRenderer used to ask for: setEGLContextClientVersion(isVRDevice() ? 3 : 2). + // Asking for 2 is not a cap: GLES3 is detected from GL_VERSION at runtime (see GLFeatures.cpp), + // which is how the Java path always ended up using GLES3 on devices that have it. const bool wantGLES3 = IsVREnabled(); if (!ChooseConfig(wantGLES3, errorMessage)) { DestroyEGL(); @@ -151,6 +152,7 @@ bool AndroidEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, vo // Line the window's pixel format up with the config we picked. Skipping this is how you get // EGL_BAD_MATCH out of eglCreateWindowSurface on drivers that take the window format // literally - historically the reason the Java path asked for no particular config at all. + // That is also why we can afford to be picky in kConfigAttempts where Java could not. // Width and height stay 0 so whatever size Java set (see iAndroidHwScale) is left alone. EGLint nativeVisualID = 0; if (eglGetConfigAttrib(display_, config_, EGL_NATIVE_VISUAL_ID, &nativeVisualID)) { @@ -192,6 +194,12 @@ bool AndroidEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, vo return false; } + // As on Windows (see WindowsGLContext), surface GL errors to the user. The Java path did this + // from displayInit; now that the context owns its own init, it belongs here. + draw_->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { + g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); + }, nullptr); + renderManager_->SetSwapFunction([this]() { if (!eglSwapBuffers(display_, surface_)) { // Most likely the surface went away under us. The render loop is about to be asked to @@ -201,9 +209,9 @@ bool AndroidEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, vo }); // NOTE: Deliberately *not* calling SetSwapIntervalFunction. GLSurfaceView gave us no way to - // change the interval, so PresentMode::IMMEDIATE has always been a no-op here, and honoring it - // now would be a behavior change rather than a port. Worth revisiting once this path is the - // only one. + // change the interval, so PresentMode::IMMEDIATE was always a no-op on Android GL, and this + // class set out to behave identically. Now that it's the only path, eglSwapInterval(0) is + // there for the taking - but that's a behavior change, so it wants its own commit. return true; } diff --git a/android/jni/AndroidEGLGraphicsContext.h b/android/jni/AndroidEGLGraphicsContext.h index 27997ec315..dde60aad60 100644 --- a/android/jni/AndroidEGLGraphicsContext.h +++ b/android/jni/AndroidEGLGraphicsContext.h @@ -23,18 +23,15 @@ struct ANativeWindow; -// Owns EGL for the OpenGL backend on Android, instead of letting Java's GLSurfaceView own it -// (NativeRenderer.java / NativeGLSurfaceView.java, the "JavaGL" path). +// Owns EGL for the OpenGL backend on Android. Until 2026 this was Java's job, through a +// GLSurfaceView (NativeRenderer.java / NativeGLSurfaceView.java, the "JavaGL" path). // -// The point is not EGL itself but who owns the render thread. With GLSurfaceView, Java creates -// the thread and the context and calls us back, so a lost context is only ever observable after -// the fact, as a second onSurfaceCreated - which is what forces app-android.cpp's displayInit to -// tear down and restart the emu thread from inside a JNI callback. Owning EGL here lets the GL -// backend use the same lifecycle as Vulkan: we start a render thread when we get a surface and -// join it when the surface goes away, which is the whole reason this exists. -// -// Deliberately behaves the same as the Java path otherwise - same GLES client version, same -// window pixel format, same separate emu thread. See Core/Config.h, bAndroidNativeEGL. +// The point is not EGL itself but who owns the render thread. With GLSurfaceView, Java created +// the thread and the context and called us back, so a lost context was only ever observable +// after the fact, as a second onSurfaceCreated - which forced app-android.cpp to tear down and +// restart the emu thread from inside a JNI callback. Owning EGL here lets the GL backend use the +// same lifecycle as Vulkan: we start a render thread when we get a surface and join it when the +// surface goes away, which is the whole reason this exists. class AndroidEGLGraphicsContext : public OpenGLGraphicsContext { public: // Note: not just DestroyEGL(). The base destructor deletes the thin3d context, which still diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 458dfa5506..e034d6584a 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -88,7 +88,6 @@ struct JNIEnv {}; #include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/GPU/GraphicsContext.h" #include "Common/GPU/Vulkan/VulkanGraphicsContext.h" -#include "Common/GPU/OpenGL/OpenGLGraphicsContext.h" #include "android/jni/AndroidEGLGraphicsContext.h" #include "Common/StringUtils.h" #include "Common/TimeUtil.h" @@ -115,9 +114,6 @@ enum class EmuThreadState { STOPPED, }; -// OpenGL emu thread -static std::thread g_emuThread; - AndroidAudioState *g_audioState; struct FrameCommand { @@ -572,18 +568,6 @@ static std::string QueryConfig(std::string_view query) { return g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()).bImmersiveMode ? "1" : "0"; } else if (query == "sustainedPerformanceMode") { return g_Config.bSustainedPerformanceMode ? "1" : "0"; - } else if (query == "androidJavaGL") { - // If we're using Vulkan, we say no... need C++ to use Vulkan. - if (GetGPUBackend() == GPUBackend::VULKAN) { - return "false"; - } - // GL with our own EGL wants the same treatment as Vulkan: a plain SurfaceView, and a - // render thread we start ourselves once there's a surface. - if (g_Config.bAndroidNativeEGL) { - return "false"; - } - // Otherwise, some devices prefer the Java init so play it safe. - return "true"; } else if (query == "audioMixWithOthers") { return g_Config.bAudioMixWithOthers ? "1" : "0"; } else { @@ -780,15 +764,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init retry: switch (g_Config.iGPUBackend) { case (int)GPUBackend::OPENGL: - if (g_Config.bAndroidNativeEGL) { - INFO_LOG(Log::System, "NativeApp.init() -- creating OpenGL context (native EGL)"); - graphicsContext = new AndroidEGLGraphicsContext(); - // Like Vulkan, we wait for a surface before doing anything - see runRenderLoop. - } else { - INFO_LOG(Log::System, "NativeApp.init() -- creating OpenGL context (JavaGL)"); - graphicsContext = new OpenGLGraphicsContext(); - INFO_LOG(Log::System, "NativeApp.init() - not yet launching emuthread, waiting to displayInit"); - } + INFO_LOG(Log::System, "NativeApp.init() -- creating OpenGL context"); + graphicsContext = new AndroidEGLGraphicsContext(); + // Like Vulkan, we wait for a surface before doing anything - see runRenderLoop. break; case (int)GPUBackend::VULKAN: { @@ -909,20 +887,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) { extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { INFO_LOG(Log::System, "NativeApp.shutdown() -- begin"); - // Only the JavaGL path gets here with a thread to join: it's the one that starts the emu thread - // from a JNI callback and leaves it in g_emuThread. On the native-EGL and Vulkan paths the - // render loop thread owns its own, and has already been joined via requestExitRenderLoop. - if (renderer_inited && graphicsContext && graphicsContext->NeedsSeparateEmuThread() && g_emuThread.joinable()) { - // The join drains the render queue right here, on a thread without a GL context. - graphicsContext->NotifyContextLost(); - INFO_LOG(Log::System, "Joining emuthread."); - EmuThread_Join(graphicsContext, g_emuThread); - - INFO_LOG(Log::System, "EmuThread joined."); - graphicsContext->ShutdownSurface(); - INFO_LOG(Log::System, "Graphics context now shut down from NativeApp_shutdown"); - } - + // Nothing to join here: the render loop thread owns both itself and the emu thread, and + // requestExitRenderLoop has already brought them down along with the surface. { if (graphicsContext) { INFO_LOG(Log::G3D, "Shutting down renderer"); @@ -945,79 +911,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { INFO_LOG(Log::System, "NativeApp.shutdown() -- end"); } -// JavaEGL. This doesn't get called on the Vulkan path. -// This gets called from onSurfaceCreated. -extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { - if (!graphicsContext) { - ERROR_LOG(Log::G3D, "NativeApp.displayInit() - graphicsContext is null!"); - return false; - } - - _assert_(graphicsContext->NeedsSeparateEmuThread()); - - INFO_LOG(Log::G3D, "NativeApp.displayInit()"); - bool firstStart = !renderer_inited; - - // We should be running on the render thread here. - std::string errorMessage; - if (!renderer_inited) { - INFO_LOG(Log::G3D, "NativeApp.displayInit() first time"); - if (!graphicsContext->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr, &errorMessage)) { - System_Toast("Graphics initialization failed. Quitting."); - return false; - } - - graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); - }, nullptr); - - // This is where we start the emuthread now - after InitFromRenderThread. This eliminates a race condition. - g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext) { - NativeFrame(graphicsContext); - ProcessFrameCommands(); - return true; - }); - renderer_inited = true; - } else { - // Would be really nice if we could get something on the GL thread immediately when shutting down, - // but the only mechanism for handling lost devices seems to be that onSurfaceCreated is called again, - // which ends up calling displayInit. - INFO_LOG(Log::G3D, "NativeApp.displayInit(): Second time, joining the emuthread and starting it up again."); - // We only get here with a fresh EGL context. What's left in the render queue refers to objects - // from the old one, so it mustn't be run against this one when the join drains it. - graphicsContext->NotifyContextLost(); - EmuThread_Join(graphicsContext, g_emuThread); - - graphicsContext->ShutdownSurface(); - - INFO_LOG(Log::G3D, "Shut down both threads. Now let's bring it up again!"); - - if (!graphicsContext->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr, &errorMessage)) { - System_Toast(("Graphics initialization failed: Quitting: " + errorMessage).c_str()); - return false; - } - - graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); - }, nullptr); - - g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext) { - NativeFrame(graphicsContext); - ProcessFrameCommands(); - return true; - }); - - INFO_LOG(Log::G3D, "Restored."); - } - - System_PostUIMessage(UIMessage::RECREATE_VIEWS); - - if (IsVREnabled()) { - EnterVR(firstStart); - } - return true; -} - extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint pixel_xres, jint pixel_yres, jint format) { INFO_LOG(Log::System, "NativeApp.backbufferResize(%d x %d)", pixel_xres, pixel_yres); @@ -1157,38 +1050,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendRequestResult(JNIEn } } -// This doesn't get called on the Vulkan path. -// We don't need a render thread "loop" as this gets called repeatedly by the system, by a system -// render thread. -extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { - static bool hasSetThreadName = false; - if (!hasSetThreadName) { - hasSetThreadName = true; - SetCurrentThreadName("AndroidRender"); - } - - if (IsVREnabled() && !StartVRRender()) { - return; - } - - // This is the "GPU thread". Call ThreadFrame. - if (!graphicsContext) { - return; - } - _assert_(graphicsContext->NeedsSeparateEmuThread()); - - if (!graphicsContext->ThreadFrame()) { - INFO_LOG(Log::G3D, "ThreadFrame returned false"); - // TODO: We should stop calling ThreadFrame here. - return; - } - - if (IsVREnabled()) { - UpdateVRInput(g_Config.bHapticFeedback, g_display.dpi_scale_x, g_display.dpi_scale_y); - FinishVRRender(); - } -} - void System_AskForPermission(SystemPermission permission) { switch (permission) { case SYSTEM_PERMISSION_STORAGE: @@ -1649,8 +1510,7 @@ std::thread g_renderLoopThread; static void RenderLoopThread(ANativeWindow *wnd, GraphicsContext *graphicsContext); -// Runs on the Vulkan path, and on the OpenGL path when we own EGL (bAndroidNativeEGL). Not in -// JavaGL mode - there, Java owns the thread and calls displayInit/displayRender instead. +// Runs on both the Vulkan and the OpenGL path. // This handles the entire lifecycle of the graphics context's surface, init and exit. extern "C" jboolean JNICALL Java_org_ppsspp_ppsspp_PpssppActivity_runRenderLoop(JNIEnv * env, jobject obj, jobject _surf) { if (!graphicsContext) { @@ -1744,9 +1604,35 @@ static void RenderLoopThread(ANativeWindow *wnd, GraphicsContext *graphicsContex // EmuThread_Start calls ThreadStart() for us, and EmuThread_Join calls ThreadEnd(), so // neither is called directly here. std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), frame); - // This is the GL "GPU thread" - the loop Java's displayRender used to drive one call at a + + // The views were created against the previous surface, if there was one. + System_PostUIMessage(UIMessage::RECREATE_VIEWS); + + if (IsVREnabled()) { + // Only ever a GL thing, which is why it lives in this branch. + static bool vrFirstStart = true; + EnterVR(vrFirstStart); + vrFirstStart = false; + } + + // This is the GL "GPU thread" - the loop Java's GLSurfaceView used to drive one call at a // time. ThreadFrame returns false once the emu thread is done producing. - while (!exitRenderLoop && graphicsContext->ThreadFrame()) {} + while (!exitRenderLoop) { + if (IsVREnabled() && !StartVRRender()) { + // The session isn't active, so there's no frame to render into. Java's GL thread + // just missed a vsync here; we drive the loop ourselves, so pace it by hand + // rather than spinning. + sleep_ms(16, "vr-session-idle"); + continue; + } + if (!graphicsContext->ThreadFrame()) { + break; + } + if (IsVREnabled()) { + UpdateVRInput(g_Config.bHapticFeedback, g_display.dpi_scale_x, g_display.dpi_scale_y); + FinishVRRender(); + } + } // Also drains whatever the emu thread still has queued, so it can't block on us. EmuThread_Join(graphicsContext, emuThread); } else { diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index a2b575e193..199d80af55 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -1,7 +1,5 @@ package org.ppsspp.ppsspp; -// Note that the display* methods are in NativeRenderer.java - import android.os.Build; import android.util.Log; import android.view.InputDevice; diff --git a/android/src/org/ppsspp/ppsspp/NativeGLSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeGLSurfaceView.java deleted file mode 100644 index 503adfc381..0000000000 --- a/android/src/org/ppsspp/ppsspp/NativeGLSurfaceView.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.ppsspp.ppsspp; - -// Touch-enabled GLSurfaceView. -// Used when javaGL = true. - -import android.annotation.SuppressLint; -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.view.MotionEvent; - -public class NativeGLSurfaceView extends GLSurfaceView { - public NativeGLSurfaceView(Context context) { - super(context); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(final MotionEvent ev) { - NativeApp.processTouchEvent(ev); - return true; - } -} diff --git a/android/src/org/ppsspp/ppsspp/NativeRenderer.java b/android/src/org/ppsspp/ppsspp/NativeRenderer.java deleted file mode 100644 index c7837d807e..0000000000 --- a/android/src/org/ppsspp/ppsspp/NativeRenderer.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.ppsspp.ppsspp; - -import android.opengl.GLSurfaceView; -import android.util.Log; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.opengles.GL10; - -// Only used for the OpenGL backend. -public class NativeRenderer implements GLSurfaceView.Renderer { - private static final String TAG = "NativeRenderer"; - - public void onDrawFrame(GL10 unused /*use GLES20*/) { - displayRender(); - } - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - Log.i(TAG, "NativeRenderer (OpenGL): onSurfaceCreated"); - - EGL10 egl = (EGL10)EGLContext.getEGL(); - if (egl != null) { - EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - if (dpy != null) { - int[] val = new int[1]; - egl.eglGetConfigAttrib(dpy, config, EGL10.EGL_DEPTH_SIZE, val); - int depthBits = val[0]; - egl.eglGetConfigAttrib(dpy, config, EGL10.EGL_STENCIL_SIZE, val); - int stencilBits = val[0]; - Log.i(TAG, "EGL reports " + depthBits + " bits of depth and " + stencilBits + " bits of stencil."); - } else { - Log.e(TAG, "dpy == null"); - } - } else { - Log.e(TAG, "egl == null"); - } - - if (!displayInit()) { - Log.e(TAG, "Display init failed"); - } - } - - public void onSurfaceChanged(GL10 unused, int width, int height) { - Log.i(TAG, "NativeRenderer (OpenGL): onSurfaceChanged: width=" + width + " height=" + height); - } - - // Note: This also means "device lost" and you should reload - // all buffered objects. - public native boolean displayInit(); - public native void displayRender(); -} diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 86bb1b6bcc..53d069369a 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -78,20 +78,13 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList // Allows us to skip a lot of initialization on secondary calls to onCreate. private static boolean initialized = false; - // False to use Vulkan, queried from C++ after NativeApp.init. - private static boolean javaGL = true; - // Lifecycle tracker, to detect erroneous states. private final LifeCycle lifeCycle = new LifeCycle(); - // Graphics and audio interfaces for Vulkan (javaGL = false) + // The surface we hand to the native render loop thread, for both OpenGL and Vulkan. private NativeSurfaceView mSurfaceView; private Surface mSurface; - // Graphics and audio interfaces for Java EGL (javaGL = true) - private NativeGLSurfaceView mGLSurfaceView; - protected NativeRenderer nativeRenderer; - // For accelerometer sensing. private SensorManager mSensorManager; private Sensor mAccelerometer; @@ -468,9 +461,6 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate); NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, externalFilesDir, nativeLibDir, additionalStorageDirs, cacheDir, shortcut, installerName, Build.VERSION.SDK_INT, Build.BOARD, smallestScreenWidthDp); - // Allow C++ to tell us to use JavaGL or not. - javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL")); - sendInitialGrants(); // OK, config should be initialized, we can query for screen rotation. @@ -641,8 +631,7 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList sizeManager.checkDisplayMeasurements(); } - // Starts the native render loop thread. Used by Vulkan, and by OpenGL when the native side - // owns EGL rather than the GLSurfaceView in the javaGL path. + // Starts the native render loop thread, which owns the graphics context and its surface. public native boolean runRenderLoop(Surface surface); // Tells the render loop thread to exit, so we can restart it. public native void requestExitRenderLoop(); @@ -713,49 +702,14 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList updateAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.audioInit(); - if (javaGL) { - mGLSurfaceView = new NativeGLSurfaceView(this); - nativeRenderer = new NativeRenderer(); - mGLSurfaceView.setEGLContextClientVersion(isVRDevice() ? 3 : 2); + updateSystemUiVisibility(); - sizeManager.setSurfaceView(mGLSurfaceView); - setInsetsListener(mGLSurfaceView); + mSurfaceView = new NativeSurfaceView(this); + sizeManager.setSurfaceView(mSurfaceView); + setInsetsListener(mSurfaceView); + setContentView(mSurfaceView); - // Setup the GLSurface and ask android for the correct - // Number of bits for r, g, b, a, depth and stencil components - // The PSP only has 16-bit Z so that should be enough. - // Might want to change this for other apps (24-bit might be useful). - // Actually, we might be able to do without both stencil and depth in - // the back buffer, but that would kill non-buffered rendering. - - // It appears some gingerbread devices blow up if you use a config chooser at all ???? (Xperia Play) - //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - - // On some (especially older devices), things blow up later (EGL_BAD_MATCH) if we don't set - // the format here, if we specify that we want destination alpha in the config chooser, which we do. - // http://grokbase.com/t/gg/android-developers/11bj40jm4w/fall-back - - // Tried to mess around with config choosers (NativeEGLConfigChooser) here but fail completely on Xperia Play. - - // Then I tried to require 8888/16/8 but that backfired too, does not work on Mali 450 which is - // used in popular TVs and boxes like Mi Box. So we'll just get what we get, I guess... - - // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Build.MANUFACTURER.equals("Amazon")) { - // mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 8); - // } - - mGLSurfaceView.setRenderer(nativeRenderer); - setContentView(mGLSurfaceView); - } else { - updateSystemUiVisibility(); - - mSurfaceView = new NativeSurfaceView(this); - sizeManager.setSurfaceView(mSurfaceView); - setInsetsListener(mSurfaceView); - setContentView(mSurfaceView); - - // render loop thread will be started once we get a surface. - } + // The render loop thread will be started once we get a surface. if (!firstRun && shortcutParam != null && !shortcutParam.isEmpty()) { // The native side is already up, so it didn't see this through NativeApp.init. @@ -903,39 +857,27 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList public void notifySurface(Surface surface) { mSurface = surface; - if (!javaGL) { - if (!initialized) { - Log.e(TAG, "notifySurface: Saving surface, but can't start/stop threads while not initialized"); - return; - } + if (!initialized) { + Log.e(TAG, "notifySurface: Saving surface, but can't start/stop threads while not initialized"); + return; + } - // If we got a surface, this starts the thread. - if (mSurface != null) { - // applyFramerate is called in here. - Log.i(TAG, "notifySurface: got surface, starting thread."); - startRenderLoopThread(); - } else { - // The surface must not be touched once surfaceDestroyed returns. Normally onPause has - // already joined the thread and this does nothing, but that order isn't guaranteed. - Log.i(TAG, "notifySurface: Surface is gone, making sure the render thread is too."); - joinRenderLoopThread(); - } - } else if (mSurface != null) { - // JavaGL path. - // TODO: This might not be the best place to do this. Seems to cause a surface recreation - // unnecessarily. - Log.i(TAG, "notifySurface: Applying framerate."); - applyFrameRate(mSurface, 60.0f); + // If we got a surface, this starts the thread. + if (mSurface != null) { + // applyFramerate is called in here. + Log.i(TAG, "notifySurface: got surface, starting thread."); + startRenderLoopThread(); + } else { + // The surface must not be touched once surfaceDestroyed returns. Normally onPause has + // already joined the thread and this does nothing, but that order isn't guaranteed. + Log.i(TAG, "notifySurface: Surface is gone, making sure the render thread is too."); + joinRenderLoopThread(); } updateSustainedPerformanceMode(); } // The render loop thread (EmuThread) is now spawned from the native side. protected synchronized void startRenderLoopThread() { - if (javaGL) { - Log.e(TAG, "JavaGL mode - should not get into startRenderLoopThread."); - return; - } if (mSurface == null) { Log.w(TAG, "startRenderLoopThread - not starting thread, needs surface"); return; @@ -948,11 +890,6 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList } private synchronized void joinRenderLoopThread() { - if (javaGL) { - Log.e(TAG, "JavaGL - should not get into joinRenderLoopThread."); - return; - } - // This will wait until the thread has exited. Log.i(TAG, "requestExitRenderLoop"); requestExitRenderLoop(); @@ -973,13 +910,8 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList super.onDestroy(); lifeCycle.onDestroy(); - if (javaGL) { - nativeRenderer = null; - mGLSurfaceView = null; - } else { - mSurfaceView = null; - mSurface = null; - } + mSurfaceView = null; + mSurface = null; mSensorManager = null; mAccelerometer = null; @@ -1034,14 +966,9 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList InputManager inputManager = (InputManager)getSystemService(Context.INPUT_SERVICE); inputManager.unregisterInputDeviceListener(inputDeviceListener); - if (!javaGL) { - Log.i(TAG, "Joining render thread..."); - joinRenderLoopThread(); - Log.i(TAG, "Joined render thread"); - } else if (mGLSurfaceView != null) { - Log.i(TAG, "mGLSurfaceView.onPause..."); - mGLSurfaceView.onPause(); - } + Log.i(TAG, "Joining render thread..."); + joinRenderLoopThread(); + Log.i(TAG, "Joined render thread"); Log.i(TAG, "mSensorManager.unregisterListener..."); mSensorManager.unregisterListener(this); @@ -1081,12 +1008,8 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList (InputManager)getSystemService(Context.INPUT_SERVICE); inputManager.registerInputDeviceListener(inputDeviceListener, null); - if (!javaGL) { - // Restart the render loop. - startRenderLoopThread(); - } else if (mGLSurfaceView != null) { - mGLSurfaceView.onResume(); - } + // Restart the render loop. + startRenderLoopThread(); Log.i(TAG, "onResume end"); } @@ -1475,7 +1398,7 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList } public boolean processCommand(String command, String params) { - SurfaceView surfView = javaGL ? mGLSurfaceView : mSurfaceView; + SurfaceView surfView = mSurfaceView; if (command.equals("launchBrowser")) { // Special case for twitter if (params.startsWith("https://twitter.com/#!/")) {