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 { query } from '../../database/SqlLite';
16import { TraceRow } from '../trace/base/TraceRow';
17import { FlagsConfig } from '../../component/SpFlags';
18interface VSyncData {
19  startTime: number;
20  dur: number;
21  value?: number;
22}
23
24let vSyncDataList: VSyncData[] = [];
25let vSyncEnable = false;
26let isSingle = false;
27
28export function resetVSync(): void {
29  vSyncEnable = false;
30}
31
32export const querySfVSyncData = (): Promise<Array<VSyncData>> =>
33  query(
34    'querySfVSyncData',
35    `SELECT value, c.ts - tb.start_ts startTime
36     FROM process_measure c,
37          trace_range tb
38     WHERE c.filter_id IN (SELECT process_measure_filter.id AS traceId
39                           FROM process_measure_filter
40                                    JOIN process USING (ipid)
41                           WHERE process.name = ${
42                             `'` +
43                             String.fromCharCode(115, 117, 114, 102, 97, 99, 101, 102, 108, 105, 110, 103, 101, 114) +
44                             `'`
45                           }
46                             AND process_measure_filter.name = ${
47                               `'` + String.fromCharCode(86, 83, 89, 78, 67, 45, 97, 112, 112) + `'`
48                             })`
49  );
50
51  export const querySingleVSyncData = (): Promise<Array<VSyncData>> => {
52    let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
53    let flagsItemJson = JSON.parse(flagsItem!);
54    let vsyncValue = flagsItemJson.vsyncValue;  
55    let vsyncCondition = '';
56    if (vsyncValue === 'H:VsyncGenerator' || vsyncValue === '') {
57      vsyncCondition = ` AND (callstack.name like 'H:GenerateVsyncCount%'))`;
58    } else {
59      vsyncCondition = ` AND callstack.name like '${vsyncValue}%' )`;
60    }
61  
62    let sql =
63      `SELECT c.ts - tb.start_ts startTime
64     FROM callstack c,
65          trace_range tb
66     WHERE c.id IN (SELECT callstack.id AS trackId
67                    FROM callstack
68                             JOIN process
69                    WHERE process.name = 'render_service'`
70      + vsyncCondition;
71    return query('querySingleVSyncData', sql);
72  }
73
74/**
75 * load single vsync data
76 */
77export async function setVSyncData(): Promise<void> {
78  let sfvSyncData = await querySfVSyncData();
79  if (sfvSyncData.length === 0) {
80    sfvSyncData = await querySingleVSyncData();
81    isSingle = true;
82  }
83  sfvSyncData.forEach((it, index, array): void => {
84    if (index < array.length - 1) {
85      it.dur = array[index + 1].startTime - it.startTime;
86    } else {
87      it.dur = window.totalNS - it.startTime;
88    }
89  });
90  vSyncDataList = sfvSyncData;
91}
92
93/**
94 * draw chart
95 */
96export function drawVSync(ctx: CanvasRenderingContext2D, width: number, height: number): void {
97  if (!vSyncEnable) {
98    return;
99  }
100  function draw(it: VSyncData): void {
101    let x = ns2x(it.startTime, width);
102    let x2 = ns2x(it.startTime + it.dur, width);
103    ctx.fillRect(x, 0, x2 - x, height);
104  }
105  ctx.beginPath();
106  ctx.fillStyle = '#555555';
107  ctx.lineWidth = 1;
108  ctx.globalAlpha = 0.3;
109  if (isSingle) {
110    // 单框架灰白交替
111    for (let i = 0; i < vSyncDataList.length; i++) {
112      if (i % 2 === 1) {
113        continue;
114      }
115      draw(vSyncDataList[i]);
116    }
117  } else {
118    // 双框架绘制vSync 信号为1的数据为灰
119    vSyncDataList
120      ?.filter((it) => it.value === 1)
121      .forEach((it) => {
122        draw(it);
123      });
124  }
125  ctx.stroke();
126  ctx.globalAlpha = 1.0;
127  ctx.closePath();
128}
129
130/**
131 * enable/disable SingleVSync
132 */
133export function enableVSync(press: boolean, ev: KeyboardEvent, handler?: Function): void {
134  if (ev.key.toLocaleLowerCase() === 'v' && !ev.ctrlKey) {
135    window.publish(window.SmartEvent.UI.Loading, { loading: true, text: 'Query VSync' });
136    setVSyncData();
137    window.publish(window.SmartEvent.UI.Loading, { loading: false, text: 'Query VSync' });
138    vSyncEnable = !vSyncEnable;
139    handler?.();
140  }
141}
142
143/**
144 * ns to px
145 */
146function ns2x(ns: number, width: number): number {
147  let startNS = TraceRow.range?.startNS || 0;
148  let endNS = TraceRow.range?.endNS || 0;
149  if (endNS === 0) {
150    //@ts-ignore
151    endNS = (window as unknown).totalNS;
152  }
153  let xWidth: number = ((ns - startNS) * width) / (endNS - startNS);
154  if (xWidth < 0) {
155    xWidth = 0;
156  } else if (xWidth > width) {
157    xWidth = width;
158  }
159  return xWidth;
160}
161