• 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 { RangeSelectStruct, TraceRow } from './TraceRow';
17import { Rect } from '../timer-shaft/Rect';
18import { TimerShaftElement } from '../TimerShaftElement';
19import { info } from '../../../../log/Log';
20import './Extension';
21import { SpSystemTrace } from '../../SpSystemTrace';
22import { fuzzyQueryFuncRowData, queryFuncRowData } from '../../../database/sql/Func.sql';
23import { SpLtpoChart } from '../../chart/SpLTPO';
24import { isEmpty, isNotEmpty } from './Extension';
25
26export class RangeSelect {
27  private rowsEL: HTMLDivElement | undefined | null;
28  private rowsPaneEL: HTMLDivElement | undefined | null;
29  isMouseDown: boolean = false; // @ts-ignore
30  public rangeTraceRow: Array<TraceRow<unknown>> | undefined; // @ts-ignore
31  public selectHandler: ((ds: Array<TraceRow<unknown>>, refreshCheckBox: boolean) => void) | undefined;
32  private startPageX: number = 0;
33  private startPageY: number = 0;
34  private endPageX: number = 0;
35  private endPageY: number = 0;
36  private timerShaftEL: TimerShaftElement | null | undefined;
37  private isHover: boolean = false;
38  private movingMark: string = '';
39  private mark: { startMark: number; endMark: number } = {
40    startMark: 0,
41    endMark: 0,
42  };
43  private trace: SpSystemTrace | null | undefined;
44  drag = false;
45  docomList: Array<number> = [];
46  repaintList: Array<number> = [];
47  presentList: Array<number> = [];
48
49  constructor(trace: SpSystemTrace | null | undefined) {
50    this.trace = trace;
51    this.timerShaftEL = trace?.timerShaftEL;
52    this.rowsEL = trace?.rowsEL;
53    this.rowsPaneEL = trace?.rowsPaneEL;
54  }
55
56  isInRowsEl(ev: MouseEvent): boolean {
57    return this.rowsPaneEL!.containPoint(ev, { left: 248 });
58  }
59
60  isInSpacerEL(ev: MouseEvent): boolean {
61    return this.trace!.favoriteChartListEL!.containPoint(ev, { left: 248 });
62  }
63
64  mouseDown(eventDown: MouseEvent): void {
65    this.startPageX = eventDown.pageX;
66    this.startPageY = eventDown.pageY;
67    if (TraceRow.rangeSelectObject) {
68      this.handleTouchMark(eventDown);
69    } else {
70      this.isHover = false;
71    }
72    if (this.isHover) {
73      this.isMouseDown = true;
74      return;
75    }
76    this.rangeTraceRow = [];
77    this.isMouseDown = true;
78    TraceRow.rangeSelectObject = undefined;
79    // 遍历当前可视区域所有的泳道,如果有render_service进程,查询该进程下对应泳道的方法存起来,以便框选时直接使用
80    this.trace?.visibleRows.forEach((row) => {
81      if (row.getAttribute('name')?.startsWith('render_service')) {
82        if (row.getAttribute('row-type') === 'process') {
83          this.queryRowsData(row.childrenList);
84        } else {
85          this.queryRowsData(row.parentRowEl!.childrenList);
86        }
87        return;
88      }
89    });
90  }
91
92  // 对应查询方法行所有的数据
93  // @ts-ignore
94  queryRowsData(rowList: Array<TraceRow<unknown>>): void {
95    rowList.forEach((row): void => {
96      if (row.getAttribute('row-type') === 'func') {
97        if (row.getAttribute('name')?.startsWith('render_service')) {
98          this.saveFrameRateData(row, 'H:RSMainThread::DoComposition');
99        } else if (row.getAttribute('name')?.startsWith('RSHardwareThrea')) {
100          this.saveFrameRateData(row, 'H:Repaint');
101        } else if (row.getAttribute('name')?.startsWith('Present')) {
102          this.savePresentData(row, 'H:Waiting for Present Fence');
103        }
104      }
105    });
106  }
107
108  // 查到所有的数据存储起来
109  // @ts-ignore
110  saveFrameRateData(row: TraceRow<unknown>, funcName: string): void {
111    let dataList: unknown = [];
112    queryFuncRowData(funcName, Number(row?.getAttribute('row-id'))).then((res): void => {
113      if (res.length) {
114        res.forEach((item): void => {
115          // @ts-ignore
116          dataList?.push({ startTime: item.startTime!, tid: item.tid });
117        });
118        if (funcName === 'H:RSMainThread::DoComposition') {
119          // @ts-ignore
120          this.docomList = dataList;
121        } else {
122          // @ts-ignore
123          this.repaintList = dataList;
124        }
125      }
126    });
127  }
128  // 查到present泳道所有的数据存储起来
129  // @ts-ignore
130  savePresentData(row: TraceRow<unknown>, funcName: string): void {
131    let dataList: unknown = [];
132    fuzzyQueryFuncRowData(funcName, Number(row?.getAttribute('row-id'))).then((res): void => {
133      if (res.length) {
134        res.forEach((item): void => {
135          // @ts-ignore
136          dataList?.push({ endTime: item.endTime!, tid: item.tid });
137        }); // @ts-ignore
138        this.presentList = dataList;
139      }
140    });
141  }
142
143  mouseUp(mouseEventUp?: MouseEvent): void {
144    if (mouseEventUp) {
145      this.endPageX = mouseEventUp.pageX;
146      this.endPageY = mouseEventUp.pageY;
147    }
148    if (this.drag) {
149      if (this.selectHandler) {
150        this.selectHandler(this.rangeTraceRow || [], !this.isHover);
151      }
152      //查询render_service数据
153      if (this.rangeTraceRow?.length) {
154        this.checkRowsName(this.rangeTraceRow);
155      }
156    }
157    this.isMouseDown = false;
158    this.isHover = false;
159  }
160  // @ts-ignore
161  checkRowsName(rowList: Array<TraceRow<unknown>>): void {
162    rowList.forEach((row): void => {
163      if (
164        row.getAttribute('row-type') === 'func' &&
165        row.parentRowEl?.getAttribute('name')?.startsWith('render_service')
166      ) {
167        row.frameRateList = [];
168        if (row.getAttribute('name')?.startsWith('render_service')) {
169          this.filterRateData(row, this.docomList);
170        } else if (row.getAttribute('name')?.startsWith('RSHardwareThrea')) {
171          this.filterRateData(row, this.repaintList);
172        } else if (row.getAttribute('name')?.startsWith('Present')) {
173          this.filterPresentData(row, this.presentList);
174        }
175      }
176    });
177  }
178
179  // 过滤处理数据
180  // @ts-ignore
181  filterRateData(row: TraceRow<unknown>, data: unknown): void {
182    // @ts-ignore
183    data.forEach((it: unknown): void => {
184      if (
185        // @ts-ignore
186        it.startTime >= TraceRow.rangeSelectObject!.startNS! && // @ts-ignore
187        it.startTime <= TraceRow.rangeSelectObject!.endNS! && // @ts-ignore
188        Number(row.rowId) === Number(it.tid)
189      ) {
190        // @ts-ignore
191        row.frameRateList?.push(it.startTime);
192      }
193    });
194    if (row.frameRateList?.length) {
195      if (row.frameRateList.length < 2) {
196        row.frameRateList = [];
197      } else {
198        const CONVERT_SECONDS = 1000000000;
199        let cutres: number = row.frameRateList[row.frameRateList.length - 1] - row.frameRateList[0];
200        row.avgRateTxt = `${(((row.frameRateList.length - 1) / cutres) * CONVERT_SECONDS).toFixed(1)}fps`;
201      }
202    }
203  }
204
205  // 过滤并处理present数据
206  // @ts-ignore
207  filterPresentData(row: TraceRow<unknown>, data: unknown): void {
208    // @ts-ignore
209    data.forEach((it: unknown): void => {
210      if (
211        // @ts-ignore
212        it.endTime >= TraceRow.rangeSelectObject!.startNS! && // @ts-ignore
213        it.endTime <= TraceRow.rangeSelectObject!.endNS! && // @ts-ignore
214        Number(row.rowId) === Number(it.tid)
215      ) {
216        // @ts-ignore
217        row.frameRateList?.push(it.endTime);
218      }
219    });
220    if (row.frameRateList?.length) {
221      if (row.frameRateList?.length < 2) {
222        row.frameRateList = [];
223      } else {
224        let hitchTimeList: Array<number> = [];
225        for (let i = 0; i < SpLtpoChart.sendHitchDataArr.length; i++) {
226          if (
227            SpLtpoChart.sendHitchDataArr[i].startTs! >= row.frameRateList[0]! &&
228            SpLtpoChart.sendHitchDataArr[i].startTs! < row.frameRateList[row.frameRateList.length - 1]!
229          ) {
230            hitchTimeList.push(SpLtpoChart.sendHitchDataArr[i].value!);
231          } else if (SpLtpoChart.sendHitchDataArr[i].startTs! >= row.frameRateList[row.frameRateList.length - 1]!) {
232            break;
233          }
234        }
235        const CONVERT_SECONDS = 1000000000;
236        let cutres: number = row.frameRateList[row.frameRateList.length - 1] - row.frameRateList[0];
237        let avgRate: string = `${(((row.frameRateList.length - 1) / cutres) * CONVERT_SECONDS).toFixed(1)}fps`;
238        let sum: number = hitchTimeList.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // ∑hitchTimeData
239        let hitchRate: number =
240          sum / ((TraceRow.rangeSelectObject!.endNS! - TraceRow.rangeSelectObject!.startNS!) / 1000000000);
241        row.avgRateTxt =
242          `${avgRate} ` + ',' + ' ' + 'HitchTime:' + ` ${sum.toFixed(1)}ms` + ' ' + ',' + ` ${hitchRate.toFixed(2)}ms/s`;
243      }
244    }
245  }
246
247  isDrag(): boolean {
248    return this.startPageX !== this.endPageX;
249  }
250
251  isTouchMark(ev: MouseEvent): boolean {
252    let notTimeHeight: boolean = this.rowsPaneEL!.containPoint(ev, {
253      left: 248,
254      top: -45,
255    });
256    if (!notTimeHeight) {
257      return false;
258    }
259    if ((isEmpty(this.rangeTraceRow) ?? false) && !this.isMouseDown) {
260      this.isHover = false;
261    }
262    return notTimeHeight && (isNotEmpty(this.rangeTraceRow) ?? false) && !this.isMouseDown;
263  }
264
265  mouseOut(mouseEventOut: MouseEvent): void {
266    this.endPageX = mouseEventOut.pageX;
267    this.endPageY = mouseEventOut.pageY;
268    if (this.drag) {
269      if (this.selectHandler && this.isMouseDown) {
270        this.selectHandler(this.rangeTraceRow || [], !this.isHover);
271      }
272    }
273    document.getSelection()?.removeAllRanges();
274    this.isMouseDown = false;
275    this.isHover = false;
276  }
277  // @ts-ignore
278  mouseMove(rows: Array<TraceRow<unknown>>, ev: MouseEvent): void {
279    this.endPageX = ev.pageX;
280    this.endPageY = ev.pageY;
281    if (this.isTouchMark(ev) && TraceRow.rangeSelectObject) {
282      this.handleTouchMark(ev);
283    } else {
284      document.body.style.cursor = 'default';
285    }
286    if (this.isHover && this.isMouseDown) {
287      this.handleRangeSelectAndDraw(rows, ev);
288      return;
289    }
290    if (!this.isMouseDown) {
291      this.isHover = false;
292      this.handleDrawForNotMouseDown();
293      return;
294    }
295    this.handleRangeSelect(rows);
296    this.timerShaftEL!.sportRuler!.isRangeSelect = this.rangeTraceRow!.length > 0;
297    this.timerShaftEL!.sportRuler!.draw();
298  }
299  // @ts-ignore
300  private handleRangeSelect(rows: Array<TraceRow<unknown>>): void {
301    let rangeSelect: RangeSelectStruct | undefined;
302    let favoriteRect = this.trace?.favoriteChartListEL?.getBoundingClientRect();
303    let favoriteLimit = favoriteRect!.top + favoriteRect!.height;
304    this.rangeTraceRow = rows.filter((it): boolean => {
305      let domRect = it.getBoundingClientRect();
306      let itRect = { x: domRect.x, y: domRect.y, width: domRect.width, height: domRect.height };
307      if (itRect.y < favoriteLimit && !it.collect) {
308        let offset = favoriteLimit - itRect.y;
309        itRect.y = itRect.y + offset;
310        itRect.height = itRect.height - offset;
311      }
312      if (it.sticky) {
313        itRect.y = 0;
314        itRect.height = 0;
315      }
316      let result: boolean;
317      if (
318        Rect.intersect(
319          itRect as Rect,
320          {
321            x: Math.min(this.startPageX, this.endPageX),
322            y: Math.min(this.startPageY, this.endPageY),
323            width: Math.abs(this.startPageX - this.endPageX),
324            height: Math.abs(this.startPageY - this.endPageY),
325          } as Rect
326        )
327      ) {
328        if (!rangeSelect) {
329          it.setTipLeft(0, null);
330          rangeSelect = new RangeSelectStruct();
331          let startX = Math.min(this.startPageX, this.endPageX) - it.describeEl!.getBoundingClientRect().right;
332          let endX = Math.max(this.startPageX, this.endPageX) - it.describeEl!.getBoundingClientRect().right;
333          if (startX <= 0) {
334            startX = 0;
335          }
336          if (endX > it.frame.width) {
337            endX = it.frame.width;
338          }
339          rangeSelect.startX = startX;
340          rangeSelect.endX = endX;
341          rangeSelect.startNS = RangeSelect.SetNS(it, startX);
342          rangeSelect.endNS = RangeSelect.SetNS(it, endX);
343        }
344        TraceRow.rangeSelectObject = rangeSelect;
345        it.rangeSelect = true;
346        result = true;
347      } else {
348        it.rangeSelect = false;
349        result = false;
350      }
351      return result;
352    });
353    if (this.rangeTraceRow && this.rangeTraceRow.length) {
354      if (this.rangeTraceRow[0].parentRowEl) {
355        for (let i = 0; i < this.rangeTraceRow[0].parentRowEl.childrenList.length; i++) {
356          this.rangeTraceRow[0].parentRowEl.childrenList[i].frameRateList = [];
357          this.rangeTraceRow[0].parentRowEl.childrenList[i].avgRateTxt = null;
358        }
359      }
360    }
361  }
362
363  private handleDrawForNotMouseDown(): void {
364    this.timerShaftEL!.sportRuler!.isRangeSelect = isNotEmpty(this.rangeTraceRow) ?? false;
365    this.timerShaftEL!.sportRuler!.draw();
366  }
367  // @ts-ignore
368  private handleRangeSelectAndDraw(rows: Array<TraceRow<unknown>>, ev: MouseEvent): void {
369    let rangeSelect: RangeSelectStruct | undefined;
370    let result: boolean;
371    this.rangeTraceRow = rows.filter((it) => {
372      if (it.rangeSelect) {
373        if (!rangeSelect) {
374          rangeSelect = new RangeSelectStruct();
375          let mouseX = ev.pageX - this.rowsEL!.getBoundingClientRect().left - 248;
376          mouseX = mouseX < 0 ? 0 : mouseX;
377          let markA = this.movingMark === 'markA' ? mouseX : this.mark.startMark;
378          let markB = this.movingMark === 'markB' ? mouseX : this.mark.endMark;
379          let startX = markA < markB ? markA : markB;
380          let endX = markB < markA ? markA : markB;
381          rangeSelect.startX = startX;
382          rangeSelect.endX = endX;
383          rangeSelect.startNS = RangeSelect.SetNS(it, startX);
384          rangeSelect.endNS = RangeSelect.SetNS(it, endX);
385          if (rangeSelect.startNS <= TraceRow.range!.startNS) {
386            rangeSelect.startNS = TraceRow.range!.startNS;
387          }
388          if (rangeSelect.endNS >= TraceRow.range!.endNS) {
389            rangeSelect.endNS = TraceRow.range!.endNS;
390          }
391          if (startX < 0) {
392            rangeSelect.startNS = TraceRow.rangeSelectObject!.startNS!;
393          } // @ts-ignore
394          if (endX > it.frame.width) {
395            rangeSelect.endNS = TraceRow.rangeSelectObject!.endNS!;
396          }
397        }
398        TraceRow.rangeSelectObject = rangeSelect;
399        result = true;
400      } else {
401        result = false;
402      }
403      return result;
404    });
405    this.timerShaftEL!.sportRuler!.isRangeSelect = (this.rangeTraceRow?.length || 0) > 0;
406    this.timerShaftEL!.sportRuler!.draw();
407  }
408
409  private handleTouchMark(ev: MouseEvent): void {
410    info('isTouchMark');
411    let x1 =
412      ((TraceRow.rangeSelectObject!.startNS! - TraceRow.range!.startNS) *
413        (this.timerShaftEL?.canvas?.clientWidth || 0)) /
414      (TraceRow.range!.endNS - TraceRow.range!.startNS);
415    let x2 =
416      ((TraceRow.rangeSelectObject!.endNS! - TraceRow.range!.startNS) * (this.timerShaftEL?.canvas?.clientWidth || 0)) /
417      (TraceRow.range!.endNS - TraceRow.range!.startNS);
418    this.mark = { startMark: x1, endMark: x2 };
419    let mouseX = ev.pageX - this.rowsPaneEL!.getBoundingClientRect().left - 248;
420    if (mouseX > x1 - 5 && mouseX < x1 + 5) {
421      this.isHover = true;
422      document.body.style.cursor = 'ew-resize';
423      this.movingMark = x1 < x2 ? 'markA' : 'markB';
424    } else if (mouseX > x2 - 5 && mouseX < x2 + 5) {
425      this.isHover = true;
426      document.body.style.cursor = 'ew-resize';
427      this.movingMark = x2 < x1 ? 'markA' : 'markB';
428    } else {
429      this.isHover = false;
430      document.body.style.cursor = 'default';
431    }
432  }
433  // @ts-ignore
434  static SetNS(row: TraceRow<unknown>, num: number): number {
435    return Math.floor(
436      // @ts-ignore
437      ((TraceRow.range!.endNS - TraceRow.range!.startNS) * num) / row.frame.width + TraceRow.range!.startNS!
438    );
439  }
440}
441