     CAST(CAST(STR_SPLIT(slice.name, ' ', 3) AS NUMBER) * 1e6 + slice.ts AS INTEGER) AS expected_vsync_ts,
     slice.ts,
     slice.ts + slice.dur AS ts_end,
-    slice.dur
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
+    slice.dur,
+    gcs.gcs_idx
+  FROM slice
+  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
+  LEFT JOIN android_sysui_cuj_sf_mts_to_gcs gcs ON slice.ts = gcs.ts
   WHERE slice.name GLOB 'onMessageInvalidate *'
   )
 SELECT

    slice.name,
    slice.ts,
    slice.dur,
    slice.ts + slice.dur AS ts_end
  FROM slice
  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
  WHERE slice.dur > 0 AND slice.name GLOB 'composite*';

DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj;

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
