• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2021 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {NUM, NUM_NULL, STR} from '../../common/query_result';
16import {fromNs, toNs} from '../../common/time';
17import {
18  TrackController,
19  trackControllerRegistry,
20} from '../../controller/track_controller';
21
22import {ACTUAL_FRAMES_SLICE_TRACK_KIND, Config, Data} from './common';
23
24const BLUE_COLOR = '#03A9F4';    // Blue 500
25const GREEN_COLOR = '#4CAF50';     // Green 500
26const YELLOW_COLOR = '#FFEB3B';  // Yellow 500
27const RED_COLOR = '#FF5722';      // Red 500
28const LIGHT_GREEN_COLOR = '#C0D588'; // Light Green 500
29const PINK_COLOR = '#F515E0';        // Pink 500
30
31class ActualFramesSliceTrackController extends TrackController<Config, Data> {
32  static readonly kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
33  private maxDurNs = 0;
34
35  async onBoundsChange(start: number, end: number, resolution: number):
36      Promise<Data> {
37    const startNs = toNs(start);
38    const endNs = toNs(end);
39
40    const pxSize = this.pxSize();
41
42    // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
43    // be an even number, so we can snap in the middle.
44    const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
45
46    if (this.maxDurNs === 0) {
47      const maxDurResult = await this.query(`
48        select
49          max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
50            as maxDur
51        from experimental_slice_layout
52        where filter_track_ids = '${this.config.trackIds.join(',')}'
53      `);
54      this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
55    }
56
57    const rawResult = await this.query(`
58      SELECT
59        (s.ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
60        s.ts as ts,
61        max(iif(s.dur = -1, (SELECT end_ts FROM trace_bounds) - s.ts, s.dur))
62            as dur,
63        s.layout_depth as layoutDepth,
64        s.name as name,
65        s.id as id,
66        s.dur = 0 as isInstant,
67        s.dur = -1 as isIncomplete,
68        CASE afs.jank_tag
69          WHEN 'Self Jank' THEN '${RED_COLOR}'
70          WHEN 'Other Jank' THEN '${YELLOW_COLOR}'
71          WHEN 'Dropped Frame' THEN '${BLUE_COLOR}'
72          WHEN 'Buffer Stuffing' THEN '${LIGHT_GREEN_COLOR}'
73          WHEN 'SurfaceFlinger Stuffing' THEN '${LIGHT_GREEN_COLOR}'
74          WHEN 'No Jank' THEN '${GREEN_COLOR}'
75          ELSE '${PINK_COLOR}'
76        END as color
77      from experimental_slice_layout s
78      join actual_frame_timeline_slice afs using(id)
79      where
80        filter_track_ids = '${this.config.trackIds.join(',')}' and
81        s.ts >= ${startNs - this.maxDurNs} and
82        s.ts <= ${endNs}
83      group by tsq, s.layout_depth
84      order by tsq, s.layout_depth
85    `);
86
87    const numRows = rawResult.numRows();
88    const slices: Data = {
89      start,
90      end,
91      resolution,
92      length: numRows,
93      strings: [],
94      sliceIds: new Float64Array(numRows),
95      starts: new Float64Array(numRows),
96      ends: new Float64Array(numRows),
97      depths: new Uint16Array(numRows),
98      titles: new Uint16Array(numRows),
99      colors: new Uint16Array(numRows),
100      isInstant: new Uint16Array(numRows),
101      isIncomplete: new Uint16Array(numRows),
102    };
103
104    const stringIndexes = new Map<string, number>();
105    function internString(str: string) {
106      let idx = stringIndexes.get(str);
107      if (idx !== undefined) return idx;
108      idx = slices.strings.length;
109      slices.strings.push(str);
110      stringIndexes.set(str, idx);
111      return idx;
112    }
113
114    const it = rawResult.iter({
115      'tsq': NUM,
116      'ts': NUM,
117      'dur': NUM,
118      'layoutDepth': NUM,
119      'id': NUM,
120      'name': STR,
121      'isInstant': NUM,
122      'isIncomplete': NUM,
123      'color': STR,
124    });
125    for (let i = 0; it.valid(); i++, it.next()) {
126      const startNsQ = it.tsq;
127      const startNs = it.ts;
128      const durNs = it.dur;
129      const endNs = startNs + durNs;
130
131      let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
132      endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
133
134      slices.starts[i] = fromNs(startNsQ);
135      slices.ends[i] = fromNs(endNsQ);
136      slices.depths[i] = it.layoutDepth;
137      slices.titles[i] = internString(it.name);
138      slices.colors![i] = internString(it.color);
139      slices.sliceIds[i] = it.id;
140      slices.isInstant[i] = it.isInstant;
141      slices.isIncomplete[i] = it.isIncomplete;
142    }
143    return slices;
144  }
145}
146
147
148trackControllerRegistry.register(ActualFramesSliceTrackController);
149