 JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries
 WHERE vsync >= cuj_boundaries.vsync_min AND vsync <= cuj_boundaries.vsync_max;
 
+-- Find matching GPU completion slice idx for top-level mainthread slices
+DROP TABLE IF EXISTS android_sysui_cuj_sf_mts_to_gcs;
+CREATE TABLE android_sysui_cuj_sf_mts_to_gcs AS
+  SELECT
+    slice.ts,
+    CASE
+      WHEN fence.name GLOB 'GPU completion fence *'
+        THEN CAST(STR_SPLIT(fence.name, ' ', 3) AS INTEGER)
+      WHEN fence.name GLOB 'Trace GPU completion fence *'
+        THEN CAST(STR_SPLIT(fence.name, ' ', 4) AS INTEGER)
+      ELSE NULL
+    END AS gcs_idx
+  FROM slice
+  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
+  JOIN descendant_slice(slice.id) fence ON fence.name GLOB '*GPU completion fence*'
+  WHERE (slice.name GLOB 'composite*' OR slice.name GLOB 'onMessageInvalidate*');
+
 -- Find just the onMessageInvalidate slices, within the CUJ (by vsync)
 DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj;
 CREATE VIEW android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj AS

    composite.ts_end - match.commit_ts AS dur,
    match.expected_vsync_ts,
    lag(composite.ts_end) OVER (ORDER BY composite.ts_end ASC) AS ts_prev_frame_end,
    lead(match.commit_ts) OVER (ORDER BY match.commit_ts ASC) AS ts_next_frame_start
  FROM commit_to_composite match
  JOIN android_sysui_cuj_surfaceflinger_composite_slices composite ON match.composite_ts = composite.ts;


    on_msg.dur,
    on_msg.expected_vsync_ts,
    lag(on_msg.ts_end) OVER (ORDER BY on_msg.ts_end ASC) AS ts_prev_frame_end,
    lead(on_msg.ts) OVER (ORDER BY on_msg.ts ASC) AS ts_next_frame_start,
    on_msg.gcs_idx
FROM on_msg
JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries
ON on_msg.vsync >= cuj_boundaries.vsync_min AND on_msg.vsync <= cuj_boundaries.vsync_max;

-- Find just the composite slices
DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_composite_slices;
