• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2021 The Android Open Source Project
3--
4-- Licensed under the Apache License, Version 2.0 (the "License");
5-- you may not use this file except in compliance with the License.
6-- You may obtain a copy of the License at
7--
8--     https://www.apache.org/licenses/LICENSE-2.0
9--
10-- Unless required by applicable law or agreed to in writing, software
11-- distributed under the License is distributed on an "AS IS" BASIS,
12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-- See the License for the specific language governing permissions and
14-- limitations under the License.
15--
16-- A collection of templated metrics related to continuous motion gestures that
17-- have start, end and update events.
18--
19-- We define an update to be janky if comparing forwards or backwards (ignoring
20-- coalesced updates) a given updates exceeds the duration of its predecessor or
21-- successor by 50% of a vsync interval (defaulted to 60 FPS).
22--
23-- WARNING: This metric should not be used as a source of truth. It is under
24--          active development and the values & meaning might change without
25--          notice.
26
27-- A simple table that checks the time between VSync (this can be used to
28-- determine if we're refreshing at 90 FPS or 60 FPS.
29--
30-- Note: In traces without the "Java" category there will be no VSync
31--       TraceEvents and this table will be empty.
32
33SELECT RUN_METRIC('chrome/jank_utilities.sql');
34SELECT RUN_METRIC('chrome/vsync_intervals.sql');
35
36-- Get all the "begin" and "end" events. We take their IDs to group them
37-- together into gestures later and the timestamp and duration to compute the
38-- duration of the gesture.
39DROP VIEW IF EXISTS {{prefix}}_begin_and_end;
40CREATE VIEW {{prefix}}_begin_and_end AS
41SELECT
42  slice.name,
43  slice.id,
44  slice.ts,
45  slice.dur,
46  slice.track_id,
47  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
48  AS {{id_field}},
49  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
50FROM
51  slice
52WHERE
53  slice.name IN (
54    'InputLatency::{{gesture_start}}',
55    'InputLatency::{{gesture_end}}'
56  )
57ORDER BY ts;
58
59-- Now we take the "begin" and the "end" events and join the information into a
60-- single row per gesture. We also compute the average Vysnc interval of the
61-- gesture (hopefully this would be either 60 FPS for the whole gesture or 90
62-- FPS but that isn't always the case). If the trace doesn't contain the VSync
63-- TraceEvent we just fall back on assuming its 60 FPS (this is the 1.6e+7 in
64-- the COALESCE which corresponds to 16 ms or 60 FPS).
65DROP VIEW IF EXISTS joined_{{prefix}}_begin_and_end;
66CREATE VIEW joined_{{prefix}}_begin_and_end AS
67SELECT
68  begin.id AS begin_id,
69  begin.ts AS begin_ts,
70  begin.dur AS begin_dur,
71  begin.track_id AS begin_track_id,
72  begin.trace_id AS begin_trace_id,
73  COALESCE(begin.{{id_field}}, begin.trace_id)
74  AS begin_{{id_field}},
75  end.ts AS end_ts,
76  end.ts + end.dur AS end_ts_and_dur,
77  end.trace_id AS end_trace_id,
78  CalculateAvgVsyncInterval(begin.ts, end.ts) AS avg_vsync_interval
79FROM {{prefix}}_begin_and_end begin JOIN {{prefix}}_begin_and_end end ON
80    begin.trace_id < end.trace_id
81    AND begin.name = 'InputLatency::{{gesture_start}}'
82    AND end.name = 'InputLatency::{{gesture_end}}' AND (
83      (
84        begin.{{id_field}} IS NULL
85        AND end.trace_id = (
86          SELECT MIN(trace_id)
87          FROM {{prefix}}_begin_and_end in_query
88          WHERE
89            name = 'InputLatency::{{gesture_end}}'
90            AND in_query.trace_id > begin.trace_id
91        )
92      )
93      OR end.{{id_field}} = begin.{{id_field}}
94    )
95ORDER BY begin.ts;
96
97-- Prepare all gesture updates that were not coalesced to be joined with their
98-- respective scrolls to calculate jank
99DROP VIEW IF EXISTS gesture_update;
100CREATE VIEW gesture_update AS
101SELECT
102  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
103  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
104  AS {{id_field}},
105  *
106FROM
107  slice JOIN track ON slice.track_id = track.id
108WHERE
109  slice.name = 'InputLatency::{{gesture_update}}'
110  AND slice.dur != -1
111  AND NOT COALESCE(
112    EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
113    TRUE)
114  AND slice.arg_set_id IN (
115    SELECT arg_set_id
116    FROM args
117    WHERE args.arg_set_id = slice.arg_set_id
118      AND flat_key = 'chrome_latency_info.component_info.component_type'
119      AND string_value = 'COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER'
120  );
121
122-- Get the "update" events by name ordered by the |{{id_field}}|, and
123-- timestamp. Then compute the number of frames (relative to vsync interval)
124-- that each event took. 1.6e+7 is 16 ms in nanoseconds and is used in case
125-- there are no VSync events to default to 60 fps. We join each
126-- {{gesture_update}} event to the information about its "begin" and "end"
127-- events for easy computation later.
128--
129-- We remove updates with |dur| = -1 because this means we have no "end" event
130-- and can't reasonably determine what it should be. We have separate tracking
131-- to ensure this only happens at the end of the trace where its expected.
132DROP VIEW IF EXISTS {{id_field}}_update;
133CREATE VIEW {{id_field}}_update AS
134SELECT
135  begin_id,
136  begin_ts,
137  begin_dur,
138  begin_track_id,
139  begin_trace_id,
140  COALESCE({{id_field}}, begin_trace_id) AS {{id_field}},
141  end_ts,
142  CASE WHEN
143      end_ts_and_dur > ts + dur THEN
144      end_ts_and_dur
145    ELSE
146      ts + dur
147  END AS maybe_gesture_end,
148  id,
149  ts,
150  dur,
151  track_id,
152  trace_id,
153  dur / avg_vsync_interval AS gesture_frames_exact,
154  avg_vsync_interval
155FROM joined_{{prefix}}_begin_and_end begin_and_end JOIN gesture_update ON
156  gesture_update.ts <= begin_and_end.end_ts
157  AND gesture_update.ts >= begin_and_end.begin_ts
158  AND gesture_update.trace_id > begin_and_end.begin_trace_id
159  AND gesture_update.trace_id < begin_and_end.end_trace_id AND (
160    gesture_update.{{id_field}} IS NULL
161    OR gesture_update.{{id_field}} = begin_and_end.begin_{{id_field}}
162  )
163ORDER BY {{id_field}} ASC, ts ASC;
164
165-- This takes the "update" events and get to the previous "update" event through LAG
166-- (previous row and NULL if there isn't one) and the next "update" event through LEAD
167-- (next row and again NULL if there isn't one). Then we compute the duration of the
168-- event (relative to fps).
169--
170-- We only compare an "update" event to another event within the same gesture
171-- ({{id_field}} = prev/next {{id_field}}). This controls somewhat for
172-- variability of gestures.
173--
174-- Note: Must be a TABLE because it uses a window function which can behave
175--       strangely in views.
176
177DROP TABLE IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next_without_precompute;
178CREATE TABLE {{prefix}}_jank_maybe_null_prev_and_next_without_precompute AS
179SELECT
180  *,
181  maybe_gesture_end - begin_ts AS {{prefix}}_dur,
182  LAG(ts) OVER sorted_frames AS prev_ts,
183  LAG({{id_field}}) OVER sorted_frames AS prev_{{id_field}},
184  LAG(gesture_frames_exact) OVER sorted_frames AS prev_gesture_frames_exact,
185  LEAD(ts) OVER sorted_frames AS next_ts,
186  LEAD({{id_field}}) OVER sorted_frames AS next_{{id_field}},
187  LEAD(gesture_frames_exact) OVER sorted_frames AS next_gesture_frames_exact
188FROM {{id_field}}_update
189WINDOW sorted_frames AS (ORDER BY {{id_field}} ASC, ts ASC)
190ORDER BY {{id_field}} ASC, ts ASC;
191
192
193-- We compute the duration of the event (relative to fps) and see if it
194-- increased by more than 0.5 (which is 1/2 of 16 ms at 60 fps, and so on).
195--
196-- A small number is added to 0.5 in order to make sure that the comparison does
197-- not filter out ratios that are precisely 0.5, which can fall a little above
198-- or below exact value due to inherent inaccuracy of operations with
199-- floating-point numbers. Value 1e-9 have been chosen as follows: the ratio has
200-- nanoseconds in numerator and VSync interval in denominator. Assuming refresh
201-- rate more than 1 FPS (and therefore VSync interval less than a second), this
202-- ratio should increase with increments more than minimal value in numerator
203-- (1ns) divided by maximum value in denominator, giving 1e-9.
204-- Note: Logic is inside the IsJankyFrame function found in jank_utilities.sql.
205DROP VIEW IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next;
206CREATE VIEW {{prefix}}_jank_maybe_null_prev_and_next AS
207SELECT
208  *,
209  IsJankyFrame({{id_field}}, prev_{{id_field}},
210    prev_ts, begin_ts, maybe_gesture_end,
211    gesture_frames_exact, prev_gesture_frames_exact) AS prev_jank,
212  IsJankyFrame({{id_field}}, next_{{id_field}},
213    next_ts, begin_ts, maybe_gesture_end,
214    gesture_frames_exact, next_gesture_frames_exact) AS next_jank
215FROM {{prefix}}_jank_maybe_null_prev_and_next_without_precompute
216ORDER BY {{id_field}} ASC, ts ASC;
217
218-- This just uses prev_jank and next_jank to see if each "update" event is a
219-- jank.
220--
221-- JankBudget is the time in ns that we need to reduce the current
222-- gesture (|id|) for this frame not to be considered janky (i.e., how much
223-- faster for IsJankyFrame() to have not returned true).
224--
225-- For JankBudget we use the frames_exact of current, previous and next to find
226-- the jank budget in exact frame count. We then multiply by avg_vsync_internal
227-- to get the jank budget time.
228-- Note: Logic is inside the JankBudget function found in jank_utilities.sql.
229DROP VIEW IF EXISTS {{prefix}}_jank;
230CREATE VIEW {{prefix}}_jank AS
231SELECT
232  id AS slice_id,
233  (next_jank IS NOT NULL AND next_jank)
234  OR (prev_jank IS NOT NULL AND prev_jank)
235  AS jank,
236  JankBudget(gesture_frames_exact, prev_gesture_frames_exact,
237    next_gesture_frames_exact) * avg_vsync_interval AS jank_budget,
238  *
239FROM {{prefix}}_jank_maybe_null_prev_and_next
240ORDER BY {{id_field}} ASC, ts ASC;
241
242DROP VIEW IF EXISTS {{prefix}}_jank_output;
243CREATE VIEW {{prefix}}_jank_output AS
244SELECT
245  {{proto_name}}(
246    '{{prefix}}_jank_percentage', (
247      SELECT
248        (
249          SUM(CASE WHEN jank THEN dur ELSE 0 END) / CAST(SUM(dur) AS REAL)
250        ) * 100.0
251      FROM {{prefix}}_jank
252    ),
253    '{{prefix}}_ms', (
254      SELECT
255        CAST(SUM({{prefix}}_dur) / 1e6 AS REAL)
256      FROM (
257        SELECT
258          MAX({{prefix}}_dur) AS {{prefix}}_dur
259        FROM {{prefix}}_jank
260        GROUP BY {{id_field}}
261      )
262    ),
263    '{{prefix}}_processing_ms', CAST(SUM(dur) / 1e6 AS REAL),
264    '{{prefix}}_jank_processing_ms', (
265      SELECT CAST(SUM(dur) / 1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
266    ),
267    'num_{{prefix}}_update_count', COUNT(*),
268    'num_{{prefix}}_update_jank_count', SUM(jank),
269    '{{prefix}}_jank_budget_ms', (
270      SELECT CAST(SUM(jank_budget) AS REAL) FROM {{prefix}}_jank WHERE jank
271    )
272  )
273FROM {{prefix}}_jank;
274