5
5
6
6
export namespace inputLatency {
7
7
8
+ // Measurements are recorded in typed arrays with a fixed buffer length to their own impact on
9
+ // input latency.
8
10
const enum Constants {
9
11
bufferLength = 256
10
12
}
13
+ const measurementsKeydown = new Float32Array ( Constants . bufferLength ) ;
14
+ const measurementsInput = new Float32Array ( Constants . bufferLength ) ;
15
+ const measurementsRender = new Float32Array ( Constants . bufferLength ) ;
16
+ const measurementsInputLatency = new Float32Array ( Constants . bufferLength ) ;
17
+ let measurementsCount = 0 ;
11
18
19
+ // The state of each event, this helps ensure the integrity of the measurement and that
20
+ // something unexpected didn't happen that could skew the measurement.
12
21
const enum EventPhase {
13
22
Before = 0 ,
14
23
InProgress = 1 ,
15
24
Finished = 2
16
25
}
17
-
18
26
const state = {
19
27
keydown : EventPhase . Before ,
20
28
input : EventPhase . Before ,
21
29
render : EventPhase . Before ,
22
30
selection : EventPhase . Before
23
31
} ;
24
32
25
- const measurementsKeydown = new Float32Array ( Constants . bufferLength ) ;
26
- const measurementsInput = new Float32Array ( Constants . bufferLength ) ;
27
- const measurementsRender = new Float32Array ( Constants . bufferLength ) ;
28
- const measurementsInputLatency = new Float32Array ( Constants . bufferLength ) ;
29
- let measurementsCount = 0 ;
30
-
33
+ /**
34
+ * Mark the start of the keydown event.
35
+ */
31
36
export function markKeydownStart ( ) {
32
37
performance . mark ( 'inputlatency/start' ) ;
33
38
performance . mark ( 'keydown/start' ) ;
34
39
state . keydown = EventPhase . InProgress ;
35
40
queueMicrotask ( ( ) => markKeydownEnd ( ) ) ;
36
41
}
37
42
43
+ /**
44
+ * Mark the end of the keydown event.
45
+ */
38
46
function markKeydownEnd ( ) {
39
47
// Only measure the first render after keyboard input
40
48
performance . mark ( 'keydown/end' ) ;
41
49
state . keydown = EventPhase . Finished ;
42
50
}
43
51
52
+ /**
53
+ * Mark the start of the input event.
54
+ */
44
55
export function markInputStart ( ) {
45
56
performance . mark ( 'input/start' ) ;
46
57
state . input = EventPhase . InProgress ;
47
58
}
48
59
60
+ /**
61
+ * Mark the end of the input event.
62
+ */
49
63
export function markInputEnd ( ) {
50
64
queueMicrotask ( ( ) => {
51
65
performance . mark ( 'input/end' ) ;
52
66
state . input = EventPhase . Finished ;
53
67
} ) ;
54
68
}
55
69
70
+ /**
71
+ * Mark the start of the animation frame performing the rendering.
72
+ */
56
73
export function markRenderStart ( ) {
57
74
// Render may be triggered during input, but we only measure the following animation frame
58
75
if ( state . keydown === EventPhase . Finished && state . input === EventPhase . Finished && state . render === EventPhase . Before ) {
@@ -63,17 +80,32 @@ export namespace inputLatency {
63
80
}
64
81
}
65
82
83
+ /**
84
+ * Mark the end of the animation frame performing the rendering.
85
+ *
86
+ * An input latency sample is complete when both the textarea selection change event and the
87
+ * animation frame performing the rendering has triggered.
88
+ */
66
89
function markRenderEnd ( ) {
67
90
performance . mark ( 'render/end' ) ;
68
91
state . render = EventPhase . Finished ;
69
92
record ( ) ;
70
93
}
71
94
95
+ /**
96
+ * Mark when the editor textarea selection change event occurs.
97
+ *
98
+ * An input latency sample is complete when both the textarea selection change event and the
99
+ * animation frame performing the rendering has triggered.
100
+ */
72
101
export function markTextareaSelection ( ) {
73
102
state . selection = EventPhase . Finished ;
74
103
record ( ) ;
75
104
}
76
105
106
+ /**
107
+ * Record the input latency sample if it's ready.
108
+ */
77
109
function record ( ) {
78
110
// Skip recording this frame if the buffer is full
79
111
if ( measurementsCount >= Constants . bufferLength ) {
@@ -113,6 +145,10 @@ export namespace inputLatency {
113
145
} ) ;
114
146
}
115
147
setInterval ( ( ) => console . log ( getAndClearMeasurements ( ) ) , 10000 ) ;
148
+
149
+ /**
150
+ * Clear the current sample.
151
+ */
116
152
function reset ( ) {
117
153
performance . clearMarks ( 'keydown/start' ) ;
118
154
performance . clearMarks ( 'keydown/end' ) ;
@@ -148,6 +184,10 @@ export namespace inputLatency {
148
184
max : number ;
149
185
}
150
186
187
+ /**
188
+ * Gets all input latency samples and clears the internal buffers to start recording a new set
189
+ * of samples.
190
+ */
151
191
export function getAndClearMeasurements ( ) : IInputLatencyMeasurements | undefined {
152
192
if ( measurementsCount === 0 ) {
153
193
return undefined ;
0 commit comments