• 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');
34
35-- Note: Must be a TABLE because it uses a window function which can behave
36--       strangely in views.
37DROP TABLE IF EXISTS vsync_intervals;
38CREATE TABLE vsync_intervals AS
39  SELECT
40    slice_id,
41    ts,
42    dur,
43    track_id,
44    LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync
45  FROM slice
46  WHERE name = "VSync"
47  ORDER BY track_id, ts;
48
49-- Get all the "begin" and "end" events. We take their IDs to group them
50-- together into gestures later and the timestamp and duration to compute the
51-- duration of the gesture.
52DROP VIEW IF EXISTS {{prefix}}_begin_and_end;
53CREATE VIEW {{prefix}}_begin_and_end AS
54  SELECT
55    slice.name,
56    slice.id,
57    slice.ts,
58    slice.dur,
59    slice.track_id,
60    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
61        AS {{id_field}},
62    EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
63  FROM
64    slice
65  WHERE
66    slice.name IN (
67      'InputLatency::{{gesture_start}}',
68      'InputLatency::{{gesture_end}}'
69    )
70  ORDER BY ts;
71
72-- Now we take the "begin" and the "end" events and join the information into a
73-- single row per gesture. We also compute the average Vysnc interval of the
74-- gesture (hopefully this would be either 60 FPS for the whole gesture or 90
75-- FPS but that isn't always the case). If the trace doesn't contain the VSync
76-- TraceEvent we just fall back on assuming its 60 FPS (this is the 1.6e+7 in
77-- the COALESCE which corresponds to 16 ms or 60 FPS).
78DROP VIEW IF EXISTS joined_{{prefix}}_begin_and_end;
79CREATE VIEW joined_{{prefix}}_begin_and_end AS
80  SELECT
81    begin.id AS begin_id,
82    begin.ts AS begin_ts,
83    begin.dur AS begin_dur,
84    begin.track_id AS begin_track_id,
85    begin.trace_id AS begin_trace_id,
86    COALESCE(begin.{{id_field}}, begin.trace_id)
87        AS begin_{{id_field}},
88    end.ts AS end_ts,
89    end.ts + end.dur AS end_ts_and_dur,
90    end.trace_id AS end_trace_id,
91    COALESCE((
92      SELECT
93        CAST(AVG(time_to_next_vsync) AS FLOAT)
94      FROM vsync_intervals in_query
95      WHERE
96        time_to_next_vsync IS NOT NULL AND
97        in_query.ts > begin.ts AND
98        in_query.ts < end.ts
99    ), 1e+9 / 60) AS avg_vsync_interval
100  FROM {{prefix}}_begin_and_end begin JOIN {{prefix}}_begin_and_end end ON
101    begin.trace_id < end.trace_id AND
102    begin.name = 'InputLatency::{{gesture_start}}' AND
103    end.name = 'InputLatency::{{gesture_end}}' AND (
104      (
105        begin.{{id_field}} IS NULL AND
106        end.trace_id = (
107          SELECT MIN(trace_id)
108          FROM {{prefix}}_begin_and_end in_query
109          WHERE
110            name = 'InputLatency::{{gesture_end}}' AND
111          in_query.trace_id > begin.trace_id
112        )
113      ) OR
114      end.{{id_field}} = begin.{{id_field}}
115    )
116  ORDER BY begin.ts;
117
118-- Prepare all gesture updates that were not coalesced to be joined with their
119-- respective scrolls to calculate jank
120DROP VIEW IF EXISTS gesture_update;
121CREATE VIEW gesture_update AS
122  SELECT
123      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
124      EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
125          AS {{id_field}},
126      *
127  FROM
128    slice JOIN track ON slice.track_id = track.id
129  WHERE
130    slice.name = 'InputLatency::{{gesture_update}}' AND
131    slice.dur != -1 AND
132    NOT COALESCE(
133            EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
134            TRUE)
135    AND slice.arg_set_id IN (
136      SELECT arg_set_id
137      FROM args
138      WHERE args.arg_set_id = slice.arg_set_id
139      AND flat_key = 'chrome_latency_info.component_info.component_type'
140      AND string_value = 'COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER'
141    );
142
143-- Get the "update" events by name ordered by the |{{id_field}}|, and
144-- timestamp. Then compute the number of frames (relative to vsync interval)
145-- that each event took. 1.6e+7 is 16 ms in nanoseconds and is used in case
146-- there are no VSync events to default to 60 fps. We join each
147-- {{gesture_update}} event to the information about its "begin" and "end"
148-- events for easy computation later.
149--
150-- We remove updates with |dur| == -1 because this means we have no "end" event
151-- and can't reasonably determine what it should be. We have separate tracking
152-- to ensure this only happens at the end of the trace where its expected.
153DROP VIEW IF EXISTS {{id_field}}_update;
154CREATE VIEW {{id_field}}_update AS
155  SELECT
156    begin_id,
157    begin_ts,
158    begin_dur,
159    begin_track_id,
160    begin_trace_id,
161    COALESCE({{id_field}}, begin_trace_id) AS {{id_field}},
162    CASE WHEN
163      end_ts_and_dur > ts + dur THEN
164        end_ts_and_dur
165      ELSE
166        ts + dur
167    END AS maybe_gesture_end,
168    id,
169    ts,
170    dur,
171    track_id,
172    trace_id,
173    dur/avg_vsync_interval AS gesture_frames_exact,
174    avg_vsync_interval
175  FROM joined_{{prefix}}_begin_and_end begin_and_end JOIN gesture_update ON
176  gesture_update.ts <= begin_and_end.end_ts AND
177  gesture_update.ts >= begin_and_end.begin_ts AND
178  gesture_update.trace_id > begin_and_end.begin_trace_id AND
179  gesture_update.trace_id < begin_and_end.end_trace_id AND (
180    gesture_update.{{id_field}} IS NULL OR
181    gesture_update.{{id_field}} = begin_and_end.begin_{{id_field}}
182  )
183  ORDER BY {{id_field}} ASC, ts ASC;
184
185-- This takes the "update" events and get to the previous "update" event through LAG
186-- (previous row and NULL if there isn't one) and the next "update" event through LEAD
187-- (next row and again NULL if there isn't one). Then we compute the duration of the
188-- event (relative to fps).
189--
190-- We only compare an "update" event to another event within the same gesture
191-- ({{id_field}} == prev/next {{id_field}}). This controls somewhat for
192-- variability of gestures.
193--
194-- Note: Must be a TABLE because it uses a window function which can behave
195--       strangely in views.
196
197DROP TABLE IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next_without_precompute;
198CREATE TABLE {{prefix}}_jank_maybe_null_prev_and_next_without_precompute AS
199  SELECT
200    *,
201    maybe_gesture_end - begin_ts AS {{prefix}}_dur,
202    LAG(ts) OVER sorted_frames AS prev_ts,
203    LAG({{id_field}}) OVER sorted_frames AS prev_{{id_field}},
204    LAG(gesture_frames_exact) OVER sorted_frames AS prev_gesture_frames_exact,
205    LEAD(ts) OVER sorted_frames AS next_ts,
206    LEAD({{id_field}}) OVER sorted_frames AS next_{{id_field}},
207    LEAD(gesture_frames_exact) OVER sorted_frames AS next_gesture_frames_exact
208  FROM {{id_field}}_update
209  WINDOW sorted_frames AS (ORDER BY {{id_field}} ASC, ts ASC)
210  ORDER BY {{id_field}} ASC, ts ASC;
211
212
213-- We compute the duration of the event (relative to fps) and see if it
214-- increased by more than 0.5 (which is 1/2 of 16 ms at 60 fps, and so on).
215--
216-- A small number is added to 0.5 in order to make sure that the comparison does
217-- not filter out ratios that are precisely 0.5, which can fall a little above
218-- or below exact value due to inherent inaccuracy of operations with
219-- floating-point numbers. Value 1e-9 have been chosen as follows: the ratio has
220-- nanoseconds in numerator and VSync interval in denominator. Assuming refresh
221-- rate more than 1 FPS (and therefore VSync interval less than a second), this
222-- ratio should increase with increments more than minimal value in numerator
223-- (1ns) divided by maximum value in denominator, giving 1e-9.
224-- Note: Logic is inside the IsJankyFrame function found in jank_utilities.sql.
225DROP VIEW IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next;
226CREATE VIEW {{prefix}}_jank_maybe_null_prev_and_next AS
227  SELECT
228    *,
229     IsJankyFrame({{id_field}}, prev_{{id_field}},
230     prev_ts, begin_ts, maybe_gesture_end,
231     gesture_frames_exact, prev_gesture_frames_exact) AS prev_jank,
232    IsJankyFrame({{id_field}}, next_{{id_field}},
233     next_ts, begin_ts, maybe_gesture_end,
234     gesture_frames_exact, next_gesture_frames_exact) AS next_jank
235  FROM {{prefix}}_jank_maybe_null_prev_and_next_without_precompute
236  ORDER BY {{id_field}} ASC, ts ASC;
237
238-- This just uses prev_jank and next_jank to see if each "update" event is a
239-- jank.
240--
241-- JankBudget is the time in ns that we need to reduce the current
242-- gesture (|id|) for this frame not to be considered janky (i.e., how much
243-- faster for IsJankyFrame() to have not returned true).
244--
245-- For JankBudget we use the frames_exact of current, previous and next to find
246-- the jank budget in exact frame count. We then multiply by avg_vsync_internal
247-- to get the jank budget time.
248-- Note: Logic is inside the JankBudget function found in jank_utilities.sql.
249DROP VIEW IF EXISTS {{prefix}}_jank;
250CREATE VIEW {{prefix}}_jank AS
251  SELECT
252    id AS slice_id,
253    (next_jank IS NOT NULL AND next_jank) OR
254    (prev_jank IS NOT NULL AND prev_jank)
255    AS jank,
256    JankBudget(gesture_frames_exact, prev_gesture_frames_exact,
257      next_gesture_frames_exact) * avg_vsync_interval AS jank_budget,
258    *
259  FROM {{prefix}}_jank_maybe_null_prev_and_next
260  ORDER BY {{id_field}} ASC, ts ASC;
261
262DROP VIEW IF EXISTS {{prefix}}_jank_output;
263CREATE VIEW {{prefix}}_jank_output AS
264  SELECT
265    {{proto_name}}(
266      '{{prefix}}_jank_percentage', (
267        SELECT
268          (
269            SUM(CASE WHEN jank THEN dur ELSE 0 END)/CAST(SUM(dur) AS REAL)
270          ) * 100.0
271        FROM {{prefix}}_jank
272      ),
273      '{{prefix}}_ms', (
274        SELECT
275          CAST(SUM({{prefix}}_dur)/1e6 AS REAL)
276        FROM (
277          SELECT
278            MAX({{prefix}}_dur) AS {{prefix}}_dur
279          FROM {{prefix}}_jank
280          GROUP BY {{id_field}}
281        )
282      ),
283      '{{prefix}}_processing_ms', CAST(SUM(dur)/1e6 AS REAL),
284      '{{prefix}}_jank_processing_ms', (
285        SELECT CAST(SUM(dur)/1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
286      ),
287      'num_{{prefix}}_update_count', COUNT(*),
288      'num_{{prefix}}_update_jank_count', SUM(jank),
289      '{{prefix}}_jank_budget_ms', (
290        SELECT CAST(SUM(jank_budget) AS REAL) FROM {{prefix}}_jank WHERE jank
291      )
292    )
293  FROM {{prefix}}_jank;
294