diff --git a/app/src/main/cpp/waylandcomp/src/sc_layer.c b/app/src/main/cpp/waylandcomp/src/sc_layer.c index 1fcbf3357..e8a651c42 100644 --- a/app/src/main/cpp/waylandcomp/src/sc_layer.c +++ b/app/src/main/cpp/waylandcomp/src/sc_layer.c @@ -836,6 +836,7 @@ int sc_layer_present_ahb(AHardwareBuffer *ahb, int w, int h, int acquire_fd, voi apply_geometry(tx, l, r); apply_frame_rate(tx, l); apply_colour(tx, l, color); api.txApply(tx); api.txDelete(tx); } + vkp_signal_first_frame(); return 0; } ASurfaceTransaction *tx = api.txCreate(); @@ -863,7 +864,10 @@ int sc_layer_present_ahb(AHardwareBuffer *ahb, int w, int h, int acquire_fd, voi l->cur_slot = -1; l->cur_token = token; if (!l->shown) { l->shown = 1; banner_log("layer", "%s: layer shown", l->name); } - if (!l->first_logged) { l->first_logged = 1; vkp_signal_first_frame(); } + /* The notification is re-armed for each session; the layer and its logging flag can + * outlive it. Let the notification's own one-shot decide whether to fire. */ + if (!l->first_logged) l->first_logged = 1; + vkp_signal_first_frame(); if (color && color->dataspace) banner_color_frame_shown(color, BANNER_HDR_ZERO_COPY, ahb_format); return 0; unavailable: @@ -891,8 +895,8 @@ int sc_layer_present(struct vkp_image *src, int scene_w, int scene_h, const stru banner_log("layer", "presenting %dx%d game frames on their own SurfaceControl layer (%s pool, %d buffers); " "HUD and pointer stay Android views above it", sw, sh, l->pool_modifier == MOD_QCOM_COMPRESSED ? "UBWC" : "linear", l->pool_n); - vkp_signal_first_frame(); } + vkp_signal_first_frame(); return 0; } @@ -915,8 +919,8 @@ int sc_layer_present_pass(const struct vkp_draw *draws, int n, int scene_w, int banner_log("layer", "presenting %dx%d frames on their own SurfaceControl layer (%s pool, %d buffers); " "HUD and pointer stay Android views above it", rw, rh, l->pool_modifier == MOD_QCOM_COMPRESSED ? "UBWC" : "linear", l->pool_n); - vkp_signal_first_frame(); } + vkp_signal_first_frame(); return 0; } @@ -971,7 +975,8 @@ int sc_layer_present_hdr_scene(const struct vkp_draw *draws, int n, const struct cov[0] ? ", " : "", cov); } } - if (!l->first_logged) { l->first_logged = 1; vkp_signal_first_frame(); } + if (!l->first_logged) l->first_logged = 1; + vkp_signal_first_frame(); return 0; } diff --git a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt index 83d4e726d..c6c03b320 100644 --- a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt @@ -907,7 +907,6 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback { // whole process, so the second Play after a session ended used to re-attach the Surface, // start nothing, and leave the loading panel counting up over a dead session. if (!SessionState.running) { - CompositorHost.newSession() SessionEvents.transition(SessionPhase.STARTING_GUEST, "guest.starting") SessionService.start( this, intent.getStringExtra(SessionService.EXTRA_MODE) ?: SessionService.MODE_STEAM, diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt index 4c31cba4f..139db9fae 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt @@ -217,6 +217,9 @@ class SessionService : Service() { SessionState.firstFrameSeen = false SessionState.guestPid = -1 SessionEvents.transition(SessionPhase.STARTING_GUEST, "service.started", mapOf("mode" to SessionState.mode)) + // Re-arm after clearing readiness: a retained picture can present between the activity's + // start request and this callback, and that notice must not be erased by the reset above. + com.droiddeck.launcher.wayland.CompositorHost.newSession() // This session's number, claimed here and not when its process starts: the session it // replaces can report its own exit in the gap between the two, and that exit must not // be taken as this one's.