Add service worker support (#3419)
* Generate Web Worker script for every test file This will be needed to launch Service Workers, which cannot be generated at runtime and cannot use dynamic import(). * Add service worker support * Update wpt.ts * Run tests * Address kainino0x feedback * Address feedback | part 2 * Address feedback | part 3 * Address feedback | part 4 * Address feedback | part 5 * Address feedback | part 6 * Address feedback | part 7 * Address feedback | part 8 * Apply suggestions from code review * use WorkerTestRunRequest in the postMessage/onmessage interface * Clean up resolvers map * Use express routing for .worker.js * Skip worker tests when run in a worker that can't support them DedicatedWorker can be created from DedicatedWorker, but none of the other nested worker pairs are allowed. * Clean up all service workers on startup and shutdown * Avoid reinitializing service workers for every single case * lint fixes * Catch errors in wrapTestGroupForWorker onMessage * Make sure the service worker has the correct URL --------- Co-authored-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
f9f6c90b80
commit
5ad758908a
27 changed files with 514 additions and 281 deletions
10
Gruntfile.js
10
Gruntfile.js
|
|
@ -24,9 +24,9 @@ module.exports = function (grunt) {
|
|||
cmd: 'node',
|
||||
args: ['tools/gen_version'],
|
||||
},
|
||||
'generate-listings': {
|
||||
'generate-listings-and-webworkers': {
|
||||
cmd: 'node',
|
||||
args: ['tools/gen_listings', 'gen/', ...kAllSuites.map(s => 'src/' + s)],
|
||||
args: ['tools/gen_listings_and_webworkers', 'gen/', ...kAllSuites.map(s => 'src/' + s)],
|
||||
},
|
||||
validate: {
|
||||
cmd: 'node',
|
||||
|
|
@ -159,14 +159,14 @@ module.exports = function (grunt) {
|
|||
// Must run after generate-common and run:build-out.
|
||||
files: [
|
||||
{ expand: true, dest: 'out/', cwd: 'gen', src: 'common/internal/version.js' },
|
||||
{ expand: true, dest: 'out/', cwd: 'gen', src: '*/listing.js' },
|
||||
{ expand: true, dest: 'out/', cwd: 'gen', src: '*/**/*.js' },
|
||||
],
|
||||
},
|
||||
'gen-to-out-wpt': {
|
||||
// Must run after generate-common and run:build-out-wpt.
|
||||
files: [
|
||||
{ expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'common/internal/version.js' },
|
||||
{ expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'webgpu/listing.js' },
|
||||
{ expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'webgpu/**/*.js' },
|
||||
],
|
||||
},
|
||||
'htmlfiles-to-out': {
|
||||
|
|
@ -243,7 +243,7 @@ module.exports = function (grunt) {
|
|||
|
||||
grunt.registerTask('generate-common', 'Generate files into gen/ and src/', [
|
||||
'run:generate-version',
|
||||
'run:generate-listings',
|
||||
'run:generate-listings-and-webworkers',
|
||||
'run:generate-cache',
|
||||
]);
|
||||
grunt.registerTask('build-standalone', 'Build out/ (no checks; run after generate-common)', [
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ The following url parameters change how the harness runs:
|
|||
- `debug=1` enables verbose debug logging from tests.
|
||||
- `worker=dedicated` runs the tests on a dedicated worker instead of the main thread.
|
||||
- `worker=shared` runs the tests on a shared worker instead of the main thread.
|
||||
- `worker=service` runs the tests on a service worker instead of the main thread.
|
||||
- `power_preference=low-power` runs most tests passing `powerPreference: low-power` to `requestAdapter`
|
||||
- `power_preference=high-performance` runs most tests passing `powerPreference: high-performance` to `requestAdapter`
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ Each Suite has one Listing File (`suite/listing.[tj]s`), containing a list of th
|
|||
in the suite.
|
||||
|
||||
In `src/suite/listing.ts`, this is computed dynamically.
|
||||
In `out/suite/listing.js`, the listing has been pre-baked (by `tools/gen_listings`).
|
||||
In `out/suite/listing.js`, the listing has been pre-baked (by `tools/gen_listings_and_webworkers`).
|
||||
|
||||
**Type:** Once `import`ed, `ListingFile`
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ function compareOneLevel(ordering: Ordering, aIsBig: boolean, bIsBig: boolean):
|
|||
return Ordering.Unordered;
|
||||
}
|
||||
|
||||
function comparePaths(a: readonly string[], b: readonly string[]): Ordering {
|
||||
/**
|
||||
* Compare two file paths, or file-local test paths, returning an Ordering between the two.
|
||||
*/
|
||||
export function comparePaths(a: readonly string[], b: readonly string[]): Ordering {
|
||||
const shorter = Math.min(a.length, b.length);
|
||||
|
||||
for (let i = 0; i < shorter; ++i) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// A listing of all specs within a single suite. This is the (awaited) type of
|
||||
// `groups` in '{cts,unittests}/listing.ts' and `listing` in the auto-generated
|
||||
// 'out/{cts,unittests}/listing.js' files (see tools/gen_listings).
|
||||
// 'out/{cts,unittests}/listing.js' files (see tools/gen_listings_and_webworkers).
|
||||
export type TestSuiteListing = TestSuiteListingEntry[];
|
||||
|
||||
export type TestSuiteListingEntry = TestSuiteListingEntrySpec | TestSuiteListingEntryReadme;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function optionString(
|
|||
* The possible options for the tests.
|
||||
*/
|
||||
export interface CTSOptions {
|
||||
worker?: 'dedicated' | 'shared' | '';
|
||||
worker?: 'dedicated' | 'shared' | 'service' | '';
|
||||
debug: boolean;
|
||||
compatibility: boolean;
|
||||
forceFallbackAdapter: boolean;
|
||||
|
|
@ -68,6 +68,7 @@ export const kCTSOptionsInfo: OptionsInfos<CTSOptions> = {
|
|||
{ value: '', description: 'no worker' },
|
||||
{ value: 'dedicated', description: 'dedicated worker' },
|
||||
{ value: 'shared', description: 'shared worker' },
|
||||
{ value: 'service', description: 'service worker' },
|
||||
],
|
||||
},
|
||||
debug: { description: 'show more info' },
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
import { setBaseResourcePath } from '../../framework/resources.js';
|
||||
import { globalTestConfig } from '../../framework/test_config.js';
|
||||
import { DefaultTestFileLoader } from '../../internal/file_loader.js';
|
||||
import { Logger } from '../../internal/logging/logger.js';
|
||||
import { parseQuery } from '../../internal/query/parseQuery.js';
|
||||
import { TestQueryWithExpectation } from '../../internal/query/query.js';
|
||||
import { setDefaultRequestAdapterOptions } from '../../util/navigator_gpu.js';
|
||||
import { assert } from '../../util/util.js';
|
||||
|
||||
import { CTSOptions } from './options.js';
|
||||
import { setupWorkerEnvironment, WorkerTestRunRequest } from './utils_worker.js';
|
||||
|
||||
// Should be WorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom".
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
|
|
@ -18,24 +14,9 @@ const loader = new DefaultTestFileLoader();
|
|||
setBaseResourcePath('../../../resources');
|
||||
|
||||
async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) {
|
||||
const query: string = ev.data.query;
|
||||
const expectations: TestQueryWithExpectation[] = ev.data.expectations;
|
||||
const ctsOptions: CTSOptions = ev.data.ctsOptions;
|
||||
const { query, expectations, ctsOptions } = ev.data as WorkerTestRunRequest;
|
||||
|
||||
const { debug, unrollConstEvalLoops, powerPreference, compatibility } = ctsOptions;
|
||||
globalTestConfig.unrollConstEvalLoops = unrollConstEvalLoops;
|
||||
globalTestConfig.compatibility = compatibility;
|
||||
|
||||
Logger.globalDebugMode = debug;
|
||||
const log = new Logger();
|
||||
|
||||
if (powerPreference || compatibility) {
|
||||
setDefaultRequestAdapterOptions({
|
||||
...(powerPreference && { powerPreference }),
|
||||
// MAINTENANCE_TODO: Change this to whatever the option ends up being
|
||||
...(compatibility && { compatibilityMode: true }),
|
||||
});
|
||||
}
|
||||
const log = setupWorkerEnvironment(ctsOptions);
|
||||
|
||||
const testcases = Array.from(await loader.loadCases(parseQuery(query)));
|
||||
assert(testcases.length === 1, 'worker query resulted in != 1 cases');
|
||||
|
|
@ -48,7 +29,7 @@ async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) {
|
|||
}
|
||||
|
||||
self.onmessage = (ev: MessageEvent) => {
|
||||
void reportTestResults.call(self, ev);
|
||||
void reportTestResults.call(ev.source || self, ev);
|
||||
};
|
||||
|
||||
self.onconnect = (event: MessageEvent) => {
|
||||
|
|
|
|||
|
|
@ -2,33 +2,82 @@ import { LogMessageWithStack } from '../../internal/logging/log_message.js';
|
|||
import { TransferredTestCaseResult, LiveTestCaseResult } from '../../internal/logging/result.js';
|
||||
import { TestCaseRecorder } from '../../internal/logging/test_case_recorder.js';
|
||||
import { TestQueryWithExpectation } from '../../internal/query/query.js';
|
||||
import { timeout } from '../../util/timeout.js';
|
||||
import { assert } from '../../util/util.js';
|
||||
|
||||
import { CTSOptions, kDefaultCTSOptions } from './options.js';
|
||||
import { WorkerTestRunRequest } from './utils_worker.js';
|
||||
|
||||
export class TestDedicatedWorker {
|
||||
private readonly ctsOptions: CTSOptions;
|
||||
/** Query all currently-registered service workers, and unregister them. */
|
||||
function unregisterAllServiceWorkers() {
|
||||
void navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
for (const registration of registrations) {
|
||||
void registration.unregister();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: This code runs on startup for any runtime with worker support. Here, we use that chance to
|
||||
// delete any leaked service workers, and register to clean up after ourselves at shutdown.
|
||||
unregisterAllServiceWorkers();
|
||||
window.addEventListener('beforeunload', () => {
|
||||
unregisterAllServiceWorkers();
|
||||
});
|
||||
|
||||
class TestBaseWorker {
|
||||
protected readonly ctsOptions: CTSOptions;
|
||||
protected readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>();
|
||||
|
||||
constructor(worker: CTSOptions['worker'], ctsOptions?: CTSOptions) {
|
||||
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker } };
|
||||
}
|
||||
|
||||
onmessage(ev: MessageEvent) {
|
||||
const query: string = ev.data.query;
|
||||
const result: TransferredTestCaseResult = ev.data.result;
|
||||
if (result.logs) {
|
||||
for (const l of result.logs) {
|
||||
Object.setPrototypeOf(l, LogMessageWithStack.prototype);
|
||||
}
|
||||
}
|
||||
this.resolvers.get(query)!(result as LiveTestCaseResult);
|
||||
this.resolvers.delete(query);
|
||||
|
||||
// MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and
|
||||
// update the entire results JSON somehow at some point).
|
||||
}
|
||||
|
||||
async makeRequestAndRecordResult(
|
||||
target: MessagePort | Worker | ServiceWorker,
|
||||
rec: TestCaseRecorder,
|
||||
query: string,
|
||||
expectations: TestQueryWithExpectation[]
|
||||
) {
|
||||
const request: WorkerTestRunRequest = {
|
||||
query,
|
||||
expectations,
|
||||
ctsOptions: this.ctsOptions,
|
||||
};
|
||||
target.postMessage(request);
|
||||
|
||||
const workerResult = await new Promise<LiveTestCaseResult>(resolve => {
|
||||
assert(!this.resolvers.has(query), "can't request same query twice simultaneously");
|
||||
this.resolvers.set(query, resolve);
|
||||
});
|
||||
rec.injectResult(workerResult);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestDedicatedWorker extends TestBaseWorker {
|
||||
private readonly worker: Worker;
|
||||
private readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>();
|
||||
|
||||
constructor(ctsOptions?: CTSOptions) {
|
||||
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'dedicated' } };
|
||||
super('dedicated', ctsOptions);
|
||||
const selfPath = import.meta.url;
|
||||
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
|
||||
const workerPath = selfPathDir + '/test_worker-worker.js';
|
||||
this.worker = new Worker(workerPath, { type: 'module' });
|
||||
this.worker.onmessage = ev => {
|
||||
const query: string = ev.data.query;
|
||||
const result: TransferredTestCaseResult = ev.data.result;
|
||||
if (result.logs) {
|
||||
for (const l of result.logs) {
|
||||
Object.setPrototypeOf(l, LogMessageWithStack.prototype);
|
||||
}
|
||||
}
|
||||
this.resolvers.get(query)!(result as LiveTestCaseResult);
|
||||
|
||||
// MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and
|
||||
// update the entire results JSON somehow at some point).
|
||||
};
|
||||
this.worker.onmessage = ev => this.onmessage(ev);
|
||||
}
|
||||
|
||||
async run(
|
||||
|
|
@ -36,46 +85,24 @@ export class TestDedicatedWorker {
|
|||
query: string,
|
||||
expectations: TestQueryWithExpectation[] = []
|
||||
): Promise<void> {
|
||||
this.worker.postMessage({
|
||||
query,
|
||||
expectations,
|
||||
ctsOptions: this.ctsOptions,
|
||||
});
|
||||
const workerResult = await new Promise<LiveTestCaseResult>(resolve => {
|
||||
this.resolvers.set(query, resolve);
|
||||
});
|
||||
rec.injectResult(workerResult);
|
||||
await this.makeRequestAndRecordResult(this.worker, rec, query, expectations);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestWorker extends TestDedicatedWorker {}
|
||||
|
||||
export class TestSharedWorker {
|
||||
private readonly ctsOptions: CTSOptions;
|
||||
export class TestSharedWorker extends TestBaseWorker {
|
||||
private readonly port: MessagePort;
|
||||
private readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>();
|
||||
|
||||
constructor(ctsOptions?: CTSOptions) {
|
||||
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'shared' } };
|
||||
super('shared', ctsOptions);
|
||||
const selfPath = import.meta.url;
|
||||
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
|
||||
const workerPath = selfPathDir + '/test_worker-worker.js';
|
||||
const worker = new SharedWorker(workerPath, { type: 'module' });
|
||||
this.port = worker.port;
|
||||
this.port.start();
|
||||
this.port.onmessage = ev => {
|
||||
const query: string = ev.data.query;
|
||||
const result: TransferredTestCaseResult = ev.data.result;
|
||||
if (result.logs) {
|
||||
for (const l of result.logs) {
|
||||
Object.setPrototypeOf(l, LogMessageWithStack.prototype);
|
||||
}
|
||||
}
|
||||
this.resolvers.get(query)!(result as LiveTestCaseResult);
|
||||
|
||||
// MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and
|
||||
// update the entire results JSON somehow at some point).
|
||||
};
|
||||
this.port.onmessage = ev => this.onmessage(ev);
|
||||
}
|
||||
|
||||
async run(
|
||||
|
|
@ -83,14 +110,39 @@ export class TestSharedWorker {
|
|||
query: string,
|
||||
expectations: TestQueryWithExpectation[] = []
|
||||
): Promise<void> {
|
||||
this.port.postMessage({
|
||||
query,
|
||||
expectations,
|
||||
ctsOptions: this.ctsOptions,
|
||||
});
|
||||
const workerResult = await new Promise<LiveTestCaseResult>(resolve => {
|
||||
this.resolvers.set(query, resolve);
|
||||
});
|
||||
rec.injectResult(workerResult);
|
||||
await this.makeRequestAndRecordResult(this.port, rec, query, expectations);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestServiceWorker extends TestBaseWorker {
|
||||
constructor(ctsOptions?: CTSOptions) {
|
||||
super('service', ctsOptions);
|
||||
}
|
||||
|
||||
async run(
|
||||
rec: TestCaseRecorder,
|
||||
query: string,
|
||||
expectations: TestQueryWithExpectation[] = []
|
||||
): Promise<void> {
|
||||
const [suite, name] = query.split(':', 2);
|
||||
const fileName = name.split(',').join('/');
|
||||
const serviceWorkerURL = new URL(
|
||||
`/out/${suite}/webworker/${fileName}.worker.js`,
|
||||
window.location.href
|
||||
).toString();
|
||||
|
||||
// If a registration already exists for this path, it will be ignored.
|
||||
const registration = await navigator.serviceWorker.register(serviceWorkerURL, {
|
||||
type: 'module',
|
||||
});
|
||||
// Make sure the registration we just requested is active. (We don't worry about it being
|
||||
// outdated from a previous page load, because we wipe all service workers on shutdown/startup.)
|
||||
while (!registration.active || registration.active.scriptURL !== serviceWorkerURL) {
|
||||
await new Promise(resolve => timeout(resolve, 0));
|
||||
}
|
||||
const serviceWorker = registration.active;
|
||||
|
||||
navigator.serviceWorker.onmessage = ev => this.onmessage(ev);
|
||||
await this.makeRequestAndRecordResult(serviceWorker, rec, query, expectations);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
src/common/runtime/helper/utils_worker.ts
Normal file
34
src/common/runtime/helper/utils_worker.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { globalTestConfig } from '../../framework/test_config.js';
|
||||
import { Logger } from '../../internal/logging/logger.js';
|
||||
import { TestQueryWithExpectation } from '../../internal/query/query.js';
|
||||
import { setDefaultRequestAdapterOptions } from '../../util/navigator_gpu.js';
|
||||
|
||||
import { CTSOptions } from './options.js';
|
||||
|
||||
export interface WorkerTestRunRequest {
|
||||
query: string;
|
||||
expectations: TestQueryWithExpectation[];
|
||||
ctsOptions: CTSOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config environment for workers with ctsOptions and return a Logger.
|
||||
*/
|
||||
export function setupWorkerEnvironment(ctsOptions: CTSOptions): Logger {
|
||||
const { debug, unrollConstEvalLoops, powerPreference, compatibility } = ctsOptions;
|
||||
globalTestConfig.unrollConstEvalLoops = unrollConstEvalLoops;
|
||||
globalTestConfig.compatibility = compatibility;
|
||||
|
||||
Logger.globalDebugMode = debug;
|
||||
const log = new Logger();
|
||||
|
||||
if (powerPreference || compatibility) {
|
||||
setDefaultRequestAdapterOptions({
|
||||
...(powerPreference && { powerPreference }),
|
||||
// MAINTENANCE_TODO: Change this to whatever the option ends up being
|
||||
...(compatibility && { compatibilityMode: true }),
|
||||
});
|
||||
}
|
||||
|
||||
return log;
|
||||
}
|
||||
43
src/common/runtime/helper/wrap_for_worker.ts
Normal file
43
src/common/runtime/helper/wrap_for_worker.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Fixture } from '../../framework/fixture';
|
||||
import { LogMessageWithStack } from '../../internal/logging/log_message.js';
|
||||
import { comparePaths, comparePublicParamsPaths, Ordering } from '../../internal/query/compare.js';
|
||||
import { parseQuery } from '../../internal/query/parseQuery.js';
|
||||
import { TestQuerySingleCase } from '../../internal/query/query.js';
|
||||
import { TestGroup } from '../../internal/test_group.js';
|
||||
import { assert } from '../../util/util.js';
|
||||
|
||||
import { setupWorkerEnvironment, WorkerTestRunRequest } from './utils_worker.js';
|
||||
|
||||
export function wrapTestGroupForWorker(g: TestGroup<Fixture>) {
|
||||
self.onmessage = async (ev: MessageEvent) => {
|
||||
const { query, expectations, ctsOptions } = ev.data as WorkerTestRunRequest;
|
||||
try {
|
||||
const log = setupWorkerEnvironment(ctsOptions);
|
||||
|
||||
const testQuery = parseQuery(query);
|
||||
assert(testQuery instanceof TestQuerySingleCase);
|
||||
let testcase = null;
|
||||
for (const t of g.iterate()) {
|
||||
if (comparePaths(t.testPath, testQuery.testPathParts) !== Ordering.Equal) {
|
||||
continue;
|
||||
}
|
||||
for (const c of t.iterate(testQuery.params)) {
|
||||
if (comparePublicParamsPaths(c.id.params, testQuery.params) === Ordering.Equal) {
|
||||
testcase = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(!!testcase, 'testcase not found');
|
||||
const [rec, result] = log.record(query);
|
||||
await testcase.run(rec, testQuery, expectations);
|
||||
|
||||
ev.source?.postMessage({ query, result });
|
||||
} catch (thrown) {
|
||||
const ex = thrown instanceof Error ? thrown : new Error(`${thrown}`);
|
||||
ev.source?.postMessage({
|
||||
query,
|
||||
result: { status: 'fail', timems: 0, logs: [new LogMessageWithStack('INTERNAL', ex)] },
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import {
|
|||
OptionsInfos,
|
||||
camelCaseToSnakeCase,
|
||||
} from './helper/options.js';
|
||||
import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js';
|
||||
import { TestDedicatedWorker, TestSharedWorker, TestServiceWorker } from './helper/test_worker.js';
|
||||
|
||||
const rootQuerySpec = 'webgpu:*';
|
||||
let promptBeforeReload = false;
|
||||
|
|
@ -66,6 +66,7 @@ setBaseResourcePath('../out/resources');
|
|||
const dedicatedWorker =
|
||||
options.worker === 'dedicated' ? new TestDedicatedWorker(options) : undefined;
|
||||
const sharedWorker = options.worker === 'shared' ? new TestSharedWorker(options) : undefined;
|
||||
const serviceWorker = options.worker === 'service' ? new TestServiceWorker(options) : undefined;
|
||||
|
||||
const autoCloseOnPass = document.getElementById('autoCloseOnPass') as HTMLInputElement;
|
||||
const resultsVis = document.getElementById('resultsVis')!;
|
||||
|
|
@ -182,6 +183,8 @@ function makeCaseHTML(t: TestTreeLeaf): VisualizedSubtree {
|
|||
await dedicatedWorker.run(rec, name);
|
||||
} else if (sharedWorker) {
|
||||
await sharedWorker.run(rec, name);
|
||||
} else if (serviceWorker) {
|
||||
await serviceWorker.run(rec, name);
|
||||
} else {
|
||||
await t.run(rec);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { parseExpectationsForTestQuery, relativeQueryString } from '../internal/
|
|||
import { assert } from '../util/util.js';
|
||||
|
||||
import { optionEnabled, optionString } from './helper/options.js';
|
||||
import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js';
|
||||
import { TestDedicatedWorker, TestServiceWorker, TestSharedWorker } from './helper/test_worker.js';
|
||||
|
||||
// testharness.js API (https://web-platform-tests.org/writing-tests/testharness-api.html)
|
||||
declare interface WptTestObject {
|
||||
|
|
@ -34,6 +34,7 @@ void (async () => {
|
|||
const workerString = optionString('worker');
|
||||
const dedicatedWorker = workerString === 'dedicated' ? new TestDedicatedWorker() : undefined;
|
||||
const sharedWorker = workerString === 'shared' ? new TestSharedWorker() : undefined;
|
||||
const serviceWorker = workerString === 'service' ? new TestServiceWorker() : undefined;
|
||||
|
||||
globalTestConfig.unrollConstEvalLoops = optionEnabled('unroll_const_eval_loops');
|
||||
|
||||
|
|
@ -68,6 +69,8 @@ void (async () => {
|
|||
await dedicatedWorker.run(rec, name, expectations);
|
||||
} else if (sharedWorker) {
|
||||
await sharedWorker.run(rec, name, expectations);
|
||||
} else if (serviceWorker) {
|
||||
await serviceWorker.run(rec, name, expectations);
|
||||
} else {
|
||||
await testcase.run(rec, expectations);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,19 @@ app.get('/out/:suite([a-zA-Z0-9_-]+)/listing.js', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
// Serve .worker.js files by generating the necessary wrapper.
|
||||
app.get('/out/:suite([a-zA-Z0-9_-]+)/webworker/:filepath(*).worker.js', (req, res, next) => {
|
||||
const { suite, filepath } = req.params;
|
||||
const result = `\
|
||||
import { g } from '/out/${suite}/${filepath}.spec.js';
|
||||
import { wrapTestGroupForWorker } from '/out/common/runtime/helper/wrap_for_worker.js';
|
||||
|
||||
wrapTestGroupForWorker(g);
|
||||
`;
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
res.send(result);
|
||||
});
|
||||
|
||||
// Serve all other .js files by fetching the source .ts file and compiling it.
|
||||
app.get('/out/**/*.js', async (req, res, next) => {
|
||||
const jsUrl = path.relative('/out', req.url);
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as process from 'process';
|
||||
|
||||
import { crawl } from './crawl.js';
|
||||
|
||||
function usage(rc: number): void {
|
||||
console.error(`Usage: tools/gen_listings [options] [OUT_DIR] [SUITE_DIRS...]
|
||||
|
||||
For each suite in SUITE_DIRS, generate listings and write each listing.js
|
||||
into OUT_DIR/{suite}/listing.js. Example:
|
||||
tools/gen_listings gen/ src/unittests/ src/webgpu/
|
||||
|
||||
Options:
|
||||
--help Print this message and exit.
|
||||
`);
|
||||
process.exit(rc);
|
||||
}
|
||||
|
||||
const argv = process.argv;
|
||||
if (argv.indexOf('--help') !== -1) {
|
||||
usage(0);
|
||||
}
|
||||
|
||||
{
|
||||
// Ignore old argument that is now the default
|
||||
const i = argv.indexOf('--no-validate');
|
||||
if (i !== -1) {
|
||||
argv.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.length < 4) {
|
||||
usage(0);
|
||||
}
|
||||
|
||||
const myself = 'src/common/tools/gen_listings.ts';
|
||||
|
||||
const outDir = argv[2];
|
||||
|
||||
for (const suiteDir of argv.slice(3)) {
|
||||
// Run concurrently for each suite (might be a tiny bit more efficient)
|
||||
void crawl(suiteDir, false).then(listing => {
|
||||
const suite = path.basename(suiteDir);
|
||||
const outFile = path.normalize(path.join(outDir, `${suite}/listing.js`));
|
||||
fs.mkdirSync(path.join(outDir, suite), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
outFile,
|
||||
`\
|
||||
// AUTO-GENERATED - DO NOT EDIT. See ${myself}.
|
||||
|
||||
export const listing = ${JSON.stringify(listing, undefined, 2)};
|
||||
`
|
||||
);
|
||||
});
|
||||
}
|
||||
90
src/common/tools/gen_listings_and_webworkers.ts
Normal file
90
src/common/tools/gen_listings_and_webworkers.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as process from 'process';
|
||||
|
||||
import { crawl } from './crawl.js';
|
||||
|
||||
function usage(rc: number): void {
|
||||
console.error(`Usage: tools/gen_listings_and_webworkers [options] [OUT_DIR] [SUITE_DIRS...]
|
||||
|
||||
For each suite in SUITE_DIRS, generate listings into OUT_DIR/{suite}/listing.js,
|
||||
and generate Web Worker proxies in OUT_DIR/{suite}/webworker/**/*.worker.js for
|
||||
every .spec.js file. (Note {suite}/webworker/ is reserved for this purpose.)
|
||||
|
||||
Example:
|
||||
tools/gen_listings_and_webworkers gen/ src/unittests/ src/webgpu/
|
||||
|
||||
Options:
|
||||
--help Print this message and exit.
|
||||
`);
|
||||
process.exit(rc);
|
||||
}
|
||||
|
||||
const argv = process.argv;
|
||||
if (argv.indexOf('--help') !== -1) {
|
||||
usage(0);
|
||||
}
|
||||
|
||||
{
|
||||
// Ignore old argument that is now the default
|
||||
const i = argv.indexOf('--no-validate');
|
||||
if (i !== -1) {
|
||||
argv.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.length < 4) {
|
||||
usage(0);
|
||||
}
|
||||
|
||||
const myself = 'src/common/tools/gen_listings_and_webworkers.ts';
|
||||
|
||||
const outDir = argv[2];
|
||||
|
||||
for (const suiteDir of argv.slice(3)) {
|
||||
// Run concurrently for each suite (might be a tiny bit more efficient)
|
||||
void crawl(suiteDir, false).then(listing => {
|
||||
const suite = path.basename(suiteDir);
|
||||
|
||||
// Write listing.js
|
||||
const outFile = path.normalize(path.join(outDir, `${suite}/listing.js`));
|
||||
fs.mkdirSync(path.join(outDir, suite), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
outFile,
|
||||
`\
|
||||
// AUTO-GENERATED - DO NOT EDIT. See ${myself}.
|
||||
|
||||
export const listing = ${JSON.stringify(listing, undefined, 2)};
|
||||
`
|
||||
);
|
||||
|
||||
// Write suite/webworker/**/*.worker.js
|
||||
for (const entry of listing) {
|
||||
if ('readme' in entry) continue;
|
||||
|
||||
const outFileDir = path.join(
|
||||
outDir,
|
||||
suite,
|
||||
'webworker',
|
||||
...entry.file.slice(0, entry.file.length - 1)
|
||||
);
|
||||
const outFile = path.join(outDir, suite, 'webworker', ...entry.file) + '.worker.js';
|
||||
|
||||
const relPathToSuiteRoot = Array<string>(entry.file.length).fill('..').join('/');
|
||||
|
||||
fs.mkdirSync(outFileDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
outFile,
|
||||
`\
|
||||
// AUTO-GENERATED - DO NOT EDIT. See ${myself}.
|
||||
|
||||
// g is a TestGroup<Fixture> object (defined in common/internal/test_group.ts).
|
||||
import { g } from '${relPathToSuiteRoot}/${entry.file.join('/')}.spec.js';
|
||||
import { wrapTestGroupForWorker } from '${relPathToSuiteRoot}/../common/runtime/helper/wrap_for_worker.js';
|
||||
|
||||
wrapTestGroupForWorker(g);
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
2
src/demo/webworker/.gitignore
vendored
Normal file
2
src/demo/webworker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers.
|
||||
*
|
||||
2
src/manual/webworker/.gitignore
vendored
Normal file
2
src/manual/webworker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers.
|
||||
*
|
||||
216
src/resources/cache/hashes.json
vendored
216
src/resources/cache/hashes.json
vendored
|
|
@ -1,110 +1,110 @@
|
|||
{
|
||||
"webgpu/shader/execution/binary/af_addition.bin": "b6797474",
|
||||
"webgpu/shader/execution/binary/af_logical.bin": "ff27f50f",
|
||||
"webgpu/shader/execution/binary/af_division.bin": "97ba2e6d",
|
||||
"webgpu/shader/execution/binary/af_matrix_addition.bin": "842a508f",
|
||||
"webgpu/shader/execution/binary/af_matrix_subtraction.bin": "f086bb00",
|
||||
"webgpu/shader/execution/binary/af_multiplication.bin": "fb6d6475",
|
||||
"webgpu/shader/execution/binary/af_remainder.bin": "aa462ca7",
|
||||
"webgpu/shader/execution/binary/af_subtraction.bin": "f04fbb32",
|
||||
"webgpu/shader/execution/binary/f16_addition.bin": "3f136f5a",
|
||||
"webgpu/shader/execution/binary/f16_logical.bin": "8556deb1",
|
||||
"webgpu/shader/execution/binary/f16_division.bin": "693df765",
|
||||
"webgpu/shader/execution/binary/f16_matrix_addition.bin": "296a11b6",
|
||||
"webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cf1ca531",
|
||||
"webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d3a317bd",
|
||||
"webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "a7a1b148",
|
||||
"webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "8f540eee",
|
||||
"webgpu/shader/execution/binary/f16_multiplication.bin": "1a7bca1c",
|
||||
"webgpu/shader/execution/binary/f16_remainder.bin": "62b7bff8",
|
||||
"webgpu/shader/execution/binary/f16_subtraction.bin": "c921e09d",
|
||||
"webgpu/shader/execution/binary/f32_addition.bin": "7d7629bd",
|
||||
"webgpu/shader/execution/binary/f32_logical.bin": "3e903bfb",
|
||||
"webgpu/shader/execution/binary/f32_division.bin": "35b49326",
|
||||
"webgpu/shader/execution/binary/f32_matrix_addition.bin": "1c2aff7c",
|
||||
"webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "966b264e",
|
||||
"webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "4ab99310",
|
||||
"webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "77f37d5a",
|
||||
"webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "50dfea9a",
|
||||
"webgpu/shader/execution/binary/f32_multiplication.bin": "2524db25",
|
||||
"webgpu/shader/execution/binary/f32_remainder.bin": "b47ad113",
|
||||
"webgpu/shader/execution/binary/f32_subtraction.bin": "e98584ae",
|
||||
"webgpu/shader/execution/binary/i32_arithmetic.bin": "88d15602",
|
||||
"webgpu/shader/execution/binary/i32_comparison.bin": "ddd815a3",
|
||||
"webgpu/shader/execution/binary/u32_arithmetic.bin": "d882b7f6",
|
||||
"webgpu/shader/execution/binary/u32_comparison.bin": "e3dfeec7",
|
||||
"webgpu/shader/execution/abs.bin": "b6f6d4ec",
|
||||
"webgpu/shader/execution/acos.bin": "d681fe45",
|
||||
"webgpu/shader/execution/acosh.bin": "a58482ca",
|
||||
"webgpu/shader/execution/asin.bin": "3856670",
|
||||
"webgpu/shader/execution/asinh.bin": "7567d4b7",
|
||||
"webgpu/shader/execution/atan.bin": "80fc2c53",
|
||||
"webgpu/shader/execution/atan2.bin": "2fc91f76",
|
||||
"webgpu/shader/execution/atanh.bin": "b68b4e98",
|
||||
"webgpu/shader/execution/bitcast.bin": "142a34e",
|
||||
"webgpu/shader/execution/ceil.bin": "65c551a0",
|
||||
"webgpu/shader/execution/clamp.bin": "369a3396",
|
||||
"webgpu/shader/execution/cos.bin": "894e3d79",
|
||||
"webgpu/shader/execution/cosh.bin": "3bf810f1",
|
||||
"webgpu/shader/execution/cross.bin": "582e87e8",
|
||||
"webgpu/shader/execution/degrees.bin": "762053b5",
|
||||
"webgpu/shader/execution/determinant.bin": "a60ac4f9",
|
||||
"webgpu/shader/execution/distance.bin": "602fc718",
|
||||
"webgpu/shader/execution/dot.bin": "c33454b1",
|
||||
"webgpu/shader/execution/exp.bin": "ad6670cf",
|
||||
"webgpu/shader/execution/exp2.bin": "6f8a0b39",
|
||||
"webgpu/shader/execution/faceForward.bin": "759ef028",
|
||||
"webgpu/shader/execution/floor.bin": "d8f91dbe",
|
||||
"webgpu/shader/execution/fma.bin": "5019731d",
|
||||
"webgpu/shader/execution/fract.bin": "3a9c858d",
|
||||
"webgpu/shader/execution/frexp.bin": "33ffb585",
|
||||
"webgpu/shader/execution/inverseSqrt.bin": "3684c8a",
|
||||
"webgpu/shader/execution/ldexp.bin": "30881102",
|
||||
"webgpu/shader/execution/length.bin": "fed952",
|
||||
"webgpu/shader/execution/log.bin": "b55f7cd0",
|
||||
"webgpu/shader/execution/log2.bin": "e4fddf41",
|
||||
"webgpu/shader/execution/max.bin": "ca77aba5",
|
||||
"webgpu/shader/execution/min.bin": "6b57ac82",
|
||||
"webgpu/shader/execution/mix.bin": "89f42f5a",
|
||||
"webgpu/shader/execution/modf.bin": "77eb4ff5",
|
||||
"webgpu/shader/execution/normalize.bin": "36214034",
|
||||
"webgpu/shader/execution/pack2x16float.bin": "8bee7573",
|
||||
"webgpu/shader/execution/pow.bin": "8bcaf788",
|
||||
"webgpu/shader/execution/quantizeToF16.bin": "8c1bc956",
|
||||
"webgpu/shader/execution/radians.bin": "c35eecd0",
|
||||
"webgpu/shader/execution/reflect.bin": "3591dbf7",
|
||||
"webgpu/shader/execution/refract.bin": "8b2a061d",
|
||||
"webgpu/shader/execution/round.bin": "58787d04",
|
||||
"webgpu/shader/execution/saturate.bin": "b5c39be6",
|
||||
"webgpu/shader/execution/sign.bin": "3b6e8c88",
|
||||
"webgpu/shader/execution/sin.bin": "1e39b6f9",
|
||||
"webgpu/shader/execution/sinh.bin": "296767d4",
|
||||
"webgpu/shader/execution/smoothstep.bin": "2771eb6a",
|
||||
"webgpu/shader/execution/sqrt.bin": "78c4534c",
|
||||
"webgpu/shader/execution/step.bin": "b8817ec0",
|
||||
"webgpu/shader/execution/tan.bin": "f2537ac4",
|
||||
"webgpu/shader/execution/tanh.bin": "c611a910",
|
||||
"webgpu/shader/execution/transpose.bin": "7fdac41d",
|
||||
"webgpu/shader/execution/trunc.bin": "da4434eb",
|
||||
"webgpu/shader/execution/unpack2x16float.bin": "a5db67bb",
|
||||
"webgpu/shader/execution/unpack2x16snorm.bin": "b0e6f0f4",
|
||||
"webgpu/shader/execution/unpack2x16unorm.bin": "ddf30c4f",
|
||||
"webgpu/shader/execution/unpack4x8snorm.bin": "599b3fb9",
|
||||
"webgpu/shader/execution/unpack4x8unorm.bin": "d47c58be",
|
||||
"webgpu/shader/execution/unary/af_arithmetic.bin": "790d7c95",
|
||||
"webgpu/shader/execution/unary/af_assignment.bin": "679cfa9a",
|
||||
"webgpu/shader/execution/unary/bool_conversion.bin": "d10f1dbb",
|
||||
"webgpu/shader/execution/unary/f16_arithmetic.bin": "1b44c7f",
|
||||
"webgpu/shader/execution/unary/f16_conversion.bin": "436a5636",
|
||||
"webgpu/shader/execution/unary/f32_arithmetic.bin": "8f8fcd42",
|
||||
"webgpu/shader/execution/unary/f32_conversion.bin": "97d0e21a",
|
||||
"webgpu/shader/execution/unary/i32_arithmetic.bin": "41460aa",
|
||||
"webgpu/shader/execution/unary/i32_conversion.bin": "d5a46ee6",
|
||||
"webgpu/shader/execution/unary/u32_conversion.bin": "f03edff7",
|
||||
"webgpu/shader/execution/unary/ai_assignment.bin": "5d612a40",
|
||||
"webgpu/shader/execution/binary/ai_arithmetic.bin": "c0dd04f9",
|
||||
"webgpu/shader/execution/unary/ai_arithmetic.bin": "3ea222b8",
|
||||
"webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "3d9cf9b7",
|
||||
"webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "a84ecc73",
|
||||
"webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "6fe3e741"
|
||||
"webgpu/shader/execution/binary/af_addition.bin": "1043cbb2",
|
||||
"webgpu/shader/execution/binary/af_logical.bin": "9ab41597",
|
||||
"webgpu/shader/execution/binary/af_division.bin": "9ec22a4e",
|
||||
"webgpu/shader/execution/binary/af_matrix_addition.bin": "1da2ca22",
|
||||
"webgpu/shader/execution/binary/af_matrix_subtraction.bin": "25303b8e",
|
||||
"webgpu/shader/execution/binary/af_multiplication.bin": "70d8f01a",
|
||||
"webgpu/shader/execution/binary/af_remainder.bin": "101c7835",
|
||||
"webgpu/shader/execution/binary/af_subtraction.bin": "26791651",
|
||||
"webgpu/shader/execution/binary/f16_addition.bin": "2d67db8f",
|
||||
"webgpu/shader/execution/binary/f16_logical.bin": "80f9e754",
|
||||
"webgpu/shader/execution/binary/f16_division.bin": "53e3bdaa",
|
||||
"webgpu/shader/execution/binary/f16_matrix_addition.bin": "911d18f7",
|
||||
"webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "a306da0a",
|
||||
"webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "be77024a",
|
||||
"webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "ea813df5",
|
||||
"webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "1692bec1",
|
||||
"webgpu/shader/execution/binary/f16_multiplication.bin": "7c618e51",
|
||||
"webgpu/shader/execution/binary/f16_remainder.bin": "9de2be15",
|
||||
"webgpu/shader/execution/binary/f16_subtraction.bin": "c77bff8a",
|
||||
"webgpu/shader/execution/binary/f32_addition.bin": "d4282b93",
|
||||
"webgpu/shader/execution/binary/f32_logical.bin": "47467130",
|
||||
"webgpu/shader/execution/binary/f32_division.bin": "4d79a5f6",
|
||||
"webgpu/shader/execution/binary/f32_matrix_addition.bin": "5972ccf0",
|
||||
"webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "8bc3f04a",
|
||||
"webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "7376da03",
|
||||
"webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c4785f3a",
|
||||
"webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "20d4b1",
|
||||
"webgpu/shader/execution/binary/f32_multiplication.bin": "7586ed6d",
|
||||
"webgpu/shader/execution/binary/f32_remainder.bin": "59f46306",
|
||||
"webgpu/shader/execution/binary/f32_subtraction.bin": "4311ba10",
|
||||
"webgpu/shader/execution/binary/i32_arithmetic.bin": "d345f9c7",
|
||||
"webgpu/shader/execution/binary/i32_comparison.bin": "6d4ae7e0",
|
||||
"webgpu/shader/execution/binary/u32_arithmetic.bin": "163f8a38",
|
||||
"webgpu/shader/execution/binary/u32_comparison.bin": "f6c1497b",
|
||||
"webgpu/shader/execution/abs.bin": "63fa988e",
|
||||
"webgpu/shader/execution/acos.bin": "47521b3e",
|
||||
"webgpu/shader/execution/acosh.bin": "59762b6f",
|
||||
"webgpu/shader/execution/asin.bin": "db0c6641",
|
||||
"webgpu/shader/execution/asinh.bin": "f0329d7f",
|
||||
"webgpu/shader/execution/atan.bin": "f6fea79e",
|
||||
"webgpu/shader/execution/atan2.bin": "1c390d0f",
|
||||
"webgpu/shader/execution/atanh.bin": "4c430905",
|
||||
"webgpu/shader/execution/bitcast.bin": "2dfc8f9",
|
||||
"webgpu/shader/execution/ceil.bin": "3319384e",
|
||||
"webgpu/shader/execution/clamp.bin": "4873dd79",
|
||||
"webgpu/shader/execution/cos.bin": "c16d181a",
|
||||
"webgpu/shader/execution/cosh.bin": "137f4997",
|
||||
"webgpu/shader/execution/cross.bin": "aebda306",
|
||||
"webgpu/shader/execution/degrees.bin": "5001b340",
|
||||
"webgpu/shader/execution/determinant.bin": "c461c715",
|
||||
"webgpu/shader/execution/distance.bin": "d05e2f8d",
|
||||
"webgpu/shader/execution/dot.bin": "7f8b7e13",
|
||||
"webgpu/shader/execution/exp.bin": "d448dc91",
|
||||
"webgpu/shader/execution/exp2.bin": "1d232c0e",
|
||||
"webgpu/shader/execution/faceForward.bin": "87c0576c",
|
||||
"webgpu/shader/execution/floor.bin": "c3a60f36",
|
||||
"webgpu/shader/execution/fma.bin": "6b94384d",
|
||||
"webgpu/shader/execution/fract.bin": "d49c3d50",
|
||||
"webgpu/shader/execution/frexp.bin": "252e0615",
|
||||
"webgpu/shader/execution/inverseSqrt.bin": "1deb1185",
|
||||
"webgpu/shader/execution/ldexp.bin": "617e4cc4",
|
||||
"webgpu/shader/execution/length.bin": "f7b51c96",
|
||||
"webgpu/shader/execution/log.bin": "ae124706",
|
||||
"webgpu/shader/execution/log2.bin": "c428e9f2",
|
||||
"webgpu/shader/execution/max.bin": "a45914cd",
|
||||
"webgpu/shader/execution/min.bin": "1017e86d",
|
||||
"webgpu/shader/execution/mix.bin": "3f5ebb6e",
|
||||
"webgpu/shader/execution/modf.bin": "fedc0c50",
|
||||
"webgpu/shader/execution/normalize.bin": "60173a1e",
|
||||
"webgpu/shader/execution/pack2x16float.bin": "e472a927",
|
||||
"webgpu/shader/execution/pow.bin": "c5a2a9c1",
|
||||
"webgpu/shader/execution/quantizeToF16.bin": "8d1cb7a6",
|
||||
"webgpu/shader/execution/radians.bin": "94cbdf6c",
|
||||
"webgpu/shader/execution/reflect.bin": "39a479a0",
|
||||
"webgpu/shader/execution/refract.bin": "63e5b11a",
|
||||
"webgpu/shader/execution/round.bin": "13bf324",
|
||||
"webgpu/shader/execution/saturate.bin": "c368b1a7",
|
||||
"webgpu/shader/execution/sign.bin": "5cd05b5c",
|
||||
"webgpu/shader/execution/sin.bin": "85679e",
|
||||
"webgpu/shader/execution/sinh.bin": "eaa61750",
|
||||
"webgpu/shader/execution/smoothstep.bin": "bd06d7cc",
|
||||
"webgpu/shader/execution/sqrt.bin": "997f5572",
|
||||
"webgpu/shader/execution/step.bin": "87505049",
|
||||
"webgpu/shader/execution/tan.bin": "9d7ca121",
|
||||
"webgpu/shader/execution/tanh.bin": "8bfa913c",
|
||||
"webgpu/shader/execution/transpose.bin": "7f8fa4c6",
|
||||
"webgpu/shader/execution/trunc.bin": "9a054523",
|
||||
"webgpu/shader/execution/unpack2x16float.bin": "ed71c71d",
|
||||
"webgpu/shader/execution/unpack2x16snorm.bin": "89b4cd2d",
|
||||
"webgpu/shader/execution/unpack2x16unorm.bin": "df3b3851",
|
||||
"webgpu/shader/execution/unpack4x8snorm.bin": "aa10073",
|
||||
"webgpu/shader/execution/unpack4x8unorm.bin": "2fe14b98",
|
||||
"webgpu/shader/execution/unary/af_arithmetic.bin": "accfc1bd",
|
||||
"webgpu/shader/execution/unary/af_assignment.bin": "cb3e120e",
|
||||
"webgpu/shader/execution/unary/bool_conversion.bin": "d0c1e5a3",
|
||||
"webgpu/shader/execution/unary/f16_arithmetic.bin": "310a05dd",
|
||||
"webgpu/shader/execution/unary/f16_conversion.bin": "9d94aa72",
|
||||
"webgpu/shader/execution/unary/f32_arithmetic.bin": "fe955d67",
|
||||
"webgpu/shader/execution/unary/f32_conversion.bin": "918f6cb3",
|
||||
"webgpu/shader/execution/unary/i32_arithmetic.bin": "a8649cbb",
|
||||
"webgpu/shader/execution/unary/i32_conversion.bin": "e5157a69",
|
||||
"webgpu/shader/execution/unary/u32_conversion.bin": "d07d0c20",
|
||||
"webgpu/shader/execution/unary/ai_assignment.bin": "f62c765c",
|
||||
"webgpu/shader/execution/binary/ai_arithmetic.bin": "43501242",
|
||||
"webgpu/shader/execution/unary/ai_arithmetic.bin": "8e448c53",
|
||||
"webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7ea0df71",
|
||||
"webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "4919c460",
|
||||
"webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "33c10dad"
|
||||
}
|
||||
2
src/stress/webworker/.gitignore
vendored
Normal file
2
src/stress/webworker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers.
|
||||
*
|
||||
2
src/unittests/webworker/.gitignore
vendored
Normal file
2
src/unittests/webworker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers.
|
||||
*
|
||||
|
|
@ -2219,6 +2219,7 @@
|
|||
"webgpu:web_platform,external_texture,video:importExternalTexture,sampleWithVideoFrameWithVisibleRectParam:*": { "subcaseMS": 29.160 },
|
||||
"webgpu:web_platform,external_texture,video:importExternalTexture,sample_non_YUV_video_frame:*": { "subcaseMS": 36.270 },
|
||||
"webgpu:web_platform,worker,worker:dedicated_worker:*": { "subcaseMS": 245.901 },
|
||||
"webgpu:web_platform,worker,worker:service_worker:*": { "subcaseMS": 102.800 },
|
||||
"webgpu:web_platform,worker,worker:shared_worker:*": { "subcaseMS": 26.801 },
|
||||
"_end": ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ Tests WebGPU is available in a dedicated worker and a shared worker.
|
|||
Note: The CTS test can be run respectively in a dedicated worker and a shared worker by
|
||||
passing in worker=dedicated and worker=shared as a query parameter. These tests
|
||||
are specifically to check that WebGPU is available in a dedicated worker and a shared worker.
|
||||
|
||||
TODO[2]: Figure out how to make these tests run in service workers (not actually
|
||||
important unless service workers gain the ability to launch other workers).
|
||||
`;
|
||||
|
||||
import { Fixture } from '../../../common/framework/fixture.js';
|
||||
|
|
@ -12,24 +15,26 @@ import { assert } from '../../../common/util/util.js';
|
|||
|
||||
export const g = makeTestGroup(Fixture);
|
||||
|
||||
function isNode(): boolean {
|
||||
return typeof process !== 'undefined' && process?.versions?.node !== undefined;
|
||||
}
|
||||
const isNode = typeof process !== 'undefined' && process?.versions?.node !== undefined;
|
||||
|
||||
// [1]: we load worker_launcher dynamically because ts-node support
|
||||
// is using commonjs which doesn't support import.meta. Further,
|
||||
// we need to put the url in a string and pass the string to import
|
||||
// otherwise typescript tries to parse the file which again, fails.
|
||||
// worker_launcher.js is excluded in node.tsconfig.json.
|
||||
|
||||
// [2]: That hack does not work in Service Workers.
|
||||
const isServiceWorker = globalThis.constructor.name === 'ServiceWorkerGlobalScope';
|
||||
|
||||
g.test('dedicated_worker')
|
||||
.desc(`test WebGPU is available in dedicated workers and check for basic functionality`)
|
||||
.fn(async t => {
|
||||
if (isNode()) {
|
||||
t.skip('node does not support 100% compatible workers');
|
||||
return;
|
||||
}
|
||||
// Note: we load worker_launcher dynamically because ts-node support
|
||||
// is using commonjs which doesn't support import.meta. Further,
|
||||
// we need to put the url in a string add pass the string to import
|
||||
// otherwise typescript tries to parse the file which again, fails.
|
||||
// worker_launcher.js is excluded in node.tsconfig.json.
|
||||
t.skipIf(isNode, 'node does not support 100% compatible workers');
|
||||
|
||||
t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2]
|
||||
const url = './worker_launcher.js';
|
||||
const { launchDedicatedWorker } = await import(url);
|
||||
const { launchDedicatedWorker } = await import(url); // [1]
|
||||
|
||||
const result = await launchDedicatedWorker();
|
||||
assert(result.error === undefined, `should be no error from worker but was: ${result.error}`);
|
||||
});
|
||||
|
|
@ -37,17 +42,25 @@ g.test('dedicated_worker')
|
|||
g.test('shared_worker')
|
||||
.desc(`test WebGPU is available in shared workers and check for basic functionality`)
|
||||
.fn(async t => {
|
||||
if (isNode()) {
|
||||
t.skip('node does not support 100% compatible workers');
|
||||
return;
|
||||
}
|
||||
// Note: we load worker_launcher dynamically because ts-node support
|
||||
// is using commonjs which doesn't support import.meta. Further,
|
||||
// we need to put the url in a string add pass the string to import
|
||||
// otherwise typescript tries to parse the file which again, fails.
|
||||
// worker_launcher.js is excluded in node.tsconfig.json.
|
||||
t.skipIf(isNode, 'node does not support 100% compatible workers');
|
||||
|
||||
t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2]
|
||||
const url = './worker_launcher.js';
|
||||
const { launchSharedWorker } = await import(url);
|
||||
const { launchSharedWorker } = await import(url); // [1]
|
||||
|
||||
const result = await launchSharedWorker();
|
||||
assert(result.error === undefined, `should be no error from worker but was: ${result.error}`);
|
||||
});
|
||||
|
||||
g.test('service_worker')
|
||||
.desc(`test WebGPU is available in service workers and check for basic functionality`)
|
||||
.fn(async t => {
|
||||
t.skipIf(isNode, 'node does not support 100% compatible workers');
|
||||
|
||||
t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2]
|
||||
const url = './worker_launcher.js';
|
||||
const { launchServiceWorker } = await import(url); // [1]
|
||||
|
||||
const result = await launchServiceWorker();
|
||||
assert(result.error === undefined, `should be no error from worker but was: ${result.error}`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) {
|
|||
}
|
||||
|
||||
self.onmessage = (ev: MessageEvent) => {
|
||||
void reportTestResults.call(self, ev);
|
||||
void reportTestResults.call(ev.source || self, ev);
|
||||
};
|
||||
|
||||
self.onconnect = (event: MessageEvent) => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { SkipTestCase } from '../../../common/framework/fixture.js';
|
||||
import { getDefaultRequestAdapterOptions } from '../../../common/util/navigator_gpu.js';
|
||||
|
||||
export type TestResult = {
|
||||
|
|
@ -5,6 +6,10 @@ export type TestResult = {
|
|||
};
|
||||
|
||||
export async function launchDedicatedWorker() {
|
||||
if (typeof Worker === 'undefined') {
|
||||
throw new SkipTestCase(`Worker undefined in context ${globalThis.constructor.name}`);
|
||||
}
|
||||
|
||||
const selfPath = import.meta.url;
|
||||
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
|
||||
const workerPath = selfPathDir + '/worker.js';
|
||||
|
|
@ -18,6 +23,10 @@ export async function launchDedicatedWorker() {
|
|||
}
|
||||
|
||||
export async function launchSharedWorker() {
|
||||
if (typeof SharedWorker === 'undefined') {
|
||||
throw new SkipTestCase(`SharedWorker undefined in context ${globalThis.constructor.name}`);
|
||||
}
|
||||
|
||||
const selfPath = import.meta.url;
|
||||
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
|
||||
const workerPath = selfPathDir + '/worker.js';
|
||||
|
|
@ -33,3 +42,34 @@ export async function launchSharedWorker() {
|
|||
});
|
||||
return await promise;
|
||||
}
|
||||
|
||||
export async function launchServiceWorker() {
|
||||
if (typeof navigator === 'undefined' || typeof navigator.serviceWorker === 'undefined') {
|
||||
throw new SkipTestCase(
|
||||
`navigator.serviceWorker undefined in context ${globalThis.constructor.name}`
|
||||
);
|
||||
}
|
||||
|
||||
const selfPath = import.meta.url;
|
||||
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
|
||||
const serviceWorkerPath = selfPathDir + '/worker.js';
|
||||
const registration = await navigator.serviceWorker.register(serviceWorkerPath, {
|
||||
type: 'module',
|
||||
});
|
||||
await registration.update();
|
||||
|
||||
const promise = new Promise<TestResult>(resolve => {
|
||||
navigator.serviceWorker.addEventListener(
|
||||
'message',
|
||||
ev => {
|
||||
resolve(ev.data as TestResult);
|
||||
void registration.unregister();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
registration.active?.postMessage({
|
||||
defaultRequestAdapterOptions: getDefaultRequestAdapterOptions(),
|
||||
});
|
||||
return await promise;
|
||||
}
|
||||
|
|
|
|||
2
src/webgpu/webworker/.gitignore
vendored
Normal file
2
src/webgpu/webworker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers.
|
||||
*
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Crawl a suite directory (e.g. src/webgpu/) to generate a listing.js containing
|
||||
// the listing of test files in the suite.
|
||||
|
||||
require('../src/common/tools/setup-ts-in-node.js');
|
||||
require('../src/common/tools/gen_listings.ts');
|
||||
8
tools/gen_listings_and_webworkers
Executable file
8
tools/gen_listings_and_webworkers
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Crawl a suite directory (e.g. src/webgpu/) to generate a listing.js containing
|
||||
// the listing of test files in the suite, and some generated test files
|
||||
// (like suite/serviceworker/**/*.spec.js).
|
||||
|
||||
require('../src/common/tools/setup-ts-in-node.js');
|
||||
require('../src/common/tools/gen_listings_and_webworkers.ts');
|
||||
Loading…
Reference in a new issue