Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Common/System/NativeApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ void NativeFrame(GraphicsContext *graphicsContext);
// The app must fill the buffer completely, doing its own internal buffering if needed.
void NativeMix(short *audio, int num_samples, int sampleRateHz, void *userdata);

// Runs if System_GetProperty(SYSPROP_HAS_VSYNC_CALLBACK) is supported.
void NativeVSync(int64_t vsyncId, double frameTime, double expectedPresentationTime);

// Called when it's time to shutdown. After this has been called,
// no more calls to any other function will be made from the framework
// before process exit.
Expand Down
2 changes: 2 additions & 0 deletions Common/System/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ enum SystemProperty {
SYSPROP_USE_IAP,
SYSPROP_USE_APP_STORE,
SYSPROP_SUPPORTS_SHARE_TEXT,

SYSPROP_HAS_VSYNC_CALLBACK,
};

enum class SystemNotification {
Expand Down
4 changes: 4 additions & 0 deletions UI/NativeApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,10 @@ void NativeResized() {
resized = true;
}

void NativeVSync(int64_t vsyncId, double frameTime, double expectedPresentationTime) {
// TODO: Make use of this.
}

void NativeSetRestarting() {
restarting = true;
}
Expand Down
8 changes: 8 additions & 0 deletions android/jni/app-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ bool System_GetPropertyBool(SystemProperty prop) {
return false; // Update if we add support in FileUtil.cpp: OpenFileInEditor
case SYSPROP_SUPPORTS_SHARE_TEXT:
return true;
case SYSPROP_HAS_VSYNC_CALLBACK:
return true;
case SYSPROP_APP_GOLD:
#ifdef GOLD
return true;
Expand Down Expand Up @@ -622,6 +624,12 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioConfig
optimalSampleRate = optimalSR;
}

// Allow the app to intercept the back button.
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_vsync(JNIEnv *env, jclass, long long frameTimeNanos, long long vsyncId, long long expectedPresentationTimeNanos) {
// The frame times should match the raw times we get from the system.
NativeVSync(vsyncId, frameTimeNanos > 0 ? from_time_raw(frameTimeNanos) : -1.0, expectedPresentationTimeNanos > 0 ? from_time_raw(expectedPresentationTimeNanos) : -1.0);
}

// Easy way for the Java side to ask the C++ side for configuration options, such as
// the rotation lock which must be controlled from Java on Android.
static std::string QueryConfig(std::string_view query) {
Expand Down
3 changes: 3 additions & 0 deletions android/src/org/ppsspp/ppsspp/NativeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public class NativeApp {
public static native void setSatInfoAndroid(short index, short id, short elevation, short azimuth, short snr, short good);
public static native void pushCameraImageAndroid(byte[] image);

// From the choreographer.
public static native void vsync(long frameTimeNanos, long vsyncId, long expectedPresentationTimeNanos);

// Wrappers
public static void reportException(Exception e, String data) {
StringBuilder str = new StringBuilder(e.toString() + "\n" + e.getMessage() + "\n");
Expand Down
55 changes: 55 additions & 0 deletions android/src/org/ppsspp/ppsspp/PpssppActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import android.text.InputType;
import android.util.Log;
import android.database.Cursor;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
Expand Down Expand Up @@ -90,6 +91,12 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
// Lifecycle tracker, to detect erroneous states.
private final LifeCycle lifeCycle = new LifeCycle();

private final Choreographer choreographer = Choreographer.getInstance();

// Callback objects (we’ll reuse the same instance so we can remove them cleanly)
private Choreographer.FrameCallback frameCallback;
private Choreographer.VsyncCallback vsyncCallback;

// Graphics and audio interfaces for Vulkan (javaGL = false)
private NativeSurfaceView mSurfaceView;
private Surface mSurface;
Expand Down Expand Up @@ -623,6 +630,40 @@ private void updateSystemUiVisibility() {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
vsyncCallback = new Choreographer.VsyncCallback() {
@Override
public void onVsync(Choreographer.FrameData frameData) {
// API 33+ path
long frameTimeNanos = frameData.getFrameTimeNanos();
Choreographer.FrameTimeline timeline = frameData.getPreferredFrameTimeline();
long vsyncId = timeline.getVsyncId();
long expectedPresentationTimeNanos = timeline.getExpectedPresentationTimeNanos();

// Call your native framework:
NativeApp.vsync(frameTimeNanos, vsyncId, expectedPresentationTimeNanos);

// Re-post for next vsync
choreographer.postVsyncCallback(this);
}
};
} else {
frameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
// This is the fallback path (pre-API 33)
// Convert to e.g. milliseconds if needed:
long frameTimeMs = frameTimeNanos / 1_000_000L;

// Call your native framework, e.g. nativeOnVsync(frameTimeNanos, -1, -1);
NativeApp.vsync(frameTimeNanos, /*vsyncId=*/-1, /*expectedPresentationTimeNanos=*/-1);

// Re-post for next frame
choreographer.postFrameCallback(this);
}
};
}

if (m_hasNoNativeBinary) {
new Thread() {
@Override
Expand Down Expand Up @@ -969,6 +1010,13 @@ protected void onPause() {
super.onPause();
lifeCycle.onPause();

// Remove callbacks so we stop receiving events
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
choreographer.removeVsyncCallback(vsyncCallback);
} else {
choreographer.removeFrameCallback(frameCallback);
}

if (!javaGL) {
Log.i(TAG, "Joining render thread...");
joinRenderLoopThread();
Expand Down Expand Up @@ -1014,6 +1062,12 @@ protected void onResume() {
} else if (mGLSurfaceView != null) {
mGLSurfaceView.onResume();
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
choreographer.postVsyncCallback(vsyncCallback);
} else {
choreographer.postFrameCallback(frameCallback);
}
Log.i(TAG, "onResume end");
}

Expand Down Expand Up @@ -1880,6 +1934,7 @@ public String getDebugString(String str) {
return "bad debug string: " + str;
}
}

private static String exitReasonToString(int reason) {
switch (reason) {
case android.app.ApplicationExitInfo.REASON_ANR:
Expand Down
3 changes: 2 additions & 1 deletion headless/Headless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ bool System_AudioRecordingState() { return false; }

// Temporary hacks around annoying linking errors.
void NativeFrame(GraphicsContext *graphicsContext) { }
void NativeResized() { }
void NativeResized() {}
void NativeVSync(int64_t vsyncId, double frameTime, double expectedPresentationTime) {}

std::string System_GetProperty(SystemProperty prop) { return ""; }
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
Expand Down
1 change: 1 addition & 0 deletions libretro/libretro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,7 @@ void System_PostUIMessage(UIMessage message, std::string_view param) {}
void System_RunOnMainThread(std::function<void()>) {}
void NativeFrame(GraphicsContext *graphicsContext) {}
void NativeResized() {}
void NativeVSync(int64_t vsyncId, double frameTime, double expectedPresentationTime) {}

void System_Toast(std::string_view str) {}

Expand Down
5 changes: 3 additions & 2 deletions unittest/JitHarness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
#include "Core/HLE/HLE.h"

// Temporary hacks around annoying linking errors. Copied from Headless.
void NativeFrame(GraphicsContext *graphicsContext) { }
void NativeResized() { }
void NativeFrame(GraphicsContext *graphicsContext) {}
void NativeResized() {}
void NativeVSync(int64_t vsyncId, double frameTime, double expectedPresentationTime) {}

bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) { return false; }
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) { cb(false, ""); }
Expand Down
Loading