ui: tp: Move Choreographer stage from core to extension
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/input.sql b/src/trace_processor/perfetto_sql/stdlib/android/input.sql index a27f51f..09d2d67 100644 --- a/src/trace_processor/perfetto_sql/stdlib/android/input.sql +++ b/src/trace_processor/perfetto_sql/stdlib/android/input.sql
@@ -552,7 +552,6 @@ ts_dispatch LONG, ts_receive LONG, ts_consume LONG, - ts_frame LONG, -- InputReader Stage id_reader LONG, track_reader LONG, @@ -569,10 +568,6 @@ id_consume LONG, track_consume LONG, dur_consume LONG, - -- Choreographer Frame Stage - id_frame LONG, - track_frame LONG, - dur_frame LONG, is_speculative_frame BOOL ) AS @@ -584,7 +579,6 @@ e.dispatch_ts AS ts_dispatch, e.receive_ts AS ts_receive, s_cons.ts AS ts_consume, - s_frame.ts AS ts_frame, s_read.id AS id_reader, s_read.track_id AS track_reader, s_read.dur AS dur_reader, @@ -597,9 +591,6 @@ s_cons.id AS id_consume, s_cons.track_id AS track_consume, s_cons.dur AS dur_consume, - s_frame.id AS id_frame, - s_frame.track_id AS track_frame, - s_frame.dur AS dur_frame, e.is_speculative_frame FROM android_input_events AS e LEFT JOIN slice AS s_read @@ -613,7 +604,16 @@ AND s_recv.track_id = e.receive_track_id LEFT JOIN _input_consumers_lookup AS s_cons ON s_cons.cookie = e.event_seq -LEFT JOIN _frame_choreographer_lookup AS s_frame - ON s_frame.frame_id = CAST(e.frame_id AS LONG) WHERE - $slice_id IN (s_read.id, s_disp.id, s_recv.id, s_cons.id, s_frame.id); + $slice_id IN (s_read.id, s_disp.id, s_recv.id, s_cons.id); + +CREATE PERFETTO VIEW _android_input_frames AS +SELECT + chor.upid, + chor.frame_id, + chor.id AS id_do_frame, + s_chor.track_id AS track_do_frame, + chor.ts AS ts_do_frame, + s_chor.dur AS dur_do_frame +FROM android_frames_choreographer_do_frame AS chor +JOIN slice AS s_chor ON s_chor.id = chor.id;
diff --git a/ui/src/plugins/com.android.AndroidInputLifecycle/android_input_event_source.ts b/ui/src/plugins/com.android.AndroidInputLifecycle/android_input_event_source.ts index bebbb3f..cd82b02 100644 --- a/ui/src/plugins/com.android.AndroidInputLifecycle/android_input_event_source.ts +++ b/ui/src/plugins/com.android.AndroidInputLifecycle/android_input_event_source.ts
@@ -263,13 +263,4 @@ tsField: 'ts_consume', durField: 'dur_consume', }, - { - key: 'frame', - headerName: 'App Frame', - sequenceNumber: 5000, - idField: 'id_frame', - trackField: 'track_frame', - tsField: 'ts_frame', - durField: 'dur_frame', - }, ];
diff --git a/ui/src/plugins/com.android.AndroidInputLifecycle/extensions/android_frames_extension.ts b/ui/src/plugins/com.android.AndroidInputLifecycle/extensions/android_frames_extension.ts new file mode 100644 index 0000000..1328cbb --- /dev/null +++ b/ui/src/plugins/com.android.AndroidInputLifecycle/extensions/android_frames_extension.ts
@@ -0,0 +1,75 @@ +// Copyright (C) 2026 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 type {Trace} from '../../../public/trace'; +import type { + InputLifecycleExtension, + StageDefinition, + SqlJoinSpec, +} from './interface'; +import {STR_NULL} from '../../../trace_processor/query_result'; + +export class AndroidFramesInputLifecycleExtension + implements InputLifecycleExtension +{ + readonly id = 'android.InputLifecycleAndroidFrames'; + readonly name = 'Android Frame Pipeline'; + + async isEligible(trace: Trace): Promise<boolean> { + const result = await trace.engine.query( + `SELECT 1 FROM slice WHERE name GLOB 'Choreographer#doFrame *' LIMIT 1`, + ); + return result.numRows() > 0; + } + + getSqlJoinSpec(): SqlJoinSpec { + return { + tableName: '_android_input_frames', + tableAlias: 'frames', + joinOn: 'frames.frame_id = a_evt.frame_id AND frames.upid = a_evt.upid', + }; + } + + getStages(): StageDefinition[] { + return [ + { + key: 'choreographer_do_frame', + headerName: 'Choreographer#doFrame', + sequenceNumber: 5000, + idField: 'id_do_frame', + trackField: 'track_do_frame', + tsField: 'ts_do_frame', + durField: 'dur_do_frame', + }, + ]; + } + + async resolveInputId( + trace: Trace, + sliceId: number, + ): Promise<string | undefined> { + const query = ` + SELECT e.input_event_id AS input_id + FROM android_input_events e + JOIN android_frames_choreographer_do_frame chor + ON chor.frame_id = e.frame_id + AND chor.upid = e.upid + WHERE chor.id = ${sliceId} + LIMIT 1 + `; + const result = await trace.engine.query(query); + const it = result.iter({input_id: STR_NULL}); + return it.valid() ? it.input_id ?? undefined : undefined; + } +}
diff --git a/ui/src/plugins/com.android.AndroidInputLifecycle/index.ts b/ui/src/plugins/com.android.AndroidInputLifecycle/index.ts index 71920fe..9f50747 100644 --- a/ui/src/plugins/com.android.AndroidInputLifecycle/index.ts +++ b/ui/src/plugins/com.android.AndroidInputLifecycle/index.ts
@@ -32,9 +32,11 @@ import type {QueryResult} from '../../base/query_slot'; import type {InputLifecycleExtension, NavTarget} from './extensions/interface'; import {PixelInputLifecycleExtension} from './extensions/pixel_extension'; +import {AndroidFramesInputLifecycleExtension} from './extensions/android_frames_extension'; const EXTENSIONS: InputLifecycleExtension[] = [ new PixelInputLifecycleExtension(), + new AndroidFramesInputLifecycleExtension(), ]; export default class AndroidInputLifecyclePlugin implements PerfettoPlugin {