neocities/public/js/monaco/esm/vs/base/browser/performance.js

220 lines
9.6 KiB
JavaScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export var inputLatency;
(function (inputLatency) {
const totalKeydownTime = { total: 0, min: Number.MAX_VALUE, max: 0 };
const totalInputTime = { ...totalKeydownTime };
const totalRenderTime = { ...totalKeydownTime };
const totalInputLatencyTime = { ...totalKeydownTime };
let measurementsCount = 0;
const state = {
keydown: 0 /* EventPhase.Before */,
input: 0 /* EventPhase.Before */,
render: 0 /* EventPhase.Before */,
};
/**
* Record the start of the keydown event.
*/
function onKeyDown() {
/** Direct Check C. See explanation in {@link recordIfFinished} */
recordIfFinished();
performance.mark('inputlatency/start');
performance.mark('keydown/start');
state.keydown = 1 /* EventPhase.InProgress */;
queueMicrotask(markKeyDownEnd);
}
inputLatency.onKeyDown = onKeyDown;
/**
* Mark the end of the keydown event.
*/
function markKeyDownEnd() {
if (state.keydown === 1 /* EventPhase.InProgress */) {
performance.mark('keydown/end');
state.keydown = 2 /* EventPhase.Finished */;
}
}
/**
* Record the start of the beforeinput event.
*/
function onBeforeInput() {
performance.mark('input/start');
state.input = 1 /* EventPhase.InProgress */;
/** Schedule Task A. See explanation in {@link recordIfFinished} */
scheduleRecordIfFinishedTask();
}
inputLatency.onBeforeInput = onBeforeInput;
/**
* Record the start of the input event.
*/
function onInput() {
if (state.input === 0 /* EventPhase.Before */) {
// it looks like we didn't receive a `beforeinput`
onBeforeInput();
}
queueMicrotask(markInputEnd);
}
inputLatency.onInput = onInput;
function markInputEnd() {
if (state.input === 1 /* EventPhase.InProgress */) {
performance.mark('input/end');
state.input = 2 /* EventPhase.Finished */;
}
}
/**
* Record the start of the keyup event.
*/
function onKeyUp() {
/** Direct Check D. See explanation in {@link recordIfFinished} */
recordIfFinished();
}
inputLatency.onKeyUp = onKeyUp;
/**
* Record the start of the selectionchange event.
*/
function onSelectionChange() {
/** Direct Check E. See explanation in {@link recordIfFinished} */
recordIfFinished();
}
inputLatency.onSelectionChange = onSelectionChange;
/**
* Record the start of the animation frame performing the rendering.
*/
function onRenderStart() {
// Render may be triggered during input, but we only measure the following animation frame
if (state.keydown === 2 /* EventPhase.Finished */ && state.input === 2 /* EventPhase.Finished */ && state.render === 0 /* EventPhase.Before */) {
// Only measure the first render after keyboard input
performance.mark('render/start');
state.render = 1 /* EventPhase.InProgress */;
queueMicrotask(markRenderEnd);
/** Schedule Task B. See explanation in {@link recordIfFinished} */
scheduleRecordIfFinishedTask();
}
}
inputLatency.onRenderStart = onRenderStart;
/**
* Mark the end of the animation frame performing the rendering.
*/
function markRenderEnd() {
if (state.render === 1 /* EventPhase.InProgress */) {
performance.mark('render/end');
state.render = 2 /* EventPhase.Finished */;
}
}
function scheduleRecordIfFinishedTask() {
// Here we can safely assume that the `setTimeout` will not be
// artificially delayed by 4ms because we schedule it from
// event handlers
setTimeout(recordIfFinished);
}
/**
* Record the input latency sample if input handling and rendering are finished.
*
* The challenge here is that we want to record the latency in such a way that it includes
* also the layout and painting work the browser does during the animation frame task.
*
* Simply scheduling a new task (via `setTimeout`) from the animation frame task would
* schedule the new task at the end of the task queue (after other code that uses `setTimeout`),
* so we need to use multiple strategies to make sure our task runs before others:
*
* We schedule tasks (A and B):
* - we schedule a task A (via a `setTimeout` call) when the input starts in `markInputStart`.
* If the animation frame task is scheduled quickly by the browser, then task A has a very good
* chance of being the very first task after the animation frame and thus will record the input latency.
* - however, if the animation frame task is scheduled a bit later, then task A might execute
* before the animation frame task. We therefore schedule another task B from `markRenderStart`.
*
* We do direct checks in browser event handlers (C, D, E):
* - if the browser has multiple keydown events queued up, they will be scheduled before the `setTimeout` tasks,
* so we do a direct check in the keydown event handler (C).
* - depending on timing, sometimes the animation frame is scheduled even before the `keyup` event, so we
* do a direct check there too (E).
* - the browser oftentimes emits a `selectionchange` event after an `input`, so we do a direct check there (D).
*/
function recordIfFinished() {
if (state.keydown === 2 /* EventPhase.Finished */ && state.input === 2 /* EventPhase.Finished */ && state.render === 2 /* EventPhase.Finished */) {
performance.mark('inputlatency/end');
performance.measure('keydown', 'keydown/start', 'keydown/end');
performance.measure('input', 'input/start', 'input/end');
performance.measure('render', 'render/start', 'render/end');
performance.measure('inputlatency', 'inputlatency/start', 'inputlatency/end');
addMeasure('keydown', totalKeydownTime);
addMeasure('input', totalInputTime);
addMeasure('render', totalRenderTime);
addMeasure('inputlatency', totalInputLatencyTime);
// console.info(
// `input latency=${performance.getEntriesByName('inputlatency')[0].duration.toFixed(1)} [` +
// `keydown=${performance.getEntriesByName('keydown')[0].duration.toFixed(1)}, ` +
// `input=${performance.getEntriesByName('input')[0].duration.toFixed(1)}, ` +
// `render=${performance.getEntriesByName('render')[0].duration.toFixed(1)}` +
// `]`
// );
measurementsCount++;
reset();
}
}
function addMeasure(entryName, cumulativeMeasurement) {
const duration = performance.getEntriesByName(entryName)[0].duration;
cumulativeMeasurement.total += duration;
cumulativeMeasurement.min = Math.min(cumulativeMeasurement.min, duration);
cumulativeMeasurement.max = Math.max(cumulativeMeasurement.max, duration);
}
/**
* Clear the current sample.
*/
function reset() {
performance.clearMarks('keydown/start');
performance.clearMarks('keydown/end');
performance.clearMarks('input/start');
performance.clearMarks('input/end');
performance.clearMarks('render/start');
performance.clearMarks('render/end');
performance.clearMarks('inputlatency/start');
performance.clearMarks('inputlatency/end');
performance.clearMeasures('keydown');
performance.clearMeasures('input');
performance.clearMeasures('render');
performance.clearMeasures('inputlatency');
state.keydown = 0 /* EventPhase.Before */;
state.input = 0 /* EventPhase.Before */;
state.render = 0 /* EventPhase.Before */;
}
/**
* Gets all input latency samples and clears the internal buffers to start recording a new set
* of samples.
*/
function getAndClearMeasurements() {
if (measurementsCount === 0) {
return undefined;
}
// Assemble the result
const result = {
keydown: cumulativeToFinalMeasurement(totalKeydownTime),
input: cumulativeToFinalMeasurement(totalInputTime),
render: cumulativeToFinalMeasurement(totalRenderTime),
total: cumulativeToFinalMeasurement(totalInputLatencyTime),
sampleCount: measurementsCount
};
// Clear the cumulative measurements
clearCumulativeMeasurement(totalKeydownTime);
clearCumulativeMeasurement(totalInputTime);
clearCumulativeMeasurement(totalRenderTime);
clearCumulativeMeasurement(totalInputLatencyTime);
measurementsCount = 0;
return result;
}
inputLatency.getAndClearMeasurements = getAndClearMeasurements;
function cumulativeToFinalMeasurement(cumulative) {
return {
average: cumulative.total / measurementsCount,
max: cumulative.max,
min: cumulative.min,
};
}
function clearCumulativeMeasurement(cumulative) {
cumulative.total = 0;
cumulative.min = Number.MAX_VALUE;
cumulative.max = 0;
}
})(inputLatency || (inputLatency = {}));