Skip to content

Commit 5cb2cfd

Browse files
authored
Merge pull request #22451 from hrydgard/android-egl-replace-javagl
Android: Replace JavaGL init with EGL
2 parents d74f16e + bd94e81 commit 5cb2cfd

15 files changed

Lines changed: 454 additions & 383 deletions

‎CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,8 @@ if(ANDROID)
925925
android/jni/app-android.cpp
926926
android/jni/AndroidAudio.cpp
927927
android/jni/AndroidAudio.h
928+
android/jni/AndroidEGLGraphicsContext.cpp
929+
android/jni/AndroidEGLGraphicsContext.h
928930
android/jni/OpenSLContext.cpp
929931
android/jni/OpenSLContext.h
930932
)

‎Common/GPU/GraphicsContext.h‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ class GraphicsContext {
5454
// Called from the emu thread, so be ready for that.
5555
virtual void NotifyEmuThreadExit() {}
5656

57-
// The API context that queued-up commands were recorded against is gone (or isn't current on the
58-
// thread about to drain them), so they must not be executed for real. Only meaningful on Android GL.
59-
virtual void NotifyContextLost() {}
60-
6157
// Useful for checks that need to be performed every frame.
6258
// Should strive to get rid of these.
6359
virtual void Poll() {}

‎Common/GPU/OpenGL/OpenGLGraphicsContext.h‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,15 @@ class OpenGLGraphicsContext : public GraphicsContext {
3333
renderManager_->ThreadEnd();
3434
}
3535

36-
void NotifyContextLost() override {
37-
if (renderManager_) {
38-
renderManager_->SetSkipGLCalls();
39-
}
40-
}
41-
4236
// Call from emu thread
4337
void NotifyEmuThreadExit() override {
4438
renderManager_->SetSkipGLCalls();
4539
renderManager_->NotifyEmuThreadExit();
4640
}
4741

48-
private:
42+
protected:
43+
// Protected rather than private so a subclass that owns the real context (see
44+
// android/jni/AndroidEGLGraphicsContext.h) can hook up a swap function.
4945
Draw::DrawContext *draw_ = nullptr;
5046
GLRenderManager *renderManager_ = nullptr;
5147
};

‎Common/GPU/Vulkan/VulkanQueueRunner.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
using namespace PPSSPP_VK;
1010

11-
// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper PPSSPPSizeManager
11+
// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper PPSSPPSizeManager
1212

1313
static void MergeRenderAreaRectInto(VkRect2D *dest, const VkRect2D &src) {
1414
if (dest->offset.x > src.offset.x) {

‎Core/EmuThread.cpp‎

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
#include "Common/Log.h"
1414
#include "Common/StringUtils.h"
1515
#include "Common/GPU/GraphicsContext.h"
16+
#include "Common/System/Display.h"
1617
#include "Common/Thread/ThreadUtil.h"
18+
#include "Common/TimeUtil.h"
19+
#include "Common/VR/PPSSPPVR.h"
1720

1821
#include "Core/EmuThread.h"
1922
#include "Core/Core.h"
@@ -125,34 +128,68 @@ bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std
125128
return true;
126129
}
127130

128-
// Call InitAPI and ShutdownAPI outside this!
129-
bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, const WindowDesc &windowDesc, std::function<bool(GraphicsContext *)> frame, std::string *errorMessage) {
130-
// This is now the render thread, and will spawn the emu thread below.
131-
if (!graphicsContext->InitSurface(windowDesc.winsys, windowDesc.data1, windowDesc.data2, errorMessage)) {
132-
ERROR_LOG(Log::G3D, "MainThreadFunc: InitSurface failed: %s", errorMessage->c_str());
133-
delete application;
134-
return false;
131+
void RunGraphicsLoop(GraphicsContext *graphicsContext, Application *application, std::function<bool(GraphicsContext *)> frame, std::function<bool()> shouldExit) {
132+
if (!graphicsContext->NeedsSeparateEmuThread()) {
133+
// The backend spawns its own render thread, so this thread is simply where emulation runs.
134+
SetCurrentThreadName("EmuThread");
135+
RunMainLoop(graphicsContext, application, frame);
136+
return;
135137
}
136-
if (graphicsContext->NeedsSeparateEmuThread()) {
137-
SetCurrentThreadName("RenderThread");
138138

139-
g_inLoop = true;
140-
std::thread emuThread = EmuThread_Start(graphicsContext, application, frame);
141-
graphicsContext->ThreadStart();
142-
// This thread becomes the render thread. EmuThread will tell it when to quit by sending a message.
143-
while (graphicsContext->ThreadFrame()) {}
144-
EmuThread_Join(graphicsContext, emuThread);
145-
g_inLoop = false;
139+
// OpenGL wants its API calls on the thread its context is current on, which is this one. So
140+
// this thread becomes the render thread - all it does is execute the GPU commands the emu
141+
// thread queues up - and emulation moves to a thread we spawn here.
142+
SetCurrentThreadName("RenderThread");
146143

147-
graphicsContext->ThreadEnd();
144+
g_inLoop = true;
148145

149-
INFO_LOG(Log::System, "RenderThread - joined");
146+
// EmuThread_Start calls ThreadStart() for us, and EmuThread_Join calls ThreadEnd(), so neither
147+
// is called directly here.
148+
std::thread emuThread = EmuThread_Start(graphicsContext, application, frame);
150149

151-
} else {
152-
SetCurrentThreadName("MainThread");
150+
if (IsVREnabled()) {
151+
static bool vrFirstStart = true;
152+
EnterVR(vrFirstStart);
153+
vrFirstStart = false;
154+
}
153155

154-
RunMainLoop(graphicsContext, application, frame);
156+
// Normally ThreadFrame() returning false is what gets us out of here - the frame callback
157+
// decides emulation is done, and the emu thread's NotifyEmuThreadExit then queues the exit.
158+
// That isn't enough in VR: while the session is idle we skip ThreadFrame() entirely, and going
159+
// to the background is precisely when the session goes idle, so the exit would never be seen
160+
// and whoever is waiting to join this thread would wait forever. Hence shouldExit.
161+
while (!(shouldExit && shouldExit())) {
162+
if (IsVREnabled() && !StartVRRender()) {
163+
// The session isn't active, so there's no frame to render into. Nothing to do until it
164+
// comes back - wait out a frame rather than spinning on it.
165+
sleep_ms(16, "vr-session-idle");
166+
continue;
167+
}
168+
if (!graphicsContext->ThreadFrame()) {
169+
break;
170+
}
171+
if (IsVREnabled()) {
172+
UpdateVRInput(g_Config.bHapticFeedback, g_display.dpi_scale_x, g_display.dpi_scale_y);
173+
FinishVRRender();
174+
}
175+
}
176+
177+
// Also drains whatever the emu thread still had queued, so it can't get stuck waiting on us.
178+
EmuThread_Join(graphicsContext, emuThread);
179+
180+
g_inLoop = false;
181+
182+
INFO_LOG(Log::System, "RenderThread - joined");
183+
}
184+
185+
// Call InitAPI and ShutdownAPI outside this!
186+
bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, const WindowDesc &windowDesc, std::function<bool(GraphicsContext *)> frame, std::string *errorMessage) {
187+
if (!graphicsContext->InitSurface(windowDesc.winsys, windowDesc.data1, windowDesc.data2, errorMessage)) {
188+
ERROR_LOG(Log::G3D, "MainThreadFunc: InitSurface failed: %s", errorMessage->c_str());
189+
delete application;
190+
return false;
155191
}
192+
RunGraphicsLoop(graphicsContext, application, frame);
156193
graphicsContext->ShutdownSurface();
157194
return true;
158195
}

‎Core/EmuThread.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,15 @@ void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread);
5050
// Call from the main thread.
5151
// NOTE: Does take ownership over Application (which is just a wrapper for NativeInitGraphics/NativeShutdownGraphics/NativeFrame).
5252
bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std::function<bool(GraphicsContext *)> frame);
53+
54+
// Runs whichever thread arrangement graphicsContext asks for, on the calling thread, and doesn't
55+
// return until the emu thread is done. Either this thread becomes the render thread and the emu
56+
// thread is spawned alongside it (OpenGL, which needs its API calls on the thread its context is
57+
// current on), or this thread just runs the main loop because the backend spawns its own render
58+
// thread (Vulkan, D3D11).
59+
// The surface has to be initialized already, and is left alone - use MainThreadFunc if you want
60+
// that taken care of too.
61+
// shouldExit is optional, but required if you might run this in VR: see the comment on the render
62+
// loop in EmuThread.cpp for why ThreadFrame() alone isn't enough to get out of there.
63+
// NOTE: Does take ownership over Application (which is just a wrapper for NativeInitGraphics/NativeShutdownGraphics/NativeFrame).
64+
void RunGraphicsLoop(GraphicsContext *graphicsContext, Application *application, std::function<bool(GraphicsContext *)> frame, std::function<bool()> shouldExit = nullptr);

‎android/jni/Android.mk‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ LOCAL_MODULE := ppsspp_jni
940940
LOCAL_SRC_FILES := \
941941
$(SRC)/android/jni/app-android.cpp \
942942
$(SRC)/android/jni/AndroidAudio.cpp \
943+
$(SRC)/android/jni/AndroidEGLGraphicsContext.cpp \
943944
$(SRC)/android/jni/OpenSLContext.cpp \
944945
$(SRC)/UI/ImDebugger/ImDebugger.cpp \
945946
$(SRC)/UI/ImDebugger/ImGe.cpp \

0 commit comments

Comments
 (0)