• 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 {
21  queryActualFrameDate,
22  queryExpectedFrameDate,
23  queryFrameAnimationData,
24  queryFrameApp,
25  queryFrameDynamicData,
26  queryFrameSpacing,
27  queryFrameTimeData,
28  queryPhysicalData
29} from '../../database/SqlLite.js';
30import { JanksStruct } from '../../bean/JanksStruct.js';
31import { ns2xByTimeShaft, PairPoint } from '../../database/ui-worker/ProcedureWorkerCommon.js';
32import { LitPopover } from '../../../base-ui/popover/LitPopoverV.js';
33import {
34  FrameDynamicRender,
35  FrameDynamicStruct
36} from '../../database/ui-worker/ProcedureWorkerFrameDynamic.js';
37import {
38  FrameAnimationRender,
39  FrameAnimationStruct
40} from '../../database/ui-worker/ProcedureWorkerFrameAnimation.js';
41import { BaseStruct } from '../../bean/BaseStruct.js';
42import {
43  FrameSpacingRender,
44  FrameSpacingStruct
45} from '../../database/ui-worker/ProcedureWorkerFrameSpacing.js';
46import { FlagsConfig, Params } from '../SpFlags.js';
47import { AnimationRanges, DeviceStruct } from '../../bean/FrameComponentBean.js';
48import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js';
49
50export class SpFrameTimeChart {
51  private trace: SpSystemTrace;
52  private flagConfig: Params | undefined;
53
54  constructor(trace: SpSystemTrace) {
55    this.trace = trace;
56  }
57
58  async init(): Promise<void> {
59    let frameTimeData = await queryFrameTimeData();
60    if (frameTimeData.length > 0) {
61      let frameTimeLineRow: TraceRow<JanksStruct> = await this.initFrameTimeLine();
62      await this.initExpectedChart(frameTimeLineRow);
63      await this.initActualChart(frameTimeLineRow);
64    }
65  }
66
67  async initFrameTimeLine(): Promise<TraceRow<JanksStruct>> {
68    let frameTimeLineRow: TraceRow<JanksStruct> = TraceRow.skeleton<JanksStruct>();
69    frameTimeLineRow.rowId = 'frameTime';
70    frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
71    frameTimeLineRow.rowParentId = '';
72    frameTimeLineRow.style.width = '100%';
73    frameTimeLineRow.style.height = '40px';
74    frameTimeLineRow.folder = true;
75    frameTimeLineRow.name = 'FrameTimeline';
76    frameTimeLineRow.setAttribute('children', '');
77    frameTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve) => {
78      resolve([]);
79    });
80    frameTimeLineRow.addTemplateTypes('AppStartup');
81    frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
82    frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
83    frameTimeLineRow.onThreadHandler = (useCache: boolean): void => {
84      let context: CanvasRenderingContext2D = frameTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! :
85        this.trace.canvasPanelCtx!;
86      frameTimeLineRow!.canvasSave(context);
87      (renders.jank as JankRender).renderMainThread({
88        context: context, useCache: useCache, type: 'expected_frame_timeline_slice'
89      }, frameTimeLineRow!);
90      frameTimeLineRow!.canvasRestore(context);
91    };
92    this.trace.rowsEL?.appendChild(frameTimeLineRow);
93    return frameTimeLineRow;
94  }
95
96  async initExpectedChart(
97    frameTimeLineRow: TraceRow<JanksStruct>
98  ): Promise<void> {
99    let frameExpectedData = await this.getExpectedFrameDate();
100    let unitIndex: number = 1;
101    let unitHeight: number = 20;
102    let max: number = Math.max(...frameExpectedData.map((it) => it.depth || 0)) + unitIndex;
103    let maxHeight: number = max * unitHeight;
104    let expectedTimeLineRow = TraceRow.skeleton<JanksStruct>();
105    expectedTimeLineRow.rowId = 'expected frameTime';
106    expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
107    expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
108    expectedTimeLineRow.rowParentId = 'frameTime';
109    expectedTimeLineRow.style.width = '100%';
110    expectedTimeLineRow.style.height = '40px';
111    expectedTimeLineRow.style.height = `${maxHeight}px`;
112    expectedTimeLineRow.name = 'Expected Timeline';
113    expectedTimeLineRow.addTemplateTypes('FrameTimeline');
114    expectedTimeLineRow.setAttribute('height', `${maxHeight}`);
115    expectedTimeLineRow.setAttribute('children', '');
116    expectedTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve): void => {
117      resolve(frameExpectedData);
118    });
119    expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
120    expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
121    expectedTimeLineRow.onThreadHandler = (useCache: boolean): void => {
122      let context: CanvasRenderingContext2D = expectedTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! :
123        this.trace.canvasPanelCtx!;
124      expectedTimeLineRow!.canvasSave(context);
125      (renders.jank as JankRender).renderMainThread({
126        context: context, useCache: useCache, type: 'expected_frame_timeline_slice'
127      }, expectedTimeLineRow!);
128      expectedTimeLineRow!.canvasRestore(context);
129    };
130    frameTimeLineRow.addChildTraceRow(expectedTimeLineRow);
131  }
132
133  async initActualChart(
134    frameTimeLineRow: TraceRow<JanksStruct>
135  ): Promise<void> {
136    let frameActualData = await this.getActualFrameDate();
137    let unitIndex: number = 1;
138    let unitHeight: number = 20;
139    let maxHeight: number = (Math.max(...frameActualData.map((it) => it.depth || 0)) + unitIndex) * unitHeight;
140    let actualTimeLineRow = TraceRow.skeleton<JanksStruct>();
141    actualTimeLineRow.rowId = 'actual frameTime';
142    actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
143    actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
144    actualTimeLineRow.rowParentId = 'frameTime';
145    actualTimeLineRow.style.width = '100%';
146    actualTimeLineRow.style.height = `${maxHeight}px`;
147    actualTimeLineRow.name = 'Actual Timeline';
148    actualTimeLineRow.addTemplateTypes('FrameTimeline');
149    actualTimeLineRow.setAttribute('height', `${maxHeight}`);
150    actualTimeLineRow.setAttribute('children', '');
151    actualTimeLineRow.dataList = frameActualData;
152    actualTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve): void => {
153      resolve(frameActualData);
154    });
155    actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
156    actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
157    actualTimeLineRow.onThreadHandler = (useCache: boolean): void => {
158      let context: CanvasRenderingContext2D = actualTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! :
159        this.trace.canvasPanelCtx!;
160      actualTimeLineRow!.canvasSave(context);
161      (renders.jank as JankRender).renderMainThread({
162        context: context, useCache: useCache, type: 'expected_frame_timeline_slice'
163      }, actualTimeLineRow!);
164      actualTimeLineRow!.canvasRestore(context);
165    };
166    frameTimeLineRow.addChildTraceRow(actualTimeLineRow);
167    let offsetYTimeOut: number = 0;
168    frameTimeLineRow.addEventListener('expansion-change', (customEventInit: CustomEventInit) => {
169      JankStruct.delJankLineFlag = false;
170      if (offsetYTimeOut) {
171        clearTimeout(offsetYTimeOut);
172      }
173      if (customEventInit.detail?.expansion) {
174        offsetYTimeOut = this.frameExpandTimeOut(customEventInit, actualTimeLineRow);
175      } else {
176        offsetYTimeOut = this.frameNoExpandTimeOut(customEventInit, frameTimeLineRow);
177      }
178    });
179  }
180
181  async initAnimatedScenesChart(
182    processRow: TraceRow<BaseStruct>,
183    process: { pid: number | null; processName: string | null; },
184    firstRow: TraceRow<BaseStruct>
185  ): Promise<void> {
186    this.flagConfig = FlagsConfig.getFlagsConfig('AnimationAnalysis');
187    if (this.flagConfig?.AnimationAnalysis === 'Enabled') {
188      if (process.processName?.startsWith('render_service')) {
189        let targetRowList = processRow.childrenList.filter(childRow =>
190          childRow.rowType === 'thread' && childRow.name.startsWith('render_service'));
191        let nameArr: { name: string }[] = await queryFrameApp();
192        if (nameArr && nameArr.length > 0) {
193          let currentName = nameArr[0].name;
194          let frameChart = await this.initFrameChart(processRow, nameArr);
195          processRow.addChildTraceRowAfter(frameChart, targetRowList[0]);
196          let animationRanges = await this.initAnimationChart(processRow, firstRow);
197          await this.initDynamicCurveChart(frameChart, currentName, animationRanges);
198          await this.initFrameSpacing(nameArr, frameChart, currentName, animationRanges);
199        }
200      }
201    }
202  }
203
204  private async initFrameChart(
205    processRow: TraceRow<BaseStruct>,
206    nameArr: { name: string }[]
207  ): Promise<TraceRow<BaseStruct>> {
208    let frameChart: TraceRow<BaseStruct> = TraceRow.skeleton<BaseStruct>();
209    let systemPopover = this.addSystemConfigButton(frameChart, nameArr);
210    systemPopover.style.zIndex = '101';
211    let radioList = systemPopover.querySelectorAll<HTMLInputElement>('.radio');
212    let divElement = systemPopover.querySelectorAll<HTMLDivElement>('.option');
213    radioList[0].checked = true;
214    divElement.forEach((divEl, index) => {
215      divEl.addEventListener('click', () => {
216        if (radioList[index]) {
217          radioList[index].checked = true;
218          frameChart.name = radioList[index].value;
219          frameChart.childrenList.forEach(childrenRow => {
220            childrenRow.setAttribute('model-name', `${radioList[index].value}`);
221          });
222          systemPopover.blur();
223          TraceRow.range!.refresh = true;
224          this.trace.refreshCanvas(true);
225          this.trace.clickEmptyArea();
226        }
227      });
228    });
229    frameChart.rowId = 'frame';
230    frameChart.rowType = TraceRow.ROW_TYPE_FRAME;
231    frameChart.rowHidden = !processRow.expansion;
232    frameChart.rowParentId = processRow.rowId;
233    frameChart.style.width = '100%';
234    frameChart.style.height = '40px';
235    frameChart.folder = true;
236    frameChart.name = nameArr[0].name;
237    frameChart.setAttribute('children', '');
238    frameChart.supplier = (): Promise<BaseStruct[]> => new Promise((resolve) => {
239      resolve([]);
240    });
241    frameChart.favoriteChangeHandler = this.trace.favoriteChangeHandler;
242    frameChart.selectChangeHandler = this.trace.selectChangeHandler;
243    frameChart.onThreadHandler = (useCache: boolean): void => {
244      let context: CanvasRenderingContext2D = frameChart!.collect ? this.trace.canvasFavoritePanelCtx! :
245        this.trace.canvasPanelCtx!;
246      frameChart!.canvasSave(context);
247      (renders.empty as EmptyRender).renderMainThread({
248        context: context, useCache: useCache, type: 'frame'
249      }, frameChart!);
250      frameChart!.canvasRestore(context);
251    };
252    this.trace.rowsEL?.appendChild(frameChart);
253    return frameChart;
254  }
255
256  async initAnimationChart(
257    processRow: TraceRow<BaseStruct>,
258    firstRow: TraceRow<BaseStruct>
259  ): Promise<AnimationRanges[]> {
260    let frameAnimationData: FrameAnimationStruct[] = await queryFrameAnimationData();
261    let animationRanges: AnimationRanges[] = [];
262    if (frameAnimationData.length > 0) {
263      frameAnimationData.forEach(data => {
264        let range = {
265          start: data.dynamicStartTs, end: data.dynamicEndTs
266        };
267        animationRanges.push(range);
268      });
269    }
270    let frameAnimationRow = TraceRow.skeleton<FrameAnimationStruct>();
271    frameAnimationRow.rowId = 'Animation';
272    frameAnimationRow.rowType = TraceRow.ROW_TYPE_FRAME_ANIMATION;
273    frameAnimationRow.rowHidden = !processRow.expansion;
274    frameAnimationRow.rowParentId = processRow.rowId;
275    frameAnimationRow.style.width = '100%';
276    frameAnimationRow.style.height = '40px';
277    frameAnimationRow.name = 'Animation';
278    frameAnimationRow.addTemplateTypes('Animation Effect');
279    frameAnimationRow.setAttribute('children', '');
280    frameAnimationRow.supplier = (): Promise<FrameAnimationStruct[]> => new Promise((resolve) => {
281      resolve(frameAnimationData);
282    });
283    frameAnimationRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
284    frameAnimationRow.selectChangeHandler = this.trace.selectChangeHandler;
285    frameAnimationRow.onThreadHandler = (useCache): void => {
286      let context: CanvasRenderingContext2D = frameAnimationRow!.collect ? this.trace.canvasFavoritePanelCtx! :
287        this.trace.canvasPanelCtx!;
288      frameAnimationRow!.canvasSave(context);
289      (renders.frameAnimation as FrameAnimationRender).renderMainThread({
290        context: context, useCache: useCache, type: 'frameAnimation'
291      }, frameAnimationRow!);
292      frameAnimationRow!.canvasRestore(context);
293    };
294    processRow.addChildTraceRowBefore(frameAnimationRow, firstRow);
295    return animationRanges;
296  }
297
298  async initDynamicCurveChart(
299    frameChart: TraceRow<BaseStruct>,
300    name: string,
301    animationRanges: AnimationRanges[]
302  ): Promise<void> {
303    let frameDynamicCurveData: FrameDynamicStruct[] = await queryFrameDynamicData();
304    let systemConfigList: {
305      name: string
306    }[] = [{name: 'x'}, {name: 'y'}, {name: 'width'}, {name: 'height'}, {name: 'alpha'}];
307    let dynamicCurveRow: TraceRow<FrameDynamicStruct> = TraceRow.skeleton<FrameDynamicStruct>();
308    let systemPopover = this.addSystemConfigButton(dynamicCurveRow, systemConfigList);
309    this.initSystemConfig(systemPopover, dynamicCurveRow);
310    dynamicCurveRow.rowId = 'animation-Effect-Curve';
311    dynamicCurveRow.rowType = TraceRow.ROW_TYPE_FRAME_DYNAMIC;
312    dynamicCurveRow.rowHidden = !frameChart.expansion;
313    dynamicCurveRow.rowParentId = frameChart.rowId;
314    dynamicCurveRow.style.width = '100%';
315    dynamicCurveRow.style.height = '40px';
316    dynamicCurveRow.style.height = '100px';
317    let labelName = dynamicCurveRow.shadowRoot?.querySelector('.name') as HTMLLabelElement;
318    labelName.style.marginRight = '77px';
319    dynamicCurveRow.name = 'Animation Effect Curve';
320    dynamicCurveRow.addTemplateTypes('Animation Effect');
321    dynamicCurveRow.setAttribute('height', '100px');
322    dynamicCurveRow.setAttribute('children', '');
323    dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name);
324    dynamicCurveRow.setAttribute('model-name', name);
325    dynamicCurveRow.supplier = (): Promise<FrameDynamicStruct[]> => new Promise((resolve): void => {
326      resolve(frameDynamicCurveData);
327    });
328    dynamicCurveRow.favoriteChangeHandler = (): void => {
329      this.favoriteSelect(systemPopover, dynamicCurveRow);
330    };
331    dynamicCurveRow.selectChangeHandler = this.trace.selectChangeHandler;
332    dynamicCurveRow.onThreadHandler = (useCache: boolean): void => {
333      let context: CanvasRenderingContext2D = dynamicCurveRow!.collect ? this.trace.canvasFavoritePanelCtx! :
334        this.trace.canvasPanelCtx!;
335      dynamicCurveRow!.canvasSave(context);
336      (renders.frameDynamicCurve as FrameDynamicRender).renderMainThread({
337        context: context,
338        useCache: useCache,
339        type: 'dynamicEffectCurve',
340        animationRanges: animationRanges
341      }, dynamicCurveRow!);
342      dynamicCurveRow!.canvasRestore(context);
343    };
344    frameChart.addChildTraceRow(dynamicCurveRow);
345  }
346
347  private initSystemConfig(
348    systemPopover: LitPopover,
349    dynamicCurveRow: TraceRow<FrameDynamicStruct>
350  ): void {
351    let radioList = systemPopover.querySelectorAll<HTMLInputElement>('.radio');
352    let divElement = systemPopover.querySelectorAll<HTMLDivElement>('.option');
353    radioList[0].checked = true;
354    dynamicCurveRow.setAttribute('model-type', radioList[0].value);
355    divElement.forEach((divEl, index) => {
356      divEl.addEventListener('click', () => {
357        if (radioList[index]) {
358          radioList[index].checked = true;
359          dynamicCurveRow.setAttribute('model-type', `${radioList[index].value}`);
360          systemPopover.blur();
361          TraceRow.range!.refresh = true;
362          this.trace.refreshCanvas(true);
363          this.trace.clickEmptyArea();
364        }
365      });
366    });
367  }
368
369  private favoriteSelect(
370    systemPopover: LitPopover,
371    dynamicCurveRow: TraceRow<FrameDynamicStruct>
372  ): void {
373    let popover = systemPopover.querySelector('.dynamicPopover') as HTMLDivElement;
374    if (dynamicCurveRow.collect) {
375      systemPopover.setAttribute('placement', 'right');
376      popover.style.display = 'flex';
377    } else {
378      systemPopover.setAttribute('placement', 'bottomLeft');
379      popover.style.display = 'block';
380    }
381  }
382
383  async initFrameSpacing(
384    nameArr: { name: string }[],
385    frameChart: TraceRow<BaseStruct>,
386    name: string,
387    animationRanges: AnimationRanges[]
388  ): Promise<void> {
389    let frameData: FrameSpacingStruct[] = await queryFrameSpacing();
390    let deviceStructArray = await queryPhysicalData();
391    let deviceStruct: DeviceStruct = deviceStructArray[0];
392    let frameResultData: FrameSpacingStruct[] = [];
393    for (let index = 0; index < nameArr.length; index++) {
394      let appName: string = nameArr[index].name;
395      let filterData = frameData.filter(spacingData => spacingData.nameId === appName);
396      this.dataProcessing(filterData, deviceStruct);
397      frameResultData.push(...filterData);
398    }
399    let frameSpacingRow = TraceRow.skeleton<FrameSpacingStruct>();
400    frameSpacingRow.rowId = 'frame spacing';
401    frameSpacingRow.rowType = TraceRow.ROW_TYPE_FRAME_SPACING;
402    frameSpacingRow.rowHidden = !frameChart.expansion;
403    frameSpacingRow.rowParentId = frameChart.rowId;
404    frameSpacingRow.style.width = '100%';
405    frameSpacingRow.style.height = '140px';
406    frameSpacingRow.name = 'Frame spacing';
407    frameSpacingRow.addTemplateTypes('Animation Effect');
408    frameSpacingRow.setAttribute('height', '140');
409    frameSpacingRow.setAttribute('children', '');
410    frameSpacingRow.setAttribute('model-name', name);
411    frameSpacingRow.supplier = (): Promise<FrameSpacingStruct[]> => new Promise((resolve): void => {
412      resolve(frameResultData);
413    });
414    frameSpacingRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
415    frameSpacingRow.selectChangeHandler = this.trace.selectChangeHandler;
416    frameSpacingRow.onThreadHandler = (useCache: boolean): void => {
417      let context = frameSpacingRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
418      frameSpacingRow!.canvasSave(context);
419      (renders.frameSpacing as FrameSpacingRender).renderMainThread({
420        context: context,
421        useCache: useCache,
422        type: 'frame_spacing_slice',
423        frameRate: deviceStruct.physicalFrameRate,
424        animationRanges: animationRanges
425      }, frameSpacingRow!);
426      frameSpacingRow!.canvasRestore(context);
427    };
428    frameChart.addChildTraceRow(frameSpacingRow);
429  }
430
431  dataProcessing(
432    frameData: FrameSpacingStruct[],
433    deviceStruct: DeviceStruct
434  ): void {
435    let unitIndex: number = 1;
436    let secondToNanosecond: number = 1000_000_000;
437    let physicalWidth = Number(this.flagConfig!.physicalWidth);
438    let physicalHeight = Number(this.flagConfig!.physicalHeight);
439    FrameSpacingStruct.physicalWidth = physicalWidth !== 0 ? physicalWidth : deviceStruct.physicalWidth;
440    FrameSpacingStruct.physicalHeight = physicalHeight !== 0 ? physicalHeight : deviceStruct.physicalHeight;
441    for (let index = 0; index < frameData.length; index++) {
442      if (index > 0) {
443        let intervalTime = (frameData[index].currentTs - frameData[index - unitIndex].currentTs) / secondToNanosecond;
444        let widthDifference = frameData[index].currentFrameWidth! - frameData[index - unitIndex].currentFrameWidth!;
445        let heightDifference = frameData[index].currentFrameHeight! - frameData[index - unitIndex].currentFrameHeight!;
446        let xDifference = frameData[index].x! - frameData[index - unitIndex].x!;
447        let yDifference = frameData[index].y! - frameData[index - unitIndex].y!;
448        let frameWidth = Math.abs(widthDifference / FrameSpacingStruct.physicalWidth / intervalTime);
449        let frameHeight = Math.abs(heightDifference / FrameSpacingStruct.physicalHeight / intervalTime);
450        let frameX = Math.abs(xDifference / FrameSpacingStruct.physicalWidth / intervalTime);
451        let frameY = Math.abs(yDifference / FrameSpacingStruct.physicalHeight / intervalTime);
452        let result = Math.max(frameWidth, frameHeight, frameX, frameY);
453        frameData[index].frameSpacingResult = Number(result.toFixed(unitIndex));
454        frameData[index].preTs = frameData[index - unitIndex].currentTs;
455        frameData[index].preFrameWidth = frameData[index - unitIndex].currentFrameWidth;
456        frameData[index].preFrameHeight = frameData[index - unitIndex].currentFrameHeight;
457      } else {
458        frameData[index].frameSpacingResult = 0;
459        frameData[index].preTs = 0;
460        frameData[index].preFrameWidth = 0;
461        frameData[index].preFrameHeight = 0;
462      }
463    }
464  }
465
466  addSystemConfigButton(
467    systemTraceRow: TraceRow<BaseStruct>,
468    systemConfigList: { name: string }[]
469  ): LitPopover {
470    let rowContent: HTMLDivElement = systemTraceRow.shadowRoot?.querySelector('.describe') as HTMLDivElement;
471    let systemPopover: LitPopover = document.createElement('lit-popover') as LitPopover;
472    systemPopover.style.zIndex = '100';
473    systemPopover.style.position = 'absolute';
474    systemPopover.style.left = '165px';
475    systemPopover.style.display = 'flex';
476    systemPopover.setAttribute('placement', 'bottomLeft');
477    systemPopover.setAttribute('trigger', 'click');
478    systemPopover.setAttribute('haveRadio', 'true');
479    systemPopover.innerHTML = `
480    <div style="display: block; overflow: auto" slot="content" class="dynamicPopover">
481      ${systemConfigList.map((it): string => `
482              <div class="option" style="margin-bottom: 5px; color: black;">
483                <input class="radio" name="status" type="radio" value='${it.name}'
484                style="margin-right: 10px;"/>${it.name}</div>`).join('')}
485    </div>
486    <lit-icon name="setting" size="19" id="setting"></lit-icon>`;
487    rowContent.appendChild(systemPopover);
488    return systemPopover;
489  }
490
491  private frameNoExpandTimeOut(
492    event: CustomEventInit<{
493      expansion: boolean,
494      rowType: string,
495      rowId: string,
496      rowParentId: string
497    }>,
498    frameTimeLineRow: TraceRow<JanksStruct>
499  ): number {
500    if (JankStruct!.selectJankStruct) {
501      JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
502    }
503    let topPadding: number = 195;
504    let halfNumber: number = 2;
505    let offsetYTime: number = 300;
506    let refreshTime: number = 360;
507    let offsetYTimeOut: number = window.setTimeout(() => {
508      this.trace.linkNodes.forEach((linkNode: PairPoint[]) => {
509        if (linkNode[0].rowEL.collect) {
510          linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - topPadding;
511        } else {
512          linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
513        }
514        linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY;
515        if (linkNode[1].rowEL.collect) {
516          linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - topPadding;
517        } else {
518          linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
519        }
520        linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY;
521        if (linkNode[0].rowEL.rowParentId === event.detail?.rowId) {
522          linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!);
523          linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / halfNumber;
524          linkNode[0].offsetY = linkNode[0].offsetY / halfNumber;
525          linkNode[0].rowEL = frameTimeLineRow;
526        } else if (linkNode[1].rowEL.rowParentId === event.detail?.rowId) {
527          linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!);
528          linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / halfNumber;
529          linkNode[1].offsetY = linkNode[1].offsetY / halfNumber;
530          linkNode[1].rowEL = frameTimeLineRow!;
531        }
532      });
533    }, offsetYTime);
534    let refreshTimeOut: number = window.setTimeout(() => {
535      this.trace.refreshCanvas(true);
536      clearTimeout(refreshTimeOut);
537    }, refreshTime);
538    return offsetYTimeOut;
539  }
540
541  private frameExpandTimeOut(
542    event: CustomEventInit<{ expansion: boolean, rowType: string, rowId: string, rowParentId: string }>,
543    actualTimeLineRow: TraceRow<JanksStruct>
544  ): number {
545    let topPadding: number = 195;
546    let halfNumber: number = 2;
547    let offsetYTime: number = 300;
548    let refreshTime: number = 360;
549    let offsetYTimeOut: number = window.setTimeout(() => {
550      this.trace.linkNodes.forEach((linkFrameNode: PairPoint[]) => {
551        JankStruct.selectJankStructList?.forEach((dat: JankStruct) => {
552          if (event.detail?.rowId === dat.pid) {
553            JankStruct.selectJankStruct = dat;
554            JankStruct.hoverJankStruct = dat;
555          }
556        });
557        if (linkFrameNode[0].rowEL.collect) {
558          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - topPadding;
559        } else {
560          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
561        }
562        linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY;
563        if (linkFrameNode[1].rowEL.collect) {
564          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - topPadding;
565        } else {
566          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
567        }
568        linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY;
569        if (linkFrameNode[0].rowEL.rowId === event.detail?.rowId) {
570          linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!);
571          linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * halfNumber;
572          linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * halfNumber;
573          linkFrameNode[0].rowEL = actualTimeLineRow;
574        } else if (linkFrameNode[1].rowEL.rowId === event.detail?.rowId) {
575          linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!);
576          linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * halfNumber;
577          linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * halfNumber;
578          linkFrameNode[1].rowEL = actualTimeLineRow!;
579        }
580      });
581    }, offsetYTime);
582    let refreshTimeOut: number = window.setTimeout(() => {
583      this.trace.refreshCanvas(true);
584      clearTimeout(refreshTimeOut);
585    }, refreshTime);
586    return offsetYTimeOut;
587  }
588
589  private async getExpectedFrameDate(): Promise<JanksStruct[]> {
590    let frameExpectedData = await queryExpectedFrameDate();
591    if (frameExpectedData.length > 0) {
592      let unitIndex: number = 1;
593      let isIntersect = (a: JanksStruct, b: JanksStruct): boolean => Math.max(a.ts! + a.dur!, b.ts! + b.dur!) -
594        Math.min(a.ts!, b.ts!) < a.dur! + b.dur!;
595      let depths = [];
596      for (let i: number = 0; i < frameExpectedData.length; i++) {
597        let it = frameExpectedData[i];
598        if (!it.dur || it.dur < 0) {
599          continue;
600        }
601        if (depths.length === 0) {
602          it.depth = 0;
603          depths[0] = it;
604        } else {
605          let index: number = 0;
606          let isContinue: boolean = true;
607          while (isContinue) {
608            if (isIntersect(depths[index], it)) {
609              if (depths[index + unitIndex] === undefined || !depths[index + unitIndex]) {
610                it.depth = index + unitIndex;
611                depths[index + unitIndex] = it;
612                isContinue = false;
613              }
614            } else {
615              it.depth = index;
616              depths[index] = it;
617              isContinue = false;
618            }
619            index++;
620          }
621        }
622      }
623    }
624    return frameExpectedData;
625  }
626
627  private async getActualFrameDate(): Promise<JanksStruct[]> {
628    let frameActualData = await queryActualFrameDate();
629    if (frameActualData.length > 0) {
630      let unitIndex: number = 1;
631      let isIntersect = (leftStruct: JanksStruct, rightStruct: JanksStruct): boolean =>
632        Math.max(leftStruct.ts! + leftStruct.dur!, rightStruct.ts! + rightStruct.dur!) -
633        Math.min(leftStruct.ts!, rightStruct.ts!) < leftStruct.dur! + rightStruct.dur!;
634      let depthArray = [];
635      for (let index: number = 0; index < frameActualData.length; index++) {
636        let it = frameActualData[index];
637        if (!it.dur || it.dur < 0) {
638          continue;
639        }
640        if (depthArray.length === 0) {
641          it.depth = 0;
642          depthArray[0] = it;
643        } else {
644          let index: number = 0;
645          let isContinue: boolean = true;
646          while (isContinue) {
647            if (isIntersect(depthArray[index], it)) {
648              if (depthArray[index + unitIndex] === undefined || !depthArray[index + unitIndex]) {
649                it.depth = index + unitIndex;
650                depthArray[index + unitIndex] = it;
651                isContinue = false;
652              }
653            } else {
654              it.depth = index;
655              depthArray[index] = it;
656              isContinue = false;
657            }
658            index++;
659          }
660        }
661      }
662    }
663    return frameActualData;
664  }
665}
666