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