• 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 '../../component/trace/base/TraceRow.js';
17import { BaseStruct, computeUnitWidth, isSurroundingPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon.js';
18import { AnimationRanges } from '../../bean/FrameComponentBean.js';
19import { ColorUtils } from '../../component/trace/base/ColorUtils.js';
20
21export class FrameSpacingRender extends Render {
22  renderMainThread(
23    req: {
24      useCache: boolean;
25      context: CanvasRenderingContext2D;
26      type: string;
27      frameRate: number
28      animationRanges: AnimationRanges[]
29    },
30    row: TraceRow<FrameSpacingStruct>
31  ): void {
32    let frameSpacingList = row.dataList;
33    let frameSpacingFilter = row.dataListCache;
34    this.frameSpacing(
35      frameSpacingList,
36      frameSpacingFilter,
37      TraceRow.range!.startNS,
38      TraceRow.range!.endNS,
39      TraceRow.range!.totalNS,
40      row,
41      req.animationRanges,
42      req.useCache || !TraceRow.range!.refresh
43    );
44    this.render(req, frameSpacingFilter, row);
45  }
46
47  private render(req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number;
48  animationRanges: AnimationRanges[] }, frameSpacingFilter: Array<FrameSpacingStruct>,
49  row: TraceRow<FrameSpacingStruct>) : void {
50    if (req.animationRanges.length > 0 && req.animationRanges[0] && frameSpacingFilter.length > 0) {
51      let preFrameSpacing: FrameSpacingStruct = frameSpacingFilter[0];
52      // @ts-ignore
53      let smallTickStandard = smallTick[req.frameRate];
54      let [minValue, maxValue] = this.maxMinData(smallTickStandard.firstLine,
55        smallTickStandard.thirdLine, frameSpacingFilter);
56      let isDraw = false;
57      let selectUnitWidth: number = 0;
58      for (let index: number = 0; index < frameSpacingFilter.length; index++) {
59        let currentStruct = frameSpacingFilter[index];
60        selectUnitWidth = computeUnitWidth(preFrameSpacing.currentTs, currentStruct.currentTs,
61          row.frame.width, selectUnitWidth);
62        FrameSpacingStruct.refreshHoverStruct(preFrameSpacing, currentStruct, row, minValue, maxValue);
63        if (currentStruct.groupId === 0) {
64          if (currentStruct.currentTs > TraceRow.range!.startNS && currentStruct.currentTs < TraceRow.range!.endNS) {
65            isDraw = true;
66            this.drawPoint(req.context, currentStruct, row, minValue, maxValue);
67          }
68        } else if (currentStruct.groupId !== invalidGroupId && index > 0 &&
69          currentStruct.groupId === preFrameSpacing!.groupId) {
70          isDraw = true;
71          FrameSpacingStruct.draw(req.context, preFrameSpacing, currentStruct, row, minValue, maxValue);
72        }
73        FrameSpacingStruct.drawSelect(currentStruct, req.context, row);
74        preFrameSpacing = currentStruct;
75      }
76      if (isDraw) {
77        this.drawDashedLines(Object.values(smallTickStandard), req, row, minValue, maxValue);
78      }
79      let findStructList = frameSpacingFilter.filter(filter =>
80        row.isHover && isSurroundingPoint(row.hoverX, filter.frame!, selectUnitWidth / multiple));
81      let find = false;
82      if (findStructList.length > 0) {
83        find = true;
84        let hoverIndex: number = 0;
85        if (findStructList.length > unitIndex) {
86          hoverIndex = Math.ceil(findStructList.length / multiple);
87        }
88        FrameSpacingStruct.hoverFrameSpacingStruct = findStructList[hoverIndex];
89      }
90      if (!find && row.isHover) {
91        FrameSpacingStruct.hoverFrameSpacingStruct = undefined;
92      }
93    }
94  }
95
96  private drawPoint(ctx: CanvasRenderingContext2D, currentStruct: FrameSpacingStruct,
97    row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number): void {
98    let currentPointY = row.frame.height - Math.floor((currentStruct.frameSpacingResult! -
99        minValue) * (row.frame.height - padding * multiple) /
100      (maxValue - minValue)) - padding;
101    ctx.beginPath();
102    ctx.lineWidth = 1;
103    ctx.globalAlpha = 1;
104    ctx.arc(currentStruct.frame!.x, currentPointY, multiple, 0, multiple * Math.PI);
105    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
106    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2];
107    ctx.stroke();
108    ctx.fill();
109    ctx.closePath();
110  }
111
112  private drawDashedLines(
113    dashedLines: number[],
114    req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number },
115    row: TraceRow<FrameSpacingStruct>,
116    minVale: number,
117    maxValue: number
118  ): void {
119    for (let i = 0; i < dashedLines.length; i++) {
120      FrameSpacingStruct.drawParallelLine(req.context, row.frame, dashedLines, i, minVale, maxValue);
121    }
122  }
123
124  private frameSpacing(
125    frameSpacingList: Array<FrameSpacingStruct>,
126    frameSpacingFilter: Array<FrameSpacingStruct>,
127    startNS: number,
128    endNS: number,
129    totalNS: number,
130    row: TraceRow<FrameSpacingStruct>,
131    animationRanges: AnimationRanges[],
132    use: boolean
133  ): void {
134    let frame: Rect = row.frame;
135    let modelName: string | undefined | null = row.getAttribute('model-name');
136    if (use && frameSpacingFilter.length > 0) {
137      for (let index = 0, len = frameSpacingFilter.length; index < len; index++) {
138        if (frameSpacingFilter[index].nameId === modelName) {
139          FrameSpacingStruct.setFrameSpacingFrame(frameSpacingFilter[index], startNS, endNS, totalNS, frame);
140        }
141      }
142      return;
143    }
144    frameSpacingFilter.length = 0;
145    if (frameSpacingList) {
146      let groupIdList: number[] = [];
147      for (let index = 0; index < frameSpacingList.length; index++) {
148        let item = frameSpacingList[index];
149        if (modelName === item.nameId) {
150          item.groupId = invalidGroupId;
151          for (let rangeIndex = 0; rangeIndex < animationRanges.length; rangeIndex++) {
152            let currentRange = animationRanges[rangeIndex];
153            if (item.currentTs >= currentRange.start && item.currentTs <= currentRange.end) {
154              item.groupId = currentRange.start;
155              break;
156            }
157          }
158          if (item.currentTs < startNS && index + unitIndex < frameSpacingList.length &&
159            frameSpacingList[index + unitIndex].currentTs >= startNS && item.groupId !== invalidGroupId) {
160            this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList);
161          }
162          if (item.currentTs >= startNS && item.currentTs <= endNS && item.groupId !== invalidGroupId) {
163            this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList);
164          }
165          if (item.currentTs > endNS && item.groupId !== invalidGroupId) {
166            this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList);
167            break;
168          }
169        }
170      }
171      this.grouping(groupIdList, frameSpacingFilter);
172    }
173  }
174
175  private grouping(groupIdList: number[], frameSpacingFilter: Array<FrameSpacingStruct>): void {
176    let simpleGroup = groupIdList.filter(groupId => {
177      return groupId !== invalidGroupId && groupIdList.indexOf(groupId) === groupIdList.lastIndexOf(groupId);
178    });
179    frameSpacingFilter.forEach(frameSpacing => {
180      if (simpleGroup.indexOf(frameSpacing.groupId!) > invalidGroupId) {
181        frameSpacing.groupId = 0;
182        frameSpacing.frameSpacingResult = 0;
183        frameSpacing.preFrameWidth = 0;
184        frameSpacing.preFrameHeight = 0;
185        frameSpacing.preTs = 0;
186      }
187    });
188  }
189
190  private refreshFrame(frameSpacingFilter: Array<FrameSpacingStruct>, item: FrameSpacingStruct,
191    startNS: number, endNS: number, totalNS: number, frame: Rect, groupIdList: number[]): void {
192    frameSpacingFilter.push(item);
193    FrameSpacingStruct.setFrameSpacingFrame(item, startNS, endNS, totalNS, frame);
194    groupIdList.push(item.groupId!);
195  }
196
197  private maxMinData(tickStandardMin: number, tickStandardMax: number, filter: FrameSpacingStruct[]): [number, number] {
198    let min = Math.min.apply(Math, filter.map(filterData => {
199      return filterData.frameSpacingResult!;
200    }));
201    let max = Math.max.apply(Math, filter.map(filterData => {
202      return filterData.frameSpacingResult!;
203    }));
204    if (max < tickStandardMax) {
205      max = tickStandardMax + padding;
206    }
207    if (min > tickStandardMin) {
208      min = tickStandardMin;
209    }
210    return [min, max];
211  }
212}
213
214export class FrameSpacingStruct extends BaseStruct {
215  static hoverFrameSpacingStruct: FrameSpacingStruct | undefined;
216  static selectFrameSpacingStruct: FrameSpacingStruct | undefined;
217  static physicalWidth: number | undefined;
218  static physicalHeight: number | undefined;
219  preTs: number | undefined;
220  currentTs: number = 0;
221  frameSpacingResult: number | undefined;
222  id: number = 0;
223  groupId: number | undefined;
224  currentFrameWidth: number | undefined;
225  preFrameWidth: number | undefined;
226  currentFrameHeight: number | undefined;
227  preFrameHeight: number | undefined;
228  x: number | undefined;
229  y: number | undefined;
230  nameId: string | undefined;
231
232  static setFrameSpacingFrame(
233    frameSpacingNode: FrameSpacingStruct,
234    startNS: number,
235    endNS: number,
236    totalNS: number,
237    row: Rect
238  ): void {
239    let pointX = ns2x(frameSpacingNode.currentTs || 0, startNS, endNS, totalNS, row);
240    if (!frameSpacingNode.frame) {
241      frameSpacingNode.frame = new Rect(0, 0, 0, 0);
242    }
243    frameSpacingNode.frame.x = Math.floor(pointX);
244  }
245
246  static isSelect(currSpacingStruct: FrameSpacingStruct) : boolean | 0 | undefined {
247    return TraceRow.rangeSelectObject &&
248      TraceRow.rangeSelectObject.startNS &&
249      TraceRow.rangeSelectObject.endNS &&
250      currSpacingStruct.currentTs >= TraceRow.rangeSelectObject.startNS &&
251      currSpacingStruct.currentTs <= TraceRow.rangeSelectObject.endNS;
252  }
253
254  static refreshHoverStruct(preFrameSpacing: FrameSpacingStruct, frameSpacing: FrameSpacingStruct,
255    row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number): void {
256    if (frameSpacing.frame) {
257      frameSpacing.frame.y = row.frame.height - Math.floor((frameSpacing.frameSpacingResult! -
258        minValue) * (row.frame.height - padding * multiple) /
259        (maxValue - minValue)) - padding;
260    }
261  };
262
263  static draw(
264    ctx: CanvasRenderingContext2D,
265    preFrameSpacing: FrameSpacingStruct,
266    currentStruct: FrameSpacingStruct,
267    rowFrame: TraceRow<FrameSpacingStruct>,
268    minValue: number,
269    maxValue: number
270  ): void {
271    if (currentStruct.frame && preFrameSpacing.frame) {
272      this.drawPolyline(ctx, preFrameSpacing, currentStruct, rowFrame, minValue, maxValue);
273    }
274  }
275
276  static drawSelect(currentStruct: FrameSpacingStruct, ctx: CanvasRenderingContext2D,
277    rowFrame: TraceRow<FrameSpacingStruct>): void {
278    if (currentStruct.frame &&
279      currentStruct.currentTs > TraceRow.range!.startNS &&
280      currentStruct.currentTs < TraceRow.range!.endNS) {
281      if ((currentStruct === FrameSpacingStruct.hoverFrameSpacingStruct && rowFrame.isHover) ||
282        currentStruct === FrameSpacingStruct.selectFrameSpacingStruct) {
283        ctx.lineWidth = 3;
284        ctx.beginPath();
285        ctx.arc(currentStruct.frame.x, currentStruct.frame.y, selectRadius, 0, multiple * Math.PI);
286        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7];
287        ctx.lineWidth = 2;
288        ctx.globalAlpha = 1;
289        ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9];
290        ctx.stroke();
291        ctx.fill();
292        ctx.closePath();
293      }
294    }
295    if (rowFrame.getAttribute('check-type') === '2' && FrameSpacingStruct.isSelect(currentStruct)) {
296      ctx.beginPath();
297      ctx.lineWidth = 3;
298      ctx.arc(currentStruct.frame!.x, currentStruct.frame!.y, selectRadius, 0, multiple * Math.PI);
299      ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7];
300      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9];
301      ctx.lineWidth = 2;
302      ctx.globalAlpha = 1;
303      ctx.stroke();
304      ctx.fill();
305      ctx.closePath();
306    }
307  }
308
309  static drawParallelLine(
310    ctx: CanvasRenderingContext2D,
311    rowFrame: Rect,
312    dashedLines: number[],
313    index: number,
314    minValue: number,
315    maxValue: number
316  ): void {
317    let pointY = rowFrame.height - Math.floor((dashedLines[index] - minValue) *
318      (rowFrame.height - padding * multiple) / (maxValue - minValue)) - padding;
319    let lineDash = 10;
320    let textPadding = 4;
321    ctx.beginPath();
322    ctx.lineWidth = 2;
323    ctx.setLineDash([lineDash]);
324    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[6];
325    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6];
326    ctx.moveTo(0, pointY);
327    ctx.lineTo(rowFrame.width, pointY);
328    ctx.stroke();
329    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3];
330    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3];
331    if (index === 0) {
332      ctx.fillText(dashedLines[index].toString(), 0, pointY + multiple * textPadding);
333    } else if (index === unitIndex) {
334      ctx.fillText(dashedLines[index].toString(), 0, pointY + textPadding);
335    } else {
336      ctx.fillText(dashedLines[index].toString(), 0, pointY - textPadding);
337    }
338    ctx.closePath();
339  }
340
341  static drawPolyline(
342    ctx: CanvasRenderingContext2D,
343    preFrameSpacing: FrameSpacingStruct,
344    currentStruct: FrameSpacingStruct,
345    rowFrame: TraceRow<FrameSpacingStruct>,
346    minValue: number,
347    maxValue: number
348  ): void {
349    ctx.beginPath();
350    let prePointY = rowFrame.frame.height - Math.floor((preFrameSpacing.frameSpacingResult! -
351        minValue) * (rowFrame.frame.height - padding * multiple) /
352      (maxValue - minValue)) - padding;
353    let currentPointY = rowFrame.frame.height - Math.floor((currentStruct.frameSpacingResult! -
354        minValue) * (rowFrame.frame.height - padding * multiple) /
355      (maxValue - minValue)) - padding;
356    if (preFrameSpacing.frame && currentStruct.frame) {
357      ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
358      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2];
359      ctx.lineWidth = 2;
360      ctx.setLineDash([0]);
361      ctx.moveTo(preFrameSpacing.frame.x, prePointY);
362      ctx.lineTo(currentStruct.frame.x, currentPointY);
363      ctx.stroke();
364      ctx.closePath();
365      FrameSpacingStruct.drawSelect(preFrameSpacing, ctx, rowFrame);
366    }
367  }
368}
369
370const padding = 3;
371const invalidGroupId: number = -1;
372const multiple: number = 2;
373const unitIndex: number = 1;
374const selectRadius: number = 3;
375
376const smallTick = {
377  60: {
378    firstLine: 11.4,
379    secondLine: 13,
380    thirdLine: 14.6
381  },
382  90: {
383    firstLine: 13.7,
384    secondLine: 19.4,
385    thirdLine: 25
386  },
387  120: {
388    firstLine: 13.7,
389    secondLine: 19.4,
390    thirdLine: 25
391  }
392};
393