@@ -26,7 +26,7 @@ export namespace inputLatency {
26
26
const measurementsInput = new Float32Array ( Constants . bufferLength ) ;
27
27
const measurementsRender = new Float32Array ( Constants . bufferLength ) ;
28
28
const measurementsInputLatency = new Float32Array ( Constants . bufferLength ) ;
29
- let measurementsIndex = 0 ;
29
+ let measurementsCount = 0 ;
30
30
31
31
export function markKeydownStart ( ) {
32
32
performance . mark ( 'inputlatency/start' ) ;
@@ -64,7 +64,6 @@ export namespace inputLatency {
64
64
}
65
65
66
66
function markRenderEnd ( ) {
67
- // Only measure the first render after keyboard input
68
67
performance . mark ( 'render/end' ) ;
69
68
state . render = EventPhase . Finished ;
70
69
record ( ) ;
@@ -77,15 +76,15 @@ export namespace inputLatency {
77
76
78
77
function record ( ) {
79
78
// Skip recording this frame if the buffer is full
80
- if ( measurementsIndex >= Constants . bufferLength ) {
79
+ if ( measurementsCount >= Constants . bufferLength ) {
81
80
return ;
82
81
}
83
82
// Selection and render must have finished to record
84
83
if ( state . selection !== EventPhase . Finished || state . render !== EventPhase . Finished ) {
85
84
return ;
86
85
}
87
- // Measures from cursor edit to the end of the task
88
- window . queueMicrotask ( ( ) => {
86
+ // Finish the recording, using setImmediate to ensure that layout/paint is captured
87
+ setImmediate ( ( ) => {
89
88
if ( state . keydown === EventPhase . Finished && state . input === EventPhase . Finished && state . selection === EventPhase . Finished && state . render === EventPhase . Finished ) {
90
89
performance . mark ( 'inputlatency/end' ) ;
91
90
@@ -94,26 +93,26 @@ export namespace inputLatency {
94
93
performance . measure ( 'render' , 'render/start' , 'render/end' ) ;
95
94
performance . measure ( 'inputlatency' , 'inputlatency/start' , 'inputlatency/end' ) ;
96
95
97
- measurementsKeydown [ measurementsIndex ] = performance . getEntriesByName ( 'keydown' ) [ 0 ] . duration ;
98
- measurementsInput [ measurementsIndex ] = performance . getEntriesByName ( 'input' ) [ 0 ] . duration ;
99
- measurementsRender [ measurementsIndex ] = performance . getEntriesByName ( 'render' ) [ 0 ] . duration ;
100
- measurementsInputLatency [ measurementsIndex ] = performance . getEntriesByName ( 'inputlatency' ) [ 0 ] . duration ;
96
+ measurementsKeydown [ measurementsCount ] = performance . getEntriesByName ( 'keydown' ) [ 0 ] . duration ;
97
+ measurementsInput [ measurementsCount ] = performance . getEntriesByName ( 'input' ) [ 0 ] . duration ;
98
+ measurementsRender [ measurementsCount ] = performance . getEntriesByName ( 'render' ) [ 0 ] . duration ;
99
+ measurementsInputLatency [ measurementsCount ] = performance . getEntriesByName ( 'inputlatency' ) [ 0 ] . duration ;
101
100
102
101
console . info (
103
- `input latency=${ measurementsInputLatency [ measurementsIndex ] . toFixed ( 1 ) } [` +
104
- `keydown=${ measurementsKeydown [ measurementsIndex ] . toFixed ( 1 ) } , ` +
105
- `input=${ measurementsInput [ measurementsIndex ] . toFixed ( 1 ) } , ` +
106
- `render=${ measurementsRender [ measurementsIndex ] . toFixed ( 1 ) } ` +
102
+ `input latency=${ measurementsInputLatency [ measurementsCount ] . toFixed ( 1 ) } [` +
103
+ `keydown=${ measurementsKeydown [ measurementsCount ] . toFixed ( 1 ) } , ` +
104
+ `input=${ measurementsInput [ measurementsCount ] . toFixed ( 1 ) } , ` +
105
+ `render=${ measurementsRender [ measurementsCount ] . toFixed ( 1 ) } ` +
107
106
`]`
108
107
) ;
109
108
110
- measurementsIndex ++ ;
109
+ measurementsCount ++ ;
111
110
112
111
reset ( ) ;
113
112
}
114
113
} ) ;
115
114
}
116
-
115
+ setInterval ( ( ) => console . log ( getAndClearMeasurements ( ) ) , 10000 ) ;
117
116
function reset ( ) {
118
117
performance . clearMarks ( 'keydown/start' ) ;
119
118
performance . clearMarks ( 'keydown/end' ) ;
@@ -135,4 +134,58 @@ export namespace inputLatency {
135
134
state . selection = EventPhase . Before ;
136
135
}
137
136
137
+ export interface IInputLatencyMeasurements {
138
+ keydown : IInputLatencySingleMeasurement ;
139
+ input : IInputLatencySingleMeasurement ;
140
+ render : IInputLatencySingleMeasurement ;
141
+ total : IInputLatencySingleMeasurement ;
142
+ sampleCount : number ;
143
+ }
144
+
145
+ export interface IInputLatencySingleMeasurement {
146
+ average : number ;
147
+ min : number ;
148
+ max : number ;
149
+ }
150
+
151
+ export function getAndClearMeasurements ( ) : IInputLatencyMeasurements | undefined {
152
+ if ( measurementsCount === 0 ) {
153
+ return undefined ;
154
+ }
155
+
156
+ // Calculate the average, min and max of each measurement
157
+ const result = {
158
+ keydown : createSingleMeasurement ( ) ,
159
+ input : createSingleMeasurement ( ) ,
160
+ render : createSingleMeasurement ( ) ,
161
+ total : createSingleMeasurement ( ) ,
162
+ sampleCount : measurementsCount
163
+ } ;
164
+ for ( let i = 0 ; i < result . sampleCount ; i ++ ) {
165
+ result . keydown . average += measurementsKeydown [ i ] ;
166
+ result . input . average += measurementsInput [ i ] ;
167
+ result . render . average += measurementsRender [ i ] ;
168
+ result . total . average += measurementsInputLatency [ i ] ;
169
+ result . keydown . min = Math . min ( result . keydown . min , measurementsKeydown [ i ] ) ;
170
+ result . input . min = Math . min ( result . input . min , measurementsInput [ i ] ) ;
171
+ result . render . min = Math . min ( result . render . min , measurementsRender [ i ] ) ;
172
+ result . total . min = Math . min ( result . total . min , measurementsInputLatency [ i ] ) ;
173
+ result . keydown . max = Math . max ( result . keydown . max , measurementsKeydown [ i ] ) ;
174
+ result . input . max = Math . max ( result . input . max , measurementsInput [ i ] ) ;
175
+ result . render . max = Math . max ( result . render . max , measurementsRender [ i ] ) ;
176
+ result . total . max = Math . max ( result . total . max , measurementsInputLatency [ i ] ) ;
177
+ }
178
+ result . keydown . average /= result . sampleCount ;
179
+ result . input . average /= result . sampleCount ;
180
+ result . render . average /= result . sampleCount ;
181
+ result . total . average /= result . sampleCount ;
182
+
183
+ measurementsCount = 0 ;
184
+ return result ;
185
+ }
186
+
187
+ function createSingleMeasurement ( ) : IInputLatencySingleMeasurement {
188
+ return { average : 0 , min : Number . MAX_VALUE , max : 0 } ;
189
+ }
190
+
138
191
}
0 commit comments