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 { SpSystemTrace } from '../SpSystemTrace'; 17import { TraceRow } from '../trace/base/TraceRow'; 18import { renders } from '../../database/ui-worker/ProcedureWorker'; 19import { 20 GpuCounterStruct, 21 MaleoonCounterObj, 22 GpuCounterType, 23 GpuCounterRender, 24} from '../../database/ui-worker/ProcedureWorkerGpuCounter'; 25import { folderSupplier, folderThreadHandler } from './SpChartManager'; 26import { queryRangeTime } from '../../database/sql/SqlLite.sql'; 27 28export class SpGpuCounterChart { 29 trace: SpSystemTrace; 30 // @ts-ignore 31 private folderRow: TraceRow<unknown> | undefined; 32 constructor(trace: SpSystemTrace) { 33 this.trace = trace; 34 } 35 36 async init(res: Array<unknown>): Promise<void> { 37 if (res.length === 0) { 38 let startTime = await queryRangeTime(); 39 this.initFolder(res, false); 40 //@ts-ignore 41 this.addTraceRowEventListener(startTime[0].start_ts, startTime[0].end_ts); 42 } else { 43 // @ts-ignore 44 const { gpuCounterType, start_time } = this.handleCsvData(res); 45 this.initFolder(res, true); 46 await this.initGpuCounters(gpuCounterType, start_time); 47 } 48 } 49 50 initFolder(res: Array<unknown>, isSimpleUpload: boolean): void { 51 this.folderRow = TraceRow.skeleton(); 52 this.folderRow.rowId = 'GpuCounter'; 53 this.folderRow.index = 0; 54 this.folderRow.rowType = TraceRow.ROW_TYPE_GPU_COUNTER_GROUP; 55 this.folderRow.rowParentId = ''; 56 this.folderRow.style.height = '40px'; 57 this.folderRow.rowHidden = this.folderRow!.expansion; 58 this.folderRow.setAttribute('children', ''); 59 this.folderRow.folder = res.length > 0 ? true : false; 60 this.folderRow.name = 'Gpu counter'; 61 //@ts-ignore 62 this.folderRow.supplier = folderSupplier(); 63 // @ts-ignore 64 this.folderRow.onThreadHandler = folderThreadHandler(this.folderRow, this.trace); 65 if (!isSimpleUpload) { 66 this.folderRow.addRowSampleUpload('.csv'); 67 } 68 this.folderRow.addEventListener('expansion-change', this.trace.extracted(this.folderRow)); 69 this.trace.rowsEL?.appendChild(this.folderRow); 70 } 71 72 async initGpuCounters(gpuCounterType: unknown, start_time: number): Promise<void> { 73 // @ts-ignore 74 for (const key in gpuCounterType) { 75 let typeRows = TraceRow.skeleton(); 76 typeRows.rowId = key; 77 typeRows.rowType = TraceRow.ROW_TYPE_GPU_COUNTER; 78 typeRows.rowParentId = this.folderRow?.rowId; 79 typeRows.folder = true; 80 typeRows.folderTextLeft = 20; 81 typeRows.rowHidden = !this.folderRow!.expansion; 82 typeRows.style.height = '40px'; 83 typeRows.name = `${key}`; 84 typeRows.selectChangeHandler = this.trace.selectChangeHandler; 85 //@ts-ignore 86 typeRows.supplier = folderSupplier(); 87 typeRows.onThreadHandler = folderThreadHandler(typeRows, this.trace); 88 this.folderRow!.addChildTraceRow(typeRows); 89 // @ts-ignore 90 this.initTypeRow(gpuCounterType[key], key, typeRows, start_time); 91 } 92 } 93 94 initTypeRow(rowList: unknown, key: string, parentRow: unknown, start_time: number): void { 95 const typeName = this.getKeyTypeName(key); 96 // @ts-ignore 97 for (let i = 0; i < rowList.length; i++) { 98 let typeRow = TraceRow.skeleton<GpuCounterStruct>(); 99 // @ts-ignore 100 let maxValue = Math.max(...rowList[i].map((it: GpuCounterStruct) => Number(it.height))); 101 typeRow.rowId = `${typeName[i]}`; 102 typeRow.rowType = TraceRow.ROW_TYPE_GPU_COUNTER; 103 typeRow.rowParentId = key; 104 typeRow.folder = false; 105 typeRow.folderTextLeft = 40; 106 typeRow.style.height = '40px'; 107 // @ts-ignore 108 typeRow.rowHidden = !parentRow.expansion; 109 typeRow.setAttribute('children', ''); 110 typeRow.name = `${typeName[i]}`; 111 typeRow.selectChangeHandler = this.trace.selectChangeHandler; 112 typeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 113 typeRow.focusHandler = (): void => this.focusHandler(typeRow, GpuCounterStruct.hoverGpuCounterStruct!); 114 typeRow.findHoverStruct = (): void => { 115 GpuCounterStruct.hoverGpuCounterStruct = typeRow.getHoverStruct(false); 116 }; 117 typeRow.supplierFrame = (): Promise<GpuCounterStruct[]> => 118 new Promise((resolve): void => { 119 // @ts-ignore 120 resolve(rowList[i]); 121 }); 122 typeRow.onThreadHandler = (useCache): void => { 123 let context: CanvasRenderingContext2D; 124 if (typeRow.currentContext) { 125 context = typeRow.currentContext; 126 } else { 127 context = typeRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 128 } 129 typeRow.canvasSave(context); 130 (renders.gpuCounter as GpuCounterRender).renderMainThread( 131 { 132 context: context, 133 useCache: useCache, 134 type: `${typeName[i]}`, 135 startTime: start_time, 136 maxValue: maxValue, 137 }, 138 typeRow 139 ); 140 typeRow.canvasRestore(context); 141 }; 142 // @ts-ignore 143 parentRow.addChildTraceRow(typeRow); 144 } 145 } 146 147 focusHandler(row: TraceRow<GpuCounterStruct>, struct: GpuCounterStruct): void { 148 let tip = ''; 149 if (struct) { 150 tip = ` 151 <span> ${struct.height}</span> 152 `; 153 } 154 this.trace?.displayTip(row, struct, tip); 155 } 156 157 getKeyTypeName(key: string): Array<string> { 158 const typeName: { [key: string]: Array<string> } = { 159 cycle: ['gpu clocks', 'tiler utilization', 'binning utilization', 'rendering utilization', 'compute utilization'], 160 drawcall: [ 161 'drawcall count', 162 'vertex count', 163 'primitives count', 164 'visible primitives count', 165 'compute invocations count', 166 ], 167 shader_cycle: [ 168 'shader utilization', 169 'eu utilization', 170 'eu stall utilization', 171 'eu idle utilization', 172 'control flow instr utilization', 173 'half float instr utilization', 174 'tu utilization', 175 ], 176 local_count: ['concurrent warps', 'instruction count', 'quads count', 'texels count'], 177 local_wr: ['memory read', 'memory write', 'memory traffic'], 178 }; 179 return typeName[key]; 180 } 181 182 handleCsvData(res: unknown, start_ts: number = 0, end_ts: number = 0): unknown { 183 // @ts-ignore 184 const minIndex = this.getMinData(res) + 1; 185 const gpuCounterMap = this.initGpuCounterMap(); 186 // @ts-ignore 187 this.maleoon_gpu_counter_init(gpuCounterMap, res[0]); 188 // @ts-ignore 189 let start_time_data = res[minIndex].split(','); 190 // @ts-ignore 191 let start_time = Number(start_time_data[gpuCounterMap.timestamp]); 192 let last_record_time = 0; 193 // @ts-ignore 194 let maleoonCounter = new maleoon_counter_obj(); 195 // @ts-ignore 196 let read_line_num = res.length - 1; 197 let utilization_array = [ 198 'tiler_utilization', 199 'binning_utilization', 200 'rendering_utilization', 201 'compute_utilization', 202 'shader_utilization', 203 'eu_utilization', 204 'eu_stall_utilization', 205 'eu_idle_utilization', 206 'control_flow_instr_utilization', 207 'half_float_instr_utilization', 208 'tu_utilization', 209 'concurrent_warps', 210 ]; 211 212 let count_array = [ 213 'gpu_clocks', 214 'drawcall_count', 215 'vertex_count', 216 'primitives_count', 217 'visible_primitives_count', 218 'instruction_count', 219 'quads_count', 220 'texels_count', 221 'compute_invocations_count', 222 'memory_read', 223 'memory_write', 224 'memory_traffic', 225 ]; 226 227 for (let i = minIndex; i < read_line_num; i++) { 228 // @ts-ignore 229 let datas = res[i].split(','); 230 if (datas.length !== 25) { 231 continue; 232 } 233 if ( 234 // @ts-ignore 235 (start_ts > 0 && Number(datas[gpuCounterMap.timestamp]) < start_ts) || 236 // @ts-ignore 237 (end_ts > 0 && Number(datas[gpuCounterMap.timestamp]) > end_ts) 238 ) { 239 continue; 240 } 241 // @ts-ignore 242 let time_passed = Number(datas[gpuCounterMap.timestamp]) - start_time; 243 //去重 244 if (time_passed <= last_record_time) { 245 continue; 246 } 247 time_passed -= last_record_time; 248 last_record_time += time_passed; 249 utilization_array.forEach((item) => { 250 maleoonCounter[item].push({ 251 // @ts-ignore 252 startNS: Number(datas[gpuCounterMap.timestamp]), 253 // @ts-ignore 254 height: gpuCounterMap[item] === -1 ? 0 : Math.floor(Number(datas[gpuCounterMap[item]])), 255 }); 256 }); 257 count_array.forEach((item) => { 258 maleoonCounter[item].push({ 259 // @ts-ignore 260 startNS: Number(datas[gpuCounterMap.timestamp]), 261 // @ts-ignore 262 height: gpuCounterMap[item] === -1 ? 0 : Math.floor(Number(datas[gpuCounterMap[item]])), 263 }); 264 }); 265 } 266 utilization_array.forEach((item) => { 267 for (let i = 0; i < maleoonCounter[item].length; i++) { 268 //@ts-ignore 269 maleoonCounter[item][i].dur = maleoonCounter[item][i + 1]?.startNS - maleoonCounter[item][i].startNS || 0; 270 } 271 }); 272 count_array.forEach((item) => { 273 for (let i = 0; i < maleoonCounter[item].length; i++) { 274 //@ts-ignore 275 maleoonCounter[item][i].dur = maleoonCounter[item][i + 1]?.startNS - maleoonCounter[item][i].startNS || 0; 276 } 277 }); 278 const gpuCounterType = this.groupByGpuCounterType(maleoonCounter); 279 return { gpuCounterType, start_time }; 280 } 281 282 initGpuCounterMap(): unknown { 283 let gpu_counter_map = { 284 timestamp: -1, 285 286 gpu_clocks: -1, 287 tiler_utilization: -1, 288 binning_utilization: -1, 289 rendering_utilization: -1, 290 compute_utilization: -1, 291 292 drawcall_count: -1, 293 vertex_count: -1, 294 primitives_count: -1, 295 visible_primitives_count: -1, 296 compute_invocations_count: -1, 297 298 shader_utilization: -1, 299 eu_utilization: -1, 300 eu_stall_utilization: -1, 301 eu_idle_utilization: -1, 302 control_flow_instr_utilization: -1, 303 half_float_instr_utilization: -1, 304 tu_utilization: -1, 305 306 concurrent_warps: -1, 307 instruction_count: -1, 308 quads_count: -1, 309 texels_count: -1, 310 311 memory_read: -1, 312 memory_write: -1, 313 memory_traffic: -1, 314 }; 315 return gpu_counter_map; 316 } 317 318 maleoon_gpu_counter_init(gpu_counter_map: unknown, head_line: string): void { 319 // @ts-ignore 320 gpu_counter_map.timestamp = -1; 321 // @ts-ignore 322 gpu_counter_map.gpu_clocks = -1; 323 // @ts-ignore 324 gpu_counter_map.tiler_utilization = -1; 325 // @ts-ignore 326 gpu_counter_map.binning_utilization = -1; 327 // @ts-ignore 328 gpu_counter_map.rendering_utilization = -1; 329 // @ts-ignore 330 gpu_counter_map.compute_utilization = -1; 331 332 // @ts-ignore 333 gpu_counter_map.drawcall_count = -1; 334 // @ts-ignore 335 gpu_counter_map.vertex_count = -1; 336 // @ts-ignore 337 gpu_counter_map.primitives_count = -1; 338 // @ts-ignore 339 gpu_counter_map.visible_primitives_count = -1; 340 // @ts-ignore 341 gpu_counter_map.compute_invocations_count = -1; 342 343 // @ts-ignore 344 gpu_counter_map.shader_utilization = -1; 345 // @ts-ignore 346 gpu_counter_map.eu_utilization = -1; 347 // @ts-ignore 348 gpu_counter_map.eu_stall_utilization = -1; 349 // @ts-ignore 350 gpu_counter_map.eu_idle_utilization = -1; 351 // @ts-ignore 352 gpu_counter_map.control_flow_instr_utilization = -1; 353 // @ts-ignore 354 gpu_counter_map.half_float_instr_utilization = -1; 355 // @ts-ignore 356 gpu_counter_map.tu_utilization = -1; 357 358 // @ts-ignore 359 gpu_counter_map.concurrent_warps = -1; 360 // @ts-ignore 361 gpu_counter_map.instruction_count = -1; 362 // @ts-ignore 363 gpu_counter_map.quads_count = -1; 364 // @ts-ignore 365 gpu_counter_map.texels_count = -1; 366 367 // @ts-ignore 368 gpu_counter_map.memory_read = -1; 369 // @ts-ignore 370 gpu_counter_map.memory_write = -1; 371 // @ts-ignore 372 gpu_counter_map.memory_traffic = -1; 373 374 let paras = head_line.split(','); 375 for (let i = 0; i < paras.length; i++) { 376 // @ts-ignore 377 if (paras[i] === 'TIMESTAMP') { 378 // @ts-ignore 379 gpu_counter_map.timestamp = i; 380 } 381 382 if (paras[i] === 'GPU Clocks') { 383 // @ts-ignore 384 gpu_counter_map.gpu_clocks = i; 385 } 386 if (paras[i] === 'Tiler Utilization') { 387 // @ts-ignore 388 gpu_counter_map.tiler_utilization = i; 389 } 390 if (paras[i] === 'Binning Queue Utilization') { 391 // @ts-ignore 392 gpu_counter_map.binning_utilization = i; 393 } 394 if (paras[i] === 'Rendering Queue Utilization') { 395 // @ts-ignore 396 gpu_counter_map.rendering_utilization = i; 397 } 398 if (paras[i] === 'Compute Queue Utilization') { 399 // @ts-ignore 400 gpu_counter_map.compute_utilization = i; 401 } 402 403 if (paras[i] === 'Drawcalls Count') { 404 // @ts-ignore 405 gpu_counter_map.drawcall_count = i; 406 } 407 if (paras[i] === 'Vertex Count') { 408 // @ts-ignore 409 gpu_counter_map.vertex_count = i; 410 } 411 if (paras[i] === 'Primitive Count') { 412 // @ts-ignore 413 gpu_counter_map.primitives_count = i; 414 } 415 if (paras[i] === 'Visible Primitive Count') { 416 // @ts-ignore 417 gpu_counter_map.visible_primitives_count = i; 418 } 419 if (paras[i] === 'Compute Shader Invocations') { 420 // @ts-ignore 421 gpu_counter_map.compute_invocations_count = i; 422 } 423 424 if (paras[i] === 'Shader Core Utilization') { 425 // @ts-ignore 426 gpu_counter_map.shader_utilization = i; 427 } 428 if (paras[i] === 'EU Utilization') { 429 // @ts-ignore 430 gpu_counter_map.eu_utilization = i; 431 } 432 if (paras[i] === 'EU Stall') { 433 // @ts-ignore 434 gpu_counter_map.eu_stall_utilization = i; 435 } 436 if (paras[i] === 'EU Idle') { 437 // @ts-ignore 438 gpu_counter_map.eu_idle_utilization = i; 439 } 440 if (paras[i] === 'Instructions Diverged') { 441 // @ts-ignore 442 gpu_counter_map.control_flow_instr_utilization = i; 443 } 444 if (paras[i] === 'Half-float Instructions') { 445 // @ts-ignore 446 gpu_counter_map.half_float_instr_utilization = i; 447 } 448 if (paras[i] === 'TU Utilization') { 449 // @ts-ignore 450 gpu_counter_map.tu_utilization = i; 451 } 452 453 if (paras[i] === 'Concurrent Warps') { 454 // @ts-ignore 455 gpu_counter_map.concurrent_warps = i; 456 } 457 if (paras[i] === 'Instructions Executed') { 458 // @ts-ignore 459 gpu_counter_map.instruction_count = i; 460 } 461 if (paras[i] === 'Quads Shaded') { 462 // @ts-ignore 463 gpu_counter_map.quads_count = i; 464 } 465 if (paras[i] === 'Texels Sampled') { 466 // @ts-ignore 467 gpu_counter_map.texels_count = i; 468 } 469 470 if (paras[i] === 'External Memory Read') { 471 // @ts-ignore 472 gpu_counter_map.memory_read = i; 473 } 474 if (paras[i] === 'External Memory Write') { 475 // @ts-ignore 476 gpu_counter_map.memory_write = i; 477 } 478 if (paras[i] === 'External Memory Traffic') { 479 // @ts-ignore 480 gpu_counter_map.memory_traffic = i; 481 } 482 } 483 } 484 485 groupByGpuCounterType(maleoonCounter: MaleoonCounterObj): GpuCounterType { 486 const gpuCounterType = new GpuCounterType(); 487 let index = 0; 488 for (const key in maleoonCounter) { 489 if (index < 5) { 490 gpuCounterType.cycle.push(maleoonCounter[key]); 491 } 492 if (index >= 5 && index < 10) { 493 gpuCounterType.drawcall.push(maleoonCounter[key]); 494 } 495 if (index >= 10 && index < 17) { 496 gpuCounterType.shader_cycle.push(maleoonCounter[key]); 497 } 498 if (index >= 17 && index < 21) { 499 gpuCounterType.local_count.push(maleoonCounter[key]); 500 } 501 if (index >= 21 && index < 24) { 502 gpuCounterType.local_wr.push(maleoonCounter[key]); 503 } 504 index++; 505 } 506 return gpuCounterType; 507 } 508 509 /** 510 * 监听文件上传事件 511 * @param row 512 * @param start_ts 513 */ 514 addTraceRowEventListener(startTime: number, endTime: number): void { 515 this.folderRow?.uploadEl?.addEventListener('sample-file-change', (e: unknown) => { 516 this.getCsvData(e).then((res: unknown) => { 517 this.resetChartData(this.folderRow!); 518 // @ts-ignore 519 const { gpuCounterType } = this.handleCsvData(res, startTime, endTime); 520 this.initGpuCounters(gpuCounterType, startTime); 521 if (!this.folderRow!.folder) { 522 this.folderRow!.folder = true; 523 } 524 }); 525 }); 526 } 527 528 /** 529 * 清空缓存 530 * @param row 531 */ 532 // @ts-ignore 533 resetChartData(row: TraceRow<unknown>): void { 534 if (row.expansion) { 535 row.describeEl?.click(); 536 } 537 row.childrenList = []; 538 } 539 540 getMinData(list: Array<unknown>): number { 541 // @ts-ignore 542 const sliceList = list.slice(1, 11).map((item) => Number(item.split(',')[0])); 543 const nonZeroList = sliceList.filter((item) => item !== 0).sort((a, b) => a - b); 544 const minIndex = sliceList.findIndex((item) => item === nonZeroList[0]); 545 return minIndex; 546 } 547 548 /** 549 * 获取上传的文件内容 转为json格式 550 * @param file 551 * @returns 552 */ 553 getCsvData(file: unknown): Promise<unknown> { 554 return new Promise((resolve, reject) => { 555 let reader = new FileReader(); 556 // @ts-ignore 557 reader.readAsText(file.detail || file); 558 reader.onloadend = (e: unknown): void => { 559 // @ts-ignore 560 const fileContent = e.target?.result.split(/[\r\n]/).filter(Boolean); 561 try { 562 resolve(fileContent); 563 document.dispatchEvent(new CustomEvent('file-correct')); 564 } catch (error) { 565 document.dispatchEvent(new CustomEvent('file-error')); 566 } 567 }; 568 }); 569 } 570} 571