• 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 { BaseStruct, drawLoadingFrame, isFrameContainPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import { SpSystemTrace } from '../../component/SpSystemTrace';
19import { ColorUtils } from '../../component/trace/base/ColorUtils';
20
21export class XpowerGpuFreqRender extends Render {
22  renderMainThread(
23    xpowerStasticReq: {
24      context: CanvasRenderingContext2D;
25      useCache: boolean;
26    },
27    row: TraceRow<XpowerGpuFreqStruct>
28  ): void {
29    // offsetW控制图例的横向偏移量 确保图例不超过画布边界 因收藏和非收藏时泳道的宽度不一致 offsetW根据情况调整
30    let offsetW: number = row.collect ? 160 : 400;
31    let checkedType = row.rowSettingCheckedBoxList;
32    let checkedValue = row.rowSettingCheckBoxList;
33    checkedValue!.forEach((item, index) => {
34      if (!XpowerGpuFreqStruct.colorMap.has(Number(item))) {
35        XpowerGpuFreqStruct.colorMap.set(Number(item), ColorUtils.MD_PALETTE[index]);
36      }
37    });
38    let xpowerGpuFreqList = row.dataListCache.filter(
39      // @ts-ignore
40      (item) => checkedType[checkedValue?.indexOf(item.frequency.toString())]
41    );
42    // 根据startNS分组
43    const groupedData = new Map();
44    xpowerGpuFreqList.forEach((item) => {
45      if(!groupedData.has(item.startNS)){
46        groupedData.set(item.startNS,[]);
47      }
48      groupedData.get(item.startNS).push(item);
49    });
50    // 对直方图每个组内数据进行排序
51    const sortedData: any = [];
52    groupedData.forEach(group => {
53      group.sort((a:any,b:any) => a.frequency - b.frequency);
54      sortedData.push(...group);
55    });
56    // 对鼠标悬浮框每个组内数据排序
57    const tooltipData: any = [];
58    groupedData.forEach(group => {
59      group.sort((a:any,b:any) => b.frequency - a.frequency);
60      tooltipData.push(...group);
61    });
62    xpowerGpuFreqList = sortedData;
63    let xpowerMap = new Map<number, XpowerGpuFreqStruct[]>();
64    setGroupByTime(xpowerMap, xpowerGpuFreqList);
65    XpowerGpuFreqStruct.xpowerMap = xpowerMap;
66    setDataFrameAndHoverHtml(tooltipData, row);
67    drawLoadingFrame(xpowerStasticReq.context, xpowerGpuFreqList, row);
68    setMaxEnergyInfo(xpowerStasticReq.context, xpowerMap);
69
70    xpowerStasticReq.context.beginPath();
71    let find = false;
72    for (let re of xpowerGpuFreqList) {
73      XpowerGpuFreqStruct.draw(xpowerStasticReq, re, row, xpowerGpuFreqList.length);
74      if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
75        XpowerGpuFreqStruct.hoverXpowerStruct = re;
76        find = true;
77      }
78    }
79    if (!find) {
80      XpowerGpuFreqStruct.hoverXpowerStruct = undefined;
81    }
82    xpowerStasticReq.context.closePath();
83    let spApplication = document.getElementsByTagName('sp-application')[0];
84    let isDark = spApplication && spApplication.hasAttribute('dark');
85    drawLegend(xpowerStasticReq, checkedType!, checkedValue!, offsetW, isDark);
86  }
87}
88
89function setGroupByTime(xpowerMap: Map<number, XpowerGpuFreqStruct[]>, xpowerGpuFreqList: XpowerGpuFreqStruct[]): void {
90  xpowerGpuFreqList.forEach((item, index) => {
91    if (xpowerMap.has(item.startNS)) {
92      let data = xpowerMap.get(item.startNS);
93      data!.push(item);
94      xpowerMap.set(item.startNS, data!);
95    } else {
96      xpowerMap.set(item.startNS, []);
97      let data = xpowerMap.get(item.startNS);
98      data!.push(item);
99      xpowerMap.set(item.startNS, data!);
100    }
101  });
102}
103
104function setDataFrameAndHoverHtml(filter: XpowerGpuFreqStruct[], row: TraceRow<XpowerGpuFreqStruct>): void {
105  filter.forEach((item) => {
106    XpowerGpuFreqStruct.setXPowerGpuFreqFrame(
107      item,
108      5,
109      TraceRow.range?.startNS ?? 0,
110      TraceRow.range?.endNS ?? 0,
111      TraceRow.range?.totalNS ?? 0,
112      row.frame
113    );
114  });
115  let hoverMap: Map<number, string> = new Map();
116  filter.forEach((item) => {
117    if (hoverMap.has(item.startNS)) {
118      hoverMap.set(item.startNS, hoverMap.get(item.startNS) + item.hoverHtml);
119    } else {
120      hoverMap.set(item.startNS, item.hoverHtml);
121    }
122  });
123  XpowerGpuFreqStruct.hoverMap = hoverMap;
124}
125
126function setMaxEnergyInfo(context: CanvasRenderingContext2D, xpowerMap: Map<number, XpowerGpuFreqStruct[]>): void {
127  XpowerGpuFreqStruct.computeMaxEnergy(xpowerMap);
128  let s = XpowerGpuFreqStruct.max + ' ms';
129  let textMetrics = context.measureText(s);
130  context.globalAlpha = 0.8;
131  context.fillStyle = '#f0f0f0';
132  context.fillRect(0, 5, textMetrics.width + 8, 18);
133  context.globalAlpha = 1;
134  context.fillStyle = '#333';
135  context.textBaseline = 'middle';
136  context.fillText(s, 4, 5 + 9);
137}
138
139export function drawLegend(
140  req: { context: CanvasRenderingContext2D; useCache: boolean },
141  checked: boolean[],
142  checkedValue: string[],
143  offsetW: number,
144  isDark?: boolean
145): void {
146  let textList: string[] = [];
147  checkedValue.forEach((item, index) => {
148    if (checked[index]) {
149      textList.push(item.toUpperCase());
150    }
151  });
152  for (let index = 0; index < textList.length; index++) {
153    let text = req.context.measureText(textList[index]);
154    req.context.fillStyle = XpowerGpuFreqStruct.colorMap.get(Number(textList[index]))!;
155    req.context.globalAlpha = 1;
156    let canvasEndX = req.context.canvas.clientWidth - offsetW;
157    let textColor = isDark ? '#FFFFFF' : '#333';
158    if (index === 0) {
159      req!.context.fillRect(canvasEndX - textList.length * 80, 12, 8, 8);
160      req.context.globalAlpha = 0.8;
161      req.context.fillStyle = textColor;
162      req.context.textBaseline = 'middle';
163      req.context.fillText(textList[index], canvasEndX - textList.length * 80 + 10, 18);
164      XpowerGpuFreqStruct.currentTextWidth = canvasEndX - textList.length * 80 + 40 + text.width;
165    } else {
166      req!.context.fillRect(XpowerGpuFreqStruct.currentTextWidth, 12, 8, 8);
167      req.context.globalAlpha = 0.8;
168      req.context.fillStyle = textColor;
169      req.context.textBaseline = 'middle';
170      req!.context.fillText(textList[index], XpowerGpuFreqStruct.currentTextWidth + 12, 18);
171      XpowerGpuFreqStruct.currentTextWidth = XpowerGpuFreqStruct.currentTextWidth + 40 + text.width;
172    }
173  }
174  req.context.fillStyle = '#333';
175}
176
177export function XpowerGpuFreqStructOnClick(
178  clickRowType: string,
179  sp: SpSystemTrace,
180  entry?: XpowerGpuFreqStruct
181): Promise<unknown> {
182  return new Promise((resolve, reject) => {
183    if (clickRowType === TraceRow.ROW_TYPE_XPOWER_GPU_FREQUENCY && (XpowerGpuFreqStruct.hoverXpowerStruct || entry)) {
184      XpowerGpuFreqStruct.selectXpowerStruct = entry || XpowerGpuFreqStruct.hoverXpowerStruct;
185      let startNs = XpowerGpuFreqStruct.selectXpowerStruct!.startNS;
186      XpowerGpuFreqStruct.xpowerMap.get(startNs)!.length > 0 &&
187        sp.traceSheetEL?.displayXpowerGpuFreqData(XpowerGpuFreqStruct.xpowerMap.get(startNs) || []);
188      sp.timerShaftEL?.modifyFlagList(undefined);
189      reject(new Error());
190    } else {
191      resolve(null);
192    }
193  });
194}
195
196export class XpowerGpuFreqStruct extends BaseStruct {
197  static rowHeight: number = 200;
198  static currentTextWidth: number = 0;
199  static hoverXpowerStruct: XpowerGpuFreqStruct | undefined;
200  static selectXpowerStruct: XpowerGpuFreqStruct | undefined;
201
202  startNS: number = 0;
203  startMS: number = 0;
204  runTime: number = 0;
205  idleTime: number = 0;
206  frequency: number = 0;
207  count: number = 0;
208  static max: number = 0;
209  hoverHtml: string = '';
210
211  static xpowerMap = new Map<number, XpowerGpuFreqStruct[]>();
212  static hoverMap: Map<number, string> = new Map();
213  static histogramHeightMap = new Map<number, number>();
214
215  static flagTime: number = 0;
216  static height: number = -1;
217  static drawY: number = 0;
218  static colorMap: Map<number, string> = new Map();
219  static gpuFreqStructMap = new Map<number, Array<XpowerGpuFreqStruct>>();
220
221  static draw(
222    req: { useCache: boolean; context: CanvasRenderingContext2D },
223    data: XpowerGpuFreqStruct,
224    row: TraceRow<XpowerGpuFreqStruct>,
225    length: number
226  ): void {
227    if (data.frame) {
228      req.context.globalAlpha = 0.8;
229      if (data.startNS !== XpowerGpuFreqStruct.flagTime || length === 1) {
230        this.height = -1;
231      } else {
232        this.height = this.drawY;
233      }
234      XpowerGpuFreqStruct.flagTime = data.startNS;
235      this.drawY = this.drawHistogram(req, data, row.frame);
236    }
237    XpowerGpuFreqStruct.drawStroke(req, data, row);
238  }
239
240  static equals(baseStruct: XpowerGpuFreqStruct, targetStruct: XpowerGpuFreqStruct): boolean {
241    return baseStruct === targetStruct;
242  }
243
244  static drawStroke(
245    req: { useCache: boolean; context: CanvasRenderingContext2D },
246    data: XpowerGpuFreqStruct,
247    row: TraceRow<XpowerGpuFreqStruct>
248  ): void {
249    let startNS = TraceRow.range!.startNS;
250    let endNS = TraceRow.range!.endNS;
251    let totalNS = TraceRow.range!.totalNS;
252    if (
253      XpowerGpuFreqStruct.equals(XpowerGpuFreqStruct.hoverXpowerStruct!, data) ||
254      XpowerGpuFreqStruct.equals(XpowerGpuFreqStruct.selectXpowerStruct!, data)
255    ) {
256      let startPointX = ns2x(data.startNS || 0, startNS, endNS, totalNS, row.frame);
257      let endPointX = ns2x((data.startNS || 0) + 3000000000, startNS, endNS, totalNS, row.frame);
258      let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
259      req!.context.lineWidth = 1;
260      req!.context.strokeStyle = '#9899a0';
261      let height = this.histogramHeightMap.get(data.startNS)! || 0;
262      req!.context.strokeRect(startPointX, this.rowHeight - height, Math.ceil(frameWidth), height + 1);
263    }
264  }
265
266  static drawHoverFrame(
267    data: XpowerGpuFreqStruct,
268    isHover: boolean,
269    wifiHeight: number,
270    req: { context: CanvasRenderingContext2D; useCache: boolean },
271    row: TraceRow<XpowerGpuFreqStruct>
272  ): void {
273    let startNS = TraceRow.range!.startNS;
274    let endNS = TraceRow.range!.endNS;
275    let totalNS = TraceRow.range!.totalNS;
276    if (
277      (data.startNS === XpowerGpuFreqStruct.hoverXpowerStruct?.startNS && isHover) ||
278      (XpowerGpuFreqStruct.selectXpowerStruct && data.startNS === XpowerGpuFreqStruct.selectXpowerStruct?.startNS)
279    ) {
280      let endPointX = ns2x((data.startNS || 0) + 3000000000, startNS, endNS, totalNS, row.frame);
281      let startPointX = ns2x(data.startNS || 0, startNS, endNS, totalNS, row.frame);
282      let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
283      req.context.globalAlpha = 1;
284      req!.context.lineWidth = 2;
285      req.context.strokeStyle = '#9899a0';
286      req!.context.strokeRect(startPointX, wifiHeight, frameWidth, data.frame!.height);
287    }
288  }
289
290  static drawHistogram(
291    req: { useCache: boolean; context: CanvasRenderingContext2D },
292    data: XpowerGpuFreqStruct,
293    rowFrame: Rect
294  ): number {
295    let endPointX = Math.ceil(
296      ns2x(
297        (data.startNS || 0) + 3000000000,
298        TraceRow.range!.startNS,
299        TraceRow.range!.endNS,
300        TraceRow.range!.totalNS,
301        rowFrame
302      )
303    );
304    let startPointX = Math.ceil(
305      ns2x(data.startNS || 0, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, rowFrame)
306    );
307    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
308    let histogramColor = this.colorMap.get(data.frequency)!;
309    req!.context.fillStyle = histogramColor;
310    let drawStartY = 0;
311    let dataHeight: number = ((data.runTime || 0) * (this.rowHeight - 40)) / XpowerGpuFreqStruct.max;
312
313    if (data.runTime !== 0 && dataHeight < 1) {
314      dataHeight = 1;
315    }
316    data.frame!.x = startPointX;
317    data.frame!.width = frameWidth;
318    data.frame!.height = dataHeight;
319    if (this.height === -1) {
320      drawStartY = this.rowHeight - dataHeight;
321      data.frame!.y = drawStartY;
322      req!.context.fillRect(startPointX, drawStartY, Math.floor(frameWidth), dataHeight);
323      return drawStartY;
324    } else {
325      drawStartY = this.height - dataHeight;
326      data.frame!.y = drawStartY;
327      req!.context.fillRect(startPointX, drawStartY, Math.floor(frameWidth), dataHeight);
328      return drawStartY;
329    }
330  }
331
332  static computeMaxEnergy(map: Map<number, XpowerGpuFreqStruct[]>): void {
333    let maxRunTime = 0;
334    map.forEach((list, key) => {
335      let total = 0;
336      list.forEach((item) => {
337        total += item.runTime;
338      });
339      if (maxRunTime < total) {
340        maxRunTime = total;
341      }
342      let mapValue = Math.ceil(((total || 0) * (XpowerGpuFreqStruct.rowHeight - 28)) / XpowerGpuFreqStruct.max);
343      XpowerGpuFreqStruct.histogramHeightMap.set(list[0].startNS, mapValue);
344    });
345    XpowerGpuFreqStruct.max = maxRunTime;
346  }
347
348  static setXPowerGpuFreqFrame(
349    node: XpowerGpuFreqStruct,
350    padding: number,
351    startNS: number,
352    endNS: number,
353    totalNS: number,
354    frame: Rect
355  ): void {
356    let startPointX: number;
357    let endPointX: number;
358    if ((node.startNS || 0) < startNS) {
359      startPointX = 0;
360    } else {
361      startPointX = ns2x(node.startNS, startNS, endNS, totalNS, frame);
362    }
363    if (node.startNS + 3000000000 > endNS) {
364      endPointX = frame.width;
365    } else {
366      endPointX = ns2x(node.startNS + 3000000000, startNS, endNS, totalNS, frame);
367    }
368    let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX;
369    if (!node.frame) {
370      node.frame = new Rect(0, 0, 0, 0);
371    }
372    node.frame.x = Math.floor(startPointX);
373    node.frame.y = frame.y + padding;
374    node.frame.width = Math.ceil(frameWidth);
375    node.frame.height = Math.floor(frame.height - padding * 2);
376  }
377}
378