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 { ColorUtils } from '../../component/trace/base/ColorUtils';
17import {
18  BaseStruct,
19  dataFilterHandler,
20  Render,
21  isFrameContainPoint,
22  ns2x,
23  drawLoadingFrame,
24  Rect,
25} from './ProcedureWorkerCommon';
26import { TraceRow } from '../../component/trace/base/TraceRow';
27
28export class MemoryAbilityRender extends Render {
29  renderMainThread(
30    req: {
31      context: CanvasRenderingContext2D;
32      useCache: boolean;
33      type: string;
34      maxMemoryByte: number;
35      maxMemoryByteName: string;
36    },
37    memoryAbilityRow: TraceRow<MemoryAbilityMonitorStruct>
38  ): void {
39    let memoryAbilityList = memoryAbilityRow.dataList;
40    let memoryAbilityFilter = memoryAbilityRow.dataListCache;
41    dataFilterHandler(memoryAbilityList, memoryAbilityFilter, {
42      startKey: 'startNS',
43      durKey: 'dur',
44      startNS: TraceRow.range?.startNS ?? 0,
45      endNS: TraceRow.range?.endNS ?? 0,
46      totalNS: TraceRow.range?.totalNS ?? 0,
47      frame: memoryAbilityRow.frame,
48      paddingTop: 5,
49      useCache: req.useCache || !(TraceRow.range?.refresh ?? false),
50    });
51    drawLoadingFrame(req.context, memoryAbilityRow.dataListCache, memoryAbilityRow);
52    req.context.beginPath();
53    let find = false;
54    for (let re of memoryAbilityFilter) {
55      MemoryAbilityMonitorStruct.draw(req.context, re, req.maxMemoryByte, memoryAbilityRow.isHover);
56      if (
57        memoryAbilityRow.isHover &&
58        re.frame &&
59        isFrameContainPoint(re.frame, memoryAbilityRow.hoverX, memoryAbilityRow.hoverY)
60      ) {
61        MemoryAbilityMonitorStruct.hoverMemoryAbilityStruct = re;
62        find = true;
63      }
64    }
65
66    if (!find && memoryAbilityRow.isHover) {
67      MemoryAbilityMonitorStruct.hoverMemoryAbilityStruct = undefined;
68    }
69    req.context.closePath();
70    let textMetrics = req.context.measureText(req.maxMemoryByteName);
71    req.context.globalAlpha = 0.8;
72    req.context.fillStyle = '#f0f0f0';
73    req.context.fillRect(0, 5, textMetrics.width + 8, 18);
74    req.context.globalAlpha = 1;
75    req.context.fillStyle = '#333';
76    req.context.textBaseline = 'middle';
77    req.context.fillText(req.maxMemoryByteName, 4, 5 + 9);
78  }
79}
80
81export function memoryAbility(
82  memoryAbilityList: Array<MemoryAbilityMonitorStruct>,
83  res: Array<MemoryAbilityMonitorStruct>,
84  startNS: number,
85  endNS: number,
86  totalNS: number,
87  frame: Rect,
88  use: boolean
89): void {
90  if (use && res.length > 0) {
91    for (let i = 0; i < res.length; i++) {
92      let memoryAbilityItem = res[i] as MemoryAbilityMonitorStruct;
93      if (
94        (memoryAbilityItem.startNS || 0) + (memoryAbilityItem.dur || 0) > startNS &&
95        (memoryAbilityItem.startNS || 0) < endNS
96      ) {
97        MemoryAbilityMonitorStruct.setMemoryFrame(memoryAbilityItem, 5, startNS, endNS, totalNS, frame);
98      } else {
99        memoryAbilityItem.frame = undefined;
100      }
101    }
102    return;
103  }
104  res.length = 0;
105  setMemoryAbility(memoryAbilityList, res, startNS, endNS, totalNS, frame);
106}
107function setMemoryAbility(
108  list: Array<MemoryAbilityMonitorStruct>,
109  res: Array<MemoryAbilityMonitorStruct>,
110  startNS: number,
111  endNS: number,
112  totalNS: number,
113  frame: Rect
114): void {
115  if (list) {
116    for (let index = 0; index < list.length; index++) {
117      let item = list[index] as MemoryAbilityMonitorStruct;
118      item.dur =
119        index === list.length - 1
120          ? (endNS || 0) - (item.startNS || 0)
121          : // @ts-ignore
122            (list[index + 1].startNS || 0) - (item.startNS || 0);
123      if ((item.startNS || 0) + (item.dur || 0) > startNS && (item.startNS || 0) < endNS) {
124        MemoryAbilityMonitorStruct.setMemoryFrame(item, 5, startNS, endNS, totalNS, frame);
125        if (
126          !(
127            index > 0 &&
128            // @ts-ignore
129            (list[index - 1].frame.x || 0) === (item.frame.x || 0) &&
130            // @ts-ignore
131            (list[index - 1].frame.width || 0) === (item.frame.width || 0)
132          )
133        ) {
134          res.push(item);
135        }
136      }
137    }
138  }
139}
140
141export class MemoryAbilityMonitorStruct extends BaseStruct {
142  static maxMemoryByte: number = 0;
143  static maxMemoryByteName: string = '0 MB';
144  static hoverMemoryAbilityStruct: MemoryAbilityMonitorStruct | undefined;
145  static selectMemoryAbilityStruct: MemoryAbilityMonitorStruct | undefined;
146  cpu: number | undefined;
147  value: number | undefined;
148  startNS: number | undefined;
149  dur: number | undefined;
150
151  static draw(
152    memoryAbilityContext2D: CanvasRenderingContext2D,
153    memoryAbilityData: MemoryAbilityMonitorStruct,
154    maxMemoryByte: number,
155    isHover: boolean
156  ): void {
157    if (memoryAbilityData.frame) {
158      let width = memoryAbilityData.frame.width || 0;
159      let index = 2;
160      memoryAbilityContext2D.fillStyle = ColorUtils.colorForTid(index);
161      memoryAbilityContext2D.strokeStyle = ColorUtils.colorForTid(index);
162      let drawHeight: number = Math.floor(
163        ((memoryAbilityData.value || 0) * (memoryAbilityData.frame.height || 0) * 1.0) / maxMemoryByte
164      );
165      let y = memoryAbilityData.frame.y + memoryAbilityData.frame.height - drawHeight + 4;
166      if (memoryAbilityData.startNS === MemoryAbilityMonitorStruct.hoverMemoryAbilityStruct?.startNS && isHover) {
167        memoryAbilityContext2D.lineWidth = 1;
168        memoryAbilityContext2D.globalAlpha = 0.6;
169        memoryAbilityContext2D.fillRect(memoryAbilityData.frame.x, y, width, drawHeight);
170        memoryAbilityContext2D.beginPath();
171        memoryAbilityContext2D.arc(memoryAbilityData.frame.x, y, 3, 0, 2 * Math.PI, true);
172        memoryAbilityContext2D.fill();
173        memoryAbilityContext2D.globalAlpha = 1.0;
174        memoryAbilityContext2D.stroke();
175        memoryAbilityContext2D.beginPath();
176        memoryAbilityContext2D.moveTo(memoryAbilityData.frame.x + 3, y);
177        memoryAbilityContext2D.lineWidth = 3;
178        memoryAbilityContext2D.lineTo(memoryAbilityData.frame.x + width, y);
179        memoryAbilityContext2D.stroke();
180      } else {
181        memoryAbilityContext2D.globalAlpha = 0.6;
182        memoryAbilityContext2D.lineWidth = 1;
183        let drawHeight: number = Math.floor(
184          ((memoryAbilityData.value || 0) * (memoryAbilityData.frame.height || 0)) / maxMemoryByte
185        );
186        memoryAbilityContext2D.fillRect(memoryAbilityData.frame.x, y, width, drawHeight);
187      }
188    }
189    memoryAbilityContext2D.globalAlpha = 1.0;
190    memoryAbilityContext2D.lineWidth = 1;
191  }
192
193  static setMemoryFrame(
194    memoryNode: MemoryAbilityMonitorStruct,
195    padding: number,
196    startNS: number,
197    endNS: number,
198    totalNS: number,
199    frame: Rect
200  ): void {
201    let memoryStartPointX: number;
202    let memoryEndPointX: number;
203
204    if ((memoryNode.startNS || 0) < startNS) {
205      memoryStartPointX = 0;
206    } else {
207      memoryStartPointX = ns2x(memoryNode.startNS || 0, startNS, endNS, totalNS, frame);
208    }
209    if ((memoryNode.startNS || 0) + (memoryNode.dur || 0) > endNS) {
210      memoryEndPointX = frame.width;
211    } else {
212      memoryEndPointX = ns2x((memoryNode.startNS || 0) + (memoryNode.dur || 0), startNS, endNS, totalNS, frame);
213    }
214    let frameWidth: number = memoryEndPointX - memoryStartPointX <= 1 ? 1 : memoryEndPointX - memoryStartPointX;
215    if (!memoryNode.frame) {
216      memoryNode.frame = new Rect(0, 0, 0, 0);
217    }
218    memoryNode.frame.x = Math.floor(memoryStartPointX);
219    memoryNode.frame.y = frame.y + padding;
220    memoryNode.frame.width = Math.ceil(frameWidth);
221    memoryNode.frame.height = Math.floor(frame.height - padding * 2);
222  }
223}
224
225const textPadding = 2;
226