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