• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
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 */
15
16import { TraceRow } from '../trace/base/TraceRow.js';
17import { renders } from '../../database/ui-worker/ProcedureWorker.js';
18import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank.js';
19import { SpSystemTrace } from '../SpSystemTrace.js';
20import { queryActualFrameDate, queryExpectedFrameDate, queryFrameTimeData } from '../../database/SqlLite.js';
21import { JanksStruct } from '../../bean/JanksStruct.js';
22import { ns2xByTimeShaft } from '../../database/ui-worker/ProcedureWorkerCommon.js';
23
24export class SpFrameTimeChart {
25  private trace: SpSystemTrace;
26
27  constructor(trace: SpSystemTrace) {
28    this.trace = trace;
29  }
30
31  async init() {
32    let frameTimeData = await queryFrameTimeData();
33    if (frameTimeData.length > 0) {
34      let frameTimeLineRow = await this.initFrameTimeLine();
35      await this.initExpectedChart(frameTimeLineRow);
36      await this.initActualChart(frameTimeLineRow);
37    }
38  }
39
40  async initFrameTimeLine() {
41    let frameTimeLineRow = TraceRow.skeleton<any>();
42    frameTimeLineRow.rowId = `frameTime`;
43    frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
44    frameTimeLineRow.rowParentId = ``;
45    frameTimeLineRow.style.width = `100%`;
46    frameTimeLineRow.style.height = `40px`;
47    frameTimeLineRow.folder = true;
48    frameTimeLineRow.name = `FrameTimeline`;
49    frameTimeLineRow.setAttribute('children', '');
50    frameTimeLineRow.supplier = () =>
51      new Promise((resolve) => {
52        resolve([]);
53      });
54    frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
55    frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
56    frameTimeLineRow.onThreadHandler = (useCache) => {
57      let context = frameTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
58      frameTimeLineRow!.canvasSave(context);
59      (renders['jank'] as JankRender).renderMainThread(
60        {
61          context: context,
62          useCache: useCache,
63          type: `expected_frame_timeline_slice`,
64        },
65        frameTimeLineRow!
66      );
67      frameTimeLineRow!.canvasRestore(context);
68    };
69    this.trace.rowsEL?.appendChild(frameTimeLineRow);
70    return frameTimeLineRow;
71  }
72
73  async initExpectedChart(frameTimeLineRow: TraceRow<JanksStruct>) {
74    let frameExpectedData = await queryExpectedFrameDate();
75    if (frameExpectedData.length > 0) {
76      let isIntersect = (a: JanksStruct, b: JanksStruct) =>
77        Math.max(a.ts! + a.dur!, b.ts! + b.dur!) - Math.min(a.ts!, b.ts!) < a.dur! + b.dur!;
78      let depths: any = [];
79      for (let i = 0; i < frameExpectedData.length; i++) {
80        let it = frameExpectedData[i];
81        if (!it.dur || it.dur < 0) {
82          continue;
83        }
84        if (depths.length == 0) {
85          it.depth = 0;
86          depths[0] = it;
87        } else {
88          let index = 0;
89          let isContinue = true;
90          while (isContinue) {
91            if (isIntersect(depths[index], it)) {
92              if (depths[index + 1] == undefined || !depths[index + 1]) {
93                it.depth = index + 1;
94                depths[index + 1] = it;
95                isContinue = false;
96              }
97            } else {
98              it.depth = index;
99              depths[index] = it;
100              isContinue = false;
101            }
102            index++;
103          }
104        }
105      }
106    }
107    let max = Math.max(...frameExpectedData.map((it) => it.depth || 0)) + 1;
108    let maxHeight = max * 20;
109    let expectedTimeLineRow = TraceRow.skeleton<any>();
110    expectedTimeLineRow.rowId = `expected frameTime`;
111    expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
112    expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
113    expectedTimeLineRow.rowParentId = `frameTime`;
114    expectedTimeLineRow.style.width = `100%`;
115    expectedTimeLineRow.style.height = `40px`;
116    expectedTimeLineRow.style.height = `${maxHeight}px`;
117    expectedTimeLineRow.name = `Expected Timeline`;
118    expectedTimeLineRow.addTemplateTypes('FrameTimeline');
119    expectedTimeLineRow.setAttribute('height', `${maxHeight}`);
120    expectedTimeLineRow.setAttribute('children', '');
121    expectedTimeLineRow.supplier = () =>
122      new Promise((resolve) => {
123        resolve(frameExpectedData);
124      });
125    expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
126    expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
127    expectedTimeLineRow.onThreadHandler = (useCache) => {
128      let context = expectedTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
129      expectedTimeLineRow!.canvasSave(context);
130      (renders['jank'] as JankRender).renderMainThread(
131        {
132          context: context,
133          useCache: useCache,
134          type: `expected_frame_timeline_slice`,
135        },
136        expectedTimeLineRow!
137      );
138      expectedTimeLineRow!.canvasRestore(context);
139    };
140    frameTimeLineRow.addChildTraceRow(expectedTimeLineRow);
141  }
142
143  async initActualChart(frameTimeLineRow: TraceRow<any>) {
144    let frameActualData = await queryActualFrameDate();
145    if (frameActualData.length > 0) {
146      let isIntersect = (a: JanksStruct, b: JanksStruct) =>
147        Math.max(a.ts! + a.dur!, b.ts! + b.dur!) - Math.min(a.ts!, b.ts!) < a.dur! + b.dur!;
148      let depthArray: any = [];
149      for (let i = 0; i < frameActualData.length; i++) {
150        let it = frameActualData[i];
151        if (!it.dur || it.dur < 0) {
152          continue;
153        }
154        if (depthArray.length == 0) {
155          it.depth = 0;
156          depthArray[0] = it;
157        } else {
158          let index = 0;
159          let isContinue = true;
160          while (isContinue) {
161            if (isIntersect(depthArray[index], it)) {
162              if (depthArray[index + 1] == undefined || !depthArray[index + 1]) {
163                it.depth = index + 1;
164                depthArray[index + 1] = it;
165                isContinue = false;
166              }
167            } else {
168              it.depth = index;
169              depthArray[index] = it;
170              isContinue = false;
171            }
172            index++;
173          }
174        }
175      }
176    }
177
178    let max = Math.max(...frameActualData.map((it) => it.depth || 0)) + 1;
179    let maxHeight = max * 20;
180    let actualTimeLineRow = TraceRow.skeleton<any>();
181    actualTimeLineRow.rowId = `actual frameTime`;
182    actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
183    actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
184    actualTimeLineRow.rowParentId = `frameTime`;
185    actualTimeLineRow.style.width = `100%`;
186    actualTimeLineRow.style.height = `${maxHeight}px`;
187    actualTimeLineRow.name = `Actual Timeline`;
188    actualTimeLineRow.addTemplateTypes('FrameTimeline');
189    actualTimeLineRow.setAttribute('height', `${maxHeight}`);
190    actualTimeLineRow.setAttribute('children', '');
191    actualTimeLineRow.dataList = frameActualData;
192    actualTimeLineRow.supplier = () =>
193      new Promise((resolve) => {
194        resolve(frameActualData);
195      });
196    actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
197    actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
198    actualTimeLineRow.onThreadHandler = (useCache) => {
199      let context = actualTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
200      actualTimeLineRow!.canvasSave(context);
201      (renders['jank'] as JankRender).renderMainThread(
202        {
203          context: context,
204          useCache: useCache,
205          type: `expected_frame_timeline_slice`,
206        },
207        actualTimeLineRow!
208      );
209      actualTimeLineRow!.canvasRestore(context);
210    };
211    frameTimeLineRow.addChildTraceRow(actualTimeLineRow);
212    let offsetYTimeOut: any = undefined;
213    frameTimeLineRow.addEventListener('expansion-change', (e: any) => {
214      JankStruct.delJankLineFlag = false;
215      if (offsetYTimeOut) {
216        clearTimeout(offsetYTimeOut);
217      }
218      if (e.detail.expansion) {
219        offsetYTimeOut = setTimeout(() => {
220          this.trace.linkNodes.forEach((linkFrameNode) => {
221            JankStruct.selectJankStructList?.forEach((dat: any) => {
222              if (e.detail.rowId == dat.pid) {
223                JankStruct.selectJankStruct = dat;
224                JankStruct.hoverJankStruct = dat;
225              }
226            });
227            if (linkFrameNode[0].rowEL.collect) {
228              linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - 195;
229            } else {
230              linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
231            }
232            linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY;
233            if (linkFrameNode[1].rowEL.collect) {
234              linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - 195;
235            } else {
236              linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
237            }
238            linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY;
239            if (linkFrameNode[0].rowEL.rowId == e.detail.rowId) {
240              linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!);
241              linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * 2;
242              linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * 2;
243              linkFrameNode[0].rowEL = actualTimeLineRow;
244            } else if (linkFrameNode[1].rowEL.rowId == e.detail.rowId) {
245              linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!);
246              linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * 2;
247              linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * 2;
248              linkFrameNode[1].rowEL = actualTimeLineRow!;
249            }
250          });
251        }, 300);
252      } else {
253        if (JankStruct!.selectJankStruct) {
254          JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
255        }
256        offsetYTimeOut = setTimeout(() => {
257          this.trace.linkNodes.forEach((linkNode) => {
258            if (linkNode[0].rowEL.collect) {
259              linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - 195;
260            } else {
261              linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
262            }
263            linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY;
264            if (linkNode[1].rowEL.collect) {
265              linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - 195;
266            } else {
267              linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
268            }
269            linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY;
270            if (linkNode[0].rowEL.rowParentId == e.detail.rowId) {
271              linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!);
272              linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / 2;
273              linkNode[0].offsetY = linkNode[0].offsetY / 2;
274              linkNode[0].rowEL = frameTimeLineRow;
275            } else if (linkNode[1].rowEL.rowParentId == e.detail.rowId) {
276              linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!);
277              linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / 2;
278              linkNode[1].offsetY = linkNode[1].offsetY / 2;
279              linkNode[1].rowEL = frameTimeLineRow!;
280            }
281          });
282        }, 300);
283      }
284      let refreshTimeOut = setTimeout(() => {
285        this.trace.refreshCanvas(true);
286        clearTimeout(refreshTimeOut);
287      }, 360);
288    });
289  }
290}
291