Skip to content
Merged
6 changes: 4 additions & 2 deletions resources/benchmark-runner.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metric } from "./metric.mjs";
import { params } from "./shared/params.mjs";
import { forceLayout } from "./shared/helpers.mjs";
import { SUITE_RUNNER_LOOKUP } from "./suite-runner.mjs";

const performance = globalThis.performance;
Expand Down Expand Up @@ -30,8 +31,9 @@ class Page {
}

layout() {
const body = this._frame.contentDocument.body.getBoundingClientRect();
this.layout.e = document.elementFromPoint((body.width / 2) | 0, (body.height / 2) | 0);
const body = this._frame ? this._frame.contentDocument.body : document.body;
const value = forceLayout(body, params.layoutMode);
body._leakedLayoutValue = value; // Prevent dead code elimination.
}

async waitForElement(selector) {
Expand Down
28 changes: 27 additions & 1 deletion resources/developer-mode.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Suites, Tags } from "./tests.mjs";
import { params } from "./shared/params.mjs";
import { params, LAYOUT_MODES } from "./shared/params.mjs";

export function createDeveloperModeContainer() {
const container = document.createElement("div");
Expand All @@ -23,6 +23,7 @@ export function createDeveloperModeContainer() {
settings.append(createUIForWarmupBeforeSync());
settings.append(createUIForSyncStepDelay());
settings.append(createUIForAsyncSteps());
settings.append(createUIForLayoutMode());

content.append(document.createElement("hr"));
content.append(settings);
Expand Down Expand Up @@ -114,6 +115,31 @@ function createTimeRangeUI(labelText, paramKey, unit = "ms", min = 0, max = 1000
return label;
}

function createUIForLayoutMode() {
return createSelectUI("Force layout mode", params.layoutMode, LAYOUT_MODES, (value) => {
params.layoutMode = value;
});
}

function createSelectUI(labelValue, initialValue, choices, paramsUpdateCallback) {
const select = document.createElement("select");
select.onchange = () => {
paramsUpdateCallback(select.value);
updateURL();
};

choices.forEach((choice) => {
const option = new Option(choice, choice);
select.add(option);
});
select.value = initialValue;

const label = document.createElement("label");
label.append(span(labelValue), select);

return label;
}

function createUIForSuites() {
const control = document.createElement("nav");
control.className = "suites";
Expand Down
15 changes: 11 additions & 4 deletions resources/shared/helpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ export function getAllElements(selector, path = [], lookupStartNode = document)
return elements;
}

export function forceLayout() {
const rect = document.body.getBoundingClientRect();
const e = document.elementFromPoint((rect.width / 2) | 0, (rect.height / 2) | 0);
return e;
export function forceLayout(body, layoutMode = "getBoundingRectAndElementFromPoint") {
body ??= document.body;
const rect = body.getBoundingClientRect();
switch (layoutMode) {
case "getBoundingRectAndElementFromPoint":
return document.elementFromPoint((rect.width / 2) | 0, (rect.height / 2) | 0);
case "getBoundingClientRect":
return rect.height;
default:
throw Error(`Invalid layoutMode: ${layoutMode}`);
}
}
23 changes: 14 additions & 9 deletions resources/shared/params.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const LAYOUT_MODES = Object.freeze(["getBoundingClientRect", "getBoundingRectAndElementFromPoint"]);

export class Params {
viewport = {
width: 800,
Expand Down Expand Up @@ -27,6 +29,8 @@ export class Params {
// "generate": generate a random seed
// <integer>: use the provided integer as a seed
shuffleSeed = "off";
// Choices: "getBoundingClientRect" or "getBoundingRectAndElementFromPoint"
layoutMode = LAYOUT_MODES[0];
// Measure more workload prepare time.
measurePrepare = false;

Expand Down Expand Up @@ -57,8 +61,9 @@ export class Params {
this.useAsyncSteps = this._parseBooleanParam(searchParams, "useAsyncSteps");
this.waitBeforeSync = this._parseIntParam(searchParams, "waitBeforeSync", 0);
this.warmupBeforeSync = this._parseIntParam(searchParams, "warmupBeforeSync", 0);
this.measurementMethod = this._parseMeasurementMethod(searchParams);
this.measurementMethod = this._parseEnumParam(searchParams, "measurementMethod", ["raf"]);
this.shuffleSeed = this._parseShuffleSeed(searchParams);
this.layoutMode = this._parseEnumParam(searchParams, "layoutMode", LAYOUT_MODES);
this.measurePrepare = this._parseBooleanParam(searchParams, "measurePrepare");

const unused = Array.from(searchParams.keys());
Expand Down Expand Up @@ -124,14 +129,14 @@ export class Params {
return tags;
}

_parseMeasurementMethod(searchParams) {
if (!searchParams.has("measurementMethod"))
return defaultParams.measurementMethod;
const measurementMethod = searchParams.get("measurementMethod");
if (measurementMethod !== "raf")
throw new Error(`Invalid measurement method: '${measurementMethod}', must be 'raf'.`);
searchParams.delete("measurementMethod");
return measurementMethod;
_parseEnumParam(searchParams, paramKey, enumArray) {
if (!searchParams.has(paramKey))
return defaultParams[paramKey];
const value = searchParams.get(paramKey);
if (!enumArray.includes(value))
throw new Error(`Got invalid ${paramKey}: '${value}', choices are ${enumArray}`);
searchParams.delete(paramKey);
return value;
}

_parseShuffleSeed(searchParams) {
Expand Down
5 changes: 1 addition & 4 deletions resources/shared/test-runner.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,9 @@ export class TestRunner {
asyncStartTime = syncEndTime;
};
const measureAsync = () => {
const bodyReference = this.#frame ? this.#frame.contentDocument.body : document.body;
const windowReference = this.#frame ? this.#frame.contentWindow : window;
// Some browsers don't immediately update the layout for paint.
// Force the layout here to ensure we're measuring the layout time.
const height = bodyReference.getBoundingClientRect().height;
windowReference._unusedHeightValue = height; // Prevent dead code elimination.
this.page.layout();

const asyncEndTime = performance.now();
performance.mark(asyncEndLabel);
Expand Down
Loading