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 { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { FlagsConfig } from '../SpFlags';
20import { CpuFreqStruct } from '../../database/ui-worker/ProcedureWorkerFreq';
21import {
22  queryFanceNameList,
23  queryFpsNameList,
24  queryRealFpsList,
25  queryRSNowTimeList,
26  querySignaledList,
27  querySkipDataList,
28} from '../../database/sql/Ltpo.sql';
29import { LtpoRender, LtpoStruct } from '../../database/ui-worker/ProcedureWorkerLTPO';
30import { HitchTimeStruct, hitchTimeRender } from '../../database/ui-worker/ProcedureWorkerHitchTime';
31import { lostFrameSender } from '../../database/data-trafic/LostFrameSender';
32
33export class SpLtpoChart {
34  private readonly trace: SpSystemTrace | undefined;
35  static APP_STARTUP_PID_ARR: Array<number> = [];
36  static jsonRow: TraceRow<CpuFreqStruct> | undefined;
37  static trace: SpSystemTrace;
38  static presentArr: Array<LtpoStruct> = [];
39  static fanceNameList: Array<LtpoStruct> = [];
40  static fpsnameList: Array<LtpoStruct> = [];
41  static realFpsList: Array<LtpoStruct> = [];
42  static rsNowTimeList: Array<LtpoStruct> = [];
43  static skipDataList: Array<LtpoStruct> = [];
44  static ltpoDataArr: Array<LtpoStruct> = [];
45  static sendLTPODataArr: Array<LtpoStruct> = [];
46  static sendHitchDataArr: Array<LtpoStruct> = [];
47  static signaledFence: Array<LtpoStruct> = [];
48  static tempRsNowTimeList: Array<LtpoStruct> = [];
49  static threadName: String = 'Present%';
50  static funName: String = 'H:Waiting for Present Fence%';
51  static signaledList: Array<LtpoStruct> = [];
52  constructor(trace: SpSystemTrace) {
53    SpLtpoChart.trace = trace;
54  }
55
56  async init() {
57    let loadLtpo: boolean = FlagsConfig.getFlagsConfigEnableStatus('LTPO');
58    if (!loadLtpo) {
59      return;
60    }
61    SpLtpoChart.ltpoDataArr = [];
62    SpLtpoChart.fanceNameList = await queryFanceNameList();
63    SpLtpoChart.fpsnameList = await queryFpsNameList();
64    SpLtpoChart.realFpsList = await queryRealFpsList();
65    SpLtpoChart.rsNowTimeList = await queryRSNowTimeList();
66    SpLtpoChart.skipDataList = await querySkipDataList();
67    SpLtpoChart.signaledList = await querySignaledList();
68    this.initFenceName();
69    this.initFpsName();
70    if (SpLtpoChart.realFpsList.length > 0) {
71      this.initRealFps();
72    }
73    this.initRsNowTime();
74    //特殊情况:当前trace的RSHardwareThrea泳道最前面多一个单独的fence
75    if (SpLtpoChart.fpsnameList.length > 0 && SpLtpoChart.fanceNameList.length - SpLtpoChart.fpsnameList.length === 1) {
76      if (Number(SpLtpoChart.fanceNameList[0].ts) < Number(SpLtpoChart.fpsnameList[0].ts)) {
77        SpLtpoChart.fanceNameList.splice(0, 1);
78      }
79    }
80    if (SpLtpoChart.fanceNameList!.length && SpLtpoChart.fpsnameList.length !== SpLtpoChart.fanceNameList.length) {
81      let fpsIndex = 0;
82      let fanceIndex = 0;
83      while (fpsIndex < SpLtpoChart.fpsnameList!.length) {
84        if (SpLtpoChart.fanceNameList[fanceIndex] && SpLtpoChart.fpsnameList[fpsIndex]) {
85          if (
86            SpLtpoChart.fanceNameList[fanceIndex].ts! > SpLtpoChart.fpsnameList[fpsIndex].ts! &&
87            SpLtpoChart.fanceNameList[fanceIndex].ts! <
88            SpLtpoChart.fpsnameList[fpsIndex].ts! + SpLtpoChart.fpsnameList[fpsIndex].dur!
89          ) {
90            fpsIndex++;
91            fanceIndex++;
92          } else if (SpLtpoChart.fanceNameList[fanceIndex].ts! < SpLtpoChart.fpsnameList[fpsIndex].ts!) {
93            SpLtpoChart.fanceNameList.splice(fanceIndex, 1);
94          } else if (
95            SpLtpoChart.fanceNameList[fanceIndex].ts! >
96            SpLtpoChart.fpsnameList[fpsIndex].ts! + SpLtpoChart.fpsnameList[fpsIndex].dur!
97          ) {
98            SpLtpoChart.fpsnameList.splice(fpsIndex, 1);
99          }
100        } else if (SpLtpoChart.fanceNameList[fanceIndex] && !SpLtpoChart.fpsnameList[fpsIndex]) {
101          SpLtpoChart.fanceNameList.splice(fanceIndex);
102        } else if (!SpLtpoChart.fanceNameList[fanceIndex] && SpLtpoChart.fpsnameList[fpsIndex]) {
103          SpLtpoChart.fpsnameList.splice(fpsIndex);
104        } else {
105          return;
106        }
107      }
108    }
109    if (SpLtpoChart.fanceNameList!.length && SpLtpoChart.fpsnameList.length === SpLtpoChart.fanceNameList.length) {
110      for (let i = 0; i < SpLtpoChart.fanceNameList.length; i++) {
111        let tmpFps = SpLtpoChart.fpsnameList[i]!.fps ? Number(SpLtpoChart.fpsnameList[i]!.fps) : 60;
112        let signaled = Number(SpLtpoChart.fanceNameList[i]!.signaled);
113        let startTime = Number(SpLtpoChart.fanceNameList[i]!.ts);
114        let durtaion = Number(SpLtpoChart.fanceNameList[i]!.dur);
115        if (SpLtpoChart.fanceNameList[i]!.signaled) {
116          this.pushLtpoData(
117            SpLtpoChart.ltpoDataArr,
118            SpLtpoChart.fanceNameList[i]!.fanceId!,
119            tmpFps,
120            signaled,
121            startTime,
122            durtaion,
123            0,
124            0
125          );
126        } else {
127          this.pushLtpoData(SpLtpoChart.ltpoDataArr, SpLtpoChart.fanceNameList[i]!.fanceId!, tmpFps, 0, 0, 0, 0, 0);
128        }
129      }
130    } else {
131      return;
132    }
133    this.fenceToFps();
134    this.fpsToRenderService();
135    this.filterNowTime();
136    if (SpLtpoChart.fanceNameList && SpLtpoChart.fanceNameList.length) {
137      await this.initFolder();
138      await this.initHitchTime();
139    }
140  }
141  //提取FenceId
142  initFenceName(): void {
143    SpLtpoChart.fanceNameList.map((item) => {
144      let cutFanceNameArr = item.name!.split(' ');
145      if (cutFanceNameArr[cutFanceNameArr.length - 1] === 'signaled') {
146        item.fanceId = Number(cutFanceNameArr[2]);
147        item.signaled = 1;
148        SpLtpoChart.signaledFence.push(item);
149      } else {
150        item.fanceId = Number(cutFanceNameArr[cutFanceNameArr.length - 1]);
151      }
152    });
153  }
154  //从数据库中查询的name中提取fps
155  initFpsName(): void {
156    SpLtpoChart.fpsnameList.map((item) => {
157      if (item.name!.indexOf('=') === -1) {
158        let cutFpsNameArr = item.name!.split(',')[0].split(':');
159        let cutFpsNameTimeArr = item.name!.split(',')[1].split(':');
160        item.fps = Number(cutFpsNameArr[cutFpsNameArr.length - 1]);
161        item.nowTime = Number(cutFpsNameTimeArr[cutFpsNameTimeArr.length - 1]);
162      } else {
163        let cutFpsNameArr = item.name!.split('=');
164        item.fps = Number(cutFpsNameArr[cutFpsNameArr.length - 1]);
165      }
166    });
167  }
168  //如果存在切帧,提取fps
169  initRealFps(): void {
170    SpLtpoChart.realFpsList.map((item) => {
171      let cutRealFpsArr = item.name!.split(' ');
172      item.fps = Number(cutRealFpsArr[cutRealFpsArr.length - 1]);
173    });
174    this.setRealFps();
175  }
176  //从render_service中获取nowTime
177  initRsNowTime(): void {
178    SpLtpoChart.rsNowTimeList.map((item) => {
179      let cutRsNameArr = item.name!.split(' ')[2].split(':');
180      item.nowTime = Number(cutRsNameArr[cutRsNameArr.length - 1]);
181    });
182  }
183  //处理fps
184  setRealFps(): void {
185    let moreIndex = 0;
186    let reallIndex = 0;
187    while (moreIndex < SpLtpoChart.fpsnameList.length) {
188      let itemMoreEndTs =
189        Number(SpLtpoChart.fpsnameList[moreIndex].ts) + Number(SpLtpoChart.fpsnameList[moreIndex].dur);
190      if (Number(SpLtpoChart.realFpsList[reallIndex].ts) < itemMoreEndTs) {
191        //此时这一帧包含了两个fps,将真实的fps赋给SpLtpoChart.fpsnameList
192        SpLtpoChart.fpsnameList[moreIndex].fps = SpLtpoChart.realFpsList[reallIndex].fps;
193        moreIndex++;
194        if (reallIndex < SpLtpoChart.realFpsList.length - 1) {
195          //判断SpLtpoChart.realFpsList有没有遍历完,没有就继续
196          reallIndex++;
197        } else {
198          //否则跳出
199          return;
200        }
201      } else {
202        //如果不满足的话,SpLtpoChart.fpsnameList数组往下走,而reallIndex不变
203        moreIndex++;
204      }
205    }
206  }
207  //RSHardwareThread泳道的fps数组集成FenceId数组中的FenceId和signaled
208  fenceToFps() {
209    if (SpLtpoChart.fanceNameList.length === SpLtpoChart.fpsnameList.length) {
210      let fenceIndex = 0;
211      let fpsIndex = 0;
212      while (fpsIndex < SpLtpoChart.fpsnameList.length) {
213        if (SpLtpoChart.fpsnameList[fpsIndex] && SpLtpoChart.fanceNameList[fenceIndex]) {
214          if (
215            SpLtpoChart.fanceNameList[fenceIndex].ts! > SpLtpoChart.fpsnameList[fpsIndex].ts! &&
216            SpLtpoChart.fanceNameList[fenceIndex].ts! <
217            SpLtpoChart.fpsnameList[fpsIndex].ts! + SpLtpoChart.fpsnameList[fpsIndex].dur!
218          ) {
219            SpLtpoChart.fpsnameList[fpsIndex].fanceId = SpLtpoChart.fanceNameList[fenceIndex].fanceId;
220            if (SpLtpoChart.fanceNameList[fenceIndex].signaled) {
221              SpLtpoChart.fpsnameList[fpsIndex].signaled = SpLtpoChart.fanceNameList[fenceIndex].signaled;
222            }
223            fenceIndex++;
224            fpsIndex++;
225          } else if (SpLtpoChart.fanceNameList[fenceIndex].ts! < SpLtpoChart.fpsnameList[fpsIndex].ts!) {
226            fenceIndex++;
227          } else if (
228            SpLtpoChart.fanceNameList[fenceIndex].ts! >
229            SpLtpoChart.fpsnameList[fpsIndex].ts! + SpLtpoChart.fpsnameList[fpsIndex].dur!
230          ) {
231            fpsIndex++;
232          }
233        } else {
234          return;
235        }
236      }
237    }
238  }
239  //render_service的nowTime 集成RSHardThread泳道Fps数组的FenceId和Signaled
240  fpsToRenderService(): void {
241    let rsIndex = 0;
242    let hardIndex = 0;
243    while (rsIndex < SpLtpoChart.rsNowTimeList.length) {
244      if (SpLtpoChart.fpsnameList[hardIndex] && SpLtpoChart.rsNowTimeList[rsIndex]) {
245        if (SpLtpoChart.rsNowTimeList[rsIndex].nowTime! > SpLtpoChart.fpsnameList[hardIndex].nowTime!) {
246          //处理nowTime不一致的情况
247          hardIndex++;
248        } else if (SpLtpoChart.rsNowTimeList[rsIndex].nowTime! < SpLtpoChart.fpsnameList[hardIndex].nowTime!) {
249          rsIndex++;
250        } else {
251          SpLtpoChart.rsNowTimeList[rsIndex].fanceId = SpLtpoChart.fpsnameList[hardIndex].fanceId;
252          SpLtpoChart.rsNowTimeList[rsIndex].fps = SpLtpoChart.fpsnameList[hardIndex].fps;
253          if (SpLtpoChart.fpsnameList[hardIndex].signaled) {
254            SpLtpoChart.rsNowTimeList[rsIndex].signaled = SpLtpoChart.fpsnameList[hardIndex].signaled;
255          }
256          hardIndex++;
257          rsIndex++;
258        }
259      } else {
260        return;
261      }
262    }
263  }
264  //render_service中找出skip和signaled,将需要从上平时间减去的时间计算出来存在对应的nowTime的item中
265  filterNowTime(): void {
266    let skipIndex = 0;
267    let nowTimeIndex = 0;
268    let cutTimeSum = 0;
269    let tempFps = 0; //如果中间出现signaled,记录一下fps;
270    //将render_service中的nowTime数组中的skip去除掉
271    SpLtpoChart.tempRsNowTimeList = SpLtpoChart.rsNowTimeList.filter((item) => item.fps);
272    while (skipIndex < SpLtpoChart.skipDataList.length) {
273      if (SpLtpoChart.skipDataList[skipIndex] && SpLtpoChart.tempRsNowTimeList[nowTimeIndex]) {
274        if (SpLtpoChart.skipDataList[skipIndex].ts! > SpLtpoChart.tempRsNowTimeList[nowTimeIndex].ts!) {
275          if (cutTimeSum > 0 && nowTimeIndex > 0) {
276            SpLtpoChart.tempRsNowTimeList[nowTimeIndex - 1].cutTime = cutTimeSum;
277            if (!SpLtpoChart.tempRsNowTimeList[nowTimeIndex].signaled) {
278              cutTimeSum = 0;
279            }
280          }
281          if (SpLtpoChart.tempRsNowTimeList[nowTimeIndex].signaled && nowTimeIndex > 0) {
282            //两帧之间初心signaled
283            tempFps = SpLtpoChart.tempRsNowTimeList[nowTimeIndex].fps!;
284            cutTimeSum += 1000 / tempFps;
285            SpLtpoChart.tempRsNowTimeList[nowTimeIndex - 1].cutTime = cutTimeSum;
286            SpLtpoChart.tempRsNowTimeList.splice(nowTimeIndex, 1);
287          } else {
288            nowTimeIndex++;
289            cutTimeSum = 0;
290            tempFps = 0;
291          }
292        } else if (SpLtpoChart.skipDataList[skipIndex].ts! <= SpLtpoChart.tempRsNowTimeList[nowTimeIndex].ts!) {
293          if (nowTimeIndex > 0) {
294            cutTimeSum += tempFps ? 1000 / tempFps : 1000 / SpLtpoChart.tempRsNowTimeList[nowTimeIndex - 1].fps!;
295          }
296          skipIndex++;
297        }
298      } else {
299        return;
300      }
301    }
302  }
303  pushLtpoData(
304    lptoArr: unknown[] | undefined,
305    fanceId: Number,
306    fps: Number,
307    signaled: Number,
308    startTs: Number,
309    dur: Number,
310    nextStartTs: Number,
311    nextDur: number
312  ): void {
313    lptoArr?.push({
314      fanceId: fanceId,
315      fps: fps,
316      signaled: signaled,
317      startTs: startTs,
318      dur: dur,
319      nextStartTs: nextStartTs,
320      nextDur: nextDur,
321    });
322  }
323  sendDataHandle(presentArr: LtpoStruct[], ltpoDataArr: LtpoStruct[]): Array<LtpoStruct> {
324    let sendDataArr: LtpoStruct[] = [];
325    let ltpoDataIndex = 0;
326    let tempRsNowTimeIndex = 0;
327    let presentIndex = 0;
328    let ltpoIndex = 0;
329    //当有present缺失时:
330    this.deleteUselessFence(presentArr, ltpoDataArr);
331    while (presentIndex < presentArr.length) {
332      if (presentArr[presentIndex] && ltpoDataArr[ltpoIndex]) {
333        if (
334          // @ts-ignore
335          presentArr[presentIndex].startTime! + presentArr[presentIndex].dur! - (window as unknown).recordStartNS ===
336          TraceRow.range!.totalNS
337        ) {
338          presentArr.splice(presentIndex, 1);
339        }
340        if (presentArr[presentIndex].presentId === ltpoDataArr[ltpoIndex].fanceId) {
341          // @ts-ignore
342          ltpoDataArr[ltpoIndex].startTs = Number(presentArr[presentIndex].startTime) - (window as unknown).recordStartNS;
343          ltpoDataArr[ltpoIndex].dur = presentArr[presentIndex].dur;
344          ltpoDataArr[ltpoIndex].nextStartTs = presentArr[presentIndex + 1]
345            // @ts-ignore
346            ? Number(presentArr[presentIndex + 1].startTime) - (window as unknown).recordStartNS
347            : '';
348          ltpoDataArr[ltpoIndex].nextDur = presentArr[presentIndex + 1] ? presentArr[presentIndex + 1].dur : 0;
349          presentIndex++;
350          ltpoIndex++;
351        } else if (presentArr[presentIndex].presentId! < ltpoDataArr[ltpoIndex].fanceId!) {
352          presentArr.splice(presentIndex, 1);
353        } else if (presentArr[presentIndex].presentId! > ltpoDataArr[ltpoIndex].fanceId!) {
354          ltpoDataArr.splice(ltpoIndex, 1);
355        }
356      } else {
357        break;
358      }
359    }
360    while (ltpoDataIndex < ltpoDataArr.length) {
361      let sendStartTs: number | undefined = 0;
362      let sendDur: number | undefined = 0;
363      let cutSendDur: number | undefined = 0;
364      if (ltpoDataArr[ltpoDataIndex] && SpLtpoChart.tempRsNowTimeList[tempRsNowTimeIndex]) {
365        if (ltpoDataArr[ltpoDataIndex].fanceId! < SpLtpoChart.tempRsNowTimeList[tempRsNowTimeIndex].fanceId!) {
366          if (ltpoDataArr[ltpoDataIndex].fanceId !== -1 && ltpoDataArr[ltpoDataIndex].nextDur) {
367            sendStartTs = Number(ltpoDataArr[ltpoDataIndex].startTs) + Number(ltpoDataArr[ltpoDataIndex].dur);
368            sendDur =
369              Number(ltpoDataArr[ltpoDataIndex].nextStartTs) + Number(ltpoDataArr[ltpoDataIndex].nextDur) - sendStartTs;
370          }
371          let tmpDur = cutSendDur ? Math.ceil(cutSendDur / 100000) / 10 : Math.ceil(sendDur / 100000) / 10;
372          if (tmpDur < 170) {
373            sendDataArr.push({
374              dur: sendDur,
375              cutSendDur: cutSendDur,
376              value: 0,
377              startTs: sendStartTs,
378              pid: ltpoDataArr[ltpoDataIndex].fanceId,
379              itid: ltpoDataArr[ltpoDataIndex].fanceId,
380              name: undefined,
381              presentId: ltpoDataArr[ltpoDataIndex].fanceId,
382              ts: undefined,
383              fanceId: ltpoDataArr[ltpoDataIndex].fanceId,
384              fps: ltpoDataArr[ltpoDataIndex].fps,
385              nextStartTs: ltpoDataArr[ltpoDataIndex].nextStartTs,
386              nextDur: ltpoDataArr[ltpoDataIndex].nextDur,
387              translateY: undefined,
388              isHover: false,
389              startTime: undefined,
390              signaled: undefined,
391              nowTime: undefined,
392              cutTime: undefined,
393              frame: undefined,
394            });
395          }
396          ltpoDataIndex++;
397        } else if (ltpoDataArr[ltpoDataIndex].fanceId! > SpLtpoChart.tempRsNowTimeList[tempRsNowTimeIndex].fanceId!) {
398          tempRsNowTimeIndex++;
399        } else {
400          if (SpLtpoChart.tempRsNowTimeList[tempRsNowTimeIndex].cutTime) {
401            cutSendDur = sendDur - SpLtpoChart.tempRsNowTimeList[tempRsNowTimeIndex].cutTime! * 1000000;
402            cutSendDur = cutSendDur < 0 ? 0 : cutSendDur;
403          }
404          if (ltpoDataArr[ltpoDataIndex].fanceId !== -1 && ltpoDataArr[ltpoDataIndex].nextDur) {
405            sendStartTs = Number(ltpoDataArr[ltpoDataIndex].startTs) + Number(ltpoDataArr[ltpoDataIndex].dur);
406            sendDur =
407              Number(ltpoDataArr[ltpoDataIndex].nextStartTs) + Number(ltpoDataArr[ltpoDataIndex].nextDur) - sendStartTs;
408          }
409          let tmpDur = cutSendDur ? Math.ceil(cutSendDur / 100000) / 10 : Math.ceil(sendDur / 100000) / 10;
410          if (tmpDur < 170) {
411            sendDataArr.push({
412              dur: sendDur,
413              cutSendDur: cutSendDur,
414              value: 0,
415              startTs: sendStartTs,
416              pid: ltpoDataArr[ltpoDataIndex].fanceId,
417              itid: ltpoDataArr[ltpoDataIndex].fanceId,
418              name: undefined,
419              presentId: ltpoDataArr[ltpoDataIndex].fanceId,
420              ts: undefined,
421              fanceId: ltpoDataArr[ltpoDataIndex].fanceId,
422              fps: ltpoDataArr[ltpoDataIndex].fps,
423              nextStartTs: ltpoDataArr[ltpoDataIndex].nextStartTs,
424              nextDur: ltpoDataArr[ltpoDataIndex].nextDur,
425              translateY: undefined,
426              isHover: false,
427              startTime: undefined,
428              signaled: undefined,
429              nowTime: undefined,
430              cutTime: undefined,
431              frame: undefined,
432            });
433          }
434          ltpoDataIndex++;
435          tempRsNowTimeIndex++;
436        }
437      } else {
438        break;
439      }
440    }
441    return sendDataArr;
442  }
443  deleteUselessFence(presentArr: LtpoStruct[], ltpoDataArr: LtpoStruct[]) {
444    //当有present缺失时:
445    let presentIndex = 0;
446    let fpsIndex = 0;
447    while (fpsIndex < ltpoDataArr.length) {
448      //遍历present,把ltpoDataArr中不包含present中presentFance的item舍弃掉
449      if (presentArr[presentIndex] && ltpoDataArr[fpsIndex]) {
450        if (Number(presentArr[presentIndex].presentId) < Number(ltpoDataArr[fpsIndex].fanceId)) {
451          presentArr.splice(presentIndex, 1);
452        } else if (Number(presentArr[presentIndex].presentId) > Number(ltpoDataArr[fpsIndex].fanceId)) {
453          ltpoDataArr.splice(fpsIndex, 1);
454        } else {
455          if (presentIndex === presentArr.length - 1 && fpsIndex < ltpoDataArr.length - 1) {
456            //此时present已经遍历到最后一项,如果ltpoDataArr还没有遍历到最后一项,就把后面的舍弃掉
457            ltpoDataArr.splice(fpsIndex);
458          }
459          presentIndex++;
460          fpsIndex++;
461        }
462      } else {
463        return;
464      }
465    }
466  }
467  //六舍七入
468  specialValue(num: number) {
469    if (num < 0.7) {
470      return 0;
471    } else {
472      if (!num.toString().split('.')[1]) {
473        return num;
474      } else {
475        let tempNum = Number(num.toString().split('.')[1].charAt(0));
476        if (tempNum > 6) {
477          return Math.ceil(num);
478        } else {
479          return Math.floor(num);
480        }
481      }
482    }
483  }
484
485  async initFolder() {
486    SpLtpoChart.presentArr = [];
487    let row: TraceRow<LtpoStruct> = TraceRow.skeleton<LtpoStruct>();
488    row.rowId = SpLtpoChart.fanceNameList!.length ? `LTPO ${SpLtpoChart.fanceNameList[0].fanceId}` : '';
489    row.rowParentId = '';
490    row.rowType = TraceRow.ROW_TYPE_LTPO;
491    row.folder = false;
492    row.style.height = '40px';
493    row.name = `Lost Frames`;
494    row.favoriteChangeHandler = SpLtpoChart.trace.favoriteChangeHandler;
495    row.selectChangeHandler = SpLtpoChart.trace.selectChangeHandler;
496    row.supplierFrame = () => {
497      return lostFrameSender(SpLtpoChart.threadName, SpLtpoChart.funName, row).then((res) => {
498        SpLtpoChart.presentArr = res;
499        SpLtpoChart.sendLTPODataArr = this.sendDataHandle(SpLtpoChart.presentArr, SpLtpoChart.ltpoDataArr);
500        for (let i = 0; i < SpLtpoChart.sendLTPODataArr.length; i++) {
501          let tmpDur = SpLtpoChart.sendLTPODataArr[i].cutSendDur
502            ? SpLtpoChart.sendLTPODataArr[i].cutSendDur! / 1000000
503            : SpLtpoChart.sendLTPODataArr[i].dur! / 1000000;
504          let mathValue = (tmpDur * Number(SpLtpoChart.sendLTPODataArr[i].fps)) / 1000 - 1;
505          SpLtpoChart.sendLTPODataArr[i].value = this.specialValue(mathValue);
506        }
507        return SpLtpoChart.sendLTPODataArr;
508      });
509    };
510    row.focusHandler = () => {
511      SpLtpoChart.trace?.displayTip(
512        row!,
513        LtpoStruct.hoverLtpoStruct,
514        `<span>${LtpoStruct.hoverLtpoStruct?.value!}</span>`
515      );
516    };
517    row.findHoverStruct = (): void => {
518      LtpoStruct.hoverLtpoStruct = row.getHoverStruct(true, false, 'value');
519    };
520    row.onThreadHandler = (useCache): void => {
521      let context: CanvasRenderingContext2D;
522      if (row.currentContext) {
523        context = row.currentContext;
524      } else {
525        context = row.collect ? SpLtpoChart.trace.canvasFavoritePanelCtx! : SpLtpoChart.trace.canvasPanelCtx!;
526      }
527      row.canvasSave(context);
528      (renders['ltpo-present'] as LtpoRender).renderMainThread(
529        {
530          ltpoContext: context,
531          useCache: useCache,
532          type: `ltpo-present ${row.rowId}`,
533        },
534        row
535      );
536      row.canvasRestore(context);
537    };
538    SpLtpoChart.trace.rowsEL?.appendChild(row);
539  }
540  async initHitchTime() {
541    SpLtpoChart.presentArr = [];
542    let row: TraceRow<HitchTimeStruct> = TraceRow.skeleton<HitchTimeStruct>();
543    this.takeStaticArg(row);
544    row.supplierFrame = () => {
545      return lostFrameSender(SpLtpoChart.threadName, SpLtpoChart.funName, row).then((res) => {
546        SpLtpoChart.presentArr = res;
547        SpLtpoChart.sendHitchDataArr = this.sendDataHandle(SpLtpoChart.presentArr, SpLtpoChart.ltpoDataArr);
548        for (let i = 0; i < SpLtpoChart.sendHitchDataArr.length; i++) {
549          let tmpVale = 0;
550          let tmpDur = 0;
551          if (SpLtpoChart.sendHitchDataArr[i].cutSendDur) {
552            tmpVale =
553              SpLtpoChart.sendHitchDataArr[i].cutSendDur! / 1000000 - 1000 / SpLtpoChart.sendHitchDataArr[i].fps!;
554            tmpDur = SpLtpoChart.sendHitchDataArr[i].cutSendDur! / 1000000;
555          } else {
556            tmpVale = SpLtpoChart.sendHitchDataArr[i].dur! / 1000000 - 1000 / SpLtpoChart.sendHitchDataArr[i].fps!;
557            tmpDur = SpLtpoChart.sendHitchDataArr[i].dur! / 1000000;
558          }
559
560          let mathValue = (tmpDur * Number(SpLtpoChart.sendHitchDataArr[i].fps)) / 1000 - 1;
561          let finalValue = (tmpVale! / (1000 / SpLtpoChart.sendHitchDataArr[i].fps!)) < 0.7 ? 0 : tmpVale;
562          SpLtpoChart.sendHitchDataArr[i].value = Number(finalValue.toFixed(1));
563          SpLtpoChart.sendHitchDataArr[i].name = this.specialValue(mathValue)!.toString();
564        }
565        return SpLtpoChart.sendHitchDataArr;
566      });
567    };
568    row.focusHandler = () => {
569      SpLtpoChart.trace?.displayTip(
570        row!,
571        HitchTimeStruct.hoverHitchTimeStruct,
572        `<span>${HitchTimeStruct.hoverHitchTimeStruct?.value!}</span>`
573      );
574    };
575    row.findHoverStruct = (): void => {
576      HitchTimeStruct.hoverHitchTimeStruct = row.getHoverStruct(true, false, 'value');
577    };
578    row.onThreadHandler = (useCache): void => {
579      let context: CanvasRenderingContext2D;
580      if (row.currentContext) {
581        context = row.currentContext;
582      } else {
583        context = row.collect ? SpLtpoChart.trace.canvasFavoritePanelCtx! : SpLtpoChart.trace.canvasPanelCtx!;
584      }
585      row.canvasSave(context);
586      (renders['hitch'] as hitchTimeRender).renderMainThread(
587        {
588          hitchTimeContext: context,
589          useCache: useCache,
590          type: `hitch ${row.rowId}`,
591        },
592        row
593      );
594      row.canvasRestore(context);
595    };
596    SpLtpoChart.trace.rowsEL?.appendChild(row);
597  }
598  takeStaticArg(row: TraceRow<HitchTimeStruct>) {
599    row.rowId = SpLtpoChart.fanceNameList!.length ? `hitch-time ${SpLtpoChart.fanceNameList[0].fanceId}` : '';
600    row.rowParentId = '';
601    row.rowType = TraceRow.ROW_TYPE_HITCH_TIME;
602    row.folder = false;
603    row.style.height = '40px';
604    row.name = `Hitch Time`;
605    row.favoriteChangeHandler = SpLtpoChart.trace.favoriteChangeHandler;
606    row.selectChangeHandler = SpLtpoChart.trace.selectChangeHandler;
607  }
608}
609