Files
ppsspp/android/jni/AndroidEGLGraphicsContext.h
T
Henrik RydgårdandClaude Opus 5 812d8f4c09 Android GL: Delete the JavaGL path, leaving only native EGL
The EGL path from the previous commit is now the only one, so GLSurfaceView
and everything that existed to serve it are gone:

- NativeRenderer.java and NativeGLSurfaceView.java, deleted.
- The javaGL branches in PpssppActivity. GL and Vulkan now share one
  lifecycle: a plain SurfaceView, startRenderLoopThread() when the surface
  arrives, joinRenderLoopThread() when it goes away.
- The "androidJavaGL" queryConfig, and the AndroidNativeEGL setting and its
  developer-tools checkbox.
- displayInit/displayRender, and with them the "Second time, joining the
  emuthread and starting it up again" dance - restarting the emu thread from
  inside a JNI callback because a lost context was only observable as a
  second onSurfaceCreated.
- GraphicsContext::NotifyContextLost, which existed solely because that join
  drained the render queue on a thread with no current context. The render
  thread now joins the emu thread with the context still current, so the
  drain is real work and nothing has to be skipped.
- g_emuThread, now that nothing starts an emu thread outside the render loop.

Three things displayInit/displayRender did that nothing else did, moved
rather than dropped:

- EnterVR and the per-frame StartVRRender/UpdateVRInput/FinishVRRender hooks
  now live in RenderLoopThread's GL branch. Java's GL thread could simply
  return early and wait for the next vsync when the VR session was inactive;
  this loop drives itself, so that case sleeps 16ms instead of spinning.
  Not tested - no headset here.
- RECREATE_VIEWS after the emu thread starts, as before. Vulkan never did
  this, so it's a candidate for removal when the two paths converge.
- The thin3d error callback, which surfaces GL errors as OSD messages. That
  one moved into AndroidEGLGraphicsContext::InitSurface, next to the rest of
  the context's own setup, matching WindowsGLContext.

Tested on a nubia Z2464N (Unisoc UMS9632, Mali-G57, Android 15): cold start,
UI, booting a homebrew, backgrounding and resuming both in the menu and
in-game, and a density change, which destroys and recreates the activity the
way a rotation does. Every cycle tears the EGL context down and builds a new
one, and the renderer comes back with FBOs and textures intact. The config
ladder's first choice, RGBA8888 D24S8, is what the Java chooser landed on
here too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 15:22:25 +02:00

59 lines
2.6 KiB
C++

// Copyright (c) 2026- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <EGL/egl.h>
#include "Common/GPU/OpenGL/OpenGLGraphicsContext.h"
struct ANativeWindow;
// 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 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
// makes GL calls, so EGL has to outlive it - ShutdownSurface() gets that order right, and is
// a no-op if the render loop already called it.
~AndroidEGLGraphicsContext() override { ShutdownSurface(); }
// Must be called on the thread that will become the render thread - the GL context ends up
// current on the calling thread. data1 is the ANativeWindow, as on the Vulkan path.
bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) override;
void ShutdownSurface() override;
private:
// Picks a config, from the most to the least desirable. Fills in config_.
bool ChooseConfig(bool wantGLES3, std::string *errorMessage);
void DestroyEGL();
EGLDisplay display_ = EGL_NO_DISPLAY;
EGLSurface surface_ = EGL_NO_SURFACE;
EGLContext context_ = EGL_NO_CONTEXT;
EGLConfig config_ = nullptr;
// Not owned - the caller releases it after the render loop exits.
ANativeWindow *wnd_ = nullptr;
};