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 */ 15import { BaseStruct, Rect, Render, isFrameContainPoint, ns2x } from './ProcedureWorkerCommon.js'; 16import { TraceRow } from '../../component/trace/base/TraceRow.js'; 17import { Utils } from '../../component/trace/base/Utils.js'; 18import { MemoryConfig } from '../../bean/MemoryConfig.js'; 19 20export class SnapshotRender extends Render { 21 renderMainThread( 22 req: { 23 context: CanvasRenderingContext2D; 24 useCache: boolean; 25 type: string; 26 }, 27 row: TraceRow<SnapshotStruct> 28 ): void { 29 let list = row.dataList; 30 let filter = row.dataListCache; 31 let maxValue = 0; 32 for (let item of list) { 33 maxValue = Math.max(maxValue, item.value || 0); 34 } 35 snapshot(list, filter, maxValue, TraceRow.range?.startNS ?? 0, (TraceRow.range?.endNS ?? 0) - (TraceRow.range?.startNS! ?? 0), row.frame); 36 req.context!.beginPath(); 37 let find = false; 38 for (let re of filter) { 39 SnapshotStruct.draw(req.context, re); 40 if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 41 SnapshotStruct.hoverSnapshotStruct = re; 42 find = true; 43 } 44 } 45 if (!find && row.isHover) { 46 SnapshotStruct.hoverSnapshotStruct = undefined; 47 } 48 req.context!.closePath(); 49 } 50} 51export function snapshot( 52 list: Array<SnapshotStruct>, 53 filter: Array<SnapshotStruct>, 54 maxValue: number, 55 startNs: number, 56 totalNs: number, 57 frame: Rect 58): void { 59 for (let file of list) { 60 SnapshotStruct.setFrame(file, maxValue, startNs || 0, totalNs || 0, frame); 61 } 62 filter.length = 0; 63 for (let i = 0, len = list.length; i < len; i++) { 64 if (list[i].frame) { 65 filter.push(list[i]); 66 } 67 } 68} 69const padding = 2; 70export class SnapshotStruct extends BaseStruct { 71 startNs: number = 0; 72 endNs: number = 0; 73 dur: number = 0; 74 name: string = ''; 75 textWidth: number = 0; 76 value: number = 0; 77 type: string = ''; 78 static hoverSnapshotStruct: SnapshotStruct | undefined; 79 static selectSnapshotStruct: SnapshotStruct | undefined; 80 static setFrame(node: SnapshotStruct, maxValue: number, startNs: number, totalNs: number, frame: Rect): void { 81 node.frame = undefined; 82 frame.height = 40 - padding * 2; 83 // sample_interval单位是ms,startNs和endNs单位是纳秒,每一次采样的时间按采样间隔的五分之一算 84 node.dur = MemoryConfig.getInstance().snapshotDur; 85 node.endNs = node.startNs + node.dur; 86 if ((node.startNs - startNs || node.startNs - startNs === 0) && node.endNs - node.startNs) { 87 let rectangle: Rect = new Rect( 88 Math.floor(((node.startNs - startNs) / totalNs) * frame.width), 89 Math.floor(((maxValue - node.value) / maxValue) * frame.height), 90 Math.ceil(((node.endNs - node.startNs) / totalNs) * frame.width), 91 Math.ceil((node.value / maxValue) * frame.height) 92 ); 93 node.frame = rectangle; 94 } 95 if (node.value === 0) { 96 let rectangle: Rect = new Rect( 97 Math.floor(((node.startNs - startNs) / totalNs) * frame.width), 98 30, 99 Math.ceil((node.dur / totalNs) * frame.width), 100 1 101 ); 102 node.frame = rectangle; 103 } 104 } 105 static draw(ctx: CanvasRenderingContext2D, data: SnapshotStruct): void { 106 if (data.frame) { 107 ctx.fillStyle = 'rgb(86,192,197)'; 108 ctx!.fillRect(data.frame!.x, data.frame!.y + padding, data.frame!.width, data.frame!.height); 109 if (data.frame!.width > 7) { 110 ctx.globalAlpha = 1.0; 111 ctx.lineWidth = 1; 112 ctx.fillStyle = '#fff'; 113 ctx.textBaseline = 'middle'; 114 if (data.frame!.height > 10 && data.frame!.height < 25) { 115 SnapshotStruct.drawString(ctx, data.name || '', 4, data.frame!, data, 4); 116 } else if (data.frame!.height > 25) { 117 SnapshotStruct.drawString(ctx, data.name || '', 4, data.frame!, data, 4); 118 SnapshotStruct.drawString(ctx, Utils.getBinaryByteWithUnit(data.value || 0), 11, data.frame!, data, 2); 119 } 120 } 121 if (SnapshotStruct.selectSnapshotStruct && SnapshotStruct.equals(SnapshotStruct.selectSnapshotStruct, data)) { 122 ctx.strokeStyle = '#232c5d'; 123 ctx.lineWidth = 2; 124 ctx.strokeRect(data.frame!.x, data.frame!.y + padding, data.frame!.width - 2, data.frame!.height); 125 } 126 } 127 } 128 /** 129 * 130 * @param ctx current context 131 * @param str text 132 * @param textPadding padding 133 * @param frame rectangle 134 * @param data PurgeableStruct 135 * @param location the position of the string, the bigger the numerical value, the higher the position on the canvas 136 */ 137 static drawString( 138 ctx: CanvasRenderingContext2D, 139 str: string, 140 textPadding: number, 141 frame: Rect, 142 data: SnapshotStruct, 143 location: number 144 ): void { 145 if (data.textWidth === undefined) { 146 data.textWidth = ctx.measureText(str).width; 147 } 148 let textWidth = Math.round(data.textWidth / str.length); 149 let fillTextWidth = frame.width - textPadding * 2; 150 if (data.textWidth < fillTextWidth) { 151 let x = Math.floor(frame.width / 2 - data.textWidth / 2 + frame.x + textPadding); 152 ctx.fillText(str, x, Math.floor(frame.y + frame.height / location + textPadding), fillTextWidth); 153 } else { 154 if (fillTextWidth >= textWidth) { 155 let characterNum = fillTextWidth / textWidth; 156 let x = frame.x + textPadding; 157 if (characterNum < 2) { 158 ctx.fillText( 159 str.substring(0, 1), 160 x, 161 Math.floor(frame.y + frame.height / location + textPadding), 162 fillTextWidth 163 ); 164 } else { 165 ctx.fillText( 166 `${str.substring(0, characterNum - 1)}...`, 167 x, 168 Math.floor(frame.y + frame.height / location + textPadding), 169 fillTextWidth 170 ); 171 } 172 } 173 } 174 } 175 static equals(baseSnapshot: SnapshotStruct, targetSnapshot: SnapshotStruct): boolean { 176 return baseSnapshot === targetSnapshot; 177 } 178} 179