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 { queryActualFrameDate, queryExpectedFrameDate, queryFrameTimeData } from '../../database/SqlLite.js'; 21import { JanksStruct } from '../../bean/JanksStruct.js'; 22import { ns2xByTimeShaft } from '../../database/ui-worker/ProcedureWorkerCommon.js'; 23 24export class SpFrameTimeChart { 25 private trace: SpSystemTrace; 26 27 constructor(trace: SpSystemTrace) { 28 this.trace = trace; 29 } 30 31 async init() { 32 let frameTimeData = await queryFrameTimeData(); 33 if (frameTimeData.length > 0) { 34 let frameTimeLineRow = await this.initFrameTimeLine(); 35 await this.initExpectedChart(frameTimeLineRow); 36 await this.initActualChart(frameTimeLineRow); 37 } 38 } 39 40 async initFrameTimeLine() { 41 let frameTimeLineRow = TraceRow.skeleton<any>(); 42 frameTimeLineRow.rowId = `frameTime`; 43 frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 44 frameTimeLineRow.rowParentId = ``; 45 frameTimeLineRow.style.width = `100%`; 46 frameTimeLineRow.style.height = `40px`; 47 frameTimeLineRow.folder = true; 48 frameTimeLineRow.name = `FrameTimeline`; 49 frameTimeLineRow.setAttribute('children', ''); 50 frameTimeLineRow.supplier = () => 51 new Promise((resolve) => { 52 resolve([]); 53 }); 54 frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 55 frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 56 frameTimeLineRow.onThreadHandler = (useCache) => { 57 let context = frameTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 58 frameTimeLineRow!.canvasSave(context); 59 (renders['jank'] as JankRender).renderMainThread( 60 { 61 context: context, 62 useCache: useCache, 63 type: `expected_frame_timeline_slice`, 64 }, 65 frameTimeLineRow! 66 ); 67 frameTimeLineRow!.canvasRestore(context); 68 }; 69 this.trace.rowsEL?.appendChild(frameTimeLineRow); 70 return frameTimeLineRow; 71 } 72 73 async initExpectedChart(frameTimeLineRow: TraceRow<JanksStruct>) { 74 let frameExpectedData = await queryExpectedFrameDate(); 75 if (frameExpectedData.length > 0) { 76 let isIntersect = (a: JanksStruct, b: JanksStruct) => 77 Math.max(a.ts! + a.dur!, b.ts! + b.dur!) - Math.min(a.ts!, b.ts!) < a.dur! + b.dur!; 78 let depths: any = []; 79 for (let i = 0; i < frameExpectedData.length; i++) { 80 let it = frameExpectedData[i]; 81 if (!it.dur || it.dur < 0) { 82 continue; 83 } 84 if (depths.length == 0) { 85 it.depth = 0; 86 depths[0] = it; 87 } else { 88 let index = 0; 89 let isContinue = true; 90 while (isContinue) { 91 if (isIntersect(depths[index], it)) { 92 if (depths[index + 1] == undefined || !depths[index + 1]) { 93 it.depth = index + 1; 94 depths[index + 1] = it; 95 isContinue = false; 96 } 97 } else { 98 it.depth = index; 99 depths[index] = it; 100 isContinue = false; 101 } 102 index++; 103 } 104 } 105 } 106 } 107 let max = Math.max(...frameExpectedData.map((it) => it.depth || 0)) + 1; 108 let maxHeight = max * 20; 109 let expectedTimeLineRow = TraceRow.skeleton<any>(); 110 expectedTimeLineRow.rowId = `expected frameTime`; 111 expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 112 expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion; 113 expectedTimeLineRow.rowParentId = `frameTime`; 114 expectedTimeLineRow.style.width = `100%`; 115 expectedTimeLineRow.style.height = `40px`; 116 expectedTimeLineRow.style.height = `${maxHeight}px`; 117 expectedTimeLineRow.name = `Expected Timeline`; 118 expectedTimeLineRow.addTemplateTypes('FrameTimeline'); 119 expectedTimeLineRow.setAttribute('height', `${maxHeight}`); 120 expectedTimeLineRow.setAttribute('children', ''); 121 expectedTimeLineRow.supplier = () => 122 new Promise((resolve) => { 123 resolve(frameExpectedData); 124 }); 125 expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 126 expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 127 expectedTimeLineRow.onThreadHandler = (useCache) => { 128 let context = expectedTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 129 expectedTimeLineRow!.canvasSave(context); 130 (renders['jank'] as JankRender).renderMainThread( 131 { 132 context: context, 133 useCache: useCache, 134 type: `expected_frame_timeline_slice`, 135 }, 136 expectedTimeLineRow! 137 ); 138 expectedTimeLineRow!.canvasRestore(context); 139 }; 140 frameTimeLineRow.addChildTraceRow(expectedTimeLineRow); 141 } 142 143 async initActualChart(frameTimeLineRow: TraceRow<any>) { 144 let frameActualData = await queryActualFrameDate(); 145 if (frameActualData.length > 0) { 146 let isIntersect = (a: JanksStruct, b: JanksStruct) => 147 Math.max(a.ts! + a.dur!, b.ts! + b.dur!) - Math.min(a.ts!, b.ts!) < a.dur! + b.dur!; 148 let depthArray: any = []; 149 for (let i = 0; i < frameActualData.length; i++) { 150 let it = frameActualData[i]; 151 if (!it.dur || it.dur < 0) { 152 continue; 153 } 154 if (depthArray.length == 0) { 155 it.depth = 0; 156 depthArray[0] = it; 157 } else { 158 let index = 0; 159 let isContinue = true; 160 while (isContinue) { 161 if (isIntersect(depthArray[index], it)) { 162 if (depthArray[index + 1] == undefined || !depthArray[index + 1]) { 163 it.depth = index + 1; 164 depthArray[index + 1] = it; 165 isContinue = false; 166 } 167 } else { 168 it.depth = index; 169 depthArray[index] = it; 170 isContinue = false; 171 } 172 index++; 173 } 174 } 175 } 176 } 177 178 let max = Math.max(...frameActualData.map((it) => it.depth || 0)) + 1; 179 let maxHeight = max * 20; 180 let actualTimeLineRow = TraceRow.skeleton<any>(); 181 actualTimeLineRow.rowId = `actual frameTime`; 182 actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK; 183 actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion; 184 actualTimeLineRow.rowParentId = `frameTime`; 185 actualTimeLineRow.style.width = `100%`; 186 actualTimeLineRow.style.height = `${maxHeight}px`; 187 actualTimeLineRow.name = `Actual Timeline`; 188 actualTimeLineRow.addTemplateTypes('FrameTimeline'); 189 actualTimeLineRow.setAttribute('height', `${maxHeight}`); 190 actualTimeLineRow.setAttribute('children', ''); 191 actualTimeLineRow.dataList = frameActualData; 192 actualTimeLineRow.supplier = () => 193 new Promise((resolve) => { 194 resolve(frameActualData); 195 }); 196 actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 197 actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler; 198 actualTimeLineRow.onThreadHandler = (useCache) => { 199 let context = actualTimeLineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 200 actualTimeLineRow!.canvasSave(context); 201 (renders['jank'] as JankRender).renderMainThread( 202 { 203 context: context, 204 useCache: useCache, 205 type: `expected_frame_timeline_slice`, 206 }, 207 actualTimeLineRow! 208 ); 209 actualTimeLineRow!.canvasRestore(context); 210 }; 211 frameTimeLineRow.addChildTraceRow(actualTimeLineRow); 212 let offsetYTimeOut: any = undefined; 213 frameTimeLineRow.addEventListener('expansion-change', (e: any) => { 214 JankStruct.delJankLineFlag = false; 215 if (offsetYTimeOut) { 216 clearTimeout(offsetYTimeOut); 217 } 218 if (e.detail.expansion) { 219 offsetYTimeOut = setTimeout(() => { 220 this.trace.linkNodes.forEach((linkFrameNode) => { 221 JankStruct.selectJankStructList?.forEach((dat: any) => { 222 if (e.detail.rowId == dat.pid) { 223 JankStruct.selectJankStruct = dat; 224 JankStruct.hoverJankStruct = dat; 225 } 226 }); 227 if (linkFrameNode[0].rowEL.collect) { 228 linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - 195; 229 } else { 230 linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 231 } 232 linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY; 233 if (linkFrameNode[1].rowEL.collect) { 234 linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - 195; 235 } else { 236 linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 237 } 238 linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY; 239 if (linkFrameNode[0].rowEL.rowId == e.detail.rowId) { 240 linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!); 241 linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * 2; 242 linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * 2; 243 linkFrameNode[0].rowEL = actualTimeLineRow; 244 } else if (linkFrameNode[1].rowEL.rowId == e.detail.rowId) { 245 linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!); 246 linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * 2; 247 linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * 2; 248 linkFrameNode[1].rowEL = actualTimeLineRow!; 249 } 250 }); 251 }, 300); 252 } else { 253 if (JankStruct!.selectJankStruct) { 254 JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct); 255 } 256 offsetYTimeOut = setTimeout(() => { 257 this.trace.linkNodes.forEach((linkNode) => { 258 if (linkNode[0].rowEL.collect) { 259 linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - 195; 260 } else { 261 linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 262 } 263 linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY; 264 if (linkNode[1].rowEL.collect) { 265 linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - 195; 266 } else { 267 linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 268 } 269 linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY; 270 if (linkNode[0].rowEL.rowParentId == e.detail.rowId) { 271 linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!); 272 linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / 2; 273 linkNode[0].offsetY = linkNode[0].offsetY / 2; 274 linkNode[0].rowEL = frameTimeLineRow; 275 } else if (linkNode[1].rowEL.rowParentId == e.detail.rowId) { 276 linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!); 277 linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / 2; 278 linkNode[1].offsetY = linkNode[1].offsetY / 2; 279 linkNode[1].rowEL = frameTimeLineRow!; 280 } 281 }); 282 }, 300); 283 } 284 let refreshTimeOut = setTimeout(() => { 285 this.trace.refreshCanvas(true); 286 clearTimeout(refreshTimeOut); 287 }, 360); 288 }); 289 } 290} 291