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 { TraceRow } from '../trace/base/TraceRow.js'; 17import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 18import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank.js'; 19import { SpSystemTrace } from '../SpSystemTrace.js'; 20import { 21 queryActualFrameDate, 22 queryExpectedFrameDate, 23 queryFrameAnimationData, 24 queryFrameApp, 25 queryFrameDynamicData, 26 queryFrameSpacing, 27 queryFrameTimeData, 28 queryPhysicalData 29} from '../../database/SqlLite.js'; 30import { JanksStruct } from '../../bean/JanksStruct.js'; 31import { ns2xByTimeShaft, PairPoint } from '../../database/ui-worker/ProcedureWorkerCommon.js'; 32import { LitPopover } from '../../../base-ui/popover/LitPopoverV.js'; 33import { 34 FrameDynamicRender, 35 FrameDynamicStruct 36} from '../../database/ui-worker/ProcedureWorkerFrameDynamic.js'; 37import { 38 FrameAnimationRender, 39 FrameAnimationStruct 40} from '../../database/ui-worker/ProcedureWorkerFrameAnimation.js'; 41import { BaseStruct } from '../../bean/BaseStruct.js'; 42import { 43 FrameSpacingRender, 44 FrameSpacingStruct 45} from '../../database/ui-worker/ProcedureWorkerFrameSpacing.js'; 46import { FlagsConfig, Params } from '../SpFlags.js'; 47import { AnimationRanges, DeviceStruct } from '../../bean/FrameComponentBean.js'; 48import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js'; 49 50export class SpFrameTimeChart { 51 private trace: SpSystemTrace; 52 private flagConfig: Params | undefined; 53 54 constructor(trace: SpSystemTrace) { 55 this.trace = trace; 56 } 57 58 async init(): Promise<void> { 59 let frameTimeData = await queryFrameTimeData(); 60 if (frameTimeData.length > 0) { 61 let frameTimeLineRow: TraceRow<JanksStruct> = await this.initFrameTimeLine(); 62 await this.initExpectedChart(frameTimeLineRow); 63 await this.initActualChart(frameTimeLineRow); 64 } 65 } 66 67 async initFrameTimeLine(): Promise<TraceRow<JanksStruct>> { 68 let frameTimeLineRow: TraceRow<JanksStruct> = TraceRow.skeleton<JanksStruct>(); 69 frameTimeLineRow.rowId = 'frameTime'; 70 frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 71 frameTimeLineRow.rowParentId = ''; 72 frameTimeLineRow.style.width = '100%'; 73 frameTimeLineRow.style.height = '40px'; 74 frameTimeLineRow.folder = true; 75 frameTimeLineRow.name = 'FrameTimeline'; 76 frameTimeLineRow.setAttribute('children', ''); 77 frameTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve) => { 78 resolve([]); 79 }); 80 frameTimeLineRow.addTemplateTypes('AppStartup'); 81 frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 82 frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 83 frameTimeLineRow.onThreadHandler = (useCache: boolean): void => { 84 let context: CanvasRenderingContext2D = frameTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : 85 this.trace.canvasPanelCtx!; 86 frameTimeLineRow!.canvasSave(context); 87 (renders.jank as JankRender).renderMainThread({ 88 context: context, useCache: useCache, type: 'expected_frame_timeline_slice' 89 }, frameTimeLineRow!); 90 frameTimeLineRow!.canvasRestore(context); 91 }; 92 this.trace.rowsEL?.appendChild(frameTimeLineRow); 93 return frameTimeLineRow; 94 } 95 96 async initExpectedChart( 97 frameTimeLineRow: TraceRow<JanksStruct> 98 ): Promise<void> { 99 let frameExpectedData = await this.getExpectedFrameDate(); 100 let unitIndex: number = 1; 101 let unitHeight: number = 20; 102 let max: number = Math.max(...frameExpectedData.map((it) => it.depth || 0)) + unitIndex; 103 let maxHeight: number = max * unitHeight; 104 let expectedTimeLineRow = TraceRow.skeleton<JanksStruct>(); 105 expectedTimeLineRow.rowId = 'expected frameTime'; 106 expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 107 expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion; 108 expectedTimeLineRow.rowParentId = 'frameTime'; 109 expectedTimeLineRow.style.width = '100%'; 110 expectedTimeLineRow.style.height = '40px'; 111 expectedTimeLineRow.style.height = `${maxHeight}px`; 112 expectedTimeLineRow.name = 'Expected Timeline'; 113 expectedTimeLineRow.addTemplateTypes('FrameTimeline'); 114 expectedTimeLineRow.setAttribute('height', `${maxHeight}`); 115 expectedTimeLineRow.setAttribute('children', ''); 116 expectedTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve): void => { 117 resolve(frameExpectedData); 118 }); 119 expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 120 expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 121 expectedTimeLineRow.onThreadHandler = (useCache: boolean): void => { 122 let context: CanvasRenderingContext2D = expectedTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : 123 this.trace.canvasPanelCtx!; 124 expectedTimeLineRow!.canvasSave(context); 125 (renders.jank as JankRender).renderMainThread({ 126 context: context, useCache: useCache, type: 'expected_frame_timeline_slice' 127 }, expectedTimeLineRow!); 128 expectedTimeLineRow!.canvasRestore(context); 129 }; 130 frameTimeLineRow.addChildTraceRow(expectedTimeLineRow); 131 } 132 133 async initActualChart( 134 frameTimeLineRow: TraceRow<JanksStruct> 135 ): Promise<void> { 136 let frameActualData = await this.getActualFrameDate(); 137 let unitIndex: number = 1; 138 let unitHeight: number = 20; 139 let maxHeight: number = (Math.max(...frameActualData.map((it) => it.depth || 0)) + unitIndex) * unitHeight; 140 let actualTimeLineRow = TraceRow.skeleton<JanksStruct>(); 141 actualTimeLineRow.rowId = 'actual frameTime'; 142 actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 143 actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion; 144 actualTimeLineRow.rowParentId = 'frameTime'; 145 actualTimeLineRow.style.width = '100%'; 146 actualTimeLineRow.style.height = `${maxHeight}px`; 147 actualTimeLineRow.name = 'Actual Timeline'; 148 actualTimeLineRow.addTemplateTypes('FrameTimeline'); 149 actualTimeLineRow.setAttribute('height', `${maxHeight}`); 150 actualTimeLineRow.setAttribute('children', ''); 151 actualTimeLineRow.dataList = frameActualData; 152 actualTimeLineRow.supplier = (): Promise<JanksStruct[]> => new Promise((resolve): void => { 153 resolve(frameActualData); 154 }); 155 actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 156 actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 157 actualTimeLineRow.onThreadHandler = (useCache: boolean): void => { 158 let context: CanvasRenderingContext2D = actualTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : 159 this.trace.canvasPanelCtx!; 160 actualTimeLineRow!.canvasSave(context); 161 (renders.jank as JankRender).renderMainThread({ 162 context: context, useCache: useCache, type: 'expected_frame_timeline_slice' 163 }, actualTimeLineRow!); 164 actualTimeLineRow!.canvasRestore(context); 165 }; 166 frameTimeLineRow.addChildTraceRow(actualTimeLineRow); 167 let offsetYTimeOut: number = 0; 168 frameTimeLineRow.addEventListener('expansion-change', (customEventInit: CustomEventInit) => { 169 JankStruct.delJankLineFlag = false; 170 if (offsetYTimeOut) { 171 clearTimeout(offsetYTimeOut); 172 } 173 if (customEventInit.detail?.expansion) { 174 offsetYTimeOut = this.frameExpandTimeOut(customEventInit, actualTimeLineRow); 175 } else { 176 offsetYTimeOut = this.frameNoExpandTimeOut(customEventInit, frameTimeLineRow); 177 } 178 }); 179 } 180 181 async initAnimatedScenesChart( 182 processRow: TraceRow<BaseStruct>, 183 process: { pid: number | null; processName: string | null; }, 184 firstRow: TraceRow<BaseStruct> 185 ): Promise<void> { 186 this.flagConfig = FlagsConfig.getFlagsConfig('AnimationAnalysis'); 187 if (this.flagConfig?.AnimationAnalysis === 'Enabled') { 188 if (process.processName?.startsWith('render_service')) { 189 let targetRowList = processRow.childrenList.filter(childRow => 190 childRow.rowType === 'thread' && childRow.name.startsWith('render_service')); 191 let nameArr: { name: string }[] = await queryFrameApp(); 192 if (nameArr && nameArr.length > 0) { 193 let currentName = nameArr[0].name; 194 let frameChart = await this.initFrameChart(processRow, nameArr); 195 processRow.addChildTraceRowAfter(frameChart, targetRowList[0]); 196 let animationRanges = await this.initAnimationChart(processRow, firstRow); 197 await this.initDynamicCurveChart(frameChart, currentName, animationRanges); 198 await this.initFrameSpacing(nameArr, frameChart, currentName, animationRanges); 199 } 200 } 201 } 202 } 203 204 private async initFrameChart( 205 processRow: TraceRow<BaseStruct>, 206 nameArr: { name: string }[] 207 ): Promise<TraceRow<BaseStruct>> { 208 let frameChart: TraceRow<BaseStruct> = TraceRow.skeleton<BaseStruct>(); 209 let systemPopover = this.addSystemConfigButton(frameChart, nameArr); 210 systemPopover.style.zIndex = '101'; 211 let radioList = systemPopover.querySelectorAll<HTMLInputElement>('.radio'); 212 let divElement = systemPopover.querySelectorAll<HTMLDivElement>('.option'); 213 radioList[0].checked = true; 214 divElement.forEach((divEl, index) => { 215 divEl.addEventListener('click', () => { 216 if (radioList[index]) { 217 radioList[index].checked = true; 218 frameChart.name = radioList[index].value; 219 frameChart.childrenList.forEach(childrenRow => { 220 childrenRow.setAttribute('model-name', `${radioList[index].value}`); 221 }); 222 systemPopover.blur(); 223 TraceRow.range!.refresh = true; 224 this.trace.refreshCanvas(true); 225 this.trace.clickEmptyArea(); 226 } 227 }); 228 }); 229 frameChart.rowId = 'frame'; 230 frameChart.rowType = TraceRow.ROW_TYPE_FRAME; 231 frameChart.rowHidden = !processRow.expansion; 232 frameChart.rowParentId = processRow.rowId; 233 frameChart.style.width = '100%'; 234 frameChart.style.height = '40px'; 235 frameChart.folder = true; 236 frameChart.name = nameArr[0].name; 237 frameChart.setAttribute('children', ''); 238 frameChart.supplier = (): Promise<BaseStruct[]> => new Promise((resolve) => { 239 resolve([]); 240 }); 241 frameChart.favoriteChangeHandler = this.trace.favoriteChangeHandler; 242 frameChart.selectChangeHandler = this.trace.selectChangeHandler; 243 frameChart.onThreadHandler = (useCache: boolean): void => { 244 let context: CanvasRenderingContext2D = frameChart!.collect ? this.trace.canvasFavoritePanelCtx! : 245 this.trace.canvasPanelCtx!; 246 frameChart!.canvasSave(context); 247 (renders.empty as EmptyRender).renderMainThread({ 248 context: context, useCache: useCache, type: 'frame' 249 }, frameChart!); 250 frameChart!.canvasRestore(context); 251 }; 252 this.trace.rowsEL?.appendChild(frameChart); 253 return frameChart; 254 } 255 256 async initAnimationChart( 257 processRow: TraceRow<BaseStruct>, 258 firstRow: TraceRow<BaseStruct> 259 ): Promise<AnimationRanges[]> { 260 let frameAnimationData: FrameAnimationStruct[] = await queryFrameAnimationData(); 261 let animationRanges: AnimationRanges[] = []; 262 if (frameAnimationData.length > 0) { 263 frameAnimationData.forEach(data => { 264 let range = { 265 start: data.dynamicStartTs, end: data.dynamicEndTs 266 }; 267 animationRanges.push(range); 268 }); 269 } 270 let frameAnimationRow = TraceRow.skeleton<FrameAnimationStruct>(); 271 frameAnimationRow.rowId = 'Animation'; 272 frameAnimationRow.rowType = TraceRow.ROW_TYPE_FRAME_ANIMATION; 273 frameAnimationRow.rowHidden = !processRow.expansion; 274 frameAnimationRow.rowParentId = processRow.rowId; 275 frameAnimationRow.style.width = '100%'; 276 frameAnimationRow.style.height = '40px'; 277 frameAnimationRow.name = 'Animation'; 278 frameAnimationRow.addTemplateTypes('Animation Effect'); 279 frameAnimationRow.setAttribute('children', ''); 280 frameAnimationRow.supplier = (): Promise<FrameAnimationStruct[]> => new Promise((resolve) => { 281 resolve(frameAnimationData); 282 }); 283 frameAnimationRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 284 frameAnimationRow.selectChangeHandler = this.trace.selectChangeHandler; 285 frameAnimationRow.onThreadHandler = (useCache): void => { 286 let context: CanvasRenderingContext2D = frameAnimationRow!.collect ? this.trace.canvasFavoritePanelCtx! : 287 this.trace.canvasPanelCtx!; 288 frameAnimationRow!.canvasSave(context); 289 (renders.frameAnimation as FrameAnimationRender).renderMainThread({ 290 context: context, useCache: useCache, type: 'frameAnimation' 291 }, frameAnimationRow!); 292 frameAnimationRow!.canvasRestore(context); 293 }; 294 processRow.addChildTraceRowBefore(frameAnimationRow, firstRow); 295 return animationRanges; 296 } 297 298 async initDynamicCurveChart( 299 frameChart: TraceRow<BaseStruct>, 300 name: string, 301 animationRanges: AnimationRanges[] 302 ): Promise<void> { 303 let frameDynamicCurveData: FrameDynamicStruct[] = await queryFrameDynamicData(); 304 let systemConfigList: { 305 name: string 306 }[] = [{name: 'x'}, {name: 'y'}, {name: 'width'}, {name: 'height'}, {name: 'alpha'}]; 307 let dynamicCurveRow: TraceRow<FrameDynamicStruct> = TraceRow.skeleton<FrameDynamicStruct>(); 308 let systemPopover = this.addSystemConfigButton(dynamicCurveRow, systemConfigList); 309 this.initSystemConfig(systemPopover, dynamicCurveRow); 310 dynamicCurveRow.rowId = 'animation-Effect-Curve'; 311 dynamicCurveRow.rowType = TraceRow.ROW_TYPE_FRAME_DYNAMIC; 312 dynamicCurveRow.rowHidden = !frameChart.expansion; 313 dynamicCurveRow.rowParentId = frameChart.rowId; 314 dynamicCurveRow.style.width = '100%'; 315 dynamicCurveRow.style.height = '40px'; 316 dynamicCurveRow.style.height = '100px'; 317 let labelName = dynamicCurveRow.shadowRoot?.querySelector('.name') as HTMLLabelElement; 318 labelName.style.marginRight = '77px'; 319 dynamicCurveRow.name = 'Animation Effect Curve'; 320 dynamicCurveRow.addTemplateTypes('Animation Effect'); 321 dynamicCurveRow.setAttribute('height', '100px'); 322 dynamicCurveRow.setAttribute('children', ''); 323 dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name); 324 dynamicCurveRow.setAttribute('model-name', name); 325 dynamicCurveRow.supplier = (): Promise<FrameDynamicStruct[]> => new Promise((resolve): void => { 326 resolve(frameDynamicCurveData); 327 }); 328 dynamicCurveRow.favoriteChangeHandler = (): void => { 329 this.favoriteSelect(systemPopover, dynamicCurveRow); 330 }; 331 dynamicCurveRow.selectChangeHandler = this.trace.selectChangeHandler; 332 dynamicCurveRow.onThreadHandler = (useCache: boolean): void => { 333 let context: CanvasRenderingContext2D = dynamicCurveRow!.collect ? this.trace.canvasFavoritePanelCtx! : 334 this.trace.canvasPanelCtx!; 335 dynamicCurveRow!.canvasSave(context); 336 (renders.frameDynamicCurve as FrameDynamicRender).renderMainThread({ 337 context: context, 338 useCache: useCache, 339 type: 'dynamicEffectCurve', 340 animationRanges: animationRanges 341 }, dynamicCurveRow!); 342 dynamicCurveRow!.canvasRestore(context); 343 }; 344 frameChart.addChildTraceRow(dynamicCurveRow); 345 } 346 347 private initSystemConfig( 348 systemPopover: LitPopover, 349 dynamicCurveRow: TraceRow<FrameDynamicStruct> 350 ): void { 351 let radioList = systemPopover.querySelectorAll<HTMLInputElement>('.radio'); 352 let divElement = systemPopover.querySelectorAll<HTMLDivElement>('.option'); 353 radioList[0].checked = true; 354 dynamicCurveRow.setAttribute('model-type', radioList[0].value); 355 divElement.forEach((divEl, index) => { 356 divEl.addEventListener('click', () => { 357 if (radioList[index]) { 358 radioList[index].checked = true; 359 dynamicCurveRow.setAttribute('model-type', `${radioList[index].value}`); 360 systemPopover.blur(); 361 TraceRow.range!.refresh = true; 362 this.trace.refreshCanvas(true); 363 this.trace.clickEmptyArea(); 364 } 365 }); 366 }); 367 } 368 369 private favoriteSelect( 370 systemPopover: LitPopover, 371 dynamicCurveRow: TraceRow<FrameDynamicStruct> 372 ): void { 373 let popover = systemPopover.querySelector('.dynamicPopover') as HTMLDivElement; 374 if (dynamicCurveRow.collect) { 375 systemPopover.setAttribute('placement', 'right'); 376 popover.style.display = 'flex'; 377 } else { 378 systemPopover.setAttribute('placement', 'bottomLeft'); 379 popover.style.display = 'block'; 380 } 381 } 382 383 async initFrameSpacing( 384 nameArr: { name: string }[], 385 frameChart: TraceRow<BaseStruct>, 386 name: string, 387 animationRanges: AnimationRanges[] 388 ): Promise<void> { 389 let frameData: FrameSpacingStruct[] = await queryFrameSpacing(); 390 let deviceStructArray = await queryPhysicalData(); 391 let deviceStruct: DeviceStruct = deviceStructArray[0]; 392 let frameResultData: FrameSpacingStruct[] = []; 393 for (let index = 0; index < nameArr.length; index++) { 394 let appName: string = nameArr[index].name; 395 let filterData = frameData.filter(spacingData => spacingData.nameId === appName); 396 this.dataProcessing(filterData, deviceStruct); 397 frameResultData.push(...filterData); 398 } 399 let frameSpacingRow = TraceRow.skeleton<FrameSpacingStruct>(); 400 frameSpacingRow.rowId = 'frame spacing'; 401 frameSpacingRow.rowType = TraceRow.ROW_TYPE_FRAME_SPACING; 402 frameSpacingRow.rowHidden = !frameChart.expansion; 403 frameSpacingRow.rowParentId = frameChart.rowId; 404 frameSpacingRow.style.width = '100%'; 405 frameSpacingRow.style.height = '140px'; 406 frameSpacingRow.name = 'Frame spacing'; 407 frameSpacingRow.addTemplateTypes('Animation Effect'); 408 frameSpacingRow.setAttribute('height', '140'); 409 frameSpacingRow.setAttribute('children', ''); 410 frameSpacingRow.setAttribute('model-name', name); 411 frameSpacingRow.supplier = (): Promise<FrameSpacingStruct[]> => new Promise((resolve): void => { 412 resolve(frameResultData); 413 }); 414 frameSpacingRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 415 frameSpacingRow.selectChangeHandler = this.trace.selectChangeHandler; 416 frameSpacingRow.onThreadHandler = (useCache: boolean): void => { 417 let context = frameSpacingRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 418 frameSpacingRow!.canvasSave(context); 419 (renders.frameSpacing as FrameSpacingRender).renderMainThread({ 420 context: context, 421 useCache: useCache, 422 type: 'frame_spacing_slice', 423 frameRate: deviceStruct.physicalFrameRate, 424 animationRanges: animationRanges 425 }, frameSpacingRow!); 426 frameSpacingRow!.canvasRestore(context); 427 }; 428 frameChart.addChildTraceRow(frameSpacingRow); 429 } 430 431 dataProcessing( 432 frameData: FrameSpacingStruct[], 433 deviceStruct: DeviceStruct 434 ): void { 435 let unitIndex: number = 1; 436 let secondToNanosecond: number = 1000_000_000; 437 let physicalWidth = Number(this.flagConfig!.physicalWidth); 438 let physicalHeight = Number(this.flagConfig!.physicalHeight); 439 FrameSpacingStruct.physicalWidth = physicalWidth !== 0 ? physicalWidth : deviceStruct.physicalWidth; 440 FrameSpacingStruct.physicalHeight = physicalHeight !== 0 ? physicalHeight : deviceStruct.physicalHeight; 441 for (let index = 0; index < frameData.length; index++) { 442 if (index > 0) { 443 let intervalTime = (frameData[index].currentTs - frameData[index - unitIndex].currentTs) / secondToNanosecond; 444 let widthDifference = frameData[index].currentFrameWidth! - frameData[index - unitIndex].currentFrameWidth!; 445 let heightDifference = frameData[index].currentFrameHeight! - frameData[index - unitIndex].currentFrameHeight!; 446 let xDifference = frameData[index].x! - frameData[index - unitIndex].x!; 447 let yDifference = frameData[index].y! - frameData[index - unitIndex].y!; 448 let frameWidth = Math.abs(widthDifference / FrameSpacingStruct.physicalWidth / intervalTime); 449 let frameHeight = Math.abs(heightDifference / FrameSpacingStruct.physicalHeight / intervalTime); 450 let frameX = Math.abs(xDifference / FrameSpacingStruct.physicalWidth / intervalTime); 451 let frameY = Math.abs(yDifference / FrameSpacingStruct.physicalHeight / intervalTime); 452 let result = Math.max(frameWidth, frameHeight, frameX, frameY); 453 frameData[index].frameSpacingResult = Number(result.toFixed(unitIndex)); 454 frameData[index].preTs = frameData[index - unitIndex].currentTs; 455 frameData[index].preFrameWidth = frameData[index - unitIndex].currentFrameWidth; 456 frameData[index].preFrameHeight = frameData[index - unitIndex].currentFrameHeight; 457 } else { 458 frameData[index].frameSpacingResult = 0; 459 frameData[index].preTs = 0; 460 frameData[index].preFrameWidth = 0; 461 frameData[index].preFrameHeight = 0; 462 } 463 } 464 } 465 466 addSystemConfigButton( 467 systemTraceRow: TraceRow<BaseStruct>, 468 systemConfigList: { name: string }[] 469 ): LitPopover { 470 let rowContent: HTMLDivElement = systemTraceRow.shadowRoot?.querySelector('.describe') as HTMLDivElement; 471 let systemPopover: LitPopover = document.createElement('lit-popover') as LitPopover; 472 systemPopover.style.zIndex = '100'; 473 systemPopover.style.position = 'absolute'; 474 systemPopover.style.left = '165px'; 475 systemPopover.style.display = 'flex'; 476 systemPopover.setAttribute('placement', 'bottomLeft'); 477 systemPopover.setAttribute('trigger', 'click'); 478 systemPopover.setAttribute('haveRadio', 'true'); 479 systemPopover.innerHTML = ` 480 <div style="display: block; overflow: auto" slot="content" class="dynamicPopover"> 481 ${systemConfigList.map((it): string => ` 482 <div class="option" style="margin-bottom: 5px; color: black;"> 483 <input class="radio" name="status" type="radio" value='${it.name}' 484 style="margin-right: 10px;"/>${it.name}</div>`).join('')} 485 </div> 486 <lit-icon name="setting" size="19" id="setting"></lit-icon>`; 487 rowContent.appendChild(systemPopover); 488 return systemPopover; 489 } 490 491 private frameNoExpandTimeOut( 492 event: CustomEventInit<{ 493 expansion: boolean, 494 rowType: string, 495 rowId: string, 496 rowParentId: string 497 }>, 498 frameTimeLineRow: TraceRow<JanksStruct> 499 ): number { 500 if (JankStruct!.selectJankStruct) { 501 JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct); 502 } 503 let topPadding: number = 195; 504 let halfNumber: number = 2; 505 let offsetYTime: number = 300; 506 let refreshTime: number = 360; 507 let offsetYTimeOut: number = window.setTimeout(() => { 508 this.trace.linkNodes.forEach((linkNode: PairPoint[]) => { 509 if (linkNode[0].rowEL.collect) { 510 linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - topPadding; 511 } else { 512 linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 513 } 514 linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY; 515 if (linkNode[1].rowEL.collect) { 516 linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - topPadding; 517 } else { 518 linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 519 } 520 linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY; 521 if (linkNode[0].rowEL.rowParentId === event.detail?.rowId) { 522 linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!); 523 linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / halfNumber; 524 linkNode[0].offsetY = linkNode[0].offsetY / halfNumber; 525 linkNode[0].rowEL = frameTimeLineRow; 526 } else if (linkNode[1].rowEL.rowParentId === event.detail?.rowId) { 527 linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!); 528 linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / halfNumber; 529 linkNode[1].offsetY = linkNode[1].offsetY / halfNumber; 530 linkNode[1].rowEL = frameTimeLineRow!; 531 } 532 }); 533 }, offsetYTime); 534 let refreshTimeOut: number = window.setTimeout(() => { 535 this.trace.refreshCanvas(true); 536 clearTimeout(refreshTimeOut); 537 }, refreshTime); 538 return offsetYTimeOut; 539 } 540 541 private frameExpandTimeOut( 542 event: CustomEventInit<{ expansion: boolean, rowType: string, rowId: string, rowParentId: string }>, 543 actualTimeLineRow: TraceRow<JanksStruct> 544 ): number { 545 let topPadding: number = 195; 546 let halfNumber: number = 2; 547 let offsetYTime: number = 300; 548 let refreshTime: number = 360; 549 let offsetYTimeOut: number = window.setTimeout(() => { 550 this.trace.linkNodes.forEach((linkFrameNode: PairPoint[]) => { 551 JankStruct.selectJankStructList?.forEach((dat: JankStruct) => { 552 if (event.detail?.rowId === dat.pid) { 553 JankStruct.selectJankStruct = dat; 554 JankStruct.hoverJankStruct = dat; 555 } 556 }); 557 if (linkFrameNode[0].rowEL.collect) { 558 linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - topPadding; 559 } else { 560 linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 561 } 562 linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY; 563 if (linkFrameNode[1].rowEL.collect) { 564 linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - topPadding; 565 } else { 566 linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 567 } 568 linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY; 569 if (linkFrameNode[0].rowEL.rowId === event.detail?.rowId) { 570 linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!); 571 linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * halfNumber; 572 linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * halfNumber; 573 linkFrameNode[0].rowEL = actualTimeLineRow; 574 } else if (linkFrameNode[1].rowEL.rowId === event.detail?.rowId) { 575 linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!); 576 linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * halfNumber; 577 linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * halfNumber; 578 linkFrameNode[1].rowEL = actualTimeLineRow!; 579 } 580 }); 581 }, offsetYTime); 582 let refreshTimeOut: number = window.setTimeout(() => { 583 this.trace.refreshCanvas(true); 584 clearTimeout(refreshTimeOut); 585 }, refreshTime); 586 return offsetYTimeOut; 587 } 588 589 private async getExpectedFrameDate(): Promise<JanksStruct[]> { 590 let frameExpectedData = await queryExpectedFrameDate(); 591 if (frameExpectedData.length > 0) { 592 let unitIndex: number = 1; 593 let isIntersect = (a: JanksStruct, b: JanksStruct): boolean => Math.max(a.ts! + a.dur!, b.ts! + b.dur!) - 594 Math.min(a.ts!, b.ts!) < a.dur! + b.dur!; 595 let depths = []; 596 for (let i: number = 0; i < frameExpectedData.length; i++) { 597 let it = frameExpectedData[i]; 598 if (!it.dur || it.dur < 0) { 599 continue; 600 } 601 if (depths.length === 0) { 602 it.depth = 0; 603 depths[0] = it; 604 } else { 605 let index: number = 0; 606 let isContinue: boolean = true; 607 while (isContinue) { 608 if (isIntersect(depths[index], it)) { 609 if (depths[index + unitIndex] === undefined || !depths[index + unitIndex]) { 610 it.depth = index + unitIndex; 611 depths[index + unitIndex] = it; 612 isContinue = false; 613 } 614 } else { 615 it.depth = index; 616 depths[index] = it; 617 isContinue = false; 618 } 619 index++; 620 } 621 } 622 } 623 } 624 return frameExpectedData; 625 } 626 627 private async getActualFrameDate(): Promise<JanksStruct[]> { 628 let frameActualData = await queryActualFrameDate(); 629 if (frameActualData.length > 0) { 630 let unitIndex: number = 1; 631 let isIntersect = (leftStruct: JanksStruct, rightStruct: JanksStruct): boolean => 632 Math.max(leftStruct.ts! + leftStruct.dur!, rightStruct.ts! + rightStruct.dur!) - 633 Math.min(leftStruct.ts!, rightStruct.ts!) < leftStruct.dur! + rightStruct.dur!; 634 let depthArray = []; 635 for (let index: number = 0; index < frameActualData.length; index++) { 636 let it = frameActualData[index]; 637 if (!it.dur || it.dur < 0) { 638 continue; 639 } 640 if (depthArray.length === 0) { 641 it.depth = 0; 642 depthArray[0] = it; 643 } else { 644 let index: number = 0; 645 let isContinue: boolean = true; 646 while (isContinue) { 647 if (isIntersect(depthArray[index], it)) { 648 if (depthArray[index + unitIndex] === undefined || !depthArray[index + unitIndex]) { 649 it.depth = index + unitIndex; 650 depthArray[index + unitIndex] = it; 651 isContinue = false; 652 } 653 } else { 654 it.depth = index; 655 depthArray[index] = it; 656 isContinue = false; 657 } 658 index++; 659 } 660 } 661 } 662 } 663 return frameActualData; 664 } 665} 666