add toggle for frame pacing

This commit is contained in:
Chloe committed 2026-09-07 00:41:18 -07:00
1 parent a32589b06c
commit adb9dcc1e9
6 files changed
+86 -18

No files matched your search

@@ -325,6 +325,9 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
public int getRenderMode() { public int getRenderMode() {
return mGLThread.getRenderMode(); return mGLThread.getRenderMode();
} }
public void setNativeFramePacing(boolean nativeFramePacing) { mGLThread.setNativeFramePacing(nativeFramePacing); }
public boolean getNativeFramePacing() { return mGLThread.getNativeFramePacing(); }
/** /**
* Request that the renderer render a frame. * Request that the renderer render a frame.
* This method is typically used when the render mode has been set to * This method is typically used when the render mode has been set to
@@ -739,6 +742,7 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
private static class EglHelper { private static class EglHelper {
public EglHelper(WeakReference<ExplicitGLSurfaceView> glSurfaceViewWeakRef) { public EglHelper(WeakReference<ExplicitGLSurfaceView> glSurfaceViewWeakRef) {
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
mNativeFramePacing = false;
} }
/** /**
* Initialize EGL for a given configuration spec. * Initialize EGL for a given configuration spec.
@@ -814,12 +818,16 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
if (view != null) { if (view != null) {
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface( mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(
mEglDisplay, mEglConfig, view.getHolder()); mEglDisplay, mEglConfig, view.getHolder());
// NOTE: this diverges from GLSurfaceView
LauncherFix.INSTANCE.setSurface(view.getHolder().getSurface()); if (mNativeFramePacing) {
LauncherFix.INSTANCE.setSurface(view.getHolder().getSurface());
}
} else { } else {
mEglSurface = null; mEglSurface = null;
// NOTE: this diverges from GLSurfaceView
LauncherFix.INSTANCE.setSurface(null); if (mNativeFramePacing) {
LauncherFix.INSTANCE.setSurface(null);
}
} }
if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) { if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) {
int error = EGL14.eglGetError(); int error = EGL14.eglGetError();
@@ -848,11 +856,16 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
* @return the EGL error code from eglSwapBuffers. * @return the EGL error code from eglSwapBuffers.
*/ */
public int swap() { public int swap() {
// NOTE: this diverges from GLSurfaceView if (mNativeFramePacing) {
// if (! EGL14.eglSwapBuffers(mEglDisplay, mEglSurface)) { if (!LauncherFix.INSTANCE.swapFrame(mEglDisplay.getNativeHandle(), mEglSurface.getNativeHandle())) {
if (!LauncherFix.INSTANCE.swapFrame(mEglDisplay.getNativeHandle(), mEglSurface.getNativeHandle())) { return EGL14.eglGetError();
return EGL14.eglGetError(); }
} else {
if (!EGL14.eglSwapBuffers(mEglDisplay, mEglSurface)) {
return EGL14.eglGetError();
}
} }
return EGL14.EGL_SUCCESS; return EGL14.EGL_SUCCESS;
} }
public void destroySurface() { public void destroySurface() {
@@ -871,6 +884,10 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
view.mEGLWindowSurfaceFactory.destroySurface(mEglDisplay, mEglSurface); view.mEGLWindowSurfaceFactory.destroySurface(mEglDisplay, mEglSurface);
} }
mEglSurface = null; mEglSurface = null;
if (mNativeFramePacing) {
LauncherFix.INSTANCE.setSurface(null);
}
} }
} }
public void finish() { public void finish() {
@@ -911,6 +928,8 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
EGLSurface mEglSurface; EGLSurface mEglSurface;
EGLConfig mEglConfig; EGLConfig mEglConfig;
EGLContext mEglContext; EGLContext mEglContext;
boolean mNativeFramePacing;
} }
/** /**
* A generic GL Thread. Takes care of initializing EGL and GL. Delegates * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
@@ -930,6 +949,7 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
mRenderMode = RENDERMODE_CONTINUOUSLY; mRenderMode = RENDERMODE_CONTINUOUSLY;
mWantRenderNotification = false; mWantRenderNotification = false;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
mNativeFramePacing = false;
} }
@Override @Override
public void run() { public void run() {
@@ -968,6 +988,7 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
} }
private void guardedRun() throws InterruptedException { private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
mEglHelper.mNativeFramePacing = mNativeFramePacing;
mHaveEglContext = false; mHaveEglContext = false;
mHaveEglSurface = false; mHaveEglSurface = false;
mWantRenderNotification = false; mWantRenderNotification = false;
@@ -1218,8 +1239,6 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
finishDrawingRunnable.run(); finishDrawingRunnable.run();
finishDrawingRunnable = null; finishDrawingRunnable = null;
} }
// NOTE: this diverges from GLSurfaceView
requestRender();
} finally { } finally {
// Trace.traceEnd(Trace.TRACE_TAG_VIEW); // Trace.traceEnd(Trace.TRACE_TAG_VIEW);
} }
@@ -1251,6 +1270,11 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
doRenderNotification = true; doRenderNotification = true;
wantRenderNotification = false; wantRenderNotification = false;
} }
// request new frame prior to swap
if (mNativeFramePacing) {
requestRender();
}
} }
} finally { } finally {
/* /*
@@ -1279,6 +1303,21 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
sGLThreadManager.notifyAll(); sGLThreadManager.notifyAll();
} }
} }
public void setNativeFramePacing(boolean nativeFramePacing) {
synchronized(sGLThreadManager) {
if (mEglHelper != null) {
mEglHelper.mNativeFramePacing = nativeFramePacing;
}
mNativeFramePacing = nativeFramePacing;
sGLThreadManager.notifyAll();
}
}
public boolean getNativeFramePacing() {
synchronized(sGLThreadManager) {
return mNativeFramePacing;
}
}
public int getRenderMode() { public int getRenderMode() {
synchronized(sGLThreadManager) { synchronized(sGLThreadManager) {
return mRenderMode; return mRenderMode;
@@ -1475,6 +1514,8 @@ public class ExplicitGLSurfaceView extends SurfaceView implements SurfaceHolder.
private Runnable mFinishDrawingRunnable = null; private Runnable mFinishDrawingRunnable = null;
// End of member variables protected by the sGLThreadManager monitor. // End of member variables protected by the sGLThreadManager monitor.
private EglHelper mEglHelper; private EglHelper mEglHelper;
private boolean mNativeFramePacing;
/** /**
* Set once at thread construction time, nulled out when the parent view is garbage * Set once at thread construction time, nulled out when the parent view is garbage
* called. This weak reference allows the GLSurfaceView to be garbage collected while * called. This weak reference allows the GLSurfaceView to be garbage collected while
@@ -77,6 +77,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
private var mAspectRatio = 0.0f private var mAspectRatio = 0.0f
private var mScreenZoom = 1.0f private var mScreenZoom = 1.0f
private var mScreenZoomFit = false private var mScreenZoomFit = false
private var mNativeFramePacing = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setupUIState() setupUIState()
@@ -131,7 +132,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
}) })
mGLSurfaceView?.manualBackEvents = true mGLSurfaceView?.manualBackEvents = true
LauncherFix.initFramePacing(this) if (mNativeFramePacing) {
LauncherFix.initFramePacing(this)
}
} }
private fun createVersionFile() { private fun createVersionFile() {
@@ -475,9 +478,11 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
val frameRate = mLimitedRefreshRate val frameRate = mLimitedRefreshRate
if (frameRate != null) { if (frameRate != null) {
glSurfaceView.setMaxFramerate(frameRate) glSurfaceView.setMaxFrameRate(frameRate)
} }
glSurfaceView.nativeFramePacing = mNativeFramePacing
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
glSurfaceView.cocos2dxEditText = editText glSurfaceView.cocos2dxEditText = editText
@@ -513,6 +518,8 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
} }
} }
mNativeFramePacing = preferenceUtils.getBoolean(PreferenceUtils.Key.NATIVE_FRAME_PACING)
if (displayMode == DisplayMode.FULLSCREEN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (displayMode == DisplayMode.FULLSCREEN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
GeodeUtils.handleSafeArea = true GeodeUtils.handleSafeArea = true
@@ -547,7 +554,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
super.onDestroy() super.onDestroy()
FMOD.close() FMOD.close()
LauncherFix.destroyFramePacing() if (mNativeFramePacing) {
LauncherFix.destroyFramePacing()
}
} }
private fun resumeGame() { private fun resumeGame() {
@@ -168,6 +168,12 @@ fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
} }
) )
SettingsCard(
title = stringResource(R.string.preference_native_pacing),
description = stringResource(R.string.preference_native_pacing_description),
preferenceKey = PreferenceUtils.Key.NATIVE_FRAME_PACING,
)
/* /*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
SettingsCard( SettingsCard(
@@ -149,6 +149,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
LAST_UPDATE_CHECK_TIME, LAST_UPDATE_CHECK_TIME,
LAST_LAUNCHER_UPDATE, LAST_LAUNCHER_UPDATE,
DISABLE_UPDATE_CACHE, DISABLE_UPDATE_CACHE,
NATIVE_FRAME_PACING,
} }
private fun defaultValueForBooleanKey(key: Key): Boolean { private fun defaultValueForBooleanKey(key: Key): Boolean {
@@ -199,6 +200,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.LAST_UPDATE_CHECK_TIME -> "PreferenceLastUpdateCheckTime" Key.LAST_UPDATE_CHECK_TIME -> "PreferenceLastUpdateCheckTime"
Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate" Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate"
Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache" Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache"
Key.NATIVE_FRAME_PACING -> "PreferenceNativeFramePacing"
// Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel" // Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel"
// Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate" // Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate"
// Key.FORCE_HRR -> "PreferenceForceHighRefreshRate" // Key.FORCE_HRR -> "PreferenceForceHighRefreshRate"
@@ -67,6 +67,7 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
var controllerEventsEnabled = false var controllerEventsEnabled = false
private set private set
var supportsResizeEvents = false var supportsResizeEvents = false
var saveNativeFramePacing = false
var cocos2dxEditText: AppCompatEditText? = null var cocos2dxEditText: AppCompatEditText? = null
set(value) { set(value) {
@@ -142,7 +143,9 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
println("updateRefreshRate: selecting refresh rate of ${chosenDisplay.refreshRate} (display ${chosenDisplay.modeId})") println("updateRefreshRate: selecting refresh rate of ${chosenDisplay.refreshRate} (display ${chosenDisplay.modeId})")
LauncherFix.setSwapInterval((1_000_000_000L / chosenRefreshRate).roundToLong()) if (saveNativeFramePacing) {
LauncherFix.setSwapInterval((1_000_000_000L / chosenRefreshRate).roundToLong())
}
holder.surface.setFrameRate(chosenRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) holder.surface.setFrameRate(chosenRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
if (isAttachedToWindow) { if (isAttachedToWindow) {
@@ -158,8 +161,8 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
renderMode = RENDERMODE_WHEN_DIRTY
// renderMode = RENDERMODE_CONTINUOUSLY renderMode = RENDERMODE_CONTINUOUSLY
queueEvent { cocos2dxRenderer.handleOnResume() } queueEvent { cocos2dxRenderer.handleOnResume() }
} }
@@ -537,7 +540,11 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
fun setCocos2dxRenderer(renderer: Cocos2dxRenderer) { fun setCocos2dxRenderer(renderer: Cocos2dxRenderer) {
this.cocos2dxRenderer = renderer this.cocos2dxRenderer = renderer
setRenderer(this.cocos2dxRenderer) setRenderer(this.cocos2dxRenderer)
renderMode = RENDERMODE_WHEN_DIRTY }
override fun setNativeFramePacing(nativeFramePacing: Boolean) {
super.setNativeFramePacing(nativeFramePacing)
saveNativeFramePacing = nativeFramePacing
} }
private fun getContentText(): String { private fun getContentText(): String {
@@ -574,7 +581,7 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
inputManager.unregisterInputDeviceListener(InputListener) inputManager.unregisterInputDeviceListener(InputListener)
} }
fun setMaxFramerate(rate: Int) { fun setMaxFrameRate(rate: Int) {
cocos2dxRenderer.limitFrameRate(rate) cocos2dxRenderer.limitFrameRate(rate)
} }
+3
View File
@@ -298,6 +298,9 @@
<string name="preference_profiles_delete_alt">Delete</string> <string name="preference_profiles_delete_alt">Delete</string>
<string name="preference_profiles_delete_undo_alt">Undo</string> <string name="preference_profiles_delete_undo_alt">Undo</string>
<string name="preference_native_pacing">Native frame pacing</string>
<string name="preference_native_pacing_description">Reduces latency, but may worsen frame drops.</string>
<!-- ApplicationLogsActivity strings --> <!-- ApplicationLogsActivity strings -->
<string name="title_activity_application_logs">Application logs</string> <string name="title_activity_application_logs">Application logs</string>
<string name="application_logs_no_logs">No logs available.</string> <string name="application_logs_no_logs">No logs available.</string>