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