Skip to content

Commit

Permalink
Merge pull request #984 from CryZe/multivalue-abi
Browse files Browse the repository at this point in the history
Enable Multivalue again
  • Loading branch information
CryZe authored Oct 22, 2024
2 parents c39dbe1 + da58c85 commit ca5f527
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 69 deletions.
57 changes: 33 additions & 24 deletions buildCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { execSync } from "child_process";
import fs from "fs";

let toolchain = "";
let targetFolder = "debug";
let profile = "debug";
let cargoFlags = "";
let rustFlags = "-C target-feature=+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,+simd128,+extended-const";
let rustFlags =
"-C target-feature=+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,+simd128,+extended-const,+multivalue";
let wasmBindgenFlags = "";
let target = "wasm32-unknown-unknown";
let targetFolder = target;

if (process.argv.some((v) => v === "--max-opt")) {
// Do a fully optimized build ready for deployment.
targetFolder = "max-opt";
profile = "max-opt";
cargoFlags = "--profile max-opt";
} else if (process.argv.some((v) => v === "--release")) {
// Do an optimized build.
targetFolder = "release";
profile = "release";
cargoFlags = "--release";
} else {
// Do a debug build.
Expand All @@ -23,19 +26,28 @@ if (process.argv.some((v) => v === "--max-opt")) {
// Use WASM features that may not be supported by all the browsers.
if (process.argv.some((v) => v === "--unstable")) {
// Relaxed SIMD is not supported by Firefox and Safari yet.
// Tail calls are not supported by Safari and wasm-bindgen yet.
rustFlags += ",+relaxed-simd"; //,+tail-call";
rustFlags += ",+relaxed-simd";

// Tail calls are not supported by Safari yet.
rustFlags += ",+tail-call";

// Reference types are broken in webpack (or rather its underlying webassemblyjs):
// https://github.com/LiveSplit/LiveSplitOne/issues/630
// wasmBindgenFlags += " --reference-types";
}

// Use the nightly toolchain, which enables some more optimizations.
if (process.argv.some((v) => v === "--nightly")) {
toolchain = "+nightly";
cargoFlags += " -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort";
rustFlags += ",+multivalue -Z wasm-c-abi=spec";
cargoFlags +=
" -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort";
rustFlags += " -Z wasm-c-abi=spec";
target = "../wasm32-multivalue.json";
targetFolder = "wasm32-multivalue";

// Virtual function elimination requires LTO, so we can only do it for
// max-opt builds.
if (targetFolder == "max-opt") {
if (profile == "max-opt") {
// Seems like cargo itself calls rustc to check for file name patterns,
// but it forgets to pass the LTO flag that we specified in the
// Cargo.toml, so the virtual-function-elimination complains that it's
Expand All @@ -45,33 +57,30 @@ if (process.argv.some((v) => v === "--nightly")) {
}
}

execSync(
`cargo ${toolchain} run`,
{
cwd: "livesplit-core/capi/bind_gen",
stdio: "inherit",
},
);
execSync(`cargo ${toolchain} run`, {
cwd: "livesplit-core/capi/bind_gen",
stdio: "inherit",
});

execSync(
`cargo ${toolchain} rustc -p livesplit-core-capi --crate-type cdylib --features wasm-web,web-rendering --target wasm32-unknown-unknown ${cargoFlags}`,
`cargo ${toolchain} rustc -p livesplit-core-capi --crate-type cdylib --features wasm-web,web-rendering --target ${target} ${cargoFlags}`,
{
cwd: "livesplit-core",
stdio: "inherit",
env: {
...process.env,
'RUSTFLAGS': rustFlags,
RUSTFLAGS: rustFlags,
},
},
}
);

execSync(
`wasm-bindgen ${wasmBindgenFlags} livesplit-core/target/wasm32-unknown-unknown/${targetFolder}/livesplit_core.wasm --out-dir src/livesplit-core`,
`wasm-bindgen ${wasmBindgenFlags} livesplit-core/target/${targetFolder}/${profile}/livesplit_core.wasm --out-dir src/livesplit-core`,
{
stdio: "inherit",
},
}
);

fs
.createReadStream("livesplit-core/capi/bindings/wasm_bindgen/index.ts")
.pipe(fs.createWriteStream("src/livesplit-core/index.ts"));
fs.createReadStream("livesplit-core/capi/bindings/wasm_bindgen/index.ts").pipe(
fs.createWriteStream("src/livesplit-core/index.ts")
);
164 changes: 119 additions & 45 deletions test/rendering-test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import fs from "fs";
import path from "path";
import { createServer } from 'http-server';
import {
Builder,
By,
until,
} from "selenium-webdriver";
import { createServer } from "http-server";
import { Builder, By, until } from "selenium-webdriver";
import chrome from "selenium-webdriver/chrome.js";
import { } from "chromedriver";
import {} from "chromedriver";
import imghash from "imghash";
import hex64 from "hex64";
import leven from "leven";
Expand Down Expand Up @@ -39,30 +35,37 @@ describe("Layout Rendering Tests", function () {
const clickElement = async (selector) => {
const element = await findElement(selector);
await element.click();
}
};

const loadFile = async (filePath) => {
const inputElement = await findElement(By.id("file-input"));
await inputElement.sendKeys(path.resolve(filePath));
await driver.sleep(500);
}
};

const hashToBinary = (hash) => {
const buffer = Buffer.from(hash, "base64");
const values = Array.from(buffer.values());
return values.map((value) => value.toString(2).padStart(8, "0")).join("");
return values
.map((value) => value.toString(2).padStart(8, "0"))
.join("");
};

before(async () => {
console.log('Starting server...');
console.log("Starting server...");

await startServer();

console.log('Server started!');
console.log('Preparing WebDriver for tests...');
console.log("Server started!");
console.log("Preparing WebDriver for tests...");

const options = new chrome.Options().windowSize({ width: 1200, height: 2400 }).addArguments("--headless");
driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build();
const options = new chrome.Options()
.windowSize({ width: 1200, height: 2400 })
.addArguments("--headless");
driver = await new Builder()
.forBrowser("chrome")
.setChromeOptions(options)
.build();

await driver.get("http://localhost:8081");

Expand All @@ -75,29 +78,41 @@ describe("Layout Rendering Tests", function () {
this.style.display = "none";
this.id = "file-input";
document.body.appendChild(this);
}
};
});

if (!fs.existsSync(SCREENSHOTS_FOLDER)) {
fs.mkdirSync(SCREENSHOTS_FOLDER);
}

console.log('Ready to run tests!');
console.log("Ready to run tests!");
});

const testRendering = (layoutName, splitsName, expectedHash) => {
it(`Renders the ${layoutName} layout with the ${splitsName} splits correctly`, async function () {
this.timeout(10000);

await clickElement(By.xpath(".//button[contains(text(), 'Layout')]"));
await clickElement(By.xpath(".//button[contains(text(), 'Import')]"));
await clickElement(
By.xpath(".//button[contains(text(), 'Layout')]")
);
await clickElement(
By.xpath(".//button[contains(text(), 'Import')]")
);
await loadFile(`${LAYOUTS_FOLDER}/${layoutName}.ls1l`);
await clickElement(By.xpath(".//button[contains(text(), 'Back')]"));

await clickElement(By.xpath(".//button[contains(text(), 'Splits')]"));
await clickElement(By.xpath(".//button[contains(text(), 'Import')]"));
await clickElement(
By.xpath(".//button[contains(text(), 'Splits')]")
);
await clickElement(
By.xpath(".//button[contains(text(), 'Import')]")
);
await loadFile(`${SPLITS_FOLDER}/${splitsName}.lss`);
await clickElement(By.xpath("(.//button[contains(@aria-label, 'Open Splits')])[last()]"));
await clickElement(
By.xpath(
"(.//button[contains(@aria-label, 'Open Splits')])[last()]"
)
);
await clickElement(By.xpath(".//button[contains(text(), 'Back')]"));

const layoutElement = await findElement(By.className("layout"));
Expand All @@ -113,8 +128,14 @@ describe("Layout Rendering Tests", function () {
const actualHashHex = await imghash.hash(tempFilePath, 24);
const actualHash = hex64.encode(actualHashHex);

const actualScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${actualHash.replace("/", "$")}.png`;
const expectedScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${expectedHash.replace("/", "$")}.png`;
const actualScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${actualHash.replace(
"/",
"$"
)}.png`;
const expectedScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${expectedHash.replace(
"/",
"$"
)}.png`;

fs.renameSync(tempFilePath, actualScreenshotPath);

Expand All @@ -126,42 +147,95 @@ describe("Layout Rendering Tests", function () {
let showWarning = false;
try {
if (fs.existsSync(expectedScreenshotPath)) {
const actualImage = PNG.sync.read(fs.readFileSync(actualScreenshotPath));
const expectedImage = PNG.sync.read(fs.readFileSync(expectedScreenshotPath));
const actualImage = PNG.sync.read(
fs.readFileSync(actualScreenshotPath)
);
const expectedImage = PNG.sync.read(
fs.readFileSync(expectedScreenshotPath)
);
const { width, height } = actualImage;
const diff = new PNG({ width, height });

const numPixelsDifferent = pixelmatch(actualImage.data, expectedImage.data, diff.data, width, height, { threshold: 0.2 });
const numPixelsDifferent = pixelmatch(
actualImage.data,
expectedImage.data,
diff.data,
width,
height,
{ threshold: 0.2 }
);

if (numPixelsDifferent === 0) {
showWarning = true;
}

fs.writeFileSync(`${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_diff.png`, PNG.sync.write(diff));
fs.writeFileSync(
`${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_diff.png`,
PNG.sync.write(diff)
);
}
}
finally {
} finally {
if (showWarning) {
console.warn(`Render match despite mismatching hashes (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`);
console.warn(
`Render match despite mismatching hashes (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`
);
} else {
throw Error(`Render mismatch (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`)
throw Error(
`Render mismatch (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`
);
}
}
}
});
}

testRendering("all_components", "default", "PwD-XAAAAAA2AAAO____________________AAAAAAAAAAAA____AAAA____AAAA____AAAAf_AAd_AAAAD-Nz___34HbwAG");
testRendering("all_components", "pmw3", "PwD-XAAAAAA2AAAO_________sAPf8AAf4AC3_AH_-AOX-APXfAGX-AP18AOf-APXeAOX-AOD7AOQIAIAAD-b_j_____LwBs");
testRendering("default", "default", "________8AADVVVVAAAAAAAAAAAA____________AAAAAAAAAAAA____VVVVAAAA____AAAAVVVVAAAAAADgAAD_________");
testRendering("default", "pmw3", "b_gAb9-HV8AG__AHV_AG7kAGX-AGV-AOX8APX_gOX-AP28AO_-APX-APXeAOUQAOX-AOX_gP_-AfAAAAAADgAAD_q97_____");
testRendering("splits_two_rows", "celeste", "b4AAYAAH________bwAA4AAHb-AA4AAHb-AAYAAHbwAA____bwAAYAAH_9VV____b_AAYAAH__wA____b_AAYAAPb8AAYAAP");
testRendering("splits_with_labels", "celeste", "AAAAAAA8AAA3AAA3AAA3________WAAHfwAHfwAHTwAHVVVVAAAA0gAH_wAH_wAH3wAH_________IAP34AP34AP38APAAAA");
testRendering("title_centered_no_game_icon", "celeste", "________MzMzADAAADwAADwA________VX1VADwAAAAAAAAA____ABkAAP8DAP8DAP8DAP8D________V_1XAGAAAAAAAAAA");
testRendering("title_centered_with_game_icon", "celeste", "________AiIiQDAAYDwAYDwA________YDwAYDwAYAAAYAAAd393YBkAYL8DYP8DYP8DYP8D________d_1XYGAAAAAAAAAA");
testRendering("title_left_no_attempt_count", "celeste", "________MzMz4AAA8AAA-AAA_________VVV-AAAAAAAAAAA____GQAA_wAA_wAA_wAA_wAA________f_1XIAAAAAAAAAAA");
};

testRendering(
"all_components",
"default",
"fwB-WgAAAAA2AAAO____________________AAAAAAAAAAAA____AAAA____AAAA____AAAAf_AAf_AAAAD-Mj___34HbwAG"
);
testRendering(
"all_components",
"pmw3",
"fwB-WgAAAAA2AAAO_________8AGf8AAf4AC3_AH_-APX-APXfAGX-APV8AOX-APX-APX-AOL_AOQIAIAAD-b_j_____LgAs"
);
testRendering(
"default",
"default",
"________8AADVVVVAAAAAAAAAAAA____________AAAAAAAAAAAA____VVVVAAAA____AAAAVVVVAAAAAADgAAD_________"
);
testRendering(
"default",
"pmw3",
"b_gAb_-GV8AG3_AHV_AGXuAGX-AGV-AOX-AOX_AOX-AP38AO3-APX-APXeAOWWAOXeAOX_gO_-AfAAAAAADgAAD_q97_____"
);
testRendering(
"splits_two_rows",
"celeste",
"b4AAYAAH________bwAA4AAHb-AA4AAHb-AAYAAHbwAA____bwAAYAAH_9VV____b_AAYAAH__wA____b_AAYAAPb8AAYAAP"
);
testRendering(
"splits_with_labels",
"celeste",
"AAAAAAA8AAA_AAA3AAAn________WAAHfwAHfwAHXwAHVVVVAAAA2wAH_wAH_wAH3wAH________9IAP_4AP34AP34APAAAA"
);
testRendering(
"title_centered_no_game_icon",
"celeste",
"________MzMzADwAADwAADwA________VX1VADwAAAAAAAAA____ABkDAP8DAP8DAP8DAP8D________V_1XACEAAAAAAAAA"
);
testRendering(
"title_centered_with_game_icon",
"celeste",
"________AjIiQDwAYDwAYDwA________YDwAYDwAYAAAYAAAczszYBkDYP8DYP8DYP8DYP8D________d_1XYCEAAAAAAAAA"
);
testRendering(
"title_left_no_attempt_count",
"celeste",
"________IiIi8AAA-AAA-AAA_________VVV-AAAAAAAAAAA____GQAA_wAA_wAA_wAA_wAA________f_1XKQAAAAAAAAAA"
);

after(async () => {
await driver.quit();
Expand Down
Loading

0 comments on commit ca5f527

Please sign in to comment.