@@ -1800,6 +1800,8 @@ typedef struct sapp_allocator {
18001800 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \
18011801 _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \
18021802 _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity successfully created") \
1803+ _SAPP_LOGITEM_XMACRO(ANDROID_CHOREOGRAPHER_ENABLED, "Choreographer frame loop enabled") \
1804+ _SAPP_LOGITEM_XMACRO(ANDROID_CHOREOGRAPHER_UNAVAILABLE, "Choreographer unavailable, using poll loop") \
18031805 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOST, "wgpu: device lost") \
18041806 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOG, "wgpu: device log") \
18051807 _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_UNCAPTURED_ERROR, "wgpu: uncaptured error") \
@@ -2208,9 +2210,9 @@ SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index);
22082210SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc);
22092211
22102212/* get runtime environment information */
2211- sapp_environment sapp_get_environment(void);
2213+ SOKOL_APP_API_DECL sapp_environment sapp_get_environment(void);
22122214/* get current frame's swapchain information (call once per frame!) */
2213- sapp_swapchain sapp_get_swapchain(void);
2215+ SOKOL_APP_API_DECL sapp_swapchain sapp_get_swapchain(void);
22142216
22152217/* EGL: get EGLDisplay object */
22162218SOKOL_APP_API_DECL const void* sapp_egl_get_display(void);
@@ -2515,6 +2517,9 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
25152517 #include <time.h>
25162518 #include <android/native_activity.h>
25172519 #include <android/looper.h>
2520+ #if __ANDROID_API__ >= 29
2521+ #include <android/choreographer.h>
2522+ #endif
25182523 #include <EGL/egl.h>
25192524 #include <GLES3/gl3.h>
25202525#elif defined(_SAPP_LINUX)
@@ -3047,6 +3052,10 @@ typedef struct {
30473052 EGLDisplay display;
30483053 EGLContext context;
30493054 EGLSurface surface;
3055+ #if __ANDROID_API__ >= 29
3056+ AChoreographer* choreographer;
3057+ bool frame_callback_in_flight;
3058+ #endif
30503059} _sapp_android_t;
30513060
30523061#endif // _SAPP_ANDROID
@@ -10191,7 +10200,7 @@ _SOKOL_PRIVATE bool _sapp_win32_make_custom_mouse_cursor(sapp_mouse_cursor curso
1019110200 return win32_cursor != 0;
1019210201}
1019310202
10194- SOKOL_API_IMPL void _sapp_win32_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
10203+ _SOKOL_PRIVATE void _sapp_win32_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
1019510204 SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
1019610205 HCURSOR win32_cursor = _sapp.win32.custom_cursors[cursor];
1019710206 SOKOL_ASSERT(win32_cursor);
@@ -10432,11 +10441,11 @@ _SOKOL_PRIVATE void _sapp_android_shutdown(void) {
1043210441 ANativeActivity_finish(_sapp.android.activity);
1043310442}
1043410443
10435- _SOKOL_PRIVATE void _sapp_android_frame(void ) {
10444+ _SOKOL_PRIVATE void _sapp_android_frame(double external_now ) {
1043610445 SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
1043710446 SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
1043810447 SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
10439- _sapp_timing_update(&_sapp.timing, 0.0 );
10448+ _sapp_timing_update(&_sapp.timing, external_now );
1044010449 _sapp_android_update_dimensions(_sapp.android.current.window, false);
1044110450 _sapp_frame();
1044210451 eglSwapBuffers(_sapp.android.display, _sapp.android.surface);
@@ -10630,6 +10639,23 @@ _SOKOL_PRIVATE bool _sapp_android_should_update(void) {
1063010639 return is_in_front && has_surface;
1063110640}
1063210641
10642+ #if __ANDROID_API__ >= 29
10643+ _SOKOL_PRIVATE void _sapp_android_frame_callback(int64_t frame_time_nanos, void* data) {
10644+ _SOKOL_UNUSED(data);
10645+ _sapp.android.frame_callback_in_flight = false;
10646+ if (_sapp.android.is_thread_stopping) {
10647+ return;
10648+ }
10649+ if (_sapp_android_should_update()) {
10650+ // Post the next frame callback. We do this here rather than later so the runnable can be
10651+ // queued early in the looper.
10652+ AChoreographer_postFrameCallback64(_sapp.android.choreographer, _sapp_android_frame_callback, NULL);
10653+ _sapp.android.frame_callback_in_flight = true;
10654+ _sapp_android_frame((double)frame_time_nanos / 1.0e9);
10655+ }
10656+ }
10657+ #endif
10658+
1063310659_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) {
1063410660 SOKOL_ASSERT(_sapp.valid);
1063510661 /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */
@@ -10652,6 +10678,17 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
1065210678 _sapp_android_main_cb,
1065310679 NULL); /* data */
1065410680
10681+ #if __ANDROID_API__ >= 29
10682+ _sapp.android.choreographer = AChoreographer_getInstance();
10683+ if (_sapp.android.choreographer != NULL) {
10684+ _SAPP_INFO(ANDROID_CHOREOGRAPHER_ENABLED);
10685+ } else {
10686+ _SAPP_INFO(ANDROID_CHOREOGRAPHER_UNAVAILABLE);
10687+ }
10688+ #else
10689+ _SAPP_INFO(ANDROID_CHOREOGRAPHER_UNAVAILABLE);
10690+ #endif
10691+
1065510692 /* signal start to main thread */
1065610693 pthread_mutex_lock(&_sapp.android.pt.mutex);
1065710694 _sapp.android.is_thread_started = true;
@@ -10660,9 +10697,24 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
1066010697
1066110698 /* main loop */
1066210699 while (!_sapp.android.is_thread_stopping) {
10663- /* sokol frame */
10700+ #if __ANDROID_API__ >= 29
10701+ if (_sapp.android.choreographer != NULL) {
10702+ // Posts _sapp_android_frame_callback with the choreographer to start our frame
10703+ // loop (for example, on first run or when resuming). When we have a choreographer,
10704+ // we'll get frame callbacks via _sapp_android_frame_callback.
10705+ if (!_sapp.android.frame_callback_in_flight && _sapp_android_should_update()) {
10706+ AChoreographer_postFrameCallback64(_sapp.android.choreographer, _sapp_android_frame_callback, NULL);
10707+ _sapp.android.frame_callback_in_flight = true;
10708+ }
10709+ // Blocks until the next event. We don't need a while loop here because we're
10710+ // already being driven by the outer while loop.
10711+ ALooper_pollOnce(-1, NULL, NULL, NULL);
10712+ continue;
10713+ }
10714+ #endif
10715+ // sokol frame -- fallback if not updating frames from choreographer callbacks
1066410716 if (_sapp_android_should_update()) {
10665- _sapp_android_frame();
10717+ _sapp_android_frame(0.0 );
1066610718 }
1066710719
1066810720 /* process all events (or stop early if app is requested to quit) */
@@ -14059,7 +14111,7 @@ SOKOL_API_IMPL sapp_mouse_cursor sapp_bind_mouse_cursor_image(sapp_mouse_cursor
1405914111 return cursor; // returning the passed-in cursor puerly for convenience, in case you want to asign the value to a variable.
1406014112}
1406114113
14062- SOKOL_APP_API_DECL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor) {
14114+ SOKOL_API_IMPL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor) {
1406314115 SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
1406414116 if (_sapp.custom_cursor_bound[(int)cursor]) {
1406514117 // if this is the active cursor, first restore it to its default image,
0 commit comments