Use a shared async disposable to improve drilldown performance on initial load
diff --git a/ui/src/components/aggregation_adapter.ts b/ui/src/components/aggregation_adapter.ts
index 46f9324..a361a79 100644
--- a/ui/src/components/aggregation_adapter.ts
+++ b/ui/src/components/aggregation_adapter.ts
@@ -52,9 +52,10 @@
 import {AsyncMemo, AtomicTaskQueue} from '../base/async_memo';
 import {Memo} from '../base/memo';
 import type {ColumnSchema} from './widgets/datagrid/datagrid_schema';
+import {SharedAsyncDisposable} from '../base/shared_disposable';
 
 export interface AggregationData extends AsyncDisposable {
-  readonly tableName: string;
+  readonly sqlTable: DisposableSqlEntity;
   readonly barChartData?: ReadonlyArray<BarChartData>;
 }
 
@@ -62,7 +63,7 @@
   table: DisposableSqlEntity,
 ): AggregationData {
   return {
-    tableName: table.name,
+    sqlTable: table,
     [Symbol.asyncDispose]: () => table[Symbol.asyncDispose](),
   };
 }
@@ -230,6 +231,7 @@
 interface PreparedAggregation extends AsyncDisposable {
   readonly data: AggregationData;
   readonly dataSource: SQLDataSource;
+  readonly sharedTable: SharedAsyncDisposable<DisposableSqlEntity>;
 }
 
 /**
@@ -287,6 +289,7 @@
           if (!currentAggregation) return undefined;
 
           const data = await currentAggregation.prepareData(trace.engine);
+          const sharedTable = SharedAsyncDisposable.wrap(data.sqlTable);
           const dataSource = createAggregationDataSource(
             trace,
             aggregator,
@@ -296,9 +299,10 @@
           return {
             data,
             dataSource,
+            sharedTable,
             [Symbol.asyncDispose]: async () => {
               dataSource.dispose();
-              await data[Symbol.asyncDispose]();
+              await sharedTable[Symbol.asyncDispose]();
             },
           };
         },
@@ -324,7 +328,7 @@
         };
       }
 
-      const {data, dataSource} = preparedAggregation;
+      const {data, dataSource, sharedTable} = preparedAggregation;
 
       const dataGridState: DataGridState = {
         columns: dataModel.columns,
@@ -348,20 +352,14 @@
               ],
               pivot: undefined,
             };
-            const drilldownSelection: AreaSelection = {
-              ...selection,
-              trackUris: [...selection.trackUris],
-              tracks: [...selection.tracks],
-            };
             addEphemeralTab(trace, `aggregation_drilldown_${aggregator.id}`, {
               getTitle: () => `${aggregator.getTabName()} drill-down`,
               render: () =>
                 m(AggregationDrilldownPanel, {
                   trace,
                   aggregator,
-                  aggregation: currentAggregation,
-                  selection: drilldownSelection,
-                  queue,
+                  aggregationData: data,
+                  sharedTable,
                   initialDataModel,
                 }),
             });
diff --git a/ui/src/components/aggregation_drilldown_panel.ts b/ui/src/components/aggregation_drilldown_panel.ts
index 5f54663..c51c076 100644
--- a/ui/src/components/aggregation_drilldown_panel.ts
+++ b/ui/src/components/aggregation_drilldown_panel.ts
@@ -13,17 +13,13 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {AsyncMemo, type AtomicTaskQueue} from '../base/async_memo';
-import type {AreaSelection} from '../public/selection';
+import {AtomicTaskQueue} from '../base/async_memo';
 import type {Trace} from '../public/trace';
 import {Button} from '../widgets/button';
 import {DetailsShell} from '../widgets/details_shell';
-import {EmptyState} from '../widgets/empty_state';
 import {ExportButton} from '../widgets/export_button';
 import {Popup, PopupPosition} from '../widgets/popup';
-import {Spinner} from '../widgets/spinner';
 import type {
-  Aggregation,
   AggregationData,
   Aggregator,
   DataGridState,
@@ -33,6 +29,8 @@
 import type {DataGridApi} from './widgets/datagrid/datagrid';
 import type {Column, Filter, Pivot} from './widgets/datagrid/model';
 import {SQLDataSource} from './widgets/datagrid/sql_data_source';
+import type {SharedAsyncDisposable} from '../base/shared_disposable';
+import type {DisposableSqlEntity} from '../trace_processor/sql_utils';
 
 export interface DataGridModel {
   readonly columns?: readonly Column[];
@@ -40,11 +38,6 @@
   readonly filters: readonly Filter[];
 }
 
-interface PreparedAggregation extends AsyncDisposable {
-  readonly data: AggregationData;
-  readonly dataSource: SQLDataSource;
-}
-
 export function createAggregationDataSource(
   trace: Trace,
   aggregator: Aggregator,
@@ -53,7 +46,7 @@
 ): SQLDataSource {
   const gridConfig = aggregator.getGridConfig(data);
   const sqlConfig = gridConfig.sqlConfig?.(data) ?? {
-    tableOrSubquery: data.tableName,
+    tableOrSubquery: data.sqlTable.name,
   };
   return new SQLDataSource({
     queue,
@@ -65,71 +58,32 @@
 interface AggregationDrilldownPanelAttrs {
   readonly trace: Trace;
   readonly aggregator: Aggregator;
-  readonly aggregation: Aggregation;
-  readonly selection: AreaSelection;
-  readonly queue: AtomicTaskQueue;
   readonly initialDataModel: DataGridModel;
+  readonly sharedTable: SharedAsyncDisposable<DisposableSqlEntity>;
+  readonly aggregationData: AggregationData;
 }
 
 export class AggregationDrilldownPanel implements m.ClassComponent<AggregationDrilldownPanelAttrs> {
-  private readonly preparedAggregationSlot: AsyncMemo<PreparedAggregation>;
   private readonly initialDataModel: DataGridModel;
   private dataModel: DataGridModel;
   private dataGridApi?: DataGridApi;
+  private table: SharedAsyncDisposable<DisposableSqlEntity>;
+  private datasource: SQLDataSource;
 
   constructor({attrs}: m.Vnode<AggregationDrilldownPanelAttrs>) {
-    this.preparedAggregationSlot = new AsyncMemo(attrs.queue);
     this.initialDataModel = attrs.initialDataModel;
     this.dataModel = attrs.initialDataModel;
+    this.table = attrs.sharedTable.clone();
+    this.datasource = createAggregationDataSource(
+      attrs.trace,
+      attrs.aggregator,
+      attrs.aggregationData,
+      new AtomicTaskQueue(),
+    );
   }
 
   view({attrs}: m.Vnode<AggregationDrilldownPanelAttrs>): m.Children {
-    const {trace, aggregator, aggregation, selection, queue} = attrs;
-    const preparedAggregation = this.preparedAggregationSlot.use({
-      key: {
-        start: selection.start,
-        end: selection.end,
-        trackUris: selection.trackUris,
-      },
-      compute: async () => {
-        const data = await aggregation.prepareData(trace.engine);
-        const dataSource = createAggregationDataSource(
-          trace,
-          aggregator,
-          data,
-          queue,
-        );
-        return {
-          data,
-          dataSource,
-          [Symbol.asyncDispose]: async () => {
-            dataSource.dispose();
-            await data[Symbol.asyncDispose]();
-          },
-        };
-      },
-    }).data;
-
-    if (!preparedAggregation) {
-      return m(
-        DetailsShell,
-        {
-          title: 'Area Selection Drill-down',
-          description: aggregator.getTabName(),
-        },
-        m(
-          EmptyState,
-          {
-            icon: 'mediation',
-            title: 'Computing aggregation ...',
-            className: 'pf-aggregation-loading',
-          },
-          m(Spinner, {easing: true}),
-        ),
-      );
-    }
-
-    const {data, dataSource} = preparedAggregation;
+    const {trace, aggregator, aggregationData} = attrs;
     const dataGridState: DataGridState = {
       columns: this.dataModel.columns,
       pivot: this.dataModel.pivot,
@@ -150,13 +104,12 @@
       {
         title: 'Area Selection Drill-down',
         description: aggregator.getTabName(),
-        buttons: this.renderButtons(trace, dataSource),
+        buttons: this.renderButtons(trace, this.datasource),
       },
       m(AggregationPanel, {
         controls: aggregator.renderTopbarControls?.(),
-        dataSource,
-        gridConfig: aggregator.getGridConfig(data),
-        barChartData: data.barChartData,
+        dataSource: this.datasource,
+        gridConfig: aggregator.getGridConfig(aggregationData),
         onReady: (api: DataGridApi) => {
           this.dataGridApi = api;
         },
@@ -214,6 +167,6 @@
   }
 
   onremove(): void {
-    this.preparedAggregationSlot.dispose();
+    this.table[Symbol.asyncDispose]();
   }
 }
diff --git a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/slice_selection_aggregator.ts b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/slice_selection_aggregator.ts
index b9b6f6d..6fe8b07 100644
--- a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/slice_selection_aggregator.ts
+++ b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/slice_selection_aggregator.ts
@@ -380,8 +380,8 @@
       },
       // The aggregation table has an `arg_set_id` column, so we can expose a
       // parameterized `args.*` column to the datagrid.
-      sqlConfig: ({tableName}): SQLTableSchema => ({
-        tableOrSubquery: tableName,
+      sqlConfig: ({sqlTable}): SQLTableSchema => ({
+        tableOrSubquery: sqlTable.name,
         columns: {
           args: {
             expression: (alias, key) =>