ui: overhaul track decider queries to make things faster

This CL overhauls a bunch of queries in track_decider to improve
load performance.

Specifically, the main thing we are doing is extracting a bunch of O(event)
queries into the standard library and compute a summary over each of the
event tables exactly once. This significantly reduces the amount of
redundant computation that needs to be performed.

Change-Id: I49c07f681c7128cf37852ad66a8a2a8e2e17b2b5
diff --git a/Android.bp b/Android.bp
index db1ef89..afcb092 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12486,6 +12486,10 @@
         "src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql",
         "src/trace_processor/perfetto_sql/stdlib/time/conversion.sql",
         "src/trace_processor/perfetto_sql/stdlib/v8/jit.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/processes.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/slices.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/threads.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/tracks.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/arm_dsu.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/cpu_freq.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/cpu_idle.sql",
diff --git a/BUILD b/BUILD
index 0608cb1..389e3b8 100644
--- a/BUILD
+++ b/BUILD
@@ -2621,6 +2621,17 @@
     ],
 )
 
+# GN target: //src/trace_processor/perfetto_sql/stdlib/viz/summary:summary
+perfetto_filegroup(
+    name = "src_trace_processor_perfetto_sql_stdlib_viz_summary_summary",
+    srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/processes.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/slices.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/threads.sql",
+        "src/trace_processor/perfetto_sql/stdlib/viz/summary/tracks.sql",
+    ],
+)
+
 # GN target: //src/trace_processor/perfetto_sql/stdlib/wattson:wattson
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_wattson_wattson",
@@ -2656,6 +2667,7 @@
         ":src_trace_processor_perfetto_sql_stdlib_stack_trace_stack_trace",
         ":src_trace_processor_perfetto_sql_stdlib_time_time",
         ":src_trace_processor_perfetto_sql_stdlib_v8_v8",
+        ":src_trace_processor_perfetto_sql_stdlib_viz_summary_summary",
         ":src_trace_processor_perfetto_sql_stdlib_wattson_wattson",
     ],
     outs = [
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index 4c492d1..349e2f6 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -36,6 +36,7 @@
     "stack_trace",
     "time",
     "v8",
+    "viz/summary",
     "wattson",
   ]
   generated_header = "stdlib.h"
diff --git a/src/trace_processor/perfetto_sql/stdlib/viz/summary/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/viz/summary/BUILD.gn
new file mode 100644
index 0000000..3f664bd
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/viz/summary/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("summary") {
+  sources = [
+    "processes.sql",
+    "slices.sql",
+    "threads.sql",
+    "tracks.sql",
+  ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/viz/summary/processes.sql b/src/trace_processor/perfetto_sql/stdlib/viz/summary/processes.sql
new file mode 100644
index 0000000..18aa202
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/viz/summary/processes.sql
@@ -0,0 +1,93 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE viz.summary.slices;
+INCLUDE PERFETTO MODULE viz.summary.threads;
+
+CREATE PERFETTO TABLE _process_track_summary AS
+SELECT upid, SUM(cnt) AS slice_count
+FROM process_track
+JOIN _slice_track_summary USING (id)
+GROUP BY upid;
+
+CREATE PERFETTO TABLE _heap_profile_allocation_summary AS
+SELECT upid, COUNT() AS allocation_count
+FROM heap_profile_allocation
+GROUP BY upid;
+
+CREATE PERFETTO TABLE _heap_profile_graph_summary AS
+SELECT upid, COUNT() AS graph_object_count
+FROM heap_graph_object;
+
+CREATE PERFETTO TABLE _thread_process_grouped_summary AS
+SELECT
+  upid,
+  MAX(max_running_dur) AS max_running_dur,
+  SUM(sum_running_dur) AS sum_running_dur,
+  SUM(running_count) AS running_count,
+  SUM(slice_count) AS slice_count,
+  SUM(perf_sample_count) AS perf_sample_count
+FROM _thread_available_info_summary
+JOIN thread USING (utid)
+WHERE upid IS NOT NULL
+GROUP BY upid;
+
+CREATE PERFETTO TABLE _process_available_info_summary AS
+WITH r AS (
+  SELECT
+    upid,
+    t_summary.upid as summary_upid,
+    t_summary.max_running_dur AS max_running_dur,
+    t_summary.sum_running_dur,
+    t_summary.running_count,
+    t_summary.slice_count AS thread_slice_count,
+    t_summary.perf_sample_count AS perf_sample_count,
+    (
+      SELECT slice_count
+      FROM _process_track_summary
+      WHERE upid = p.upid
+    ) AS process_slice_count,
+    (
+      SELECT allocation_count
+      FROM _heap_profile_allocation_summary
+      WHERE upid = p.upid
+    ) AS allocation_count,
+    (
+      SELECT graph_object_count
+      FROM _heap_profile_graph_summary
+      WHERE upid = p.upid
+    ) AS graph_object_count
+  FROM process p
+  LEFT JOIN _thread_process_grouped_summary t_summary USING (upid)
+)
+SELECT
+  upid,
+  IFNULL(max_running_dur, 0) AS max_running_dur,
+  IFNULL(sum_running_dur, 0) AS sum_running_dur,
+  IFNULL(running_count, 0) AS running_count,
+  IFNULL(thread_slice_count, 0) AS thread_slice_count,
+  IFNULL(perf_sample_count, 0) AS perf_sample_count,
+  IFNULL(process_slice_count, 0) AS process_slice_count,
+  IFNULL(allocation_count, 0) AS allocation_count,
+  IFNULL(graph_object_count, 0) AS graph_object_count
+FROM r
+WHERE
+  NOT(
+    r.summary_upid IS NULL
+    AND process_slice_count IS NULL
+    AND allocation_count IS NULL
+    AND graph_object_count IS NULL
+  )
+  OR upid IN (SELECT upid FROM process_counter_track);
diff --git a/src/trace_processor/perfetto_sql/stdlib/viz/summary/slices.sql b/src/trace_processor/perfetto_sql/stdlib/viz/summary/slices.sql
new file mode 100644
index 0000000..c698859
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/viz/summary/slices.sql
@@ -0,0 +1,24 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+CREATE PERFETTO TABLE _slice_track_summary AS
+SELECT
+  track_id as id,
+  COUNT() AS cnt,
+  MIN(dur) AS min_dur,
+  MAX(dur) AS max_dur,
+  MAX(depth) AS max_depth
+FROM slice
+GROUP BY track_id;
diff --git a/src/trace_processor/perfetto_sql/stdlib/viz/summary/threads.sql b/src/trace_processor/perfetto_sql/stdlib/viz/summary/threads.sql
new file mode 100644
index 0000000..ed74acf
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/viz/summary/threads.sql
@@ -0,0 +1,77 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE viz.summary.slices;
+
+CREATE PERFETTO TABLE _sched_summary AS
+SELECT
+  utid,
+  MAX(dur) AS max_running_dur,
+  SUM(dur) AS sum_running_dur,
+  COUNT() AS running_count
+FROM sched
+WHERE utid != 0 AND dur != -1
+GROUP BY utid;
+
+CREATE PERFETTO TABLE _thread_track_summary AS
+SELECT utid, SUM(cnt) AS slice_count
+FROM thread_track
+JOIN _slice_track_summary USING (id)
+GROUP BY utid;
+
+CREATE PERFETTO TABLE _perf_sample_summary AS
+SELECT utid, count() AS perf_sample_cnt
+FROM perf_sample
+WHERE callsite_id IS NOT NULL
+GROUP BY utid;
+
+CREATE PERFETTO TABLE _thread_available_info_summary AS
+WITH raw AS (
+  SELECT
+    utid,
+    ss.max_running_dur,
+    ss.sum_running_dur,
+    ss.running_count,
+    (
+      SELECT slice_count
+      FROM _thread_track_summary
+      WHERE utid = t.utid
+    ) AS slice_count,
+    (
+      SELECT perf_sample_cnt
+      FROM _perf_sample_summary
+      WHERE utid = t.utid
+    ) AS perf_sample_count
+  FROM thread t
+  LEFT JOIN _sched_summary ss USING (utid)
+)
+SELECT
+  utid,
+  IFNULL(max_running_dur, 0) AS max_running_dur,
+  IFNULL(sum_running_dur, 0) AS sum_running_dur,
+  IFNULL(running_count, 0) AS running_count,
+  IFNULL(slice_count, 0) AS slice_count,
+  IFNULL(perf_sample_count, 0) AS perf_sample_count
+FROM raw r
+WHERE
+  NOT (
+    r.max_running_dur IS NULL
+    AND r.sum_running_dur IS NULL
+    AND r.running_count IS NULL
+    AND r.slice_count IS NULL
+    AND r.perf_sample_count IS NULL
+  )
+  OR utid IN (SELECT utid FROM cpu_profile_stack_sample)
+  OR utid IN (SELECT utid FROM thread_counter_track);
diff --git a/src/trace_processor/perfetto_sql/stdlib/viz/summary/tracks.sql b/src/trace_processor/perfetto_sql/stdlib/viz/summary/tracks.sql
new file mode 100644
index 0000000..7a1d5d0
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/viz/summary/tracks.sql
@@ -0,0 +1,36 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE viz.summary.slices;
+
+CREATE PERFETTO TABLE _process_track_summary_by_upid_and_name AS
+SELECT
+  upid,
+  name,
+  GROUP_CONCAT(id) AS track_ids,
+  COUNT() AS track_count
+FROM process_track
+JOIN _slice_track_summary USING (id)
+GROUP BY upid, name;
+
+CREATE PERFETTO TABLE _uid_track_track_summary_by_uid_and_name AS
+SELECT
+  uid,
+  name,
+  GROUP_CONCAT(id) AS track_ids,
+  COUNT() AS track_count
+FROM uid_track
+JOIN _slice_track_summary USING (id)
+GROUP BY uid, name;
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 5194215..c1f0d6e 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -201,20 +201,14 @@
 // ensure it's only run once.
 async function defineMaxLayoutDepthSqlFunction(engine: Engine): Promise<void> {
   await engine.query(`
-    create or replace perfetto table __max_layout_depth_state as
-    select track_id, max(depth) as max_depth
-    from slice
-    group by track_id
-    order by track_id;
-
     create perfetto function __max_layout_depth(track_count INT, track_ids STRING)
     returns INT AS
     select iif(
       $track_count = 1,
       (
         select max_depth
-        from __max_layout_depth_state
-        where track_id = cast($track_ids AS int)
+        from _slice_track_summary
+        where id = cast($track_ids AS int)
       ),
       (
         select max(layout_depth)
@@ -517,6 +511,7 @@
 
     // Make sure the helper views are available before we start adding tracks.
     await this.initialiseHelperViews();
+    await this.includeSummaryTables();
 
     await defineMaxLayoutDepthSqlFunction(engine);
 
@@ -1097,6 +1092,22 @@
     }
   }
 
+  async includeSummaryTables() {
+    const engine = assertExists<Engine>(this.engine);
+
+    this.updateStatus('Creating slice summaries');
+    await engine.query(`include perfetto module viz.summary.slices;`);
+
+    this.updateStatus('Creating thread summaries');
+    await engine.query(`include perfetto module viz.summary.threads;`);
+
+    this.updateStatus('Creating processes summaries');
+    await engine.query(`include perfetto module viz.summary.processes;`);
+
+    this.updateStatus('Creating track summaries');
+    await engine.query(`include perfetto module viz.summary.tracks;`);
+  }
+
   private updateStatus(msg: string): void {
     globals.dispatch(
       Actions.updateStatus({
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index d8f42fb..b6dd388 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -167,64 +167,35 @@
 
   async addGlobalAsyncTracks(engine: EngineProxy): Promise<void> {
     const rawGlobalAsyncTracks = await engine.query(`
-      with tracks_with_slices as materialized (
-        select distinct track_id
-        from slice
-      ),
-      global_tracks as (
-        select
-          track.parent_id as parent_id,
-          track.id as track_id,
-          track.name as name
-        from track
-        join tracks_with_slices on tracks_with_slices.track_id = track.id
-        where
-          track.type = "track"
-          or track.type = "gpu_track"
-          or track.type = "cpu_track"
-      ),
-      global_tracks_grouped as (
-        select
-          parent_id,
-          name,
-          group_concat(track_id) as trackIds,
-          count(track_id) as trackCount
-        from global_tracks track
-        group by parent_id, name
+      with global_tracks_grouped as (
+        select distinct t.parent_id, t.name
+        from track t
+        join _slice_track_summary using (id)
+        where t.type in ('track', 'gpu_track', 'cpu_track')
       )
       select
-        t.parent_id as parentId,
-        p.name as parentName,
         t.name as name,
-        t.trackIds as trackIds,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+        t.parent_id as parentId,
+        p.name as parentName
       from global_tracks_grouped AS t
       left join track p on (t.parent_id = p.id)
-      order by p.name, t.name;
+      order by p.name, t.name
     `);
     const it = rawGlobalAsyncTracks.iter({
       name: STR_NULL,
-      parentName: STR_NULL,
       parentId: NUM_NULL,
-      maxDepth: NUM_NULL,
+      parentName: STR_NULL,
     });
 
     const parentIdToGroupId = new Map<number, string>();
-
     for (; it.valid(); it.next()) {
       const kind = ASYNC_SLICE_TRACK_KIND;
       const rawName = it.name === null ? undefined : it.name;
       const rawParentName = it.parentName === null ? undefined : it.parentName;
       const name = getTrackName({name: rawName, kind});
       const parentTrackId = it.parentId;
-      const maxDepth = it.maxDepth;
       let trackGroup = SCROLLING_TRACK_GROUP;
 
-      // If there are no slices in this track, skip it.
-      if (maxDepth === null) {
-        continue;
-      }
-
       if (parentTrackId !== null) {
         const groupId = parentIdToGroupId.get(parentTrackId);
         if (groupId === undefined) {
@@ -261,11 +232,11 @@
       // Only add a gpu freq track if we have
       // gpu freq data.
       const freqExistsResult = await engine.query(`
-      select *
-      from gpu_counter_track
-      where name = 'gpufreq' and gpu_id = ${gpu}
-      limit 1;
-    `);
+        select *
+        from gpu_counter_track
+        where name = 'gpufreq' and gpu_id = ${gpu}
+        limit 1;
+      `);
       if (freqExistsResult.numRows() > 0) {
         this.tracksToAdd.push({
           uri: `perfetto.Counter#gpu_freq${gpu}`,
@@ -638,14 +609,14 @@
 
   async addThreadStateTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with ts_distinct as materialized (select distinct utid from thread_state)
       select
         utid,
         upid,
         tid,
         thread.name as threadName
       from thread
-      where utid != 0 and utid in ts_distinct`);
+      join _sched_summary using (utid)
+    `);
 
     const it = result.iter({
       utid: NUM,
@@ -726,16 +697,16 @@
 
   async addThreadCounterTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-    select
-      thread_counter_track.name as trackName,
-      utid,
-      upid,
-      tid,
-      thread.name as threadName,
-      thread_counter_track.id as trackId
-    from thread_counter_track
-    join thread using(utid)
-    where thread_counter_track.name != 'thread_time'
+      select
+        thread_counter_track.name as trackName,
+        utid,
+        upid,
+        tid,
+        thread.name as threadName,
+        thread_counter_track.id as trackId
+      from thread_counter_track
+      join thread using(utid)
+      where thread_counter_track.name != 'thread_time'
   `);
 
     const it = result.iter({
@@ -776,27 +747,15 @@
 
   async addProcessAsyncSliceTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where
-            process_track.name is null or
-            process_track.name not like "% Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
+        upid,
+        t.name as trackName,
+        t.track_ids as trackIds,
+        process.name as processName,
+        process.pid as pid
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name is null or t.name not glob "* Timeline"
     `);
 
     const it = result.iter({
@@ -805,7 +764,6 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
-      maxDepth: NUM_NULL,
     });
     for (; it.valid(); it.next()) {
       const upid = it.upid;
@@ -813,14 +771,8 @@
       const rawTrackIds = it.trackIds;
       const processName = it.processName;
       const pid = it.pid;
-      const maxDepth = it.maxDepth;
 
-      if (maxDepth === null) {
-        // If there are no slices in this track, skip it.
-        continue;
-      }
-
-      const uuid = this.getUuid(0, upid);
+      const uuid = this.getUuid(null, upid);
       const name = getTrackName({
         name: trackName,
         upid,
@@ -840,37 +792,18 @@
 
   async addUserAsyncSliceTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with tracks_with_slices as materialized (
-        select distinct track_id
-        from slice
-      ),
-      global_tracks as (
-        select
-          uid_track.name,
-          uid_track.uid,
-          group_concat(uid_track.id) as trackIds,
-          count(uid_track.id) as trackCount
-        from uid_track
-        join tracks_with_slices
-        where tracks_with_slices.track_id == uid_track.id
-        group by uid_track.uid
-      )
       select
         t.name as name,
         t.uid as uid,
-        package_list.package_name as package_name,
-        t.trackIds as trackIds,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from global_tracks t
-      join package_list
-      where t.uid = package_list.uid
-      group by t.uid
-      `);
+        package_list.package_name as packageName
+      from _uid_track_track_summary_by_uid_and_name t
+      join package_list using (uid)
+    `);
 
     const it = result.iter({
       name: STR_NULL,
       uid: NUM_NULL,
-      package_name: STR_NULL,
+      packageName: STR_NULL,
     });
 
     // Map From [name] -> [uuid, key]
@@ -882,8 +815,7 @@
       }
       const rawName = it.name;
       const uid = it.uid === null ? undefined : it.uid;
-      const userName =
-        it.package_name === null ? `UID: ${uid}` : it.package_name;
+      const userName = it.packageName === null ? `UID: ${uid}` : it.packageName;
 
       const groupUuid = `uid-track-group${rawName}`;
       if (groupMap.get(rawName) === undefined) {
@@ -910,48 +842,29 @@
 
   async addActualFramesTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where process_track.name = "Actual Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
-  `);
+        upid,
+        t.name as trackName,
+        process.name as processName,
+        process.pid as pid
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name = "Actual Timeline"
+    `);
 
     const it = result.iter({
       upid: NUM,
       trackName: STR_NULL,
       processName: STR_NULL,
       pid: NUM_NULL,
-      maxDepth: NUM_NULL,
     });
     for (; it.valid(); it.next()) {
       const upid = it.upid;
       const trackName = it.trackName;
       const processName = it.processName;
       const pid = it.pid;
-      const maxDepth = it.maxDepth;
 
-      if (maxDepth === null) {
-        // If there are no slices in this track, skip it.
-        continue;
-      }
-
-      const uuid = this.getUuid(0, upid);
-
+      const uuid = this.getUuid(null, upid);
       const kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
       const name = getTrackName({
         name: trackName,
@@ -972,33 +885,21 @@
 
   async addExpectedFramesTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where process_track.name = "Expected Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
-  `);
+        upid,
+        t.name as trackName,
+        process.name as processName,
+        process.pid as pid
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name = "Expected Timeline"
+    `);
 
     const it = result.iter({
       upid: NUM,
       trackName: STR_NULL,
       processName: STR_NULL,
       pid: NUM_NULL,
-      maxDepth: NUM_NULL,
     });
 
     for (; it.valid(); it.next()) {
@@ -1006,15 +907,8 @@
       const trackName = it.trackName;
       const processName = it.processName;
       const pid = it.pid;
-      const maxDepth = it.maxDepth;
 
-      if (maxDepth === null) {
-        // If there are no slices in this track, skip it.
-        continue;
-      }
-
-      const uuid = this.getUuid(0, upid);
-
+      const uuid = this.getUuid(null, upid);
       const kind = EXPECTED_FRAMES_SLICE_TRACK_KIND;
       const name = getTrackName({
         name: trackName,
@@ -1035,7 +929,6 @@
 
   async addThreadSliceTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      with slice_track as materialized (select distinct track_id from slice)
       select
         thread_track.utid as utid,
         thread_track.id as trackId,
@@ -1046,8 +939,8 @@
         thread.name as threadName,
         thread.upid as upid
       from thread_track
+      join _slice_track_summary using (id)
       join thread using(utid)
-      join slice_track on thread_track.id = slice_track.track_id
   `);
 
     const it = result.iter({
@@ -1091,14 +984,14 @@
 
   async addProcessCounterTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-    select
-      process_counter_track.id as trackId,
-      process_counter_track.name as trackName,
-      upid,
-      process.pid,
-      process.name as processName
-    from process_counter_track
-    join process using(upid);
+      select
+        process_counter_track.id as trackId,
+        process_counter_track.name as trackName,
+        upid,
+        process.pid,
+        process.name as processName
+      from process_counter_track
+      join process using(upid);
   `);
     const it = result.iter({
       trackId: NUM,
@@ -1113,7 +1006,7 @@
       const trackId = it.trackId;
       const trackName = it.trackName;
       const processName = it.processName;
-      const uuid = this.getUuid(0, upid);
+      const uuid = this.getUuid(null, upid);
       const name = getTrackName({
         name: trackName,
         upid,
@@ -1135,13 +1028,13 @@
 
   async addProcessHeapProfileTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-    select distinct(upid) from heap_profile_allocation
-    union
-    select distinct(upid) from heap_graph_object
+      select upid
+      from _process_available_info_summary
+      where allocation_count > 0 or graph_object_count > 0
   `);
     for (const it = result.iter({upid: NUM}); it.valid(); it.next()) {
       const upid = it.upid;
-      const uuid = this.getUuid(0, upid);
+      const uuid = this.getUuid(null, upid);
       this.tracksToAdd.push({
         uri: `perfetto.HeapProfile#${upid}`,
         trackSortKey: PrimaryTrackSortKey.HEAP_PROFILE_TRACK,
@@ -1153,14 +1046,15 @@
 
   async addProcessPerfSamplesTracks(engine: EngineProxy): Promise<void> {
     const result = await engine.query(`
-      select distinct upid, pid
-      from perf_sample join thread using (utid) join process using (upid)
-      where callsite_id is not null
+      select upid, pid
+      from _process_available_info_summary
+      join process using (upid)
+      where perf_sample_count > 0
   `);
     for (const it = result.iter({upid: NUM, pid: NUM}); it.valid(); it.next()) {
       const upid = it.upid;
       const pid = it.pid;
-      const uuid = this.getUuid(0, upid);
+      const uuid = this.getUuid(null, upid);
       this.tracksToAdd.push({
         uri: `perfetto.PerfSamplesProfile#${upid}`,
         trackSortKey: PrimaryTrackSortKey.PERF_SAMPLES_PROFILE_TRACK,
@@ -1170,22 +1064,22 @@
     }
   }
 
-  getUuidUnchecked(utid: number, upid: number | null) {
+  getUuidUnchecked(utid: number | null, upid: number | null) {
     return upid === null
-      ? this.utidToUuid.get(utid)
+      ? this.utidToUuid.get(utid!)
       : this.upidToUuid.get(upid);
   }
 
-  getUuid(utid: number, upid: number | null) {
+  getUuid(utid: number | null, upid: number | null) {
     return assertExists(this.getUuidUnchecked(utid, upid));
   }
 
-  getOrCreateUuid(utid: number, upid: number | null) {
+  getOrCreateUuid(utid: number | null, upid: number | null) {
     let uuid = this.getUuidUnchecked(utid, upid);
     if (uuid === undefined) {
       uuid = uuidv4();
       if (upid === null) {
-        this.utidToUuid.set(utid, uuid);
+        this.utidToUuid.set(utid!, uuid);
       } else {
         this.upidToUuid.set(upid, uuid);
       }
@@ -1274,181 +1168,155 @@
     //  thread name
     //  utid
     const result = await engine.query(`
-    with candidateThreadsAndProcesses as materialized (
-      select upid, 0 as utid from process_track
-      union
-      select upid, 0 as utid from process_counter_track
-      union
-      select upid, utid from thread_counter_track join thread using(utid)
-      union
-      select upid, utid from thread_track join thread using(utid)
-      union
-      select upid, utid from (
-        select distinct utid from sched
-      ) join thread using(utid) group by utid
-      union
-      select upid, 0 as utid from (
-        select distinct utid from perf_sample where callsite_id is not null
-      ) join thread using (utid)
-      union
-      select upid, utid from (
-        select distinct utid from cpu_profile_stack_sample
-      ) join thread using(utid)
-      union
-      select upid as upid, 0 as utid from heap_profile_allocation
-      union
-      select upid as upid, 0 as utid from heap_graph_object
-    ),
-    schedSum as materialized (
-      select upid, sum(thread_total_dur) as total_dur
-      from (
-        select utid, sum(dur) as thread_total_dur
-        from sched where dur != -1 and utid != 0
-        group by utid
+      with processGroups as (
+        select
+          upid,
+          process.pid as pid,
+          process.name as processName,
+          sum_running_dur as sumRunningDur,
+          thread_slice_count + process_slice_count as sliceCount,
+          perf_sample_count as perfSampleCount,
+          allocation_count as heapProfileAllocationCount,
+          graph_object_count as heapGraphObjectCount,
+          (
+            select group_concat(string_value)
+            from args
+            where
+              process.arg_set_id is not null and
+              arg_set_id = process.arg_set_id and
+              flat_key = 'chrome.process_label'
+          ) chromeProcessLabels,
+          case process.name
+            when 'Browser' then 3
+            when 'Gpu' then 2
+            when 'Renderer' then 1
+            else 0
+          end as chromeProcessRank
+        from _process_available_info_summary
+        join process using(upid)
+      ),
+      threadGroups as (
+        select
+          utid,
+          tid,
+          thread.name as threadName,
+          sum_running_dur as sumRunningDur,
+          slice_count as sliceCount,
+          perf_sample_count as perfSampleCount
+        from _thread_available_info_summary
+        join thread using (utid)
+        where upid is null
       )
-      join thread using (utid)
-      group by upid
-    ),
-    sliceSum as materialized (
-      select
-        process.upid as upid,
-        sum(cnt) as sliceCount
-      from (select track_id, count(*) as cnt from slice group by track_id)
-        left join thread_track on track_id = thread_track.id
-        left join thread on thread_track.utid = thread.utid
-        left join process_track on track_id = process_track.id
-        join process on process.upid = thread.upid
-          or process_track.upid = process.upid
-      where process.upid is not null
-      group by process.upid
-    )
-    select
-      the_tracks.upid,
-      the_tracks.utid,
-      total_dur as hasSched,
-      hasHeapProfiles,
-      process.pid as pid,
-      thread.tid as tid,
-      process.name as processName,
-      thread.name as threadName,
-      package_list.debuggable as isDebuggable,
-      ifnull((
-        select group_concat(string_value)
-        from args
-        where
-          process.arg_set_id is not null and
-          arg_set_id = process.arg_set_id and
-          flat_key = 'chrome.process_label'
-      ), '') AS chromeProcessLabels,
-      (case process.name
-         when 'Browser' then 3
-         when 'Gpu' then 2
-         when 'Renderer' then 1
-         else 0
-      end) as chromeProcessRank
-    from candidateThreadsAndProcesses the_tracks
-    left join schedSum using(upid)
-    left join (
-      select
-        distinct(upid) as upid,
-        true as hasHeapProfiles
-      from heap_profile_allocation
-      union
-      select
-        distinct(upid) as upid,
-        true as hasHeapProfiles
-      from heap_graph_object
-    ) using (upid)
-    left join (
-      select
-        thread.upid as upid,
-        sum(cnt) as perfSampleCount
+      select *
       from (
-          select utid, count(*) as cnt
-          from perf_sample where callsite_id is not null
-          group by utid
-      ) join thread using (utid)
-      group by thread.upid
-    ) using (upid)
-    left join sliceSum using (upid)
-    left join thread using(utid)
-    left join process using(upid)
-    left join package_list using(uid)
-    order by
-      chromeProcessRank desc,
-      hasHeapProfiles desc,
-      perfSampleCount desc,
-      total_dur desc,
-      sliceCount desc,
-      processName asc nulls last,
-      the_tracks.upid asc nulls last,
-      threadName asc nulls last,
-      the_tracks.utid asc nulls last;
+        select
+          upid,
+          null as utid,
+          pid,
+          null as tid,
+          processName,
+          null as threadName,
+          sumRunningDur > 0 as hasSched,
+          heapProfileAllocationCount > 0
+            or heapGraphObjectCount > 0 as hasHeapInfo,
+          ifnull(chromeProcessLabels, '') as chromeProcessLabels
+        from processGroups
+        order by
+          chromeProcessRank desc,
+          heapProfileAllocationCount desc,
+          heapGraphObjectCount desc,
+          perfSampleCount desc,
+          sumRunningDur desc,
+          sliceCount desc,
+          processName asc,
+          upid asc
+      )
+      union all
+      select *
+      from (
+        select
+          null,
+          utid,
+          null as pid,
+          tid,
+          null as processName,
+          threadName,
+          sumRunningDur > 0 as hasSched,
+          0 as hasHeapInfo,
+          '' as chromeProcessLabels
+        from threadGroups
+        order by
+          perfSampleCount desc,
+          sumRunningDur desc,
+          sliceCount desc,
+          threadName asc,
+          utid asc
+      )
   `);
 
     const it = result.iter({
-      utid: NUM,
       upid: NUM_NULL,
-      tid: NUM_NULL,
+      utid: NUM_NULL,
       pid: NUM_NULL,
-      threadName: STR_NULL,
+      tid: NUM_NULL,
       processName: STR_NULL,
+      threadName: STR_NULL,
       hasSched: NUM_NULL,
-      hasHeapProfiles: NUM_NULL,
+      hasHeapInfo: NUM_NULL,
       chromeProcessLabels: STR,
     });
     for (; it.valid(); it.next()) {
       const utid = it.utid;
-      const tid = it.tid;
       const upid = it.upid;
       const pid = it.pid;
+      const tid = it.tid;
       const threadName = it.threadName;
       const processName = it.processName;
       // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
       const hasSched = !!it.hasSched;
       // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
-      const hasHeapProfiles = !!it.hasHeapProfiles;
+      const hasHeapInfo = !!it.hasHeapInfo;
 
-      // Group by upid if present else by utid.
-      let pUuid =
-        upid === null ? this.utidToUuid.get(utid) : this.upidToUuid.get(upid);
-      // These should only happen once for each track group.
-      if (pUuid === undefined) {
-        pUuid = this.getOrCreateUuid(utid, upid);
-        const summaryTrackKey = uuidv4();
-        const type = hasSched ? 'schedule' : 'summary';
-        const uri = `perfetto.ProcessScheduling#${upid}.${utid}.${type}`;
+      const summaryTrackKey = uuidv4();
+      const type = hasSched ? 'schedule' : 'summary';
+      const uri = `perfetto.ProcessScheduling#${upid}.${utid}.${type}`;
 
-        this.tracksToAdd.push({
-          uri,
-          key: summaryTrackKey,
-          trackSortKey: hasSched
-            ? PrimaryTrackSortKey.PROCESS_SCHEDULING_TRACK
-            : PrimaryTrackSortKey.PROCESS_SUMMARY_TRACK,
-          name: `${upid === null ? tid : pid} summary`,
-          labels: it.chromeProcessLabels.split(','),
-        });
-
-        const name = getTrackName({
-          utid,
-          processName,
-          pid,
-          threadName,
-          tid,
-          upid,
-        });
-        const addTrackGroup = Actions.addTrackGroup({
-          summaryTrackKey,
-          name,
-          id: pUuid,
-          // Perf profiling tracks remain collapsed, otherwise we would have too
-          // many expanded process tracks for some perf traces, leading to
-          // jankyness.
-          collapsed: !hasHeapProfiles,
-        });
-
-        this.addTrackGroupActions.push(addTrackGroup);
+      // If previous groupings (e.g. kernel threads) picked up there tracks,
+      // don't try to regroup them.
+      const pUuid =
+        upid === null ? this.utidToUuid.get(utid!) : this.upidToUuid.get(upid);
+      if (pUuid !== undefined) {
+        continue;
       }
+
+      this.tracksToAdd.push({
+        uri,
+        key: summaryTrackKey,
+        trackSortKey: hasSched
+          ? PrimaryTrackSortKey.PROCESS_SCHEDULING_TRACK
+          : PrimaryTrackSortKey.PROCESS_SUMMARY_TRACK,
+        name: `${upid === null ? tid : pid} summary`,
+        labels: it.chromeProcessLabels.split(','),
+      });
+
+      const name = getTrackName({
+        utid,
+        processName,
+        pid,
+        threadName,
+        tid,
+        upid,
+      });
+      const addTrackGroup = Actions.addTrackGroup({
+        summaryTrackKey,
+        name,
+        id: this.getOrCreateUuid(utid, upid),
+        // Perf profiling tracks remain collapsed, otherwise we would have too
+        // many expanded process tracks for some perf traces, leading to
+        // jankyness.
+        collapsed: !hasHeapInfo,
+      });
+      this.addTrackGroupActions.push(addTrackGroup);
     }
   }
 
diff --git a/ui/src/plugins/org.kernel.LinuxKernelDevices/index.ts b/ui/src/plugins/org.kernel.LinuxKernelDevices/index.ts
index d7a37d8..b9127f0 100644
--- a/ui/src/plugins/org.kernel.LinuxKernelDevices/index.ts
+++ b/ui/src/plugins/org.kernel.LinuxKernelDevices/index.ts
@@ -27,23 +27,11 @@
 class LinuxKernelDevices implements Plugin {
   async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
     const result = await ctx.engine.query(`
-      with
-        slices_tracks as materialized (
-          select distinct track_id
-          from slice
-        ),
-        tracks as (
-          select
-            linux_device_track.id as track_id,
-            linux_device_track.name
-          from linux_device_track
-          join slices_tracks on
-          slices_tracks.track_id = linux_device_track.id
-        )
       select
-        t.name,
-        t.track_id as trackId
-      from tracks as t
+        t.id as trackId,
+        t.name
+      from linux_device_track t
+      join _slice_track_summary using (id)
       order by t.name;
     `);
 
diff --git a/ui/src/public/utils.ts b/ui/src/public/utils.ts
index 60c4487..f5d3d7a 100644
--- a/ui/src/public/utils.ts
+++ b/ui/src/public/utils.ts
@@ -22,7 +22,7 @@
 export function getTrackName(
   args: Partial<{
     name: string | null;
-    utid: number;
+    utid: number | null;
     processName: string | null;
     pid: number | null;
     threadName: string | null;
diff --git a/ui/src/tracks/async_slices/index.ts b/ui/src/tracks/async_slices/index.ts
index 2e8b3d5..18883d9 100644
--- a/ui/src/tracks/async_slices/index.ts
+++ b/ui/src/tracks/async_slices/index.ts
@@ -30,77 +30,41 @@
   async addGlobalAsyncTracks(ctx: PluginContextTrace): Promise<void> {
     const {engine} = ctx;
     const rawGlobalAsyncTracks = await engine.query(`
-      with tracks_with_slices as materialized (
-        select distinct track_id
-        from slice
-      ),
-      global_tracks as (
-        select
-          track.parent_id as parent_id,
-          track.id as track_id,
-          track.name as name
-        from track
-        join tracks_with_slices on tracks_with_slices.track_id = track.id
-        where
-          track.type = "track"
-          or track.type = "gpu_track"
-          or track.type = "cpu_track"
-      ),
-      global_tracks_grouped as (
+      with global_tracks_grouped as (
         select
           parent_id,
           name,
-          group_concat(track_id) as trackIds,
-          count(track_id) as trackCount
-        from global_tracks track
+          group_concat(id) as trackIds,
+          count() as trackCount
+        from track t
+        join _slice_track_summary using (id)
+        where t.type in ('track', 'gpu_track', 'cpu_track')
         group by parent_id, name
       )
       select
-        t.parent_id as parentId,
-        p.name as parentName,
         t.name as name,
+        t.parent_id as parentId,
         t.trackIds as trackIds,
         __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from global_tracks_grouped AS t
-      left join track p on (t.parent_id = p.id)
-      order by p.name, t.name;
+      from global_tracks_grouped t
     `);
     const it = rawGlobalAsyncTracks.iter({
       name: STR_NULL,
-      parentName: STR_NULL,
       parentId: NUM_NULL,
       trackIds: STR,
-      maxDepth: NUM_NULL,
+      maxDepth: NUM,
     });
 
-    // let scrollJankRendered = false;
-
     for (; it.valid(); it.next()) {
       const rawName = it.name === null ? undefined : it.name;
-      // const rawParentName = it.parentName === null ? undefined :
-      // it.parentName;
       const displayName = getTrackName({
         name: rawName,
         kind: ASYNC_SLICE_TRACK_KIND,
       });
       const rawTrackIds = it.trackIds;
       const trackIds = rawTrackIds.split(',').map((v) => Number(v));
-      // const parentTrackId = it.parentId;
       const maxDepth = it.maxDepth;
 
-      // If there are no slices in this track, skip it.
-      if (maxDepth === null) {
-        continue;
-      }
-
-      // if (ENABLE_SCROLL_JANK_PLUGIN_V2.get() && !scrollJankRendered &&
-      //     name.includes(INPUT_LATENCY_TRACK)) {
-      //   // This ensures that the scroll jank tracks render above the tracks
-      //   // for GestureScrollUpdate.
-      //   await this.addScrollJankTracks(this.engine);
-      //   scrollJankRendered = true;
-      // }
-
       ctx.registerTrack({
         uri: `perfetto.AsyncSlices#${rawName}.${it.parentId}`,
         displayName,
@@ -115,27 +79,16 @@
 
   async addProcessAsyncSliceTracks(ctx: PluginContextTrace): Promise<void> {
     const result = await ctx.engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where
-            process_track.name is null or
-            process_track.name not like "% Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
+        upid,
+        t.name as trackName,
+        t.track_ids as trackIds,
+        process.name as processName,
+        process.pid as pid,
+        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name is null or t.name not glob "* Timeline"
     `);
 
     const it = result.iter({
@@ -144,7 +97,7 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
-      maxDepth: NUM_NULL,
+      maxDepth: NUM,
     });
     for (; it.valid(); it.next()) {
       const upid = it.upid;
@@ -155,11 +108,6 @@
       const pid = it.pid;
       const maxDepth = it.maxDepth;
 
-      if (maxDepth === null) {
-        // If there are no slices in this track, skip it.
-        continue;
-      }
-
       const kind = ASYNC_SLICE_TRACK_KIND;
       const displayName = getTrackName({
         name: trackName,
@@ -188,37 +136,20 @@
   async addUserAsyncSliceTracks(ctx: PluginContextTrace): Promise<void> {
     const {engine} = ctx;
     const result = await engine.query(`
-      with tracks_with_slices as materialized (
-        select distinct track_id
-        from slice
-      ),
-      global_tracks as (
-        select
-          uid_track.name,
-          uid_track.uid,
-          group_concat(uid_track.id) as trackIds,
-          count(uid_track.id) as trackCount
-        from uid_track
-        join tracks_with_slices
-        where tracks_with_slices.track_id == uid_track.id
-        group by uid_track.uid
-      )
       select
         t.name as name,
         t.uid as uid,
-        package_list.package_name as package_name,
-        t.trackIds as trackIds,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from global_tracks t
-      join package_list
-      where t.uid = package_list.uid
-      group by t.uid
-      `);
+        package_list.package_name as packageName,
+        t.track_ids as trackIds,
+        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
+      from _uid_track_track_summary_by_uid_and_name t
+      join package_list using (uid)
+    `);
 
     const it = result.iter({
       name: STR_NULL,
       uid: NUM_NULL,
-      package_name: STR_NULL,
+      packageName: STR_NULL,
       trackIds: STR,
       maxDepth: NUM_NULL,
     });
@@ -226,7 +157,7 @@
     for (; it.valid(); it.next()) {
       const kind = ASYNC_SLICE_TRACK_KIND;
       const rawName = it.name === null ? undefined : it.name;
-      const userName = it.package_name === null ? undefined : it.package_name;
+      const userName = it.packageName === null ? undefined : it.packageName;
       const uid = it.uid === null ? undefined : it.uid;
       const rawTrackIds = it.trackIds;
       const trackIds = rawTrackIds.split(',').map((v) => Number(v));
diff --git a/ui/src/tracks/frames/index.ts b/ui/src/tracks/frames/index.ts
index 47e88ff..2cc877f 100644
--- a/ui/src/tracks/frames/index.ts
+++ b/ui/src/tracks/frames/index.ts
@@ -31,26 +31,17 @@
   async addExpectedFrames(ctx: PluginContextTrace): Promise<void> {
     const {engine} = ctx;
     const result = await engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where process_track.name = "Expected Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
-  `);
+        upid,
+        t.name as trackName,
+        t.track_ids as trackIds,
+        process.name as processName,
+        process.pid as pid,
+        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name = "Expected Timeline"
+    `);
 
     const it = result.iter({
       upid: NUM,
@@ -58,7 +49,7 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
-      maxDepth: NUM_NULL,
+      maxDepth: NUM,
     });
 
     for (; it.valid(); it.next()) {
@@ -70,11 +61,6 @@
       const pid = it.pid;
       const maxDepth = it.maxDepth;
 
-      if (maxDepth === null) {
-        // If there are no slices in this track, skip it.
-        continue;
-      }
-
       const displayName = getTrackName({
         name: trackName,
         upid,
@@ -103,26 +89,17 @@
   async addActualFrames(ctx: PluginContextTrace): Promise<void> {
     const {engine} = ctx;
     const result = await engine.query(`
-      with process_async_tracks as materialized (
-        select
-          process_track.upid as upid,
-          process_track.name as trackName,
-          process.name as processName,
-          process.pid as pid,
-          group_concat(process_track.id) as trackIds,
-          count(1) as trackCount
-        from process_track
-        join process using(upid)
-        where process_track.name = "Actual Timeline"
-        group by
-          process_track.upid,
-          process_track.name
-      )
       select
-        t.*,
-        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
-      from process_async_tracks t;
-  `);
+        upid,
+        t.name as trackName,
+        t.track_ids as trackIds,
+        process.name as processName,
+        process.pid as pid,
+        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
+      from _process_track_summary_by_upid_and_name t
+      join process using(upid)
+      where t.name = "Actual Timeline"
+    `);
 
     const it = result.iter({
       upid: NUM,
diff --git a/ui/src/tracks/process_summary/index.ts b/ui/src/tracks/process_summary/index.ts
index 47a93b0..3f53335 100644
--- a/ui/src/tracks/process_summary/index.ts
+++ b/ui/src/tracks/process_summary/index.ts
@@ -12,14 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {v4 as uuidv4} from 'uuid';
-
 import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
 import {
   LONG_NULL,
   NUM,
   NUM_NULL,
-  STR,
   STR_NULL,
 } from '../../trace_processor/query_result';
 import {assertExists} from '../../base/logging';
@@ -37,166 +34,61 @@
 
 // This plugin now manages both process "scheduling" and "summary" tracks.
 class ProcessSummaryPlugin implements Plugin {
-  private upidToUuid = new Map<number, string>();
-  private utidToUuid = new Map<number, string>();
-
   async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
     await this.addProcessTrackGroups(ctx);
     await this.addKernelThreadSummary(ctx);
   }
 
   private async addProcessTrackGroups(ctx: PluginContextTrace): Promise<void> {
-    this.upidToUuid.clear();
-    this.utidToUuid.clear();
-
-    // We want to create groups of tracks in a specific order.
-    // The tracks should be grouped:
-    //    by upid
-    //    or (if upid is null) by utid
-    // the groups should be sorted by:
-    //  Chrome-based process rank based on process names (e.g. Browser)
-    //  has a heap profile or not
-    //  total cpu time *for the whole parent process*
-    //  process name
-    //  upid
-    //  thread name
-    //  utid
     const result = await ctx.engine.query(`
-    with candidateThreadsAndProcesses as materialized (
-      select upid, 0 as utid from process_track
-      union
-      select upid, 0 as utid from process_counter_track
-      union
-      select upid, utid from thread_counter_track join thread using(utid)
-      union
-      select upid, utid from thread_track join thread using(utid)
-      union
-      select upid, utid from (
-        select distinct utid from sched
-      ) join thread using(utid) group by utid
-      union
-      select upid, 0 as utid from (
-        select distinct utid from perf_sample where callsite_id is not null
-      ) join thread using (utid)
-      union
-      select upid, utid from (
-        select distinct utid from cpu_profile_stack_sample
-      ) join thread using(utid)
-      union
-      select upid as upid, 0 as utid from heap_profile_allocation
-      union
-      select upid as upid, 0 as utid from heap_graph_object
-    ),
-    schedSummary as materialized (
-      select
-        upid,
-        sum(thread_total_dur) as total_dur,
-        max(thread_max_dur) as total_max_dur,
-        sum(thread_event_count) as total_event_count
+      select *
       from (
         select
-          utid,
-          sum(dur) as thread_total_dur,
-          max(dur) as thread_max_dur,
-          count() as thread_event_count
-        from sched where dur != -1 and utid != 0
-        group by utid
+          _process_available_info_summary.upid,
+          null as utid,
+          pid,
+          null as tid,
+          process.name as processName,
+          null as threadName,
+          sum_running_dur > 0 as hasSched,
+          max_running_dur as maxRunningDur,
+          running_count as runningCount,
+          android_process_metadata.debuggable as isDebuggable
+        from _process_available_info_summary
+        join process using(upid)
+        left join android_process_metadata using(upid)
       )
-      join thread using (utid)
-      group by upid
-    ),
-    sliceSum as materialized (
-      select
-        process.upid as upid,
-        sum(cnt) as sliceCount
-      from (select track_id, count(*) as cnt from slice group by track_id)
-        left join thread_track on track_id = thread_track.id
-        left join thread on thread_track.utid = thread.utid
-        left join process_track on track_id = process_track.id
-        join process on process.upid = thread.upid
-          or process_track.upid = process.upid
-      where process.upid is not null
-      group by process.upid
-    )
-    select
-      the_tracks.upid,
-      the_tracks.utid,
-      total_dur as hasSched,
-      total_max_dur as schedMaxDur,
-      total_event_count as schedEventCount,
-      hasHeapProfiles,
-      process.pid as pid,
-      thread.tid as tid,
-      process.name as processName,
-      thread.name as threadName,
-      package_list.debuggable as isDebuggable,
-      ifnull((
-        select group_concat(string_value)
-        from args
-        where
-          process.arg_set_id is not null and
-          arg_set_id = process.arg_set_id and
-          flat_key = 'chrome.process_label'
-      ), '') AS chromeProcessLabels,
-      (case process.name
-         when 'Browser' then 3
-         when 'Gpu' then 2
-         when 'Renderer' then 1
-         else 0
-      end) as chromeProcessRank
-    from candidateThreadsAndProcesses the_tracks
-    left join schedSummary using(upid)
-    left join (
-      select
-        distinct(upid) as upid,
-        true as hasHeapProfiles
-      from heap_profile_allocation
-      union
-      select
-        distinct(upid) as upid,
-        true as hasHeapProfiles
-      from heap_graph_object
-    ) using (upid)
-    left join (
-      select
-        thread.upid as upid,
-        sum(cnt) as perfSampleCount
+      union all
+      select *
       from (
-          select utid, count(*) as cnt
-          from perf_sample where callsite_id is not null
-          group by utid
-      ) join thread using (utid)
-      group by thread.upid
-    ) using (upid)
-    left join sliceSum using (upid)
-    left join thread using(utid)
-    left join process using(upid)
-    left join package_list using(uid)
-    order by
-      chromeProcessRank desc,
-      hasHeapProfiles desc,
-      perfSampleCount desc,
-      total_dur desc,
-      sliceCount desc,
-      processName asc nulls last,
-      the_tracks.upid asc nulls last,
-      threadName asc nulls last,
-      the_tracks.utid asc nulls last;
+        select
+          null,
+          utid,
+          null as pid,
+          tid,
+          null as processName,
+          thread.name threadName,
+          sum_running_dur > 0 as hasSched,
+          max_running_dur as maxRunningDur,
+          running_count as runningCount,
+          0 as isDebuggable
+        from _thread_available_info_summary
+        join thread using (utid)
+        where upid is null
+      )
   `);
 
     const it = result.iter({
-      utid: NUM,
       upid: NUM_NULL,
-      tid: NUM_NULL,
+      utid: NUM_NULL,
       pid: NUM_NULL,
-      threadName: STR_NULL,
+      tid: NUM_NULL,
       processName: STR_NULL,
+      threadName: STR_NULL,
       hasSched: NUM_NULL,
-      schedMaxDur: LONG_NULL,
-      schedEventCount: NUM_NULL,
-      hasHeapProfiles: NUM_NULL,
+      maxRunningDur: LONG_NULL,
+      runningCount: NUM_NULL,
       isDebuggable: NUM_NULL,
-      chromeProcessLabels: STR,
     });
     for (; it.valid(); it.next()) {
       const utid = it.utid;
@@ -204,59 +96,52 @@
       const upid = it.upid;
       const pid = it.pid;
       const hasSched = Boolean(it.hasSched);
-      const schedMaxDur = it.schedMaxDur;
-      const schedEventCount = it.schedEventCount;
+      const maxRunningDur = it.maxRunningDur;
+      const runningCount = it.runningCount;
       const isDebuggable = Boolean(it.isDebuggable);
 
-      // Group by upid if present else by utid.
-      let pUuid =
-        upid === null ? this.utidToUuid.get(utid) : this.upidToUuid.get(upid);
-      // These should only happen once for each track group.
-      if (pUuid === undefined) {
-        pUuid = this.getOrCreateUuid(utid, upid);
-        const pidForColor = pid ?? tid ?? upid ?? utid ?? 0;
-        const type = hasSched ? 'schedule' : 'summary';
-        const uri = `perfetto.ProcessScheduling#${upid}.${utid}.${type}`;
+      const pidForColor = pid ?? tid ?? upid ?? utid ?? 0;
+      const type = hasSched ? 'schedule' : 'summary';
+      const uri = `perfetto.ProcessScheduling#${upid}.${utid}.${type}`;
 
-        if (hasSched) {
-          const config: ProcessSchedulingTrackConfig = {
-            pidForColor,
-            upid,
-            utid,
-          };
+      if (hasSched) {
+        const config: ProcessSchedulingTrackConfig = {
+          pidForColor,
+          upid,
+          utid,
+        };
 
-          ctx.registerTrack({
-            uri,
-            displayName: `${upid === null ? tid : pid} schedule`,
-            kind: PROCESS_SCHEDULING_TRACK_KIND,
-            tags: {
-              isDebuggable,
-            },
-            trackFactory: () =>
-              new ProcessSchedulingTrack(
-                ctx.engine,
-                config,
-                assertExists(schedMaxDur),
-                assertExists(schedEventCount),
-              ),
-          });
-        } else {
-          const config: ProcessSummaryTrackConfig = {
-            pidForColor,
-            upid,
-            utid,
-          };
+        ctx.registerTrack({
+          uri,
+          displayName: `${upid === null ? tid : pid} schedule`,
+          kind: PROCESS_SCHEDULING_TRACK_KIND,
+          tags: {
+            isDebuggable,
+          },
+          trackFactory: () =>
+            new ProcessSchedulingTrack(
+              ctx.engine,
+              config,
+              assertExists(maxRunningDur),
+              assertExists(runningCount),
+            ),
+        });
+      } else {
+        const config: ProcessSummaryTrackConfig = {
+          pidForColor,
+          upid,
+          utid,
+        };
 
-          ctx.registerTrack({
-            uri,
-            displayName: `${upid === null ? tid : pid} summary`,
-            kind: PROCESS_SUMMARY_TRACK,
-            tags: {
-              isDebuggable,
-            },
-            trackFactory: () => new ProcessSummaryTrack(ctx.engine, config),
-          });
-        }
+        ctx.registerTrack({
+          uri,
+          displayName: `${upid === null ? tid : pid} summary`,
+          kind: PROCESS_SUMMARY_TRACK,
+          tags: {
+            isDebuggable,
+          },
+          trackFactory: () => new ProcessSummaryTrack(ctx.engine, config),
+        });
       }
     }
   }
@@ -313,25 +198,6 @@
       trackFactory: () => new ProcessSummaryTrack(ctx.engine, config),
     });
   }
-
-  private getOrCreateUuid(utid: number, upid: number | null) {
-    let uuid = this.getUuidUnchecked(utid, upid);
-    if (uuid === undefined) {
-      uuid = uuidv4();
-      if (upid === null) {
-        this.utidToUuid.set(utid, uuid);
-      } else {
-        this.upidToUuid.set(upid, uuid);
-      }
-    }
-    return uuid;
-  }
-
-  getUuidUnchecked(utid: number, upid: number | null) {
-    return upid === null
-      ? this.utidToUuid.get(utid)
-      : this.upidToUuid.get(upid);
-  }
 }
 
 export const plugin: PluginDescriptor = {
diff --git a/ui/src/tracks/process_summary/process_scheduling_track.ts b/ui/src/tracks/process_summary/process_scheduling_track.ts
index 07400ca..64fe72c 100644
--- a/ui/src/tracks/process_summary/process_scheduling_track.ts
+++ b/ui/src/tracks/process_summary/process_scheduling_track.ts
@@ -50,8 +50,8 @@
 
 export interface Config {
   pidForColor: number;
-  upid: null | number;
-  utid: number;
+  upid: number | null;
+  utid: number | null;
 }
 
 export class ProcessSchedulingTrack implements Track {
diff --git a/ui/src/tracks/process_summary/process_summary_track.ts b/ui/src/tracks/process_summary/process_summary_track.ts
index 6a8e687..f556728 100644
--- a/ui/src/tracks/process_summary/process_summary_track.ts
+++ b/ui/src/tracks/process_summary/process_summary_track.ts
@@ -37,7 +37,7 @@
 export interface Config {
   pidForColor: number;
   upid: number | null;
-  utid: number;
+  utid: number | null;
 }
 
 const MARGIN_TOP = 5;
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index b38eb1d..73e2faf 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -32,14 +32,14 @@
   async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
     const {engine} = ctx;
     const result = await engine.query(`
-      with ts_distinct as materialized (select distinct utid from thread_state)
       select
         utid,
         upid,
         tid,
         thread.name as threadName
       from thread
-      where utid != 0 and utid in ts_distinct`);
+      join _sched_summary using (utid)
+    `);
 
     const it = result.iter({
       utid: NUM,